AVR CTC 模式下的 16 位定时器

Posted

技术标签:

【中文标题】AVR CTC 模式下的 16 位定时器【英文标题】:16-bit timer in AVR CTC mode 【发布时间】:2014-01-02 17:25:10 【问题描述】:

我正在尝试使用 Arduino Uno 板(ATmega328,16 MHz)来实现这一目标。于是我在网上搜索了一下,想到了这样的东西:

unsigned long Time=0;

int main (void)

  Serial.begin(9600);

  cli();

  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 15999; // Compare value

  TCCR1B |= (1 << WGM12)| (1 << CS10); // Prescaler
  TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt

  sei();

  while(1) 

    Serial.println(TCNT1);
  

  return 0;



ISR(TIMER1_COMPA_vect)

  Time++;
  Serial.println(Time);

我正在尝试实现 1 kHz 的频率,因此我将能够创建几毫秒长的间隔。

这就是为什么我选择比较值是 15999(所以 16000-1)和预分频器等于 1,所以我得到(至少我认为是正确的计算):

Frequency = 16.000.000 MHz/16000 = 1000 Hz = 1 kHz

现在的问题是,即使Serial.println(TCNT1) 向我显示的数字计数到 16000,回零,最多 16000,回零,...,Serial.println(Time) 只计数到 8,它只是停止计数,尽管 TCNT1 仍在计数。

我在某处想过某种溢出,但我想不出在哪里;我想出的唯一一件事是比较值可能太大了——我认为——自2^16 -1=65.535&gt;15999以来显然不是这种情况。

例如,如果我制作预分频器,假设为 64,并保留比较值,Time 将按预期计数。所以我想知道:为什么ISR() 在值为 8 时停止被调用,但在调出预分频器时工作?

【问题讨论】:

Time 声明在哪里? 这对我来说看起来像是一个竞争条件,你正在从中断处理程序和主循环中打印。想象一下您将如何实现 Serial.println 以及如果 IRQ 被触发并开始尝试打印而主循环已经在打印不同的消息会发生什么。从中断处理程序调用像这样的高级系统函数根本是一个主要的禁忌,除非你非常确定你在做什么以及库提供的重入保证,它们不像线程那样可以被锁定并安全恢复 @doynax 我明白你想说什么,问题是即使我注释掉它仍然会发生,即 Serial.println(TCNT1) ;它仍然只计算时间直到值 8 (如果我也将最后一个 Serial.println() 注释掉并且使输出(即通过 LED 可见)也是如此) @VGD:很好奇,那我不能直接发现问题,我担心我现在手边没有板子。尝试从程序中删除所有不相关的内容,包括串行库,并在中断停止触发后使用调试器闯入程序以检查寄存器。哦,以防万一 【参考方案1】:

我不确定,但根据您使用的 Arduino 版本,println 调用会被阻塞。如果你调用它的速度快于它在 ISR 中的完成速度,堆栈就会溢出。

如果您想要更高分辨率的计时,可以尝试在您的Loop() 中区分 getMicroseconds 结果。您应该在Loop() 中循环,远远快于每毫秒一次。

如果您想每毫秒执行一次,请捕获开始微秒,然后在 Loop() 函数的条件中从当前微秒中减去它。当你看到超过 1000 人做任务时...

【讨论】:

【参考方案2】:

对于我的 Arduino Uno (16 MHz),计时器的分辨率似乎太大了。选择较低的分辨率(即较高的比较值)为我解决了这个问题。

【讨论】:

16000 仅适用于 Timer1(这是一个 16 位定时器),而 Timer0 和 Timer2 是 8 位定时器。但是您正在使用 Timer1。

以上是关于AVR CTC 模式下的 16 位定时器的主要内容,如果未能解决你的问题,请参考以下文章

如何在 avr-gcc 中定义定时器

定时/计数器 脉冲计数

单片机串口中断接收

51高级定时器简介

Avr-gcc:定时器/计数器中断与 UART 冲突?

51定时器的简介