# Programming the AVR Timers¶

Read time: 26 minutes (6668 words)

Warning

The avr-gcc tools have changed since I originally set up this course, and some older code no longer works. The current inker insists on adding code we do not need, but figuring out how to stop that is proving difficult. This lecture was originally pure assembly language, but is now a mix of “C” and AVR Assembly.

Let’s explore some “C” code and see if we can create a simple signal that can drive a Piezo buzzer.

## The Piezo Buzzer¶

• Here is the buzzer we will use
• a crystal inside of this flexes when a voltage is applied across the leads
• when it flexes, it pushes air out of the way.
• Flex it fast enough and those air pulses hit your ear making noise
• You control the tone by the pulse rate

## Buzzer Code¶

• Here is the start of our code
// Example Arduino code to sound a Piezo buzzer

#include <avr/io.h>
#include <util/delay.h>

#define CLOCK_PRESCALE(n)   (CLKPR = 0x80, CLKPR = (n))
#define BUZZER_CONFIG       (DDRB |= (1<<4))
#define BUZZ_ON             (PORTB |= (1<<4))
#define BUZZ_OFF            (PORTB &= ~(1<<4))


This code sets up the chip clock so it runs at a slower than full speed. There are defines that set up the output pin and toggle that pin on and off. We will use these macros in the main code.

## Chip setup¶

Next we configure the chip

