8bit PWM

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

I am trying to use an eight bit timer to rotate a servo back and forth. This was a relatively easy task with the 16bit timer but that is already used in this project.(Atmega8 counter2) MCU is running at 1 MHz.


#include
#include
          
int main (void)
{
unsigned char Elapsedcycles = 0;// Make a new counter        variable and initialize to zero
unsigned char Elap = 0;
 
TCCR2= (1<<COM21) | (1<<WGM20) | (1<<WGM21);
 //Initialises Timer 2 for fast PWM 0 prescaler
				

DDRB =(1<<PB3);//OC2 pin set to output				
while (1)
{
//Check timer value in if statement, true when count matches 80 toggle pinb3 
  if (TCNT2 >= 250) 
   { 
   TCNT2 = 0; // Reset timer value  Elapsedcycles++;
   }
 if (Elapsedcycles == 80) //extra cycles needed to get to 20000Hz //(1000000mpufreq/20000Hz) =50Hz
        { 
    Elapsedcycles = 0; // Reset counter variable
      
 PORTB ^= (1<<PB3); //toggle OC2 pin top
		}
if(OCR2 >= 100)
{
Elap++;
} 

for ( Elap = 4; Elap<=13;Elap++)//.4-1.4 ms  needed for sweep
{
PORTB ^= (1 << PB3);//toggle OC2 bottom
}
for (Elap=14; Elap>=5;Elap--)
{
PORTB ^= (1 << PB3);//toggle OC2 bottom
}
	
}
}	

Am I on the right track, what am I missing :?:
Any help or guidance would be greatly appreciated!

Lachlan

What we need to learn,
we learn by doing.

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

what´s the problem?
Code too long?
Color too green?

Klaus
********************************
Look at: www.megausb.de (German)
********************************

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

Quote:
what´s the problem?

The problem is that the code doesn't even come close to working.

At no time do you actually start your timer.

You set up to use OCR2, but at no time do you actually set OCR2. You check OCR2 (unnecessarily), but since OCR2 is never changed, the check will always fail.

About 90% of your code is totally unnecessary. Servos don't need exactly 50Hz. With 1MHz and a 64 pre-scaler you get 61Hz, which should be close enough. If not, the a 128 pre-scaler and 30.5 will.

You code should consist of a couple lines of code to initialize the PWM, and then just code to set the duty cycle in OCR2.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
TCCR2= (1<<COM21) | (1<<WGM20) | (1<<WGM21);
 //Initialises Timer 2 for fast PWM 0 prescaler 

You didn't initial CS in TCCR2. I guess the timer2 won't run
at all.

  if (TCNT2 >= 250)
   {
   TCNT2 = 0; // Reset timer value  Elapsedcycles++;
   } 

Since you said the time2 clock is running at the same speed as
system clock, I guess it will be only a little chance for this
program to catch TCNT2 >= 250. TCNT2 will be zero when it
gets to 256... so it has only 6 / 256 possibility to satisfy
this condition.

if(OCR2 >= 100)
{
Elap++;
}

Did I miss something? Who give a value to OCR2?

I have no idea what does the rest program do for? Sorry.

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

Seems like you mixed software PWM and hardware PWM in one software.....

use hardware PWM.
As already told:
Just setup the timer for hardware PWM.
No ISR needed.

Just set the duty cycle.
The rest makes the hardware. continously. without processing time.

The only thing you have to do if you want the servo to move: set a new duty cycle.

Klaus
********************************
Look at: www.megausb.de (German)
********************************

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

And if perchance some poor soul posted a CV program in the GCC forum, he would be chastised for posting in the wrong forum. Therefore, I feel that symmetry must apply. Unless there is a 'general c questions' forum where all compilers are accepted, I must hereby chastise you for asking a GCC question in the general avr forum, where only every avr c compiler in the world except gcc questions can be asked. Thank you.

Imagecraft compiler user

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

Quote:
I must hereby chastise you for asking a GCC question in the general avr forum

What part of the question has anything to do with GCC? It is not really even a general C question. It is a question about pulse width modulation.

Regards,
Steve A.

The Board helps those that help themselves.

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

Bob,

I now have the power to move threads to GCC when necessary (mwhahaha..!) but on this occasion I agree with Steve, there's no question here about using GCC specific compiler functions or anything like that. This person has a general question about PWM and it's completely co-incidental that he happens to be using GCC, the question would be the same if he happened to use IAR, ICC, CV or whatever.

