Friday, August 11, 2017

Blinking LEDs without using delays


Scheduling tasks on Atmel AVR Atmega328p and 
blinking two LEDs at different frequencies - (without using delays)- source code available.

Timers of microcontrollers are extremely important peripherals, today we will learn how to schedule tasks at a fixed time without using delays that hinder the main program loop.We will make the first LED blink at a rate of 2 Hz,the second LED will blink once per second 1 Hz as seen in the video.


We will find that this can be achieved easily using Timers of our AVR chip,but first let's have a quick introduction on how we will use the 8 bit Timer to achieve our project goal.

Timers

There are three timers in our Atmega chip ,timer 0 and timer 2 are two 8 bit timers each has a different clock prescaler which "divides" the clock by a certain value.Since timer 0 is 8-bit it can count to max. value of 255.


How AVR timers work


TCNT0 register stands for "timer counter 0" register which stores the current value of the counter.

Other important registers are the two compare registers OCR0A , OCR0B  output compare registers for counter 0. They work as follows: whatever value we will put into one of  them.. it is always compared to the value of the TCNT0 in hardware !

Output compare registers are very helpful registers because they will let us do these things on compare match:
  • generate a hardware interrupt at a fixed time base.
      
  • clear the value of the TCNT0 so it will start again from 0.(only available for OCR0A)
  • toggle an I/0 pin


Returning back to our project first goal is to "interrupt" the program execution at 1 millisecond time-base . For example , my chip is running on an 8 MHZ external crystal oscillator and by using a prescaler of value 64 we get a counter tick every 8 microsecond. so 125 counts of the counter register will result in a 1 millisecond .


TCCR0B=3;                  //CS0[2:0] divide by 64 ...125 ticks =1msOCR0A=124;               //125 tick =1ms

So by setting OCR0A to 124 (to get 125 count as counting is zero based) the Interrupt service routine will be executed every 1 ms by setting the interrupt on match and clear counter on match bits as follows:


TIMSK0=(1<<OCIE0A);                     //setting output compare A match interrupt enable bit 
TCCR0A=(1<<WGM01);                    //enable clear timer on compare match

The only remaining thing in our initialization of the timer0 is to enable the I -bit in the status register which is handled by the following function. you have to include .


 sei(); 


The last thing in the initialization is to set two pins of port B as output and initially turn them off.

DDRB|=0x03;       // lower 2 bits of port B as  output
PORTB &=0b11111100;   //turned off


The main function

The first Led on PORTB1 needs to be switched at a rate of 2 Hz,here is how we will achieve this;
the LED will toggle every 250 ms ,and we have previously set a time base of 1 ms that means that the ISR will be called every 1ms ,we can use this ISR to decrement a variable "time1" which is initialized with a value of 250, and the main program loop will check if this value reached 0 or not. If it reached 0 toggle the state of the pin, and assign "time1" variable to 250 again.

By using the same way we can blink the other LED at a rate of 1Hz,we will find that we need to toggle the pin every 500 ms .but since we want to declare "time2" as a char value like time1 which can carry a max value of 255 ..we can instead decrement a "time2" as from 125 four times to give a total of  500 ms then toggle the pin state.So we will create a variable tsk2counter and initialize the value 4.

In the main loop don't forget to reset the timer1 and timer2 to their original value. The source code is include below, please don't hesitate to ask any question in the comment section below.


Source Code 


/* * BlinkTwoLeds.c * * Created: 8/11/2017 10:49:30 AM *  Author: Hardwired Electronics */ 


#include <avr/io.h>
#include <avr/interrupt.h>

#define t1 250 //250 ms
#define t2 125  //125 ms


volatile unsigned char time1,time2;
unsigned char tsk2counter;

void initialize(void){
  
TCCR0A=(1<<WGM01); //enable clear timer on compare match
TCCR0B=3;          //divide by 64 ...125 ticks =1ms
OCR0A=124;         //125 tick =1ms
TIMSK0=(1<<OCIE0A);//output compare interrupt enable for timer0 ,remaining ocf0a in tirf0


DDRB|=0x03;       // lower 2 bits of port B as  output
PORTB &=0b11111100;   //turned off


time1=t1;
time2=t2;

tsk2counter=4;

sei();
} 

int main(void)
{
  initialize();
    while(1)
    {
        //TODO:: Please write your application code
   if(time1==0){time1=t1; task1();}     
   if(time2==0){time2=t2; task2();}
    }
}

ISR(TIMER0_COMPA_vect){
  if(time1>0)--time1;
  if(time2>0)--time2;

}
void task1(void){
  
  PORTB ^=(1<<PINB1);
}
void task2(void){
if(--tsk2counter==0){
  tsk2counter=4;
  PORTB^=(0x01);//toggle led on portB0
}
}