如何使Atmega328微调器中断每1秒钟滴答一次?

问题描述

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

我找到了this tutorial,并根据一些在线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始终亮或灭。我该如何运作?

解决方法

计时器初始化看起来不错 (请参见其他答案),但是看来您没有正确切换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(清除定时器打开) 比较匹配)模式与OCR1A定义的TOP值。

但是!

ICR1 = 0xB71B;

您正在将TOP值写入输入捕获寄存器ICR1(也存在这样一种模式,其中WGM12:WGM11:wGM11:WGM10设置为1110,但是它需要使用另一个中断)。 / p>

您想将值写入OCR1A

12000000/256(定时器预分频器)-1 = 46874,它是0xB71A,而不是0xb71B:您忘了减去1。

由于计时器从零开始计数,因此TOP值比计时器的整个周期小1。

在这种情况下,最好使用十进制或公式来使代码更具可读性。

OCR1A = (F_CPU / 256) - 1; // 46874 

也。如Rev1.0所述,您需要在中断中切换输出。

您可以使用按位互斥或^来做到这一点:

    PORTD ^= 1 << 4;

或者,在Atmega328P中,您只需向PINx寄存器中写入1即可切换PORTx中的位值:

    PIND = 1 << 4;