(but if you see a thread you think should be moved to GCC in future just drop me a PM and I'll do it if I agree)

By the way, to the OP, remember to use '/' and not '\' in your include paths.

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

Quote:

I must hereby chastise you for asking a GCC question in the general avr forum, ...

Get off the pompous horse for once, Bob. We've all been around this over and over. You want the post bumped to the GCC Forum just because it is GCC. Over there, they say it is general C, nothing particular to GCC, and kick it back here. Particularly, it isn't specifically a C question--it is a question on implementing PWM, and the language used happens to be C.

Since the Atmel assembler is part of AVRstudio, and AVRstudio has its own Forum, should all AVR questions where an ASM fragment is posted be moved to that Forum?

You guys all decide how this is going to happen in the future, and then play nice. I'm getting tired from watching the ping-pong match.

Lee

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

There is another new poster with a PWM question that needs to be summarily banished; I detected the dreaded .

Hey, how about this for great fun:

"Hello, Freaks--

I have this piece of AVR code that I am not satisfied with. Can you help me to improve it?

frog = 1;

For full disclosure (so that I wont be chastised for not giving enough information) I'm using GCC 1.23.45 and AVR model ATmega321."

Then, when it gets kicked from the main Forum to the GCC Forum, I just go in and edit the "GCC" to "CodeVision" or "ImageCraft" or "IAR". When it gets kicked back to the main Forum, then re-edit to "GCC"...

Lee

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

So the new posting rules are: AVR forum: any question about c or avr, any compiler including gcc. GCC forum: any question about c and avr, but must have something to do with gcc? (ambiguous). If I ask a question in the gcc forum and use an iccavr example, will I get 1)help 2)message moved 3)theoretical discussion about how to parse questions into hw, sw or compiler main problem? I think an 'all AVR 8 bit C compilers' forum would solve all problems. Keep the GCC forum for GCC specific problems.

Imagecraft compiler user

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

Bob,

Don't know about others interpreation but my understanding of the GCC forum is that it's to ask questions about GCC specific way of doing things. So if someone asks a question simply about how they do PWM then it should be in this forum and if they do that in GCC it should be moved here. But if someone asked a question about what the third paramter to itoa() was used for in the GCC specific implementation of itoa() then it should go in GCC forum and if it's posted here it should be moved (and I will). Posting some code that illustrates a general AVR usage problem that just happens to have at the top is not sufficient justification in itself to have the post moved to GCC (IMHO)

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

Quote:

So the new posting rules are: AVR forum: any question about c or avr, any compiler including gcc. GCC forum: any question about c and avr, but must have something to do with gcc? (ambiguous). If I ask a question in the gcc forum and use an iccavr example, will I get 1)help 2)message moved 3)theoretical discussion about how to parse questions into hw, sw or compiler main problem? I think an 'all AVR 8 bit C compilers' forum would solve all problems. Keep the GCC forum for GCC specific problems.

I understand the point you are trying to make. It's been hammered over and over. Y'all Forum Police PLEASE come to a consensus as my neck is getting tired of the ping-pong match. I think you can tell that from the post that you are replying to.

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

Thanks to those who replied with constructive comments!

I have almost solved the problem but am still stuck on OCR2.

I have stripped the code back and have PWM output. The problem is (I think) that OCR2 is only 8 bit and 255 cycles is too short a period to generate the .4 - 1.5 ms bottom wave set, needed to instuct the servo where to move to. This servo uses a shorter instructional period (.4 - 1.5ms) than most (1-2ms).

If I replicate this on the 16 bit timer 1 and use values of 400 - 1500 with OCR1A the servo works as intended.

So my question is how do I set these values (400 - 1500) in an 8bit register (OCR2) trying to use only hardware PWM if possible.
Code is as follows:

# include 
# include 

 
int main (void)
{
 TCCR2 = (1<<COM21) | (1<<WGM21) | (1<<WGM20)| (1<<CS22) | (1<<CS20); 
/*Initialize Timer 2 for fast PWM with 128bit
prescaler in non-inverting mode,
Freq set to 30.518Hz OCR2 set at bottom.*/

	
DDRB =(1<<PB3);
OCR2 = 75;	// set intitial OCR2 value	

while (1)
{
OCR2 = 75;/*change duty cycle of OCR2. These values are too low*/
_delay_ms (200);

OCR2 = 150;/*change duty cycle of OCR2. These values are too low*/
_delay_ms (200);
}
}

Once again thanks in advance for any helpful advice! :)

Lachlan

