AVR CTC 计时器频率明显不准确
AVR CTC Timer Frequency Apparent Inaccuracy
我是 AVR 设备编程的初学者,试图摆脱低效的 _ms_delay() 和 _us_delay() 阻塞函数我一直在尝试使用内置的程序在定时器中使用 16 位定时器上的 CTC 定时器模式来控制基本 LED 闪烁程序的定时。我的目标是让 LED 以 2 Hz 的频率闪烁,亮 0.5 秒,灭 0.5 秒。
根据ATMega328P datasheet,CTC输出的频率应该是f_CTC = f_Clock/(2N(OCR1A+1),因为我的芯片是328P Xplained mini ,它的默认 CPU 速度是 16 MHz,使用上面的公式,N=64,达到我想要的频率所需的 OCR1A 值应该是 62499。考虑到所有这些,我写了以下代码:
#include <avr/io.h>
int main(void)
{
// Setup for timer TC1 (16-bit) in CTC mode to run at 2 Hz
TCCR1A = 0x00;
OCR1A = 62499; // Sets time resolution to 0.5 s
TCCR1B = 0x0b;
TCCR1C = 0x00;
// Set pin directions
PORTD &= ~(1<<PORTD6);
DDRD |= (1<<DDD6);
while (1)
{
if(TIFR1 & (1<<OCF1A))
{
PORTD ^= (1<<PORTD6);
}
TIFR1 |= (1<<OCF1A);
}
}
然而,当我 运行 代码时,LED 以 1 Hz 的频率闪烁,我可以用我的 phone 计时。此外,当我将 OCR1A 更改为 31249 时,这应该将频率增加到 4 Hz,它似乎以 8 Hz 的频率闪烁,或者每秒闪烁 4 次。我觉得我对 CTC 模式的工作原理有一些误解,如果有人能简单地向我解释一下,或者我的代码有任何其他问题,我将不胜感激。
我注意到一件事可能会导致您遇到的问题。
您正在使用行 TIFR1 |= (1<<OCF1A);
清除 OCF1A 位。您经常 运行 该行,因此很有可能在设置 OCF1A 时,您的代码只是在 if 语句可以看到它已设置之前立即清除它。您无法控制该位何时设置;它可能发生在循环中的任何时候。
只有在确认OCF1A为1后才能清零,如下所示:
if (TIFR1 & (1 << OCF1A))
{
PORTD ^= (1 << PORTD6);
TIFR1 |= (1 << OCF1A);
}
我是 AVR 设备编程的初学者,试图摆脱低效的 _ms_delay() 和 _us_delay() 阻塞函数我一直在尝试使用内置的程序在定时器中使用 16 位定时器上的 CTC 定时器模式来控制基本 LED 闪烁程序的定时。我的目标是让 LED 以 2 Hz 的频率闪烁,亮 0.5 秒,灭 0.5 秒。
根据ATMega328P datasheet,CTC输出的频率应该是f_CTC = f_Clock/(2N(OCR1A+1),因为我的芯片是328P Xplained mini ,它的默认 CPU 速度是 16 MHz,使用上面的公式,N=64,达到我想要的频率所需的 OCR1A 值应该是 62499。考虑到所有这些,我写了以下代码:
#include <avr/io.h>
int main(void)
{
// Setup for timer TC1 (16-bit) in CTC mode to run at 2 Hz
TCCR1A = 0x00;
OCR1A = 62499; // Sets time resolution to 0.5 s
TCCR1B = 0x0b;
TCCR1C = 0x00;
// Set pin directions
PORTD &= ~(1<<PORTD6);
DDRD |= (1<<DDD6);
while (1)
{
if(TIFR1 & (1<<OCF1A))
{
PORTD ^= (1<<PORTD6);
}
TIFR1 |= (1<<OCF1A);
}
}
然而,当我 运行 代码时,LED 以 1 Hz 的频率闪烁,我可以用我的 phone 计时。此外,当我将 OCR1A 更改为 31249 时,这应该将频率增加到 4 Hz,它似乎以 8 Hz 的频率闪烁,或者每秒闪烁 4 次。我觉得我对 CTC 模式的工作原理有一些误解,如果有人能简单地向我解释一下,或者我的代码有任何其他问题,我将不胜感激。
我注意到一件事可能会导致您遇到的问题。
您正在使用行 TIFR1 |= (1<<OCF1A);
清除 OCF1A 位。您经常 运行 该行,因此很有可能在设置 OCF1A 时,您的代码只是在 if 语句可以看到它已设置之前立即清除它。您无法控制该位何时设置;它可能发生在循环中的任何时候。
只有在确认OCF1A为1后才能清零,如下所示:
if (TIFR1 & (1 << OCF1A))
{
PORTD ^= (1 << PORTD6);
TIFR1 |= (1 << OCF1A);
}