2
votes

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);

}

2
What exactly did you want the line TCCR1B |= (0 << CS12) | (0 << CS11) | (0 << CS10); to do? The way it is written, the value of TCCR1B is unchanged. - Artium
The comment above is correct. That statement will not change TCCR1B. See my answer below for how to correctly reset the register and stop the timer. - TomServo
For your use case, it would be totally OK and common to just leave the timer interrupt running and use an additional variable as flag to enable/disable the counting. - Rev

2 Answers

2
votes

Sure thing! There are two ways you can do it. You can simply stop the timer interrupt but leave the timer running using:

TIMSK1 &= ~(1 << OCIE1A);

Or, you can stop the timer altogether by altering the clock source to "none" like:

TCCR1B &= ~(1<< CS12);
TCCR1B &= ~(1<< CS11);
TCCR1B &= ~(1<< CS10);

which effectively undoes what you did to select the clock source in the first place. After this the CS12-CS11-CS10 bits will be 0-0-0 and the clock source will be stopped. See p. 134 of the datasheet.

1
votes

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.