PWM using timer 0 of ATmega16

Go To Last Post
32 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I am developing a robot arm consisting of 5 servo motors using an ATmega16 and interfacing it via USART. the USART is configured to run at 9600 kbps and the ATmega16 is running at 16 MHz. For controlling 3 servos I am using the timer 1. Here 2 servos are controlled via OCR1A and the 3rd servo via OCR1B. For the remaining 2 servos, I intend on using the timer 0 and timer 2 for generating the PWM.

Here is the test code for the timer 0 PWM output on OC0 pin.

#include 
#include

//prototypes
void InitPWM(void);
void SetPWMOutput(uint8_t duty);
void Wait(void);

int main(void)
{
	uint8_t brightness=0;

	InitPWM();

	while(1)
	{
		for(brightness = 0; brightness < 255; brightness++) 
		{
			SetPWMOutput(brightness);
			Wait();
		}

		/*for(brightness = 255; brightness > 0; brightness--)
		{
			SetPWMOutput(brightness);
			Wait();
		}*/
	}
	return 0;
}

void InitPWM(void)
{
	//Fast PWM mode; Prescaler 1024; Non-inverted PWM

	TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00)|(1<<CS02);

	//TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00);
	
	//Set OC0 PIN as output. It is  PB3 on ATmega16 ATmega32
	DDRB|=(1<<PB3);
}

void SetPWMOutput(uint8_t duty)
{
   OCR0=duty;
}

void Wait(void)
{
 	//_delay_loop_2(8000);
	_delay_ms(100);
}

Here I am seeing a lot of twitch on the servo driven via the OC0 pin. I am using a prescalar of 1024 to give me PWM pulses of roughly 16 ms. I am using fast PWM so the formula used is PWM freq. = CPU clock frequency / (N * 256) where N is set to 1024.
Not able to figure out why there is a twitching of the servo. Need help.

Thanks,
Sumair

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Servo needs pulse 1-2 ms.

for(brightness = 0; brightness < 255; brightness++)

But here you send pulses from 0 to 16 ms.
You can use only range from OCR0=15 (1 ms) to OCR0=31 (2 ms)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
You can use only range from OCR0=15 (1 ms) to OCR0=31 (2 ms)

Can you please explain how you arrived at these figures?

Thanks,
Sumair

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Can you please explain how you arrived at these figures?


What speed is your AVR running at?

What prescale divisor is being used on the AVR timer?

Given the above, what is the time for one "tick" -- one count -- of the AVR timer?

How many of those ticks comprise an approximate 20 millisecond period?

How many of those ticks comprise an approximate 1 millisecond period (which is the minimum useful pulse width)?

How many of those ticks comprise an approximate 2 millisecond period (which is the maximum useful pulse width)?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Timer resolution = Prescaler/Input frequency
= 1024/16 MHz
= 64 us
Since delay = 1 ms, the timer needs to count

1 ms/64 us = 15.625 timer periods which approximates to 15.

Similarly, if delay = 2 ms, then the timer needs to count

2 ms/64 us = 31.25 timer periods which approximates to 31.

so to answer the above question I need to verify the value of OCR0 between 16 and 31. Is this right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So based on the above, I changed my code as follows:

for(brightness = 15; brightness < 31; brightness++) 
{
        SetPWMOutput(brightness);
	Wait();
}

But I'm still seeing a lot of jitter, and the servo does not move smoothly from its min. position to its max. It seems to get stuck at some position and then the jittering starts. what could be wrong?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Servo needs pulses 1-2 ms peridicaly.
Period 20 ms is usually used.
16 ms will work as well.

Your oscillator = 16 MHz,
timer0 prescaler = 1024.
One timer tick[us] = prescaler / Fosc[Mhz] = 1024/16 = 64 us.
For 1 ms you need 1000us/64us = 15.625 = 15 ticks.
For 2 ms it is 31 ticks.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So that's what I'm doing right? I am varying the servo pulses between 1 ms and 2 ms as seen in the for loop in my above post.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, it should be

