Multi thread 3 leds

In my last blog I created a default framework to get a multi threat solution . This time I created a test project to get 3 leds blinking simultanesly on different speeds. Every led gets it’s own thread. This time I didn’t use a delay function, because it freezes the microcontroller. I created a timer library that provides milliseconds, seconds and minutes. Download all the files here.

The multi thread example

 

The timer library files

The files timer4clocklib.h and timer4clocklib.c are the only 2 new files added to the library. The files containing only 2 functions. Function tmr2_init() enables the timer2. The atmega328p contains 3 different timers and they are all a little different. The only reason I used timer2 is because it’s not used by Arduino. I thought it would be handy in the future in case I needed some arduino code to test.

The working of the timer

How does the Atmega328p timer work? Timer2 is an 8 bit timer. That means it will count from 0 till 255 (8 bits) and after reaching 255 it will trigger a function. This is function is called the interrupt service routine (ISR). The name for this function is already defined by AS6 and should have the same name and that is ISR(TIMER2_COMPA_vect){}. For counting millisseconds I would like an interrupt every 1ms, because then I can increment every intterupt with 1 to my msCounter variable and then i can use this variable to read out the milliseconds that had passed since I started the microcontroller. To get the seconds I wait until msCounter becomes 1000 (1000 milliseconds is 1 second). Every 1000 milliseconds a variable, calleds seconds, increments with 1 untill its reaches 60 seconds. After that the minutes variable will increment with 1 too. The same principle counts for the hours as well. The msCounter, seconds and minutes are global variables so you can read out the time passed everwhere you want into the project.

Initializing the timer

To get the ISR starting every 1 millisecond a calculation have to be made to let the microcontroller know when a millisecond has passed. The formula is CMR = t * (clockfreq/prescaler+1). The CMR is a value that should be given in the OCR2A register during initialization. ‘t’ is the time and that’s 1ms in this project. The clockfrequentie is the frequency of the clock you use for the Atmega328p. I used a 16MHz Crystal. The higher number you choose for the prescaler the shorter you can make the intterupt time. I used 1024. The prescaler value is set in the TCCR2B register.

CMR = 1ms * (16MHz/1024-1) = ca. 15,6.

timer4clocklib.h

/*
 * timer4clocklib.h
 *
 * Created: 20-5-2017
 * Author: HdH
 * Description: This lib uses Timer2 to create ms, sec and minutes to use in your apps
 */ 

#ifndef TIMER4CLOCKLIB_H
#define TIMER4CLOCKLIB_H

/****************************************************************************
	Bit and byte definitions
****************************************************************************/

#define CMR_SECOND 100 //count till 100. (100*10ms=1sec)
#define CMR_MINUT 6001 //count till 6001 to get till 1 minute
#define CMR_MSCOUNTER 5184000000 //max 24 hours and then this milliseconds counter will reset back to 0

extern unsigned long int msCounter;//counts 24 hours used for timings in the apps
extern unsigned long int mseconden;//counts 24 hours used for timings in the apps
extern int seconds; // provides seconds
extern int minutes; // provides minutes

//timer2 is 8bits and after all bits are count the interrupt follow.
//CMR = t * (clockfreq/prescaler+1)
//CMR = 1ms * (16MHz/1024-1) = ca. 15,6. 
//CMR decides after which time the interrupt follow. so in this case 1ms

/****************************************************************************
	Function definitions
****************************************************************************/

void tmr2_init(void); //and the ISR

#endif

timer4clocklib.c

/*
 * timer2clockLib.h
 *
 * Created: 20-5-2017
 * Author: HdH
 * Description: This lib uses Timer2 create ms, sec and minutes to use in your app
 */ 

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

unsigned long int _secondCounter = 0;
unsigned long int _minutCounter = 0;
int i = 0;

unsigned long int msCounter = 0;// counts 24 hours used for timings in the apps
int seconds = 0; // provides seconds
int minutes = 0; // provides minutes


/****************************************************************************
	tmr2_init, Timer0 and Timer2 are 8 bit. Timer0 is used for milis() and delay() in arduino. I use Timer2 
****************************************************************************/

void tmr2_init(void){
  TCCR2A = 0; // TCCR0A reset to 0
  TCCR2B = 0; // TCCR0B reset to 0
  TCNT2 = 0; // reset the counter to 0
  OCR2A = 16; // CMR = 15,6 about 16 for 1ms
  TCCR2A |= (1<<WGM21); // CTC mode
  TCCR2B |= ((1<<CS22) | (1<<CS21) | (1<<CS20)); // prescaler = 1024
  TIMSK2 |= (1<<OCIE2A); // activate interrupt timer 2
}

/****************************************************************************
	ISR, Interrupts every ms
****************************************************************************/

