1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

ATmega328P pwm generation with timer1 problem

Discussion in 'AVR' started by Cantafford, Oct 31, 2016.

  1. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    Hello,

    I'm trying to control a DC motor with an ATmega328P. Actually I will need to control a servo motor later but I need to learn to generate PWM signals properly first. Since I need a high resolution I used timer1 which can generate 16 bit pwm resolution.

    I have used 10 bit fast pwm mode and tried to implement a duty cycle of 50%. Something I don't understand though is how can I set the period(frequency) of the PWM signal. I have just started working with AVR's and I don't know how to do this. For example when programming PICs you need to set the period of the pwm signal before setting it's duty cycle. But for AVR's I have not found how to do this in the datasheet. This is how I initialized my timer1 module:
    Code (text):

    TCCR1A = 0b10111111; // fast non inverting 10 bit pwm mode
     TCCR1B = 0b00001001; // 10 bit fast pwm mode, no prescaler

     TCNT1H = 0xEF; // 50 duty cycle
     TCNT1L = 0xFF;
     
    When I simulate this the motor does not run at all.
    Here is the full code(ignore the ADC part that's not important).
    Code (text):

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #define F_CPU 8000000    // CPU speed - always define prior to including util/delay.h
    #include <util/delay.h>

    int main()
    {
     DDRB = 0xFF; // PWM is output(also the LEDs)
     DDRD = 0xFF;

     ADMUX = 0b01010000; // AREF reference, result left justified, ADC0 selected
     ADCSRA = 0b10011111;
     sei(); // enable global interrupts

     ADCSRA |= (1 << ADSC); // start conversion

     TCCR1A = 0b10111111; // fast non inverting 10 bit pwm mode
     TCCR1B = 0b00001001; // 10 bit fast pwm mode, no prescaler

     TCNT1H = 0xEF; // 50 duty cycle
     TCNT1L = 0xFF;

     while(1)
     {
         
     }  
       
    }


    ISR(ADC_vect)
    {
     PORTD = ADCL; // put result of conversion on PORTB
     PORTB = (ADCH << 2) & 0xFF; // get ADCH to RB3 and RB2 and also keep PWM pin unchanged
     ADCSRA |= (1 << ADSC); // restart conversion
    }
     
    [​IMG]
     
  2. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    So anyone knows this? :D. I've watched some tutorials and I still can't find my mistake :(
     
  3. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    The duty cycle is set by writing to the OCR1AH/L register pair (The equivalent of CCPRxL and DCB1 : DCB0 on a PIC)...NOT the TCNT register pair. However, in AVR-GCC, you can just write to OCR1A and the compiler takes care of the register pairing.

    TCNT1H/L is the timer counter itself. These registers need not be written to.

    OCR1AH/L holds the duty cycle value itself. In 10-bit PWM mode, you can write a value from 0x0000 - 0x03FF. A value of 0x1FF or 0x200 should get you close to a 50% duty cycle -

    Code (text):

        OCR1A = 0x1FF;    // 50% duty cycle
     
    As for setting the period, this is done with the prescaler setting/clock source select bits.
     
    Last edited: Nov 2, 2016
    • Like Like x 1
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2

    Thank you :D. I have one more question. I'm trying to control a DC motor's duty cycle via the pot connected on ADC0. I have tested the module with some LEDs and is implemented ok in 10 bit left justified result.

    So I modified my adc interrupt routine like this:

    Code (text):

    ISR(ADC_vect)
    {
     OCR1AL = ADCL;
     OCR1AH = ADCH;

     ADCSRA |= (1 << ADSC); // restart conversion
    }
     
    The result beiing left justified. When I simulate this the motor will run full speed if the potentiometer's position is above 15% and after that point it will run full speed regardless of the position of the pot(even if I drop it all the way to 0). Why is the motor behaving like that?
     
  6. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    The result needs to be in right justified format.
     
  7. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    I changed to that and now the motor still behaves weird. So weird I can't even explain it ::).
    But I'm guessing is something done with the way I set the frequency of the PWM signal(which I actually have not set up at all). Will look into that since I'm guessing if I set the frequency properly to say 1.5Khz for example then everything will run smooth.
     
  8. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    Even with no prescaler you're running at about 122Hz, which should be low enough for a DC motor. Assuming 8MHz Fosc -

    Period = Prescale(1/Fosc) x 65536

    1(1/8MHz) x 65536 = 8.192mS, or 1/122Hz

    Some motors don't like PWM drive. I would consult the data sheet for the particular motor you're using and see if there's any recommended PWM parameters for it.

    Do you have a digital oscilloscope so you can look at the PWM signal? If not I highly recommend getting one if you plan to stay in embedded electronics. A decent 4-channel one can be had for around $400. This is the one I use -

    https://www.amazon.com/Rigol-DS1054Z-Digital-Oscilloscopes-Bandwidth/dp/B012938E76
     
    Last edited: Nov 3, 2016
  9. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    I'm simulating the application not implementing it in hardware. Yes FOSC is 8Mhz and no prescaler as in your example there. Not sure why my motor behaves like that :(
     
  10. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    Could it be possible that my period is less than my duty cycle and this is why this happens?
     
  11. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,166
    Likes:
    910
    Location:
    Rochdale UK
    ONLINE
    This has always been an issue... Even when you did this on a PIC...
     
  12. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    I'm gonna simulate this today and look at the output on a scope. Will have screen shots. Stay tuned.

    In the meantime...FYI when you have a statement with an empty function body, you can just terminate it with a semicolon instead of empty brackets. Example -

    Code (text):

        while(1);

    // instead of

        while(1)
        {
        }
     
     
  13. Cantafford

    Cantafford Member

    Joined:
    Jul 5, 2015
    Messages:
    251
    Likes:
    2
    Yes. But if that was the case here the motor's speed was supposed to be 0 at some point right(when the duty cycle value would exced that of the period)? Whereas in my case the speed doesn't drop to 0 it just does not respect the pot's position.
     
  14. Jon Wilder

    Jon Wilder Active Member

    Joined:
    Oct 22, 2010
    Messages:
    859
    Likes:
    82
    Location:
    Fresno, CA
    When duty cycle value exceeds the period value, it should roll over to 0% and start from there as you increase the duty cycle value since it's the MSBs that are ignored.
     

Share This Page