Search code examples
ctimerdelayarduino-unobare-metal

Controlling Timer0 in ATmega328p


I'm trying to create a blocking delay for the ATmega328p for my arduino uno R3. but I'm running into problems with my function.

I am not sure if it has something to do with my clock source. I set it as 8 MHz at the top of my script, but the problem persists.

#define F_CPU 8000000UL

Here's a snip of my delay function, it's configured for a 10 ms delay at 8 MHz and uses a delayTime counter to create a longer delay

/* 
 * Function to instantiate timer operations 
 */
void makeDelay(int delayTime){
  // Call the timer for delayTime successively
  //  resulting in delay of (configured delay) * timerDelay
  while(delayTime > 0){
    /* Set the TCNT reg for 10ms @ 8 MHz */
    TCNT0 = 0xB2;
    
    // Define the mode and prescaler values
    TCCR0A = 0x00;  // Normal mode
    TCCR0B = 0x05;  //  prescaler = 1024

    // loop until overflow
    while( (TIFR0 & (1 << TOV0) == 0) );

    // Stop the timer
    TCCR0B = 0;

    // clear the overflow
    TIFR0 = 0x01;

    // decrement the counter
    delayTime--;
  }
}

Any advice would be appreciated.


Solution

  • In your program this waiting condition is wrong:

    (TIFR0 & (1 << TOV0) == 0)
    

    Operator == has higher precedence than &. (1 << TOV0) == 0 is evaluated first and it is always false. Thus TIFR0 & 0 is always false too. Rewrite the condition as

    ((TIFR0 & (1 << TOV0)) == 0)
    

    or

    ((TIFR0 & _BV(TOV0)) == 0)
    

    You can use following formula to calculate TCCNT0 register value for milliseconds:

    Nticks = 256 - ms*F_CPU/1000/prescaler
    

    As Timer0 is used by Arduino runtime library, you should disable it at first.

    Entire sketch may be like this:

    #define MS2TICKS(ms) (256 - (ms)*(F_CPU/1000)/1024)
    
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT);
    
      TCCR0B = 0;    // Stop timer
      TIMSK0 = 0;    // Disable timer interrupts
      TCCR0A = 0;    // Normal mode
      TIFR0  = ~0;   // Clear all interrupt flags
    }
    
    void my_delay()
    {
      TCNT0 = MS2TICKS(10);
      TCCR0B = 0x05;       // Enable timer with prescaler 1024
    
      while ((TIFR0 & _BV(TOV0)) == 0);
    
      TCCR0B = 0;         // Stop Timer0
      TIFR0 = _BV(TOV0);  // Clear overflow flag
    }
    
    void loop()
    {
      digitalWrite(LED_BUILTIN, HIGH);
      my_delay();
      digitalWrite(LED_BUILTIN, LOW);
      my_delay();
    }
    

    It generates 10 ms pulses with 10 ms pauses on LED output.