ISR(TIMER2_COMPA_vect){ // timer2 interrupt every 1 ms
    
  msCounter += 1;
  i += 1;
  if (i==10){
    _secondCounter += 1; // 1 count every 10 interrupts
    _minutCounter += 1; // 1 count every 10 interrupts
    i=0;
  }
  if (msCounter == CMR_MSCOUNTER){ // Counts ms for 24 hours
    msCounter = 0;
  }
  if (_secondCounter == CMR_SECOND){
    _secondCounter = 0;
    seconds = seconds+1;
	seconds = seconds % 60;
  }  
  if (_minutCounter == CMR_MINUT){
    _minutCounter = 0;
    minutes = (minutes+1)%60;
  }
}

The timer library is ready to be used and now I can remove the delay() functions and use the timer instead. The first function that needs to be adjusted is the initialize() function. Include the AS6 interrupt library and the timer4clocklib library. The function mr2_init() will activate the timer.

Initialize.c

/*
 * projectInit.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"


void initialize(){

	cli();//interupts disable
	tmr2_init();
	sei();//interupts enable
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

For this test project I used 3 seperate applications. For each led an application to simulate 3 separate threats. Later on I can use 1 application for showing information on a display. An application for listening to the USART or I2C messages. Or an application to read incomming button presses etc. In this case all applications are using a function to toggle a led between on and off. The timer is used for the time between blinking on and off. First the application checks if the timer is already activated. If it is, nothing happens untill the timer expired and then the led will toggle. By checking the time every loop and not delaying the microcontroller all the applications will keep running simultaneously.

Application1.c

/*
 * operational.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat
 */ 

#define PIN_LED1 6
#define DELAYMILIS 50 // Led blinking wait time in milliseconds
#define TIMER_IS_NOT_SET 0
#define TIMER_IS_SET 1

#include <avr/io.h>
#include "gpiolib.h"
#include "timer4clocklib.h"

void toggleLed1();

extern int alarmCode;
int previsiousvalue = 0;
int timerSet = TIMER_IS_NOT_SET;
unsigned long int endTime = 0;

void Application1(void){
	
	if(timerSet == TIMER_IS_NOT_SET){
		endTime = msCounter + DELAYMILIS;
		timerSet = TIMER_IS_SET;
		toggleLed1();
	}
	if(endTime < msCounter){ // Timer is expired
		timerSet = TIMER_IS_NOT_SET; // reset timer
		return;
	}
}

void toggleLed1(){
	if (previsiousvalue == 0){
		WritePin(PIN_LED1,HIGH);
		previsiousvalue = 1;
	}
	else{
		WritePin(PIN_LED1,LOW);
		previsiousvalue = 0;
	}
}

 

Application2.c

/*
 * inBedrijf.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat
 */ 

#define PIN_LED2 7
#define DELAYMILIS 500 // Led blinking wait time in milliseconds
#define TIMER_IS_NOT_SET 0
#define TIMER_IS_SET 1

#include <avr/io.h>
#include "gpiolib.h"
#include "timer4clocklib.h"

void toggleLed2();

extern int alarm;
int previsiousvalueL2 = 0;
int timerSetL2 = TIMER_IS_NOT_SET;
unsigned long int endTimeL2 = 0;

void Application2(){
	
	if(timerSetL2 == TIMER_IS_NOT_SET){
		endTimeL2 = msCounter + DELAYMILIS;
		timerSetL2 = TIMER_IS_SET;
		toggleLed2();
	}
	if(endTimeL2 < msCounter){ // Timer is expired
		timerSetL2 = TIMER_IS_NOT_SET; // reset timer
		return;
	}
}

void toggleLed2(){
	if (previsiousvalueL2 == 0){
		WritePin(PIN_LED2,HIGH);
		previsiousvalueL2 = 1;
	}
	else{
		WritePin(PIN_LED2,LOW);
		previsiousvalueL2 = 0;
	}
}

 

Application3.c

/*
 * inBedrijf.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat
 */ 

#define PIN_LED3 8
#define DELAYMILIS 2000 // Led blinking wait time in milliseconds
#define TIMER_IS_NOT_SET 0
#define TIMER_IS_SET 1

#include <avr/io.h>
#include "gpiolib.h"
#include "timer4clocklib.h"

void toggleLed3();

extern int alarm;
int previsiousvalueL3 = 0;
int timerSetL3 = TIMER_IS_NOT_SET;
unsigned long int endTimeL3 = 0;

void Application3(){
	if(timerSetL3 == TIMER_IS_NOT_SET){
		endTimeL3 = msCounter + DELAYMILIS;
		timerSetL3 = TIMER_IS_SET;
		toggleLed3();
	}
	if(endTimeL3 < msCounter){ // Timer is expired
		timerSetL3 = TIMER_IS_NOT_SET; // reset timer
		return;
	}
}

void toggleLed3(){
	if (previsiousvalueL3 == 0){
		WritePin(PIN_LED3,HIGH);
		previsiousvalueL3 = 1;
	}
	else{
		WritePin(PIN_LED3,LOW);
		previsiousvalueL3 = 0;
	}
}

The framework is working fine so now it’s time to use the framework do something more then just blinking leds. Stay tuned to next blog!