Arduino的定时器1串行使得无法正常工作 [英] Timer1 arduino makes Serial not work

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

问题描述

运行code以下,当我发送的序列Arduino的任何字符不打印一回来。我认为这是什么毛病定时器1 code,但它应该工作导致此code是我的老师C级给出。

 无效设置(){    Serial.begin(115200);    //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
    noInterrupts();
    TCCR1A = 0; //设置整个TCCR1A寄存器0
    TCCR1B = 0; //相同为TCCR1B
    TCNT1 = 0; //初始化计数器值到0
    //设置比较匹配寄存器与8位prescaler 1000000hz增量
    OCR1A = 1; // =(16 * 10 ^ 6)/(1000000 * 8) - 1(必须是LT; 65536)
    //打开CTC模式
    TCCR1B | =(1 <<;&下; WGM12);
    //设置CS11位8 prescaler。每个定时器都有一个不同的位code到每个prescaler
    TCCR1B | =(1 <<;&下; C​​S11);
    //启用定时器比较中断
    TIMSK1 | =(1 <<;&下; OCIE1A);
    中断();}空隙环(){    如果(Serial.available()){        Serial.println(一);    }}


解决方案

在设置TCCR1A和B是正确的方式。

查看660-PG ATmega328数据表PGS。 132〜135为更多的帮助和放大器;信息,如果你想知道在哪里可以从现在开始寻找低层次的帮助。

但是,你有2个主要问题,1名未成年人的问题,并建议1。

这里有两个主要问题彻底打破你的code:


  1. 因为要启用定时器比较匹配1A中断(TIMSK1 | =(1 <<;&LT; OCIE1A);),您还必须定义中断服务程序(ISR),当发生这种情况,将被调用,或否则你将有运行时(而不是编译时)的问题。也就是说,如果不定义ISR的输出比较匹配A,一旦输出比较A中断发生时,处理器将陷入无限,空,假的编译器为您创建ISR和主循环就不会进步(请参阅下面的证明了这一点code)。

添加到您的code的底部:

  ISR(TIMER1_COMPA_vect)
{
  //此处插入您的code要运行的每个计数器达到OCR1A时间
}

