Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pulse width modulation (PWM) on AVR Studio

I'm trying to use PWM for an LED on an ATmega8, any pin of port B. Setting up timers has been a annoying, and I don't know what to do with my OCR1A. Here's my code, and I'd love some feedback.

I'm just trying to figure out how use PWM. I know the concept, and OCR1A is supposed to be the fraction of the whole counter time I want the pulse on.

#define F_CPU 1000000  // 1 MHz

#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

int main(void){

    TCCR1A |= (1 << CS10) | (1 << CS12) | (1 << CS11);
    OCR1A = 0x0000;
    TCCR1A |= ( 0 << WGM11 ) | ( 1 << WGM10 ) | (WGM12 << 1) | (WGM13 << 0);
    TCCR1A |= ( 1 << COM1A0 ) | ( 0 << COM1A1 );
    TIMSK |= (1 << TOIE1); // Enable timer interrupt
    DDRB = 0xFF;
    sei(); // Enable global interrupts
    PORTB = 0b00000000;

    while(1)
    {
        OCR1A = 0x00FF; //I'm trying to get the timer to alternate being on for 100% of the time,
        _delay_ms(200);
        OCR1A = 0x0066; // Then 50%
        _delay_ms(200);
        OCR1A = 0x0000; // Then 0%
        _delay_ms(200);
    }
}

ISR (TIMER1_COMA_vect)  // timer0 overflow interrupt
{
    PORTB =~ PORTB;
}
like image 763
michaeljiz Avatar asked Feb 02 '23 07:02

michaeljiz


1 Answers

No, this is not the way how you should do a PWM. For example, how do you set a PWM rate of, for example, 42% with it? Also, the code size is big, it can be done in a much more efficient way. Also, you waste a 16 bit timer to do 8 bit operations. You have 2x 8 bit timers (Timer/Counter 0 and 2), and one 16 bit timer, Timer/Counter 1.

It's also a bad idea to set unused portpins to output. All portpins which are not connected to anything, should be left as inputs.

The ATmega8 has a built-in PWM generator on timers 1 and 2, there is no need in simulating it through software. You don't even have to set your ports manually (you only have to set the corresponding portpin to output)

You don't even need any interrupt.

#define fillrate OCR2A 


 //...

 // main()

PORTB=0x00;
DDRB=0x08;  //We use PORTB.3 as output, for OC2A, see the atmega8 reference manual

// Mode: Phase correct PWM top=0xFF
// OC2A output: Non-Inverted PWM
TCCR2A=0x81;
// Set the speed here, it will depend on your clock rate.
TCCR2B=0x02;

// for example, this will alternate between 75% and 42% PWM
while(1)
{
    fillrate = 191; // ca. 75% PWM
    delay_ms(2000);

    fillrate = 107; // ca. 42% PWM
    delay_ms(2000);
}

Note that you can use another LED with another PWM, by using the same timer and setting OCR2B instead of OCR2A. Don't forget to set TCCR2A to enable OCR2B as output for your PWM, as in this example only OCR2A is allowed.

like image 153
vsz Avatar answered May 01 '23 21:05

vsz