首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Arduino上的Timer1使串行打印无法正常工作

Arduino上的Timer1使串行打印无法正常工作
EN

Stack Overflow用户
提问于 2015-03-05 22:29:43
回答 5查看 7K关注 0票数 3

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

代码语言:javascript
运行
复制
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

EN

回答 5

Stack Overflow用户

发布于 2015-03-06 05:11:00

附注:您关于8 bits prescaler的代码注释具有误导性。它不是一个8位预分频器,相反,它只是一个8的预分频器,这意味着十进制值8。这意味着定时器的时钟滴答率比主时钟慢8倍,因为你将主时钟频率除以预分频器得到定时器的时钟频率。

现在是我的回答:

您设置TCCR1ATCCR1B的方式都是正确的。

但是,您有两个主要问题,一个小问题和一个建议。

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

下面是两个主要的问题,它们完全破坏了你的代码:

  1. 由于您要启用定时器比较匹配1A中断(TIMSK1 |= (1 << OCIE1A);),因此您还必须定义中断服务例程,该例程将在这种情况发生时被调用,否则您将出现运行时(但不是编译时)问题。也就是说,如果您没有为Output Compare Match A定义ISR,一旦发生Output Compare A中断,处理器将陷入由编译器为您创建的无限的、空的、虚拟的ISR中,并且您的主循环将不会继续(参见下面的代码以证明这一点)。

将这段代码添加到代码的底部:

ISR(TIMER1_COMPA_vect) { //在此处插入您希望在每次计数器达到OCR1A }时运行的代码

  1. 进入ISR需要几微秒,离开ISR需要几微秒,再加上在ISR中运行代码所需的任何时间,您需要使用一个足够大的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()的调用。这里不需要它们。

功能代码:

现在,这是我写的一些函数代码,它们将更好地演示您正在尝试做的事情,以及我正在谈论的内容:

代码语言:javascript
运行
复制
/*
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“:

代码语言:javascript
运行
复制
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声明:

代码语言:javascript
运行
复制
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在我写的一篇文章 的底部列出了我遇到的最有帮助的Arduino资源。
    1. 特别关注由Ken Shirriff和Nick Gammon在“高级”部分下的第一个链接。他们是excellent!

  1. Nick Gammon's answer here
  2. my answer
  3. my own Question and answer
票数 15
EN

Stack Overflow用户

发布于 2015-08-05 15:34:39

Gabriel Staples is quite correct,您看不到"a“的原因是因为您没有为中断提供ISR处理程序。因此,编译器生成的代码跳回到地址0x0000,并且草图重新启动。

提供“空”ISR处理程序的另一种方法是:

代码语言:javascript
运行
复制
EMPTY_INTERRUPT (TIMER1_COMPA_vect);

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

代码语言:javascript
运行
复制
OCR1A = 1;

但是,如果您不打算对中断做任何事情,那么为什么要启用中断呢?

关于Arduino上的interrupts的更多信息。

票数 2
EN

Stack Overflow用户

发布于 2015-03-06 02:46:41

你写了两次这个寄存器:

代码语言:javascript
运行
复制
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS11);

虽然我认为这可能是:

代码语言:javascript
运行
复制
TCCR1A |= (1 << WGM12);
TCCR1B |= (1 << CS11);

可能唯一的错误在那里,因为您忘记设置TCCR1A,并且设置了另一个两次。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28880226

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档