旁注:您的代码评论8 bits prescaler
具有误导性。这是not an 8-bit相反,它只是一个预分频器预分频器为 8,表示十进制值8
。这意味着定时器的时钟滴答率比主时钟慢 8 倍,因为将主时钟频率除以预分频器即可得到定时器的时钟频率。
现在我的回答是:
你设定的方式TCCR1A
and TCCR1B
都是正确的。
但是,您有 2 个主要问题、1 个小问题和 1 个建议。
请参阅 660 页 ATmega328 数据表页。如果您想知道从现在开始到哪里寻找低级帮助,请拨打 132~135 获取更多帮助和信息。
更新:新的 ATmega328 销售页面在这里:https://www.microchip.com/wwwproducts/en/ATmega328 https://www.microchip.com/wwwproducts/en/ATmega328。其新数据表可在此处获取:https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf。因此,我在上面和下面提到的页码可能不再完全匹配,因为我在编写本文时使用的是旧版本的数据表。
这里有2主要问题这完全破坏了你的代码:
-
由于您正在启用定时器比较匹配 1A 中断(TIMSK1 |= (1 << OCIE1A);
),您还必须定义发生这种情况时将调用的中断服务例程(ISR),否则您将遇到运行时(但不是编译时)问题。也就是说,如果您没有为输出比较匹配 A 定义 ISR,一旦发生输出比较 A 中断,处理器将陷入编译器为您创建的无限、空、虚拟 ISR,并且您的主循环将无法进行(请参阅下面的代码来证明这一点)。
将其添加到代码底部:
ISR(TIMER1_COMPA_vect)
{
// insert your code here that you want to run every time the counter
// reaches OCR1A
}
-
进入 ISR 需要几微秒,退出 ISR 需要几微秒,plus无论需要多少时间在 ISR 中运行代码,你需要使用一个OCR1A
值足够大,以至于 ISR 甚至有时间执行,而不是不断地快速调用,以至于您永远不会退出 ISR (这实际上会将您的代码锁定到无限循环中......这在您的情况下发生,如下所示出色地)。
我建议您调用 ISR 的频率不要超过每 10us。由于您使用的是 CTC 模式(比较匹配时清除定时器),预分频器为 8,我建议设置OCR1A
至少20
or so. OCR1A = 20
每 10us 调用一次 ISR。 (预分频器为 8 意味着每个 Timer1 滴答时间需要 0.5us,所以OCR1A = 20
每 20*0.5 = 10us 调用一次 ISR)。
如果你设置OCR1A = 20
,并添加如上所述的 ISR 代码,您的代码将运行得很好。
1 小问题:
设置是一个很好的做法OCR1A
after您配置计时器的其余部分,否则在某些情况下计时器可能无法开始计数。看“托尔斯滕”的评论在这里 http://www.righto.com/2009/07/secrets-of-arduino-pwm.html?showComment=1297626476152#c2692242728647297320(强调):
托尔斯滕说……
感谢您如此广泛地解释了这个问题!我正在寻找一种在 Arduino 引脚之一上生成 1 MHz 频率的方法。你的帖子对我实现这一目标有很大帮助。
我写这篇评论的原因如下:我花了将近6个小时才发现(主要是出于纯粹的绝望)设置定时器控制寄存器的顺序TCCR2*
和输出比较寄存器OCR2*
似乎很重要!如果您指定一个OCR
在设置相应的之前TCCR
计时器根本不开始计数。
2011 年 2 月 13 日上午 11:47 http://www.righto.com/2009/07/secrets-of-arduino-pwm.html?showComment=1297626476152#c2692242728647297320
所以,动起来OCR1A = 20;
to after你最后一次TCCR1B
线和before your TIMSK1
line.
1 推荐:
摆脱您的来电noInterrupts()
and interrupts()
。这里不需要它们。
功能代码:
现在,这是我编写的一些功能代码,它将更好地演示您正在尝试执行的操作以及我正在讨论的内容:
/*
timer1-arduino-makes-serial-not-work.ino
- a demo to help out this person here:
http://stackoverflow.com/questions/28880226/timer1-arduino-makes-serial-not-work
By Gabriel Staples
http://electricrcaircraftguy.blogspot.com/
5 March 2015
- using Arduino 1.6.0
*/
// Note: ISR stands for Interrupt Service Routine
// Global variables
volatile unsigned long numISRcalls = 0; // number of times the ISR is called
void setup()
{
Serial.begin(115200);
// http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
// noInterrupts(); // Not necessary
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// better to put this line AFTER configuring TCCR1A and TCCR1B below, but in
// Arduino 1.6.0 it appears to be ok here (may crash code in older versions),
// see comment by "Thorsten" here:
// http://www.righto.com/2009/07/secrets-of-arduino-pwm.html?showComment=1297626476152#c2692242728647297320
OCR1A = 20;
// SETTING OCR1A TO 1 OR 2 FOR SURE BREAKS THE CODE, as it calls the interrupt
// too often to even allow the main loop to run at all.
// OCR1A = 1;
// turn on CTC mode [Clear Timer on Compare match---to make timer restart at
// OCR1A; see datasheet pg. 133]
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler [0.5us ticks, datasheet pg. 135]. Each timer
// has a different bit code to each prescaler
TCCR1B |= (1 << CS11);
// enable timer compare match 1A interrupt; NOW YOU *MUST* SET UP THE
// CORRESPONDING ISR OR THIS LINE BREAKS THE CODE
// IT IS RECOMMENDED TO SET OCR1A HERE, *after* first configuring both the
// TCCR1A and TCCR1B registers, INSTEAD OF ABOVE! Like this:
// OCR1A = 20;
TIMSK1 |= (1 << OCIE1A);
// interrupts(); // Not necessary
Serial.println("setup done, input a character");
}
void loop()
{
if (Serial.available())
{
// read and throw away the first byte in the incoming serial buffer (or else
// the next line will get called every loop once you send the Arduino a
// single char)
Serial.read();
Serial.println("a");
// also print out how many times OCR1A has been reached by Timer 1's counter
noInterrupts(); // turn off interrupts while reading non-atomic (> 1 byte)
// volatile variables that could be modified by an ISR at
// any time--incl while reading the variable itself.
unsigned long numISRcalls_copy = numISRcalls;
interrupts();
Serial.print("numISRcalls = "); Serial.println(numISRcalls_copy);
}
// Serial.println("test");
// delay(1000);
}
// SINCE YOU ARE ENABLING THE COMPARE MATCH 1A INTERRUPT ABOVE, YOU *MUST*
// INCLUDE THIS CORRESPONDING INTERRUPT SERVICE ROUTINE CODE
ISR(TIMER1_COMPA_vect)
{
// insert your code here that you want to run every time the counter reaches
// OCR1A
numISRcalls++;
}
运行它,看看你的想法。
证明上述“主要问题1”是真实的
(至少据我了解,基于 Arduino Nano 上的测试,使用 IDE 1.6.0):
下面的代码可以编译,但不会继续打印“a”(但是,它可能会打印一次)。请注意,为了简单起见,我注释掉了等待串行数据的部分,并简单地告诉它每半秒打印一个“a”:
void setup() {
Serial.begin(115200);
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler. Each timer has a different bit code to each
// prescaler
TCCR1B |= (1 << CS11);
OCR1A = 20;
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
}
void loop() {
//if (Serial.available()) {
// Serial.println("a");
//}
Serial.println("a");
delay(500);
}
// ISR(TIMER1_COMPA_vect)
// {
// // insert your code here that you want to run every time the counter reaches
// // OCR1A
// }
另一方面,下面的代码可以工作,并且“a”将继续打印出来。这个和上面那个的唯一区别是这个 ISR 声明在底部没有注释:
void setup() {
Serial.begin(115200);
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler. Each timer has a different bit code to each
// prescaler
TCCR1B |= (1 << CS11);
OCR1A = 20;
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
}
void loop() {
//if (Serial.available()) {
// Serial.println("a");
//}
Serial.println("a");
delay(500);
}
ISR(TIMER1_COMPA_vect)
{
// insert your code here that you want to run every time the counter reaches
// OCR1A
}
如果此答案解决了您的问题,请点赞并接受它作为正确答案。谢谢!
额外资源:
-
I keep a running list of the most helpful Arduino resources I come across at the bottom of an article I wrote here: http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html. Check them out.
- 特别是查看 Ken Shirriff 和 Nick Gammon 的“高级”部分下的第一个链接。他们太棒了!
- 尼克·加蒙的回答在这里 https://stackoverflow.com/a/31826149/4561887
- [我的答案]哪些 Arduino 支持 ATOMIC_BLOCK?我如何在 C 中复制这个概念__attribute__((__cleanup__(func_to_call_when_x_exits_scope)))并在 C++ 中使用 class构造函数 and 析构函数? https://arduino.stackexchange.com/questions/77494/which-arduinos-support-atomic-block/77579#77579
- [我自己的问题和答案]C++ 递减单字节(易失性)数组的元素不是原子的!为什么? (另外:如何在 Atmel AVR mcus/Arduino 中强制原子性) https://stackoverflow.com/questions/36381932/c-decrementing-an-element-of-a-single-byte-volatile-array-is-not-atomic-why/39693278#39693278