Arduino 上的 Timer1 使串行打印无法正常工作 [英] Timer1 on Arduino makes printing on Serial not work

查看:24
本文介绍了Arduino 上的 Timer1 使串行打印无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

运行下面的代码,当我从串行监视器向 Arduino 发送任何字符时,Arduino 不会打印a";背部.我认为是timer1代码有问题,但应该可以,因为这段代码是我在C课上的老师给我的.

Running the code below, when I send any character to the Arduino from the Serial Monitor, the Arduino does not print "a" back. I think it's something wrong with the timer1 code, but it should work because this code was given to me by my teacher in C class.

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

推荐答案

旁注:您关于 8 位预分频器 的代码注释具有误导性.它不是一个8位预分频器,而是一个8的预分频器,意思是十进制值8代码>.这意味着定时器的时钟滴答速率比主时钟慢 8 倍,因为您将主时钟频率除以预分频器以获得定时器的时钟频率.

Side Note: your code comment about 8 bits prescaler is misleading. It is not an 8-bit prescaler, rather, it is simply a prescaler of 8, meaning the decimal value 8. All that means is the timer's clock tick rate is 8x slower than the main clock, since you divide the main clock frequency by the prescaler to get the timer's clock frequency.

你设置TCCR1ATCCR1B的方式都是正确的.

The way you set TCCR1A and TCCR1B is all correct.

但是,您有 2 个大问题、1 个小问题和 1 个建议.

请参阅 660 页 ATmega328 数据表页.132~135 获取更多帮助 &如果您想知道从现在开始到哪里寻求低级帮助,请提供信息.

See the 660-pg ATmega328 datasheet pgs. 132~135 for more help & info if you want to know where to look from now on for low-level help.

更新:新的 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.因此,我在上面和下面提到的页码可能不再完全匹配,因为我在写这篇文章时使用的是旧版本的数据表.

Update: the new ATmega328 sales page is here: https://www.microchip.com/wwwproducts/en/ATmega328. Its new datasheet is available here: https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf. So, the page numbers I mention above and below will likely no longer quite match since I was using an older version of the datasheet when I wrote this.

  1. 由于您启用了定时器比较匹配 1A 中断 (TIMSK1 |= (1 << OCIE1A);),您还必须定义中断服务程序 (ISR)将在发生这种情况时调用,否则您将遇到运行时(但不是编译时)问题.即,如果您没有为输出比较匹配 A 定义 ISR,一旦发生输出比较 A 中断,处理器将卡在编译器为您创建的无限空虚 ISR 中,您的主循环将不会进行(请参阅下面的代码以证明这一点).

  1. Since you are enabling the Timer Compare Match 1A interrupt (TIMSK1 |= (1 << OCIE1A);), you MUST also define the Interrupt Service Routine (ISR) which will be called when this happens, or else you will have run-time (but not compile-time) problems. Namely, if you do not define the ISR for Output Compare Match A, once the Output Compare A interrupt occurs, the processor will get stuck in an infinite, empty, dummy ISR created for you by the compiler, and your main loop will not progress (see code below for proof of this).

将此添加到代码的底部:

Add this to the bottom of your code:

ISR(TIMER1_COMPA_vect)
{
  // insert your code here that you want to run every time the counter
  // reaches OCR1A
}

  • 进入 ISR 需要几微秒,退出 ISR 需要几微秒,加上在 ISR 中运行代码所需的任何时间,您需要使用一个足够大的 OCR1A 值,以便 ISR 甚至有时间执行,而不是持续调用如此之快以至于您永远不会退出 ISR(这将本质上将您的代码锁定在一个无限循环......这也发生在你的情况下).

  • It takes a couple microseconds to step into an ISR, and a couple microseconds to step out of an ISR, plus whatever time is required to run your code IN the ISR, you need to use an OCR1A value that is large enough that the ISR even has time to execute, rather than being continually called so quickly that you never exit the ISR (this would lock up your code essentially into an infinite loop....which is happening in your case as well).

    我建议您调用 ISR 的频率不要超过每 10us.由于您使用的是 CTC 模式(比较匹配时清除定时器),预分频器为 8,我建议将 OCR1A 设置为不小于 20 左右.OCR1A = 20 会每 10us 调用一次 ISR.(预分频器为 8 意味着每个 Timer1 周期需要 0.5us,因此 OCR1A = 20 将每 20*0.5 = 10us 调用一次 ISR.

    I recommend you call an ISR no more often than every 10us. Since you are using CTC mode (Clear Timer on Compare match), with a prescaler of 8, I recommend setting OCR1A to nothing less than 20 or so. OCR1A = 20 would call the ISR every 10us. (A prescaler of 8 means that each Timer1 tick take 0.5us, and so OCR1A = 20 would call the ISR every 20*0.5 = 10us).

    如果您设置 OCR1A = 20,并按上述添加 ISR 代码,您的代码将运行得很好.

    If you set OCR1A = 20, and add the ISR code as described above, your code will run just fine.

    设置OCR1A 之后设置定时器的其余部分是一种很好的做法,否则在某些情况下定时器可能不会开始计数.参见 "Thorsten's在这里评论(强调):

    It is good practice to set OCR1A after you configure the rest of the timer, or else under some situations the timer may not start counting. See "Thorsten's" comment here (emphasis added):

    Thorsten 说...

    Thorsten said...

    感谢您如此广泛地解释这个问题!我正在寻找一种在其中一个 Arduino 引脚上生成 1 MHz 的方法.你的帖子帮了我很大的忙.

    Thanks for explaining this matter so extensively! I was looking for a way to generate 1 MHz on one of the Arduino-pins. Your post helped me a great deal to accomplish that.

    我写这篇评论的原因如下:我花了将近 6 个小时才发现(主要是出于绝望)设置定时器控制寄存器的顺序TCCR2* 和输出比较寄存器 OCR2* 似乎很重要!如果您在设置相应的 TCCR 之前分配了 OCR,则计时器根本不会开始计数.

    The reason I am writing this comment is the following: It took me almost 6 hours till I found out (mainly in sheer desperation) that the order of setting the timer control registers TCCR2* and the output compare registers OCR2* seems to matter! If you assign an OCR before setting the corresponding TCCR the timer simply doesn't start counting.

    2 月 13 日上午 11:47

    因此,将 OCR1A = 20; 移动到 after 你的最后一个 TCCR1B 行和 before 你的 TIMSK1 行.

    So, move OCR1A = 20; to after your last TCCR1B line and before your TIMSK1 line.

    摆脱对noInterrupts()interrupts() 的调用.这里不需要它们.

    Get rid of your calls to noInterrupts() and interrupts(). They are not needed here.

    现在,这是我编写的一些功能代码,它们将更好地展示您正在尝试做什么以及我在说什么:

    /*
    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++;
    }
    

    运行它,看看你的想法.

    Run it and see what you think.

    (至少据我所知——基于对 Arduino Nano 的测试,使用 IDE 1.6.0):

    (at least as far as I understand it--and based on tests on an Arduino Nano, using IDE 1.6.0):

    下面的代码可以编译,但不会继续打印a"(但是,它可以打印一次).请注意,为简单起见,我注释掉了等待串行数据的部分,并简单地告诉它打印一个a"每半秒:

    This code below compiles, but will not continue to print the "a" (it may print it once, however). Note that for simplicity-sake I commented out the portion waiting for serial data, and simply told it to print an "a" every half second:

    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 声明:

    This code below, on the other hand, works, and the "a" will continue to print out. The only difference between this one and the one just above is that this one has the ISR declaration uncommented at the bottom:

    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. 我在这里写的一篇文章的底部列出了我遇到的最有用的 Arduino 资源的列表:http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html.检查一下.
    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. Check them out.
    1. 特别注意高级"下的第一个链接部分,由 Ken Shirriff 和 Nick Gammon 撰写.他们太棒了!

  • Nick Gammon 的回答在这里
  • [我的回答] 哪些 Arduino 支持 ATOMIC_BLOCK?我如何在 C 中用 __attribute__((__cleanup__(func_to_call_when_x_exits_scope))) 复制这个概念,在 C++ 中用 constructorsdestructors 复制这个概念?
  • [我自己的问答] C++ 递减单字节(易失性)数组的元素不是原子的!为什么?(另外:我如何在 Atmel AVR mcus/Arduino 中强制原子性)
  • Nick Gammon's answer here
  • [my answer] Which Arduinos support ATOMIC_BLOCK? And how can I duplicate this concept in C with __attribute__((__cleanup__(func_to_call_when_x_exits_scope))) and in C++ with class constructors and destructors?
  • [my own Question and answer] C++ decrementing an element of a single-byte (volatile) array is not atomic! WHY? (Also: how do I force atomicity in Atmel AVR mcus/Arduino)
  • 这篇关于Arduino 上的 Timer1 使串行打印无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