How to make Atmega328 timmer interrupt tick every 1 second?

生来就可爱ヽ(ⅴ<●) 提交于 2020-12-15 06:36:46

问题


I've an Atmega328p and I've connected an LED to it's D4 pin and I want it to turn LED on/off every one second.

I found this tutorial and I've change it base on some online AVR timmer calculator and the 12MHZ external crystal which I've used, to this :

#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
}

The LED is always on or off no mater how I change ICR1 value. How can I make it work?


回答1:


The timer initialization looks fine (see other answer), but it looks like you are not toggling the LED correctly.

PORTD |= 0 << 4; will do nothing.

Assuming you drive the LED directly, use PORTD |= (1 << 4); to turn LED on, PORTD &= ~(1 << 4); to turn LED off and PORTD ^= (1 << 4); to toggle the output state.

Since you just want to toggle the LED state, the last option is obviously the best choice, because you do not have to check the current state of the output pin to decide if you need to switch on or off.




回答2:


TCCR1B |= (1 << WGM12);
    // Mode 4, CTC on OCR1A

the comment is correct: Setting the bit WGM12 (while other WGM1x bits are zeroes) will turn on the CTC (Clear Timer on Compare match) mode with TOP value defined by OCR1A.

But!

ICR1 = 0xB71B;

you're writing the TOP value into the input-capture register ICR1 (there is also such a mode with WGM12:WGM11:wGM11:WGM10 set to 1110, but it needs to use another interrupt).

You want to write the value into OCR1A instead.

12 000 000 / 256 (timer prescaller) - 1 = 46874 , which is 0xB71A, not 0xb71B: you forgot to subtract 1.

Since the timer counts from zero, then the TOP value is 1 less than full period of the timer

And in this case it is better to use decimal or a formula to make the code more readable.

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

Also. As it noted by Rev1.0 you need to toggle the output in the interrupt.

You can do it by use bitwise exclusive or ^:

    PORTD ^= 1 << 4;

or, in Atmega328P you can just write 1 into PINx register to toggle the value of bits in PORTx:

    PIND = 1 << 4;


来源:https://stackoverflow.com/questions/64205811/how-to-make-atmega328-timmer-interrupt-tick-every-1-second

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!