运行下面的代码,当我从串行监视器向Arduino发送任何字符时,Arduino不打印"a“back。我认为这是timer1代码的问题,但它应该可以工作,因为这段代码是我的老师在C课上给我的。
void setup() {
Serial.begin(115200);
// http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
noInterrupts();
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 1000000hz increments with 8 bits prescaler
OCR1A = 1;// = (16*10^6) / (1000000*8) - 1 (must be <65536)
// 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);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
void loop() {
if (Serial.available()) {
Serial.println("a");
}
}另请参阅:http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
发布于 2015-03-06 05:11:00
附注:您关于8 bits prescaler的代码注释具有误导性。它不是一个8位预分频器,相反,它只是一个8的预分频器,这意味着十进制值8。这意味着定时器的时钟滴答率比主时钟慢8倍,因为你将主时钟频率除以预分频器得到定时器的时钟频率。
现在是我的回答:
您设置TCCR1A和TCCR1B的方式都是正确的。
但是,您有两个主要问题,一个小问题和一个建议。
请参阅660-pg ATmega328数据表pgs。132~135获取更多帮助和信息,如果你想知道从现在开始在哪里寻找低级帮助。
更新:新的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。因此,我上面和下面提到的页码可能不再完全匹配,因为我在写这篇文章时使用的是旧版本的数据表。
下面是两个主要的问题,它们完全破坏了你的代码:
TIMSK1 |= (1 << OCIE1A);),因此您还必须定义中断服务例程,该例程将在这种情况发生时被调用,否则您将出现运行时(但不是编译时)问题。也就是说,如果您没有为Output Compare Match A定义ISR,一旦发生Output Compare A中断,处理器将陷入由编译器为您创建的无限的、空的、虚拟的ISR中,并且您的主循环将不会继续(参见下面的代码以证明这一点)。将这段代码添加到代码的底部:
ISR(TIMER1_COMPA_vect) { //在此处插入您希望在每次计数器达到OCR1A }时运行的代码
OCR1A值,使ISR甚至有时间执行,而不是不断地调用,以至于您永远不会退出ISR (这会将您的代码锁定在无限的loop....which中,在您的情况下也会发生)。我建议您呼叫ISR的频率不要超过每10us。由于您使用的是CTC模式(比较匹配时清除计时器),并且预分频器为8,因此我建议将OCR1A设置为不小于20的值。OCR1A = 20将每隔10us呼叫一次ISR。(预分频器为8意味着每个ISR时钟周期为0.5us,因此OCR1A = 20将每隔20*0.5 =10us调用Timer1 )。
如果您设置了OCR1A = 20,并添加如上所述的ISR码,那么您的代码将运行得很好。
1个小问题:
最好在配置定时器的其余部分后设置OCR1A,否则在某些情况下定时器可能无法开始计数。请参阅"Thorsten's" comment here (已添加强调):
Thorsten说...
感谢你如此详尽地解释这件事!我正在寻找一种在Arduino-pins上生成1个MHz的方法。你的帖子帮了我很大的忙。
我写这篇评论的原因如下:我花了几乎6个小时才发现(主要是在完全绝望的情况下)定时器控制寄存器 TCCR2* 和输出比较寄存器的设置顺序似乎很重要!如果您在设置相应的OCR TCCR 之前分配了一个OCR,则计时器根本不会开始计数。
February 13, 2011 at 11:47 AM
因此,将OCR1A = 20;移到最后一个TCCR1B行之后和TIMSK1行之前。
1建议:
去掉对noInterrupts()和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
}如果这个答案解决了你的问题,请给它加票,并接受它为正确答案。谢谢!
额外资源:
发布于 2015-08-05 15:34:39
Gabriel Staples is quite correct,您看不到"a“的原因是因为您没有为中断提供ISR处理程序。因此,编译器生成的代码跳回到地址0x0000,并且草图重新启动。
提供“空”ISR处理程序的另一种方法是:
EMPTY_INTERRUPT (TIMER1_COMPA_vect);使用EMPTY_INTERRUPT处理程序,我得到了一个OCR1A低至1的响应( "a"):
OCR1A = 1;但是,如果您不打算对中断做任何事情,那么为什么要启用中断呢?
关于Arduino上的interrupts的更多信息。
发布于 2015-03-06 02:46:41
你写了两次这个寄存器:
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS11);虽然我认为这可能是:
TCCR1A |= (1 << WGM12);
TCCR1B |= (1 << CS11);可能唯一的错误在那里,因为您忘记设置TCCR1A,并且设置了另一个两次。
https://stackoverflow.com/questions/28880226
复制相似问题