0

我有一个 Atmega328p,我已经将一个 LED 连接到它的 D4 引脚,我希望它每秒钟打开/关闭一次 LED。

我找到了本教程,并根据一些在线 AVR 定时器计算器和我使用的 12MHZ 外部晶体将其更改为:

#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 总是亮或灭。我怎样才能让它工作?

4

2 回答 2

3

计时器初始化看起来不错 (请参阅其他答案),但看起来您没有正确切换 LED。

PORTD |= 0 << 4;什么都不做。

假设您直接驱动 LED,使用PORTD |= (1 << 4);来打开 LED、PORTD &= ~(1 << 4);关闭 LED 并PORTD ^= (1 << 4);切换输出状态。

由于您只想切换 LED 状态,因此最后一个选项显然是最佳选择,因为您不必检查输出引脚的当前状态来决定是否需要打开或关闭。

于 2020-10-05T09:56:21.727 回答
0
TCCR1B |= (1 << WGM12);
    // Mode 4, CTC on OCR1A

注释是正确的:设置该位WGM12(而其他 WGM1x 位为零)将打开 CTC(比较匹配时清除定时器)模式,其 TOP 值由 定义OCR1A

但!

ICR1 = 0xB71B;

您正在将 TOP 值写入输入捕获寄存器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;
于 2020-10-05T10:31:43.473 回答