<醇开始=2>

  • 这需要几微秒踏入一个ISR和几个微秒走出ISR的,的以及的任何时间需要在ISR运行code,你需要基本上使用OCR1A值足够大的ISR甚至有时间来执行,而不是不断地呼吁如此之快,你永远不会退出ISR(这将锁定您的code进入一个无限循环。 ......这是在您的情况发生为好)。的结果
    我建议你​​没有更多的时候叫ISR超过每10微秒。由于使用CTC模式(清除定时器上比较匹配),为8的prescaler,我建议设置OCR1A来无外乎20左右。 OCR1A = 20将调用ISR每10微秒。 (8只A prescaler意味着每个定时器剔取0.5us内,所以OCR1A = 20将调用ISR每隔20 * 0.5 = 10微秒)。

  • 如果您设置OCR1A = 20,并且如上所述添加ISR code,你的code将运行得很好。

    1小问题:

    这是很好的做法,设置OCR1A的之后的配置定时器的其余部分,或者在某些情况下,计时器可能无法启动计数(参见托尔斯滕的的评论在这里:的 http://www.righto.com/2009/07/secrets-of-arduino- pwm.html

    所以,移动OCR1A = 20;到的 的你最后TCCR1B线后的的你TIMSK1行。

    建议1:

    摆脱noInterrupts和中断的。此处没有必要的。

    现在,这里是一个code我写这将更好地展示你想要做什么,以及我说的:

      / *
    定时器1 - Arduino的使串行 - 不work.ino
    -a演示来帮助这个人在这里:http://stackoverflow.com/questions/28880226/timer1-arduino-makes-serial-not-work
    加布里埃尔斯台普斯
    http://electricrcaircraftguy.blogspot.com/
    5 2015年3月
    - 使用Arduino的1.6.0
    * ///注:ISR代表中断服务程序//全局变量
    挥发性无符号长numISRcalls = 0; //次数的ISR被称为无效设置()
    {
      Serial.begin(115200);  //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
    // noInterrupts(); //不必要
      TCCR1A = 0; //设置整个TCCR1A寄存器0
      TCCR1B = 0; //相同为TCCR1B
      TCNT1 = 0; //初始化计数器值到0
      //设置比较匹配寄存器与8位prescaler 1000000hz增量
      OCR1A = 20; // =(16 * 10 ^ 6)/(1000000 * 8) - 1(必须是&LT; 65536)//更好地把这一行后配置TCCR1A和B,但在Arduino的1.6.0出现要确定这里(可能会崩溃code在旧版本中,看到评论的托尔斯滕在这里:http://www.righto.com/2009/07/secrets-of-arduino-pwm.html
      //打开CTC模式[清除计时器上比较匹配---使计时器重新开始在OCR1A;看看数据表皮克。 133]
      TCCR1B | =(1 <<;&下; WGM12);
      //设置CS11位8 prescaler [蜱为0.5μs,数据表皮克。 135]。每个定时器都有一个不同的位code到每个prescaler
      TCCR1B | =(1 <<;&下; C​​S11);
      //启用定时器比较匹配1A中断;现在您*必须*设置相应的ISR或本换行符code
      TIMSK1 | =(1 <<;&下; OCIE1A);// OCR1A = 20; // =(16 * 10 ^ 6)/(1000000 * 8) - 1(必须是LT; 65536)//设定OCR1A 1或2肯定打破了code,因为它调用中断过于频繁
    //中断();  Serial.println(设置完成后,输入字符);
    }无效循环()
    {
      如果(Serial.available())
      {
        Serial.read(); //读取并扔掉输入串行缓冲器的第一个字节(或者下一行会被调用每一次循环发送Arduino的一个char)
        Serial.println(一);    //还打印出多少次OCR1A已经被定时器1的计数器达到
        noInterrupts();在读取变量本身包括 - volatile变量可能会被ISR随时修改;而阅读非原子//关闭中断(1字节&GT)。
        无符号长numISRcalls_copy = numISRcalls;
        中断();
        Serial.print(numISRcalls =); Serial.println(numISRcalls_copy);
      }// Serial.println(测试);
    //延迟(1000);
    }//由于要启用比较匹配1A中断以上,你*必须*包含相应的中断服务程序code
    ISR(TIMER1_COMPA_vect)
    {
      //此处插入您的code要运行的每个计数器达到OCR1A时间
      numISRcalls ++;
    }

    运行一下,看看你的想法。

    证明,上面的主要问题1是真实的(至少就我的理解 - 基于对一个Arduino纳米测试,使用IDE 1.6.0):结果
    下面这张code编译,但不会继续打印A(它可能然而,一旦打印)。注意,为了简化-缘故,我注释掉的部分,等待串行数据,只是告诉它打印的A每半秒:

     无效设置(){    Serial.begin(115200);    //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
        TCCR1A = 0; //设置整个TCCR1A寄存器0
        TCCR1B = 0; //相同为TCCR1B
        TCNT1 = 0; //初始化计数器值到0
        //设置比较匹配寄存器与8位prescaler 1000000hz增量
        OCR1A = 20; // =(16 * 10 ^ 6)/(1000000 * 8) - 1(必须是LT; 65536)
        //打开CTC模式
        TCCR1B | =(1 <<;&下; WGM12);
        //设置CS11位8 prescaler。每个定时器都有一个不同的位code到每个prescaler
        TCCR1B | =(1 <<;&下; C​​S11);
        //启用定时器比较中断
        TIMSK1 | =(1 <<;&下; OCIE1A);
    }空隙环(){
        //如果(Serial.available()){
        // Serial.println(一);
        //}    Serial.println(一);
        延迟(500);
    }// ISR(TIMER1_COMPA_vect)
    // {
    // //此处插入您的code要运行的每个计数器达到OCR1A时间
    //}

    此$ C $低于C,另一方面,工作,和a将继续进行打印。这一次,只是上面的那个唯一的区别是,这其中有在底部注释掉了ISR声明:

     无效设置(){    Serial.begin(115200);    //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
        TCCR1A = 0; //设置整个TCCR1A寄存器0
        TCCR1B = 0; //相同为TCCR1B
        TCNT1 = 0; //初始化计数器值到0
        //设置比较匹配寄存器与8位prescaler 1000000hz增量
        OCR1A = 20; // =(16 * 10 ^ 6)/(1000000 * 8) - 1(必须是LT; 65536)
        //打开CTC模式
        TCCR1B | =(1 <<;&下; WGM12);
        //设置CS11位8 prescaler。每个定时器都有一个不同的位code到每个prescaler
        TCCR1B | =(1 <<;&下; C​​S11);
        //启用定时器比较中断
        TIMSK1 | =(1 <<;&下; OCIE1A);
    }空隙环(){
        //如果(Serial.available()){
        // Serial.println(一);
        //}    Serial.println(一);
        延迟(500);
    }ISR(TIMER1_COMPA_vect)
    {
      //此处插入您的code要运行的每个计数器达到OCR1A时间
    }

    附加资源:


    1. 我把最有用的Arduino的资源在我的文章中,我写到这里的底部遇到一个正在运行的列表:的 http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html 。检查出来。


    2. 特别是看第一环节,高级部分下,由Ken Shirriff和尼克金门。他们是优秀的!


    请投票这个答案,如果它解决您的问题,并接受它作为正确的答案;谢谢!

    真诚的,结果
    加布里埃尔斯台普斯结果
    http://www.ElectricRCAircraftGuy.com/

    Running the code below, when I send any character in the Serial arduino is not printing "a" back. I think it's something wrong with the timer1 code but it should work cause this code was given 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");
    
        }
    
    }
    

    解决方案

    The way you set TCCR1A and B is all correct.

    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.

    However, you have 2 major problems, 1 minor problem, and 1 recommendation.

    Here are the 2 major problems are completely breaking your code:

    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
    }
    

    1. 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).
      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).

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

    1 Minor problem:

    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: http://www.righto.com/2009/07/secrets-of-arduino-pwm.html)

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

    1 recommendation:

    Get rid of "noInterrupts" and "interrupts". They are not needed here.

    Now, here is a code I wrote which will better demonstrate what you're trying to do, and what I'm talking about:

    /*
    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
      // set compare match register for 1000000hz increments with 8 bits prescaler
      OCR1A = 20;// = (16*10^6) / (1000000*8) - 1 (must be <65536) //better to put this line AFTER configuring TCCR1A and B, 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
      // 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
      TIMSK1 |= (1 << OCIE1A);
    
    //  OCR1A = 20;// = (16*10^6) / (1000000*8) - 1 (must be <65536) //SETTING OCR1A TO 1 OR 2 FOR SURE BREAKS THE CODE, as it calls the interrupt too often
    //  interrupts();
    
      Serial.println("setup done, input a character");
    }
    
    void loop() 
    {
      if (Serial.available()) 
      {
        Serial.read(); //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 char)
        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 THE 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.

    Proof that "Major Problem 1" above is real (at least as far as I understand it--and based on tests on an Arduino Nano, using IDE 1.6.0):
    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);
    
        //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
        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 = 20;// = (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);
    }
    
    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
    //}
    

    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);
    
        //http://www.instructables.com/id/Arduino-Timer-Interrupts/?ALLSTEPS
        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 = 20;// = (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);
    }
    
    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
    }
    

    Extra Resources:

    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.

    2. Especially look at the first links, under the "Advanced" section, by Ken Shirriff and Nick Gammon. They are excellent!

    Please vote this answer up if it solves your problem, and accept it as the correct answer; thanks!

    Sincerely,
    Gabriel Staples
    http://www.ElectricRCAircraftGuy.com/

    这篇关于Arduino的定时器1串行使得无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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