简体   繁体   中英

16-bit Timer PWM LED Dimmer

I am new to AVR C Programming, I am testing a simple PWM using 16-bit timer on Atmega328p Counter/Timers which is suppose to act as a dimmer to an LED.

My Code:

#define F_CPU 16000000UL

void initTimer();

int x = 1;
int n = 1000;

int main(void)
{   

    initTimer();

    DDRB |= (1 << PB1)| (1 << PB2);

    while(1)
    {
        x++; 

        if(x > 65) {
            x = 1;
        }
    }
}

void initTimer() {

    TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); 
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); 
    // used for TOP, makes for 50 Hz PWM
    ICR1 = 40000; 
    OCR1A = n * x; 

} 

ISR(TIMER1_OVF_vect)
{
    OCR1A = n * x;  
}

Problem is that it doesn't display the dimming effect, the brightness of the LED stays constant as to whatever value I set for OCR1A (PB1) output pin initially, its suppose to change value as the interrupt happen but its just not doing this, this is suppose to be simple test what am I doing wrong?

Update:

As advice I enable the interrupts using the TIMSK1 register and SEI(), however still the same issue the LED brightness stays constant as to whatever the original value of OCR1A that was specified in the initTimer().

int main(void)
{   
    initTimer();
    DDRB |= (1 << PB1)| (1 << PB2);

    while(1)
    {
        x++; 
        if(x > 65) {
            x = 1;
        }
    }
}

void initTimer() {

    ICR1 = 40000; 
    OCR1A = n * x; 
    TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); 
    TIMSK1 |= (1 << ICIE1);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); 
    sei();
}

ISR (TIMER1_COMPA_vect)
{
    OCR1A = n * x;  
}

Although I tried an alternative approach and that gives me the dimming affect:

int main(void)
{   

    initTimer();

    DDRB |= (1 << PB1)| (1 << PB2);

    while(1)
    {
        _delay_ms(20);
        OCR1A = n * 4; 
        _delay_ms(20);
        OCR1A = n * 8;
        _delay_ms(20);
        OCR1A = n * 15; 
        _delay_ms(20);
        OCR1A = n * 25;
        _delay_ms(20);
        OCR1A = n * 1; 

    }
}

void initTimer() {

    ICR1 = 40000; 
    OCR1A = n * x; 
    TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);   
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); 

} 

So it seems the problem is with the interrupts since PWM affect works but its just not working with the interrupt handler.

The first thing that jumps out at me is x and n should be volatile. You also should enable the interrupt in TIMSK0 register. Also enable interrupts by calling sei.

If I were you, I'd start with some know good sample code. The page I mentioned has a example that fires an interrupt every 4ms. Take that code and toggle the led on and off.

Another problem is you are changing x without regard to whether or not the isr was called. So in effect you will get a random x each time in the isr. This code is simple enough it might get stuck in a simple pattern. Instead move the setting of x to your isr.

Here is a good intro to avr timers: https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328

ICR1 = 40000; 
OCR1A = n * x; 
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);   
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);

This is wrong, you need to configure TCCR1A and TCCR1B before initializing ICR1 and OCR1A. See this answer for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM