0

Afternoon all

I'm looking for some assistance please with something that has been confusing me whilst trying to learn timer interrupts

You'd be best treating me as a novice. I have no specific goal here other than learning something that I think would be useful feather to add to my cap!

I have written the below sketch as a stab at a rigid framework for executing different fcns at different rates. I've done something similar using millis() and whilst that worked I found it inelegant that a) there was no obvious way to check for task overruns and backing-up the execution rate and b) the processor is bunged up by checking millis() every program cycle.*

Essentially what I think should be a 1ms timer interrupt on Timer2 (16MHz/64 prescaler /250 compare register =1000hz) is coming out around 0.5ms. I've been confused for hours on this but I'm prepared to accept it could be something fundamental/basic!

What's also throwing a spanner in the works is that using serial comms to try and debug the faster task rates seems to slow things down considerably, so I'm inferring the problem by counting up 1ms tasks to call 10,100 and 1000ms tasks and debugging at the slower level. I suppose chewing through a few characters at 9600baud probably is quite slow.**

I've pasted the code below. Any pointers highly appreciated. Be as harsh as you like :)

cheers

Al

*Whilst not what I'm confused about - any comments on my logic here also welcome ** Although I don't get how Serial.println manages to slow the program down. It's driven from interrupts so it should surely just drop the comms and perform the next ISR - effectively a task overrun. Any comments here also welcome

//NOTES
//https://www.robotshop.com/letsmakerobots/arduino-101-timers-and-interrupts
//https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328
//http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf
//

//INITIALISE EVERYTHING

const int ledPin = 13;
volatile int nStepTask1ms = 0; // init 0 - to be used for counting number of 1ms tasks run and hence calling 10ms task
volatile int nStepTask10ms = 0;
volatile int nStepTask100ms = 0;
volatile int nStepTask1000ms = 0;
volatile int LEDFlashState = 0; // init 0 - variable to flip when LED on
volatile int millisNew = 0; // to store up to date time
volatile int millisOld = 0; // to store prev time
volatile int millisDelta = 0; // to store deltas

//
void setup() 
{
Serial.begin(9600); //set up serial comms back to PC
pinMode(ledPin,OUTPUT); //to flash the embedded LED

noInterrupts(); //turn off interrupts while we set the registers
//set up TIMER first

TCCR2A = 0; //sets TCCR1A byte to zero, bits to be later individually mod'd
TCCR2B = 0; //sets TCCR1B byte to zero, bits to be later individually mod'd
TCNT2 = 0; //ensures counter value starting from zero
Serial.println("Timer1 vars reset");

TCCR2B |= (1<<WGM12); // bitwise or between itself and WGM12. TCCR2B = TCCR2B | 00001000. Sets WGM12 high. (CTC mode so WGM12=1, WGM 13,11,10 all 0) https://stackoverflow.com/questions/141525/what-are-bitwise-shift-bit-shift-operators-and-how-do-they-work
Serial.println("Mode 4 CTC set");
Serial.println("TCCR2B=");
Serial.println(TCCR2B,BIN);

TCCR2B |= (1<<CS11); // sets CS11 high
TCCR2B |= (1<<CS10); // sets CS10 high (i.e. this and above give /64 prescaler)
Serial.println("Prescaler set to 64");

OCR2A = 250; //compare match register for timer2
Serial.println("Compare Register set");
Serial.println("OCR2A=");
Serial.println(OCR2A);

TIMSK2 |= (1 << OCIE2A); //enables interrupts - https://playground2014.wordpress.com/arduino/basics-timer-interrupts/
Serial.println("Interrupt Mask Register Set");
Serial.println("TIMSK2=");
Serial.println(TIMSK2);

interrupts(); //enable interrupts again - not sure if this is required given OCIE1A being set above?
}

//set up ISR for Timer2 - timer structure called every interrump (1ms) that subsequently calls 1,10,100 and 1000msec task fcns
ISR(TIMER2_COMPA_vect)
{
TASK_1ms();
if (nStepTask1ms>9)
  {
    TASK_10ms();
    if (nStepTask10ms>9)
      {
        TASK_100ms();
        if (nStepTask100ms>9)
          {
            TASK_1000ms();
          }
      }
  }
}

void TASK_1ms()
{
  // 1ms tasks here
  nStepTask1ms++;
}

void TASK_10ms()
{
  //10ms tasks here
  nStepTask1ms=0;
  nStepTask10ms++;
}

void TASK_100ms()
{
  //100ms tasks here
  nStepTask10ms=0;
  nStepTask100ms++;
  //Serial.println(nStepTask100ms);
}

void TASK_1000ms()
{
  //1000ms tasks here
  nStepTask100ms=0;


  //do something 
  changeLEDFlashState();

  //check timing tick of this task
  millisNew=millis();
  millisDelta=millisNew-millisOld;
  Serial.println(millisDelta);
  millisOld=millisNew;

  nStepTask1000ms++; 
}

void changeLEDFlashState()
{
if(LEDFlashState==0)
{
  digitalWrite(ledPin,HIGH);
  LEDFlashState=1;
  //Serial.println("LED Turned On");
}
else
{
  digitalWrite(ledPin,LOW);
  LEDFlashState=0;
  //Serial.println("LED Turned Off");
}
}


void loop() 
{
// empty
}
Al Fraser
  • 3
  • 2

1 Answers1

0

You have two lines here:

TCCR2B |= (1<<CS11); // sets CS11 high
TCCR2B |= (1<<CS10); // sets CS10 high (i.e. this and above give /64 prescaler)

These two lines set the lower three bits of TCCR2B to 011, which is a /32 prescaler.

Note that for Timer1 and Timer2, the prescaler settings are different than Timer0. For Timer0, the settings above would give you a /64 prescaler.

jfowkes
  • 1,495
  • 9
  • 17
  • Hi - thanks for the reply. I've checked out the documentation and indeed you're right! Perfect, thanks. I have read the documentation and, slightly contrary to your second statement in italics, I can see TIMER2 will be an /32 prescaler and TIMER0 and TIMER1 would give /64 prescaler. This is due to the highest two states being used for external clock sources on 0 and 1 Great stuff, thanks – Al Fraser Aug 07 '18 at 12:03
  • Oh I've just realised how wrong this was! CS11 and CS10 are of course bits from the Timer1 TCCR1B byte. If I'd have started by looking at the documentation for TCCR2B I'd find the last three bits are called CA22 CA21 and CS20. I'm surprised the above works at all but I guess the calling CS11 and CS10 still work as indices into TCCR2B. /64 prescaler would then simply be: TCCR2B |= (1< – Al Fraser Aug 08 '18 at 12:23
  • I mean CS22 CS21 CS20. I think the "CA"'s are typos in the documentation – Al Fraser Aug 08 '18 at 12:35