Arduino 上的 Timer1 导致串行打印无法工作

2023-12-30

运行下面的代码,当我从串行监视器向 Arduino 发送任何字符时,Arduino 不会打印“a”。我认为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 http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS


旁注:您的代码评论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主要问题这完全破坏了你的代码:

  1. 由于您正在启用定时器比较匹配 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
    }
    
  2. 进入 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
}

如果此答案解决了您的问题,请点赞并接受它作为正确答案。谢谢!

额外资源:

  1. 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.
    1. 特别是查看 Ken Shirriff 和 Nick Gammon 的“高级”部分下的第一个链接。他们太棒了!
  2. 尼克·加蒙的回答在这里 https://stackoverflow.com/a/31826149/4561887
  3. [我的答案]哪些 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
  4. [我自己的问题和答案]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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Arduino 上的 Timer1 导致串行打印无法工作 的相关文章

  • .net 计时器有多可靠?

    我正在考虑在 Windows 服务中使用 System Timers Timer 我想知道它们的可靠性和准确性如何 尤其 对于它们的运行频率有任何保证吗 当处理器或内存过载时会发生什么 在这种情况下 ElapsedEventArgs Sig
  • Android:定时器/延迟替代方案

    我想让一个图像在 60 毫秒内可见 然后不可见 然后我想让另一个图像执行相同的操作 等等 我认为我没有正确使用计时器 因为当我运行该应用程序时 两个图像会同时打开 并且当我按下使用此功能的按钮时 两个图像不会消失 这是一些示例代码 time
  • Processing.js 定时器

    我正在使用Processing js 开发一个应用程序 在绘图循环的每一步 我都会将帧数增加一frame 我想知道已经过去了多少时间 目前为了获取时间 知道我的应用程序设置为以 60FPS 运行 我确实这样做 time frame 60 但
  • 在 Swift 上设置计时器

    我尝试重复执行函数 pepe 我没有收到错误 但它不起作用 这是我的代码 public class MyClass var timer Timer objc func pepe gt String let hola hola return
  • 如何使用GSM模块SIM800和Arduino Uno发送短信?

    我正在尝试通过 SIM800 GSM 模块从 Arduino 发送短信 消息到达给定号码 但格式不正确 它显示 消息格式不支持 我在这里添加了我的代码 非常感谢您的快速回复 include
  • 在java中设置按钮点击延迟?

    我有一个保存按钮JFrame 单击 保存 时 保存 文本将设置为 正在保存 我需要在延迟 10 秒后将该文本设置为 已保存 在 java 中这怎么可能 请帮忙 try Thread sleep 4000 catch InterruptedE
  • 如何测量脚本的执行时间? [复制]

    这个问题在这里已经有答案了 如何测量脚本从开始运行到结束所需的时间 start timing CODE end timing EDIT 2011 年 1 月 这是最佳的可用解决方案 其他解决方案 例如performance now 现在应该
  • 在JTextArea中使用Timer实现打字机效果?

    我正在制作一款文本冒险游戏 但遇到了一个问题 我无法按照我想要的方式显示一些文本 当输入一些单词时 玩家可以开始引入一个新房间 我希望这个介绍具有 打字机 效果 该事件需要在我的程序 ActionPerformed 方法中发生 例如 当用户
  • 从 iBeacon 接收 BLE 信号到 Bluno(arduino with BLE)

    我想从 iBeacon 到 Bluno 接收 rssi 信号和 UUID Arduino 板具有 BLE 对此有一些疑问 有没有从 BLE 到 BLE 接收 UUID 和 rssi 的解决方案 两个BLE设备可以互相通信吗 我想找一些网站来
  • JavaFX 的 Swing 计时器替代方案以及线程管理差异

    使用 JavaFX 的 Swing 计时器是否安全 或者 Swing 有特殊的替代方案吗 JavaFX 和 Swing 的线程管理有什么区别 事实上我很想知道相当于摇摆计时器 SwingUtilities invokeLater and i
  • 如果 TimerTask 的执行时间超过指定的时间间隔,会发生什么情况?

    使用时 Timer schedule TimerTask task long delay long period 即固定延迟执行 如果指定的情况会发生什么TimerTask s run 方法花费的时间长于period去完成 两个并发有可能吗
  • 在链接时合并全局数组/从多个编译单元填充全局数组

    我想定义一系列的东西 比如事件处理程序 的内容 该数组在编译时是完全已知的 但定义在 多个编译单元 分布在多个库中 至少在最终 静态 链接之前是相当解耦的 我想要 也保持这种方式 因此添加或删除编译单元将 还可以自动管理事件处理程序 而无需
  • 在 Android 中的计时器内运行异步任务

    我正在开发一个基本的聊天类型应用程序 目前我正在运行代码 如下所示 class GetMsgs extends AsyncTask
  • Android:如何在触摸事件中手动实现长按?

    简短版本 我想要一种方法来在 onTouchEvent 上启动基于时间的计数器 并测试在响应之前是否已经过了一定的时间 作为手动 LongTouch 检测 解释 我有一个自定义 imageView 可以通过两根手指滑动滑入 滑出屏幕 我想向
  • 如何实现urllib2.urlopen的超时控制

    如何在Python中实现对urllib2 urlopen的控制 我只是想监控如果5秒内没有xml数据返回 则切断此连接并重新连接 我应该使用一些计时器吗 谢谢 urllib2 urlopen http www example com tim
  • Arduino Nano 33 IoT 的软件序列号

    我计划从旧的 5V Arduino Nano 迁移到新的 Arduino Nano 33 IoT 我已经使用软件串行 SoftwareSerial h 为旧的 5V Arduino Nano 编写了一个功能代码 以便与 Sim800L 模块
  • 如何制作wpf倒计时器?

    我想创建 wpf 倒数计时器 将结果显示为hh mm ss进入文本框 我将感谢任何人的帮助 您可以使用DispatcherTimer class msdn http msdn microsoft com en US library syst
  • Android相当于javascript的setTimeout和clearTimeout?

    setTimeout 有一个答案https stackoverflow com a 18381353 433570 https stackoverflow com a 18381353 433570 它没有提供我们是否可以像在 JavaSc
  • 停止从标准输入读取

    我正在用 LInux C 编写一个简单的控制台应用程序 它接受来自命令行的用户输入 我在用std getline std cin std cin gt gt text在一个线程中 10 秒后 我想停止接受控制台输入并写一条短信 然后做其他事
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您

随机推荐