Search code examples
timerarduinoavr

How to stop timer on ATmega328


I wrote some simple code where I am using the timer1 on my Arduino Uno. The problem is I can't stop the timer any way I try.

I am using this program to count and show on the display the number of external interrupts on pin 2 while measuring the time. But when I press button fin I want to stop generating interrupts for the program which is increasing the variable time called cas. Can you help somehow, please?

My code:

#include <OLED_I2C.h>
#define alarm_output 10 
#define fin 13

int suma=0;
int alarm=0;
float cas=0;

OLED  myOLED(SDA, SCL, 8);
extern uint8_t MediumNumbers[];
extern uint8_t BigNumbers[];
extern uint8_t SmallFont[];

void setup(void) {

   pinMode(alarm_output,OUTPUT);
   digitalWrite(alarm_output,LOW);
   pinMode(fin,INPUT_PULLUP);
   pinMode(9,INPUT_PULLUP);

   //interrupt
   interrupts(); 
   attachInterrupt(digitalPinToInterrupt(2), displej, CHANGE);

   //first screen
   myOLED.begin();
   myOLED.setFont(SmallFont);
   myOLED.print("TIME:", 0, 30);
   myOLED.print("INTERRUPT:", 0, 56);
   myOLED.print("Laser game", CENTER, 0);
   myOLED.setFont(MediumNumbers);
   myOLED.printNumF(cas,1,RIGHT,20);
   myOLED.setFont(BigNumbers);
   myOLED.printNumI(suma, RIGHT, 40);
   myOLED.update();

   //start loop
   up:;
   if(digitalRead(9)==1)
      goto up;

   // TIMER 1 for interrupt frequency 10 Hz:
   cli(); // stop interrupts
   TCCR1A = 0; // set entire TCCR1A register to 0
   TCCR1B = 0; // same for TCCR1B
   TCNT1  = 0; // initialize counter value to 0
   // set compare match register for 10 Hz increments
   OCR1A = 24999; // = 16000000 / (64 * 10) - 1 (must be <65536)
   // turn on CTC mode
   TCCR1B |= (1 << WGM12);
   // Set CS12, CS11 and CS10 bits for 64 prescaler
   TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
   // enable timer compare interrupt
   TIMSK1 |= (1 << OCIE1A);
   sei(); // allow interrupts
}

void displej(){
   suma++;
   alarm=3; 
}

ISR(TIMER1_COMPA_vect){
   cas=cas+0.1;
   if(alarm>0)
      alarm--;  

}

void loop(void) {
   myOLED.setFont(MediumNumbers);
   myOLED.printNumF(cas,1,RIGHT,20);
   myOLED.setFont(BigNumbers);
   myOLED.printNumI(suma, RIGHT, 40);
   myOLED.update();

   if(digitalRead(fin)==0){
      cli();
      TCCR1B |= (0 << CS12) | (0 << CS11) | (0 << CS10);  //this do now work
      detachInterrupt(digitalPinToInterrupt(2));
      sei();
   }
   if(alarm>0)
       digitalWrite(alarm_output,HIGH);
   else
       digitalWrite(alarm_output,LOW);

   delay(10);

}


Solution

  • I've tested all three methods for "turning off the timer." Just comment out the one you prefer in the code below to see it demonstrated. All three are effective at getting the Arduino's LED to quit blinking.

    void setup(void) {
       pinMode(13,OUTPUT);
       digitalWrite(13,LOW);
       interrupts(); 
    
       // TIMER 1 for interrupt frequency 10 Hz:
       cli(); // stop interrupts
       TCCR1A = 0; // set entire TCCR1A register to 0
       TCCR1B = 0; // same for TCCR1B
       TCNT1  = 0; // initialize counter value to 0
       // set compare match register for 10 Hz increments
       OCR1A = 24999; // 200 millisecond cycle
       // turn on CTC mode
       TCCR1B |= (1 << WGM12);
       // Set CS12, CS11 and CS10 bits for 64 prescaler
       TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
       // enable timer compare interrupt
       TIMSK1 |= (1 << OCIE1A);
       sei(); // allow interrupts
    }
    
    volatile uint8_t count = 0;
    volatile uint8_t timer_flip = 0;
    
    
    ISR(TIMER1_COMPA_vect){
       if (timer_flip == 0)
         timer_flip = 1;
       else
         timer_flip = 0;
    
       if (timer_flip == 1)
         digitalWrite(13, HIGH);
       else
         digitalWrite(13, LOW);
    
       count++;  
    }
    
    void loop(void)
    {
      if (count > 100)  // runs for a few seconds
      {
        //cli();  // One way to disable the timer, and all interrupts
    
        //TCCR1B &= ~(1<< CS12);  // turn off the clock altogether
        //TCCR1B &= ~(1<< CS11);
        //TCCR1B &= ~(1<< CS10);
    
        //TIMSK1 &= ~(1 << OCIE1A); // turn off the timer interrupt
        }
      }
    

    This exact code is running on an Uno right beside me now. I've tested all three "turn off" mechanisms above and they all work.

    To make a minimal, verifiable example I stripped out all the OLED stuff. I changed pin 13 to an output and set it to blink the LED while it could (and when it stops the timers and/or interrupts are clearly disabled).

    A couple of learning points:

    • You should not use pin 13 for your "fin" button without additional circuitry -- see this Arduino official reference.
    • You should declare as volatile any variable that is written to in your interrupt service routines.
    • Your choice of which method to turn off the timer depends on your goal. You just disable the timer interrupt, disable all interrupts, or simply turn the clock source off. The choice is yours.