【问题标题】:How to stop timer on ATmega328如何在 ATmega328 上停止计时器
【发布时间】:2017-06-24 10:07:04
【问题描述】:

我写了一些简单的代码,在我的 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);

}

【问题讨论】:

  • 您到底想让TCCR1B |= (0 &lt;&lt; CS12) | (0 &lt;&lt; CS11) | (0 &lt;&lt; CS10); 行做什么?这么写,TCCR1B的值不变。
  • 上面的评论是正确的。该声明不会改变 TCCR1B。请参阅下面的答案,了解如何正确重置寄存器并停止计时器。
  • 对于您的用例,让定时器中断运行并使用附加变量作为标志来启用/禁用计数是完全可以和常见的。

标签: timer arduino avr


【解决方案1】:

我已经测试了所有三种“关闭计时器”的方法。只需在下面的代码中注释掉您喜欢的那个即可查看它的演示。这三个都可以有效地让 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 上运行。我已经测试了上述所有三种“关闭”机制,它们都可以正常工作。

为了做一个最小的、可验证的例子,我去掉了所有 OLED 的东西。我将引脚 13 更改为输出,并将其设置为尽可能闪烁 LED(并且当它停止时,定时器和/或中断被明确禁用)。

几个学习点:

  • 在没有附加电路的情况下,您不应将引脚 13 用于“鳍”按钮 - 请参阅 this Arduino official reference
  • 您应该将写入中断服务例程中的任何变量声明为volatile
  • 您选择关闭计时器的方法取决于您的目标。您只需禁用定时器中断,禁用所有中断,或简单地关闭时钟源。选择权在你。

【讨论】:

  • 实际上使 'count' 易失是不够的,因为它是一个多字节变量。条件“if (count > 100)”可以在检查之间中断,然后计数可能会改变。
  • @Rev1.0 虽然在技术上是正确的,但我发明了这个变量只是为了演示。它是单调递增的,并且(关闭定时器的各种方法)的演示在它通过值 100 后结束。在实际程序中,我当然会在更新它时关闭中断,但为了帮助这个 OP,我只是想要一些能让演示运行几秒钟然后通过他选择的机制关闭的东西。感谢您指出这一点。
  • 您的代码有效,我想知道问题出在哪里。我猜库OLED_I2C.h 正在使用函数sei()cli()。感谢您的帮助和解答。顺便说一句,我看到了像这样翻转 LED 的有用命令:digitalWrite(13,digitalRead(13)^1)
  • 谢谢。如果它对您有帮助,请随意支持我的第二个答案,以后可能会对其他人有所帮助。至于^1 表达式,如果您愿意,可以使用它。我想让我的答案尽可能清晰易读,对你和其他可能关注的人来说都是如此。
  • 既然你把它命名为“学习点”,我想我最好指出来,因为很多人没有意识到这一点,认为 volatile 就足够了。当然这无论如何都与问题没有直接关系;)
【解决方案2】:

当然!有两种方法可以做到。您可以简单地停止计时器中断,但使用以下命令让计时器继续运行:

TIMSK1 &= ~(1 << OCIE1A);

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

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

这有效地撤销了您首先选择时钟源所做的操作。此后,CS12-CS11-CS10 位将为 0-0-0,时钟源将停止。见第134 的数据表。

【讨论】:

  • 感谢您的回复,我现在明白我的错了。我在 c 语言方面有差距。我认为命令“TCCR1B |= (0
  • 我可以再问一个问题,为什么函数“cli()”不停止计时器,当它应该停止所有中断时。
  • 你是对的; cli() 将停止所有中断,包括计时器。这是一种可行的方法,因此只要您的代码不依赖于其他中断,请停止计时器。
  • 我在 arduiono IDE 中编程,但 cli() 什么也不做,即使我在定时器设置函数中用 cli() 替换 sei(),定时器仍然会启动。
  • 请注意,通过使用 cli() 禁用中断,定时器将继续运行并且定时器中断标志仍将被设置。它只是阻止了 ISR 的执行。
猜你喜欢
  • 1970-01-01
  • 2012-05-05
  • 2014-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多