int main(void){
CLOCK_PRESCALE(0);
BUZZER_CONFIG;


We are going to connect the buzzer between Arduino pin 12 (PORTB bit 4) and the one of the ground lines on the opposite side of the board.

## The buzzer loop¶

Finally, we enter the main loop

    while(1) {
BUZZ_ON;
_delay_us(200);
BUZZ_OFF;
_delay_us(200);
}


## Connecting the buzzer¶

Here is how to hook up a simple Piezo buzzer to output pin 12 on the Arduino board (PB4 on the chip itself).

Note

This image shows the buzzer connected to another pin on the Arduino. Any output pin will do, you just need to modify the configuration definitions to make it work.

Here is a picture of the chip, with all of the pins identified. This will help in hooking up other stuff to the board:

## What is wrong with this?¶

Running this code makes noise (it did on my system, but not very loud!)

The program works, but it consumes all the processor’s attention That wastes power we could use for other purposes. One way to solve this is to us an internal Timer.

Warning

There is one more problem with this code: IT WILL NOT SHUT UP! The code runs forever, generating an annoying tone. At least we know it works!

A Timer is just a simple counter that cab be set up and let run. The timer counts using the system clock. Once the timer reaches a max value, it rolls over and we can detect that!

## AVR Timers¶

The chip on the Arduino has four internal timers that go by these names:

• Timer0 - 8 bit counter
• Timer1 - 16 bit counter
• Timer3 - 16 bit counter
• Timer4 - 10 bit counter

Hmmm, wonder what happened to Timer2?

## Clock source¶

We mentioned that the timer counts using the system clock. To give more control of the timing we can divide that system clock using something called a prescaler to extend the time it takes reach the max count value.

The possible prescaler settings available on these timers are:

• Clock / 8
• Clock / 64
• Clock / 256
• Clock / 1024

## Timer counting rates¶

On the Arduino, we can configure timers to increment the counter every:

• 8/16000000 = 0.5us
• 64/16000000 = 4us
• 256/16000000 = 16us
• 1024/16000000 = 64us

## Setting the timer count¶

Each timer has a fixed number of bits available for its counting register. That means they can count up to:

• 255 for an 8-bit timer
• 1024 for a 10-bit timer
• 65536 for a 16-bit timer

We can detect when the timer “rolls over” from the max value it can hold to zero. That event can signal an interrupt, or simply set a flag we can check in our code. That is called “polling”.

The timers begin operation when we load a count into their counting register. If we start the timer with zero, it will rake time to reach the maximum count. If we choose to load some value other than zero in the counter, the time it takes to “roll over” will be shorter.

## Maximum timer times¶

We will be using the 8-bit Timer0 timer for this experiment. With the available prescallar values, we cna get these times out of the timer:

• 256 * 0.5us = 128us = 7812Hz
• 256 * 4us = 1.024ms = 976Hz
• 256 * 16us = 4.096ms = 244Hz
• 256 * 64 = 16.384ms = 61Hz

Of course, we can adjust these numbers. We also have control over how fast the main clock ticks, usina another scaling register that divides down the main system oscillator.

## Detecting the roll over¶

We can deal with the signal one of two ways:

• write code that checks for roll over every so often (polling)
• configure the chip to generate an interrupt when it happens

For this experiment, we will use “polling”.

## Configuring the Timer¶

In order to use a timer, we must set it up and start it running. There are two tasks we need to do:

• set the prescaler value

For Timer0, the control registers we use to control the count rate is:

• Timer/Counter Control Register B - TCCR0B

• Bit 0 = CS0
• Bit 1 = CS1
• BIT 2 = CS2

### Setting the prescaler¶

 CS2 CS1 CS0 Function 0 0 0 Timer stopped 0 0 1 clock/1 0 1 0 clock/8 0 1 1 clock/64 1 0 0 clock/256 1 0 1 clock/1024

THe register Timer0 uses for counting is:

• Timer/Counter0 count register - TCNT0

• This register is set with an initial counter value.
• Setting the count starts the timer (if enabled)

## Detecting the roll over¶

Each timer has a bit that is set whenever the timer overflows. For Timer0 thise bit we will use is this:

• Timer Interrupt Flag Register - TIFR0
• The bit we will examine is TOV0 (overflow).

## Polling code¶

We will write the polling code in assembly language. THis file looks like others we have set up, except that we need to make the single function in this file global. THe “C” code will reference this function to do the delay. In this example, we are not passing any data between “C” and assembly language code. Doing that is a topic for later.

Here is the timer code. This function configures the timer, starts it up for the delay time, then shuts it down when done.

timer.S
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include "config.h" .section .text .global timer_delay timer_delay: out _(TCNT0), r1 ; zero counter ldi r24, 0x3c ; set compare value out _(OCR0A), r24 ldi r24, (1 << WGM01) ; CTC max trigger out _(TCCR0A), r24 ldi r24, (1 << CS02) | (1 << CS00) ; clk/1024 out _(TCCR0B), r24 1: sbis _(TIFR0), 1 ; (1 << TOV0) ; check interrupt flag rjmp 1b out _(TCCR0B), r1 ldi r24, (1 << OCF0A) out _(TIFR0), r24 ret 

### Skipping instructions¶

Some AVR instructions are a bit funny. In this code you will see the sbis instruction:

• Test some condition (bit in register is set)
• If the condition is met, skip the next instruction
• Otherwise, they execute the next instruction
• Usually this is a jump of some kind

The result is like our conditional branch, but it takes two instructions

## Is this better?¶

With this code, the processor is not really stuck in a loop until the overflow bit gets set, even though that is how we set this delay function to work. We could be off doing other things until that flag bit gets set. This is sure easier to control that that ugly nested counting loop we used earlier!

## Rewriting the Code¶

We will set up this project in a new folder with a single Makefile. The Makefile shown here can build any project involving C or AVR Assembly code:

Makefile
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # Makefile for AVR projects TARGET := $(shell basename$(PWD)) MCU := atmega328p FREQ := 16000000L PGMR := arduino include mk/os_detect.mk include mk/avr-tools.mk include mk/avr-files.mk # check these settings after plugging in board ifeq ($(PLATFORM), Mac) PORT := /dev/cu.usbmodem1411 else ifeq ($(PLATFORM), Linux) PORT := /dev/ttyACM0 else PORT := COM6 endif endif # do not modify anything below this line .SUFFIXES: -include mk/avr-build.mk -include mk/avr-utils.mk -include mk/help.mk -include mk/debug.mk -include mk/version.mk 

### main.c: Setting up¶

We start off by writing the configuration file we will use for this project:

config.h
 1 2 3 4 5 6 7 8 #include #define BUZZ_PIN 4 #define BUZZ_DIR DDRD #define BUZZ_PORT PORTB #define _(s) _SFR_IO_ADDR(s) 

We will set this project up a bit differently. This one will use a standard “C” main function that handles the basic buzz logic. We will use Timer0 in compare match mode to do the delay logic. That routine will be written in assembly language.

main.c
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include "config.h" extern void timer_delay(); #define BUZZ_ON (BUZZ_PORT |= (1<

## How it works¶

• Main sets up the chip and timer system

• It then loops toggling buzzer pins and calling the timer delay routine
• timer.S configures the timer timer
• It uses polling in the delay loop to wait for the timer overflow
• Overall logic is identical to the “C” example

## Examing the Code¶

The Makefile used in this project creates a listing file (with en extension of .lst). This file is a “disassembly” of the final binary code sent to the processor. Eamining this file is how I discovered the problems with the current linker. I am still working on figuring out how to control the link step so I can generate pure assembly language files properly!