Lachlan

What we need to learn,
we learn by doing.

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

1MHz / 128 gives you 7812.5Hz per tick of the timer, which is 0.128ms (1/7812.5). So 0.4ms is about 3 ticks of the clock and 1.5ms is about 12 ticks. So your OCR2 value will be between 3 and 12. This doesn't get you much resolution, but that is the nature of 8 bit PWM and servos. If you can change the pre-scaler to 64 (to get 61Hz), you will get twice the resolution. If that is not good enough for you, you will need to use a 16 bit timer, or go with a software PWM.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

So my question is how do I set these values (400 - 1500) in an 8bit register (OCR2) trying to use only hardware PWM if possible.

Of course, it is possible. But it is not very simple if you
don't want to lost resolution. I have a successful experience
about that.

First of all, you will need to use two bytes as pulse width.
400-1500 is 0x190 - 0x5dc. I assume you use OC2A as output
port.

You have to enable ISR of TOV2 and do all these things below
in ISR.

1. Set OCR2A to a value (we assume 0x40 now for description).
Set COM2A1:COM2A0 = 1:1 (raising OC2A port when reaching).

2. Calculate pulseWidth + 0x40. (assume pulseWidth == 0x330).
We get 0x370, put high byte 0x03 as a , low byte
0x70 to OCR2A.

3. For each time ISR of TOV2 has been triggered, decreasing
. When counter get to zero, Set COM2A1:COM2A0 =
1:0. OC2A port will fall when time reaching.

Now you get a pseudo two bytes pulse width controller.

But there is still a minor problem... If the falling time
is too close to 0 (which TOV2 be triggered), you will get
an error result. So we have to KEEP RAISING TIME AND FALLING
TIME AWAY FROM 0.

I use an easy way to do that. It depends on bit7 of
pulseWidth. If bit 7 is one, use 0xc0 as raising time.
If bit 7 is zero, use 0x40 as raising time.

For example.

pulseWidth == 0x350 => raising 0x40 / falling 0x90
pulseWidth == 0x470 => raising 0x70 / falling 0xb0
pulseWidth == 0x2a0 => raising 0xc0 / falling 0x60
pulseWidth == 0x5d0 => raising 0xc0 / falling 0x90

Raising time will be 0x40 or 0xc0. Falling will be in
range of 0x40-0xbf.

I hope this will solve your problem.

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

If you are going to do it in software, bernie's method is far more complicated than necessary. I would igore PWM mode and OCR2 entirely. Just use a pre-scaler of 8 and the TOV2 interrupt. Keep a counter and increment it every overflow. When it equals your duty cycle value (which would be between 50 and 187, send the pin low (and it can be any pin that you choose). Then when the counter reaches 2500, send the pin high and reset the counter to 0. This gives you 137 steps for the servo position, which should be plenty.

Regards,
Steve A.

The Board helps those that help themselves.

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

Steve & Bernie
Thanks for your input!

Steve I tried hardware PWM, mathematically and theoretically it should work but this servo still doesn't move using 32, 64 or 128 pre-scaler in phase correct or fast PWM. All I get is chatter! GRRRR

So software PWM it is, will let you know how I get on.
This servo has morphed into a time sink! On the positive side I have learned a lot about PWM.

Lachlan

What we need to learn,
we learn by doing.

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

Ok here is the program.
Thanks for all the input, particularly Steve for his clear and concise explanations. :)
I had a problem with the protoboard that when using a discrete power source for the servo, the servo would not move even though it had power but when connected to the same power source as the Atmega it worked as planned. Not sure why this was?

Anyway, here is the program for anyone in a similar situation.

 
# include 
# include 

int main (void)
{
DDRB=(1<<PB3);//OC2 is output
          
	
 TCCR2 = (1<<COM21) | (1<<WGM20) |(1<<WGM21) | (1<<CS22); 
//Initialize Timer 2 for fast PWM with 64bit prescaler in non-inverting mode,
//Freq set to 61Hz OCR2 set at bottom.
//
	

OCR2 = 8;	// set intitial OCR2 value	


while (1)
{
//change duty cycle to sweep servo back and forth
for (OCR2 = 8;OCR2<=24;OCR2++)/
   {_delay_ms(1);}
for (OCR2 = 9;OCR2>=25;OCR2--) 
   {_delay_ms(1);}
}
}

The above is a simple program that will sweep a servo back and forth using an eight bit timer (timer2) on an Atmega8.

Lachlan

What we need to learn,
we learn by doing.