Timer1 arduino使Serial无法正常工作

时间:2015-03-05 14:29:43

标签: timer arduino

运行下面的代码,当我发送Serial 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");

    }

}

5 个答案:

答案 0 :(得分:13)

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

请参阅660-pg ATmega328数据表。 132~135获得更多帮助&amp;如果您想知道从现在开始在哪里寻找低级帮助,请提供信息。

但是,您有2个主要问题,1个小问题和1个推荐。

以下是完全破坏您的代码的两个主要问题:

  1. 由于您正在启用定时器比较匹配1A中断(&#34; TIMSK1 | =(1&lt;&lt;&lt;&lt; OCIE1A);&#34;),您还必须定义中断服务程序(ISR)在发生这种情况时调用,否则您将遇到运行时(但不是编译时)问题。也就是说,如果没有为输出比较匹配A定义ISR,一旦输出比较A中断发生,处理器将陷入由编译器为您创建的无限空虚拟ISR中,并且您的主循环将不会进展(见下面的代码以证明这一点)。
  2. 将其添加到代码的底部:

    ISR(TIMER1_COMPA_vect)
    {
      //insert your code here that you want to run every time the counter reaches OCR1A
    }
    
    1. 需要几微秒才能进入ISR,并且只需几微秒即可退出ISR, plus 无论何时需要在ISR中运行代码,您需要使用一个足够大的OCR1A值,ISR甚至有时间执行,而不是连续调用,以至于你永远不会退出ISR(这会将你的代码锁定在一个无限循环中......这正在发生在你的情况下)。
      我建议你不要每隔10us调用一次ISR。由于您使用CTC模式(比较匹配时清除定时器),预分频器为8,我建议将OCR1A设置为不低于20左右。 OCR1A = 20将每10us调用一次ISR。 (预分频器为8表示每个Timer1标记为0.5us,因此OCR1A = 20将每隔20 * 0.5 = 10us调用ISR。)
    2. 如果您设置OCR1A = 20,并按上述方法添加ISR代码,您的代码将运行正常。

      1小问题:

      最好在配置剩余的计时器后设置OCR1A ,否则在某些情况下计时器可能无法开始计数(参见&#34; Thorsten&#39;&#34;#34 ;在此评论:http://www.righto.com/2009/07/secrets-of-arduino-pwm.html

      所以,移动OCR1A = 20; 之后你的TIMSK1系列之前的

      1推荐:

      摆脱&#34; noInterrupts&#34;和&#34;中断&#34;。这里不需要它们。

      现在,这是我编写的代码,它将更好地展示您尝试做的事情,以及我正在谈论的内容:

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

      运行它,看看你的想法。

      证明&#34;主要问题1&#34;以上是真实的(至少根据我的理解 - 并基于Arduino Nano的测试,使用IDE 1.6.0):
      下面的代码编译,但不会继续打印&#34; a&#34; (但它可以打印一次)。请注意,为了简单起见,我注释掉了等待串行数据的部分,并简单地告诉它打印一个&#34; a&#34;每半秒钟:

      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
      //}
      

      另一方面,下面的代码可以正常运行,并且&#34; a&#34;将继续打印出来。这个和上面的唯一区别是这个在底部没有注释ISR声明:

      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
      }
      

      额外资源:

      1. 我在这里写的一篇文章的最后部分列出了最有用的Arduino资源:http://electricrcaircraftguy.blogspot.com/2014/01/the-power-of-arduino.html。检查出来。

      2. 特别要看第一个链接,在&#34; Advanced&#34;由Ken Shirriff和Nick Gammon创作。他们非常棒!

      3. 如果它能解决您的问题,请将此答案投票,并接受为正确的答案;谢谢!

        此致
        加布里埃尔史泰博斯 http://www.ElectricRCAircraftGuy.com/

答案 1 :(得分:2)

Gabriel Staples非常正确,你没有看到“a”的原因是你没有为中断提供ISR处理程序。因此,编译器生成的代码会跳回到地址0x0000并重新开始草图。

提供“空”ISR处理程序的替代方法是:

EMPTY_INTERRUPT (TIMER1_COMPA_vect);

使用EMPTY_INTERRUPT处理程序,我得到一个响应(“a”),OCR1A低至1:

OCR1A = 1;

虽然如果你不打算对它们做任何事情,你不得不想知道为什么要启用中断。

有关Arduino上interrupts的更多信息。

答案 2 :(得分:0)

你写了2次这个寄存器

TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS11);

虽然我认为它可能是:

TCCR1A |= (1 << WGM12);
TCCR1B |= (1 << CS11);

可能唯一的错误就在那里,因为你忘记设置TCCR1A并且你设置了2倍的另一个。

答案 3 :(得分:0)

TCCR1A | =(1&lt;&lt; WGM12);是按位运算(按位OR)。 在这种特殊情况下,只设置TCCR1A的一位,即位置WGM12中的一位。 TCCR1B | =(1 <&lt; CS11);在位置CS11中设置不同的位

答案 4 :(得分:0)

取决于程序需要对这种快速中断执行的操作,例如在输出引脚上产生高速时钟,可以使用TCCR1A中的COM位(在我的存储器中有4个最有意义的位)将其设置为硬件,以在与定时器相关的引脚上切换输出,而无需写入ISR( )回调会在软件中处理定时器中断。