while(1)
{
  OCR0 = 15; // move servo to left limit
  _delay_ms(1000);
  
  for(i=15;i<32;i++) // slowly move to right
  {
     OCR0 = i;
     _delay_ms(100);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried the code above, but still I see the same issue. The servo moved from left and then it stayed stuck at some position and started twitching continuously. After some time the servo motor got hot and stopped working.

I measured the PWM frequency using a sound card oscilloscope and saw that the frequeny was varying between 61.036 Hz which is the correct frequency, and 75 Hz as well as 82 Hz. The servo horn probably moved correctly only when the frequency was around 61 Hz. Also the servo pulses measured were showing 1 ms and 2 ms, as I was just setting the OCR0 to 15 and 31 to test the min. and max. positions.

I am not sure why the frequency was varying between 61 Hz, 75 Hz and 82 Hz. This might have damaged the servo. Any ideas why this happened?

Thanks,
Sumair

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
No, it should be
How is your code any different from the OP's?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Also the servo pulses measured were showing 1 ms and 2 ms, as I was just setting the OCR0 to 15 and 31 to test the min. and max. positions.

??? So are you using the code you posted above, or some other code with 15 and 31 only?

Show exactly the entire test program you are using. Tell the clock speed of your AVR, and tell how you verified that. Tell what the symptoms are when running that exact test program. If the frequencies seem to vary as you mentioned earlier, tell what type of measurement you are using to get that result.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
??? So are you using the code you posted above, or some other code with 15 and 31 only?

This is what I did, if you refer to the code I have pasted in my first post:

while(1) 
{
    brightness = 15;
    SetPWMOutput(brightness);
    Wait();
    for(brightness = 15; brightness < 32; brightness++)
      {
         SetPWMOutput(brightness);
         Wait();
      }
} 

which is same as what Visovian has done, except I am setting the value of OCR0 via the SetPWMOutput function.

When this gave me the same result I decided to move the servo from left to right as shown below:

while(1)
{
    brightness = 15;
    SetPWMPutput(brightness);
    Wait();
    brightness = 31;
    SetPWMPutput(brightness);
    Wait();

}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
How is your code any different from the OP's?
Sorry, I have somehow overlooked the OP's post from Dec 25, 2012 - 06:55 PM.

Quote:
and saw that the frequeny was varying between 61.036 Hz which is the correct frequency, and 75 Hz as well as 82 Hz.

I think it is time to check the power supply.
Do not use the same supply for both mproc and servo.
For servo it is best to use 3 dry cells (4.5V). Minus connected with Avr GND.
Even hard 6V can be danger for a microservo (when it tries to move behind the limit).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This is what I did, if you refer to the code I have pasted in my first post:

Indeed--but then you said

Quote:

I was just setting the OCR0 to 15 and 31

So, I repeat:

Quote:

Show exactly the entire test program you are using. Tell the clock speed of your AVR, and tell how you verified that. Tell what the symptoms are when running that exact test program. If the frequencies seem to vary as you mentioned earlier, tell what type of measurement you are using to get that result.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is the entire program:

#include 
#include

#define F_CPU 16000000L
//prototypes
void InitPWM(void);
void SetPWMOutput(uint8_t duty);
void Wait(void);

int main(void)
{
	uint8_t brightness=0;

	InitPWM();

	while(1)
	{
		brightness = 31;
		SetPWMOutput(brightness);
		Wait();
		brightness = 15;
		SetPWMOutput(brightness);
		Wait();
		/*for(brightness = 15; brightness < 32; brightness++) 
		{
			SetPWMOutput(brightness);
			Wait();
		}*/

		/*for(brightness = 255; brightness > 0; brightness--)
		{
			SetPWMOutput(brightness);
			Wait();
		}*/
	}
	return 0;
}

void InitPWM(void)
{
	//Fast PWM mode; Prescaler 1024; Non-inverted PWM

	TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00)|(1<<CS02);

	//Set OC0 PIN as output. It is  PB3 on ATmega16 ATmega32
	DDRB|=(1<<PB3);
}

void SetPWMOutput(uint8_t duty)
{
   OCR0=duty;
}

void Wait(void)
{
 	_delay_ms(100);
}

The clock speed of my AVR (ATmega16) is 16 MHz. To test the clock speed, I wrote this small program to toggle an LED every 1 sec.

/**
 * Program to toggle LED at 1 Hz or once every second for an input clock freq. of 16 MHz
 *
 * Timer resolution = Prescale/Input frequency
 * Input freq. = 16 MHz
 *
 * Target timer count = (((1/Target freq.)/Timer resolution) - 1)
 * 
 * Here target freq. = 1 Hz or once every second
 *
 */




#include 

int main(void) 
{
	DDRA |= _BV(PA4);
	TCCR1B |= _BV(CS12) | _BV(CS10);	//Use Prescaler of 1024

	while(1)
	{
		if(TCNT1 >= 15624)
		{
			PORTA ^= _BV(PA4);
			TCNT1 = 0;
		}
	}
}

For measuring the frequencies, I am using the sound card oscilloscope where I connected a probe to the mic input of the laptop, and measured the frequencies as seen in the attached files. For some reason the pulses seem to be inverted??

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The word 'brightness' makes me think of using pwm to dim an led. This has almost nothing to do with controlling a hobby servo motor using the 1 to 2 ms pulse that repeats every 20ms. To get the servo to work correctly, you need to send a pulse every 20ms. The pulse must be 1ms to 2ms in width. If you use a 16 bit timer with top at 40000 by using mode14 and prescaler of 8 then the 1ms pulse is a timer value of 2000. Easy.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Although i have used a variable called brightness, I have modified the code originally used to dim an LED, to drive a servo as mentioned by the folks replying to this thread :)

Quote:

To get the servo to work correctly, you need to send a pulse every 20ms. The pulse must be 1ms to 2ms in width. If you use a 16 bit timer with top at 40000 by using mode14 and prescaler of 8 then the 1ms pulse is a timer value of 2000

