如何使 Atmega328 定时器中断每 1 秒滴答一次?
How to make Atmega328 timmer interrupt tick every 1 second?
我有一个 Atmega328p,我已将 LED 连接到它的 D4 引脚,我希望它每隔一秒点亮 LED on/off。
我找到了 this tutorial,我已经根据一些在线 AVR 定时器计算器和我使用的 12MHZ 外部 crystal 将其更改为:
#define F_CPU 12000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
DDRD |= 1 << 4;
PORTD |= 1 << 4;
ICR1 = 0xB71B;
TCCR1B |= (1 << WGM12);
// Mode 4, CTC on OCR1A
TIMSK1 |= (1 << ICIE1);
//Set interrupt on compare match
TCCR1B |= (1 << CS12);
// set prescaler to 256 and starts the timer
sei();
// enable interrupts
while (1)
{
// we have a working Timer
}
}
ISR (TIMER1_COMPA_vect)
{
PORTD |= 0 << 4;
// action to be done every 200ms
}
无论我如何更改ICR1 值,LED 总是亮或灭。我怎样才能让它发挥作用?
计时器初始化看起来不错(参见其他答案),但看起来您没有正确切换 LED。
PORTD |= 0 << 4;
什么都不做。
假设您直接驱动 LED,使用 PORTD |= (1 << 4);
打开 LED,使用 PORTD &= ~(1 << 4);
关闭 LED,使用 PORTD ^= (1 << 4);
切换输出状态。
由于您只想切换 LED 状态,最后一个选项显然是最佳选择,因为您不必检查输出引脚的当前状态来决定是否需要打开或关闭。
TCCR1B |= (1 << WGM12);
// Mode 4, CTC on OCR1A
注释正确:设置位 WGM12
(而其他 WGM1x 位为零)将打开 CTC(清除定时器
将 match) 模式与 OCR1A
.
定义的 TOP 值进行比较
但是!
ICR1 = 0xB71B;
你是把TOP值写入input-capture寄存器ICR1
(也有这样的模式WGM12:WGM11:wGM11:WGM10设置为1110,但是需要用到另外一个中断) .
您想将该值写入 OCR1A
。
12 000 000 / 256(定时器预分频器)- 1 = 46874 ,即 0xB71A,而不是 0xb71B:您忘记减去 1.
由于定时器从零开始计数,所以TOP值比定时器的完整周期少1
而且在这种情况下,最好使用小数或公式来使代码更具可读性。
OCR1A = (F_CPU / 256) - 1; // 46874
还有。正如 Rev1.0 所指出的,您需要在中断中切换输出。
您可以使用按位独占或 ^
:
PORTD ^= 1 << 4;
或者,在 Atmega328P 中,您只需将 1 写入 PINx
寄存器即可切换 PORTx
:
中的位值
PIND = 1 << 4;
我有一个 Atmega328p,我已将 LED 连接到它的 D4 引脚,我希望它每隔一秒点亮 LED on/off。
我找到了 this tutorial,我已经根据一些在线 AVR 定时器计算器和我使用的 12MHZ 外部 crystal 将其更改为:
#define F_CPU 12000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
DDRD |= 1 << 4;
PORTD |= 1 << 4;
ICR1 = 0xB71B;
TCCR1B |= (1 << WGM12);
// Mode 4, CTC on OCR1A
TIMSK1 |= (1 << ICIE1);
//Set interrupt on compare match
TCCR1B |= (1 << CS12);
// set prescaler to 256 and starts the timer
sei();
// enable interrupts
while (1)
{
// we have a working Timer
}
}
ISR (TIMER1_COMPA_vect)
{
PORTD |= 0 << 4;
// action to be done every 200ms
}
无论我如何更改ICR1 值,LED 总是亮或灭。我怎样才能让它发挥作用?
计时器初始化看起来不错(参见其他答案),但看起来您没有正确切换 LED。
PORTD |= 0 << 4;
什么都不做。
假设您直接驱动 LED,使用 PORTD |= (1 << 4);
打开 LED,使用 PORTD &= ~(1 << 4);
关闭 LED,使用 PORTD ^= (1 << 4);
切换输出状态。
由于您只想切换 LED 状态,最后一个选项显然是最佳选择,因为您不必检查输出引脚的当前状态来决定是否需要打开或关闭。
TCCR1B |= (1 << WGM12);
// Mode 4, CTC on OCR1A
注释正确:设置位 WGM12
(而其他 WGM1x 位为零)将打开 CTC(清除定时器
将 match) 模式与 OCR1A
.
但是!
ICR1 = 0xB71B;
你是把TOP值写入input-capture寄存器ICR1
(也有这样的模式WGM12:WGM11:wGM11:WGM10设置为1110,但是需要用到另外一个中断) .
您想将该值写入 OCR1A
。
12 000 000 / 256(定时器预分频器)- 1 = 46874 ,即 0xB71A,而不是 0xb71B:您忘记减去 1.
由于定时器从零开始计数,所以TOP值比定时器的完整周期少1
而且在这种情况下,最好使用小数或公式来使代码更具可读性。
OCR1A = (F_CPU / 256) - 1; // 46874
还有。正如 Rev1.0 所指出的,您需要在中断中切换输出。
您可以使用按位独占或 ^
:
PORTD ^= 1 << 4;
或者,在 Atmega328P 中,您只需将 1 写入 PINx
寄存器即可切换 PORTx
:
PIND = 1 << 4;