如何在 ATmega328 上停止定时器

How to stop timer on ATmega328

我在我的 Arduino Uno 上使用 timer1 编写了一些简单的代码。问题是我无法以任何方式停止计时器。

我正在使用这个程序来计算并在显示屏上显示引脚 2 上的外部中断次数,同时测量时间。但是当我按下按钮 fin 时,我想停止为正在增加称为 cas 的变量时间的程序生成中断。你能帮忙吗?

我的代码:

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

}

没问题!有两种方法可以做到这一点。您可以简单地停止计时器 interrupt 但保留计时器 运行 使用:

TIMSK1 &= ~(1 << OCIE1A);

或者,您可以通过将时钟源更改为“none”来完全停止计时器,例如:

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

这有效地撤消了您最初对 select 时钟源所做的操作。此后,CS12-CS11-CS10 位将为 0-0-0,时钟源将停止。见第数据表的 134。

我已经测试了 "turning off the timer." 的所有三种方法,只需在下面的代码中注释掉您喜欢的方法即可查看它的演示。这三个都可以有效地让 Arduino 的 LED 停止闪烁。

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
    }
  }

这个确切的代码是 运行 在我旁边的一个 Uno 上。我已经测试了上面的所有三种 "turn off" 机制,它们都有效。

为了制作一个最小的、可验证的例子,我去掉了所有的 OLED 东西。我将 pin 13 更改为输出并将其设置为使 LED 闪烁(当它停止时,定时器 and/or 中断显然被禁用)。

几个学习要点:

  • 如果没有额外的电路,您不应该将针脚 13 用于 "fin" 按钮——参见 this Arduino official reference
  • 您应该将写入中断服务例程中的任何变量声明为 volatile
  • 您选择哪种方法关闭计时器取决于您的目标。您只需禁用定时器中断、禁用所有中断或简单地关闭时钟源。选择权在你。