LOW-LEVEL ARDUINO PROGRAMMING(ASSEMBLER): TIMER INTERRUPTS

INTRODUCTION

In the third part of low-level programming AVR microcontrollers, we’ll be introduced with some new registries. We will use timer interrupts to create our own delay function in order to blink the LED on pin13. C programing will be reused and in the next tutorial, we’ll switch to Assembler.

So far we’ve done these:
Lesson 1. Blinking LED diodes
Lesson 2. Bitwise operators

WHAT IS A TIMER?

Timer (more precisely Timer/Counter) is a piece of hardware-implemented into a microcontroller (other controllers and processors also have it). The timer is programed over special registries. For example, we configure a prescaler, his mode, waveform generator which will be explained more thoroughly soon.

While we work with Dasduino (Atmel AVR ATmega328 microcontroller) we will concentrate on that part again. ATmega328 has three timers: timer0, timer1 i timer2. Timer0 and timer2 are 8bit timers, timer1 is 16bit. The main difference is the resolution, 8bit has 256 values and 16bit have 65536 for bigger a 16bitno 65536 for bigger resolutions.
All timers rely on system clocks, on Dasduino it is 16Mhz. All timers on Arduino firmware (bootloader) are configured on 1kHz frequency and all interrupts are enabled.

Imagine that you have a counter which on a press of a button increases by one. Timer/counter works on the same principle: counts beats of a clock. We’ve mentioned that the speed of counting is 16 million beats per second, approximately  63ns per beat or operation. 8bit timer will count from 0-255 and 16bit from 0-65535. The time needed for a counter to reset is 256/16.000.000= 16us, and 65536/16.000.000 = 4ms for a 16bit registry. it’s not a really practical way to illuminate the LED every 1 second. Instead of controlling the speed of timer/counter we will use something called Prescaler. In the next section, we will focus on registries in charge of timers, and in one of them, we will define the Prescaler.

Prescaler

Prescaler defines the speed of a certain timer (timer0, timer1 or timer2) according to this formula:

(timer speed [Hz]) = (Arduino clock speed (16MHz) [Hz]) / prescaler

According to the datasheet, the Prescaler has defined values of 1, 8, 64, 256 i 1024. It can be seen that the Prescaler 1 will increment the clock of 16MHz, Prescaler 8 2Mhz, Prescaler 64 250kHz, etc. Simply said, Prescaler set to 1024 will after every 1024 beats of 16Mhz timer/counter add 1 on his counter (timer0, timer1, timer2). To make it simple, In the time while John eats 1024 hamburgers, Bob will eat 1.

Let’s get back to timers. The first new registry we will mention in this tutorial is OCR – output compare register in which we will save compare match value. Does it seem to be too much at once? It is not, we’ve already mentioned all of the above, and now they’ve been given the official names. Insert it in the formula so we can visualize it better:

formula

Explanation:
16Mhz – the speed of Arduino’s clock timer/counter, we insert it as 16.000.000 Hz
brzina.timera – considering that we want to blink the LED every second, we insert the frequency 1Hz
-1 – counter number 0 so we insert -1 in the formula

compare match registar

From the table, we can see that the smallest Prescaler we can use is 256 for 16bit timer 1. Reason? Value 62499 can be saved in the 16bit registry of timer1.
To measure counters in the frequency of 1Hz(1 second) we can use the Prescaler 1024, but it is much more accurate to use a smaller Prescaler. In none of these cases we can’t use 8bit timers timer0 and timer2.

Now when we’ve chosen the timer and prescaler it’s time to meet the registries that we need to set up.

Registries

For more check the datasheet

TCCR1A Timer count control register – 0b00000000 (normal)(normal)
TCCR1B Timer count control register – 0b00000100 (no icr)(256-prescaler)
TCCR1C Timer count control register – 0b00000000 (no force)

OCR1AH output compare register – high byte 0xF4
OCR1AL output compare register – low byte 0x23

On these registries, we set up a calculated compare match value, 62499. Since we set up high and low byte separately we change 62499 from decimal to hexadecimal and that is 0xF423. So high byte is  0xF4, and low byte 0x23.

TIMSK1 timer interrupt mask 0b00000010 (output compare u enable – OCIE1A)

DDRB
PORTB

We’ve already mentioned DDRB and PORTB registries in the first lesson.

C CODE

void setup() {
  DDRB = B00100000; // portB5 (pin13) is set as output
  cli();   //we are disabling all interrupts, so the timer interrupt won't be distrupted by another interrupt with higher priority
  TCCR1A = 0b00000000;
  TCCR1B = 0b00000100;
  TCCR1C = 0b00000000;
  OCR1AH = 0xF4;
  OCR1AL = 0x23;
  TIMSK1 = 0b00000010;
  sei();   // we re-enable interrupts including timer interrupts
}
ISR(TIMER1_COMPA_vect) { // timer compare interrupt service routine
  PORTB ^= 0b00100000;   // XOR operator, if the LED is on we turn it off and vice versa
}
void loop() {
}

TIMER LIBRARY

There are a couple of timer libraries that use different functions. We will get busy only with those that we’d use in the example of blinking LED, find more on Arduino references.

Timer0
For functions it represents itself as delay(), millis() and micros().

delay() – stops the microcontroller for a given time in milliseconds, there is also this function delayMicroseconds()  etc.
millis() – a function that has saved time from the start of a microcontroller or its restart in milliseconds. Until the overflow, it can count for around 15days.
micros() – a function that has saved time from the start of a microcontroller or its restart in microseconds. Until the overflow, it can count for around 70 minutes.

Example of a blinking LED without the delay() function:

unsigned long proslo_vrijeme = 0;        // we generaly use unsigned long for variables that save time
void setup() {
  pinMode(13, OUTPUT);
}
void loop() {
  unsigned long trenutno_vrijeme = millis();  // saves current time from the start of the microcontroller
  if (trenutno_vrijeme - proslo_vrijeme >= 1000) {
    proslo_vrijeme = trenutno_vrijeme; // saves when did the LED blink last time
    digitalWrite(13, digitalRead(13) ^ 1); // change the state of LED
  }
}

CONCLUSION

Besides the fact that the functions we’ve just worked with allow us to multitask, they give us a much better insight into the duration time of the process itself. If we compare the blinking of a LED on this way and the one on Arduino digitalWrite() and delay() functions, we can see that after some time we have deviations in moments when the LED blinks.

For much more accurate work monitoring of microcontrollers and interrupts it is necessary to go down a bit on program language scale. After these short introduction lessons, we will start with Assembly programming.