Continue to Site

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.

  • 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.

ATmega328P pwm generation with timer1 problem

Status
Not open for further replies.

Cantafford

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

16k5506.png
 
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:
    OCR1A = 0x1FF;    // 50% duty cycle

As for setting the period, this is done with the prescaler setting/clock source select bits.
 
Last edited:
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:
    OCR1A = 0x1FF;    // 50% duty cycle

As for setting the period, this is done with the prescaler setting/clock source select bits.

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:
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?
 
The result needs to be in right justified format.

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.
 
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:
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 :(
 
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

Could it be possible that my period is less than my duty cycle and this is why this happens?
 
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:
    while(1);

// instead of 

    while(1)
    {
    }
 
This has always been an issue... Even when you did this on a PIC...
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.
 
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.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top