如何在 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
。
- 您选择哪种方法关闭计时器取决于您的目标。您只需禁用定时器中断、禁用所有中断或简单地关闭时钟源。选择权在你。
我在我的 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
。 - 您选择哪种方法关闭计时器取决于您的目标。您只需禁用定时器中断、禁用所有中断或简单地关闭时钟源。选择权在你。