In the very first post I have mentioned that I'm already using timer 1 to control 3 servos, so I need to drive the remaining 2 servos using timer0 and timer2 :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 _delay_ms(100); 

How far do you expect the servo to move in 0.1 seconds?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CodeVision Wizard agrees with your setup of the timer.

Note that the period is 16.384ms, corresponding the the 'scope analysis of 61Hz. I'm not familiar with the servos--perhaps they are sensitive to a period less than 20ms?

I'm also not familiar with GCC and util/delay.h. That brings us to the more-rapid change in duty cycle than 100ms. Does F_CPU need to be set >>before<< the inclusion of delay.h ?

Why the inverted appearance? I don't know. Leads connected backwards? Why is the p-p only 2V?!?

Also, an OCR value of 15 gives a hair less than 1ms duty. I don't know if the servo would be sensitive to that.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Does F_CPU need to be set >>before<< the inclusion of delay.h ?

Yes.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
In the very first post I have mentioned that I'm already using timer 1 to control 3 servos
And those 3 servos work as expected?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'm not familiar with the servos--perhaps they are sensitive to a period less than 20ms?
Most servos will handle a fairly large range of periods.
Quote:
Also, an OCR value of 15 gives a hair less than 1ms duty. I don't know if the servo would be sensitive to that.
Most servos have a somewhat larger range than the nominal 1-2ms.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

I know that with an 8-bit timer you get a lower resolution to drive the servo...but has anybody actually driven a servo using timer0 or timer2 as shown in the code above and got it working as desired?

One problem i could think of is that I got a cheap quality servo, VTS-05A which according to certain forum posts is a very bad quality servo. I could try changing the servo and try. I was hoping someone on this thread has actually got the servo working with either timer0 or timer2.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
And those 3 servos work as expected?

Yes they work fine...I'm having trouble driving the servos using timer0

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Make sure the pwm pin (OCR0) is set to output.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Make sure the pwm pin (OCR0) is set to output.

I'm doing that already:

DDRB |= (1<<PB3);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try this

while(1)
   {
      brightness = 31;
      SetPWMOutput(brightness);
      _del_ms(2000);
      brightness = 15;
      SetPWMOutput(brightness);
      _del_ms(2000);
   }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I know that with an 8-bit timer you get a lower resolution to drive the servo...but has anybody actually driven a servo using timer0 or timer2 as shown in the code above and got it working as desired?

I tested your code in a mega16 with modifications:
* make sure F_CPU is defined before delay.h
* Longer wait (I used 250ms)
it works fine with a Hitec servo here (HS-65MG - nothing special about it, a small analog servo).

That said you can get better resolution for multiple servos (at the cost of some jitter that the servos most likley will not notice):
https://www.avrfreaks.net/index.p...
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I tried the above suggestions of adding F_CPU before the delay.h header and increased the delay to 250 ms and noticed the same results. However, I noticed 2 very important things with the modifications I made below:

Instead of using the functions SetPWMOutput() and Wait(), I set the value of OCR0 directly and called the _delay_ms() function within the main while loop and saw something working:

while(1) {
    OCR0 = 7;
    _delay_ms(250);
}

Why does setting the value of OCR0 directly without using the SetPWMOutput() function work fine whereas setting it using the function has problems? The same with the Wait() function. Any explanation for this?

In the above code, I set the min. limt of the HS-311 servo which is 0.45 ms and the value of OCR0 is 0.45 ms/64 us = 7, and I see that the servo horn moves correctly to its min. position.

Next, I changed the value of OCR0 to its max. limit which is 2.55 ms, so the value of OCR0 is 2.55 ms/64 us = 39, and here also the servo moves correctly to its max. position as shown below:

while(1) {
    OCR0 = 39;
    _delay_ms(250);
}

However, the second interesting thing to note here is that when I try to move the servo from its min. max. position using a for loop, the servo seems to move only over a certain range somewhere towards its mid-position and just below its max. position. Here is the code:

while(1) {
    OCR0 = 7;
    _delay_ms(250);

    for(i = 7; i < 40; i++) {
	OCR0 = i;
	_delay_ms(250);
    }
}

Any idea why this is happening? This is quite strange.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At the end of while loop servo is say in most right position.
Then you set the most left position(OCR0 = 7 ) and wait 250 ms. But servo needs more time to move from one limit to the other.
Give its 1 or 2 sec (as I have suggested in my last code).

Quote:
Why does setting the value of OCR0 directly without using the SetPWMOutput() function work fine whereas setting it using the function has problems?
I do not know, but what is the point of making functions with only one command?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Why does setting the value of OCR0 directly without using the SetPWMOutput() function work fine whereas setting it using the function has problems?
But that is not all that you changed. The "working" code no longer has multiple positions competing against each other with insufficient time between to move to the requested position.

Regards,
Steve A.

The Board helps those that help themselves.