Search code examples
avrpwmatmega16

Why I am always getting Zero PWM output?


I want to output two different analog values with 10 bit resolution i.e. dac_value ranging from 0-1023. I am using ATmega16 with external crystal 4MHz. I have also tried connecting RC filter at output but nothing changed. I am constantly getting zero output, can someone help ??

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

void initPWM()
{
  TCCR1A |= (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) ;
  TCCR1B |= (1<<WGM12) | (1<<CS10);
}


    uint16_t dac_value1 = 100, dac_value2 = 200;
int main(void)
{
    initPWM();
      while(1) {
      OCR1A = dac_value1;
      OCR1B = dac_value2;
          }
     for (;;) {}

 }

Solution

  • You are assigning wrong bits to wrong registers. Just to clarify: PWM it is NOT a analog output. It is quickly changed high or low output state. PWM value determines how long output will be in each state (high or low) withing timer period.

    If you want to make "analog" output, you need to filter output signal, for example, passing it thru a RC-filter, also you need to make output as fast as possible, that's mean you need to select lower prescaler, and choose Fast-PWM mode. In your current configuration, you will get pwm with 1024 prescaler. I.e. less than 4 periods of timer per second.

    So, if you we will assign a fast pwm with prescaler 1 (which will give us a 3906 Hz output) with inverted output (i.e. higher OCR1x value leads to lower output value) it will be something like this:

    void initPWM()
    {
        TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0); 
        // here bits WGM10 WGM11 (with WGM12 in TCCR1B) will select the Fast PWM mode with 10 bit resolution; 
        // COM1A1 COM1A0 COM1B1 COM1B0 select an inverted PWM output on pins OC1A OC1B
        TCCR1B = (1<<WGM12) | (1<<CS10);
        // CS10 will select  1:1 prescaler (i.e. 4000000/1024 timer periods per second)
    
        // Also, your PWM output will not be visible on corresponding pins, unless you will configure an DDR bits for these pins to output. Those pins are PD5 and PD4 on ATmega16
        DDRD |= (1 << 4) | (1 << 5);
    }
    

    Next thing you need to consider: when your application main() function execution reaches it's end, it will jump to reset vector. So, put an empty loop to the end of main():

    int main(void)
    {
        uint16_t dac_value1, dac_value2;
        dac_value1 = 123; // do not forget to init variables!!!
        dac_value2 = 987; 
        initPWM();
        OCR1A = dac_value1;
        OCR1B = dac_value2;
        for(;;) {
            // main loop. Now it's empty
        } 
    }