Problem with software PWM for servo's on ATMega8

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

Dear AVRFreaks members,

I'm trying to set-up software PWM for a couple of servos. I have modified this code which was originally created for an ATMega88, based on the info in the datasheet of my Mcu.

ATMEGA8
Internal RC Osc. 8MHz; Startup time: 6CK + 64ms
High: 0xD9
LOW: 0xE4

// up to 8 servos on portB sequentially pulsed 
// uses timer1 in CTC mode 
// synchro gap is constant, 6 ms 
// frame is variable 
// min.frame = 8*1+6 = 14 ms 
// max.frame = 8*2+6 = 22 ms 

// Mega8, 8 MHz 
#define F_CPU 8000000UL

#include  
#include  
#include  

// initial pulse times in us for servo 0, 1, 2...7 (last value 6000 is the synchro gap) 
unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000}; 

//---------------------------------------------------------------- 

ISR(TIMER1_COMPA_vect) 
{ 
static unsigned char servo_num; 

   PORTB = (1<<servo_num);          // end pulse for servo (n), start pulse for servo (n+1)            
   OCR1A = servo[servo_num];        // set width of pulse (TOP)
   servo_num++;                     // prepare next servo 
   if(servo_num > 8) servo_num = 0; // again from servo 0; 
} 


////////////////// MAIN ////////////////////////////// 
int main (void) 
{ 
      DDRB = 0xFF;                        // portb output 
      TCCR1B |= (1<<WGM12) | (1<<CS11);  // pwm mode 4,CTC Clear Timer on Compare, prescale=8 
      TIMSK |= (1<<OCIE1A);             // enable T1_compareA interrupt 
      TCNT1 = 65530; 
      sei();    

// test 
      for(;;)                    
      { 
         servo[0] = 1000;          // servos 0 and 1 left 
         servo[1] = 1000; 
         _delay_ms(2000); 

         servo[0] = 1500;          // servos 0 and 1 center 
         servo[1] = 1500; 
         _delay_ms(2000); 

         servo[0] = 2000;          // servos 0 and 1 right 
         servo[1] = 2000; 
         _delay_ms(2000);  
      } 
} 
////////////////// END MAIN ////////////////////////// 

When I try to control a servo by hardware pwm it will work. But when I use the code above my servo wouldn't do anything.

The servo FITEC FS5109M is connected:
+ to the output of a 8705 voltage regulator
- to the GND of the 8705 voltage regulator
S to PB0 (pin 14) also tried PB1 (pin 15)

The microcontroller is also connected to the voltage regulator and is sharing the same GND. The voltage regulator uses a LED to see if there is power on my breadboard. The LED is on.

Thanks in advance for your help :D

Kind regards,

Patrick

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

volatile, atomic, debugger and welcome.

No RSTDISBL, no fun!

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

Hey Brutte,

I know that volatile memory only will be kept when there is power on the device. Atomic works with mutual exclusion which can be solved with semaphores. But can you give a detailed explanation of how I should use this in my code.

Thanks in Advance

Patrick

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

Volatile refers to the variable declaration not the memory. Clawson has written a tutorial on the importance of volatile.

Semaphores are one method of mutual exclusion. Temporarily disabling interrupts is another.

I wrote a tutorial on "the traps when using interrupts"

I think the problem might be a little simpler - where do you reset the bits of portb? When you set a given bit, you clear the others. Methinks you wanted |= instead of =

nevertheless, atomicity issues are going to bite you.

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

Quote:
I think the problem might be a little simpler - where do you reset the bits of portb? When you set a given bit, you clear the others. Methinks you wanted |= instead of =
But isn't that the point with the method used here, to have only one active servo signal at a time? If so then PORTB = ... is correct, to set one signal high and reset the previous (well all the others).

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

Could very well be. Myadvice is free - you want it correct also? A bit of simulator time might yield some results for the OP.

Time for bed this side of the world.

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

Thanx guys for the responses. I tried to replace "PORTB = " by "PORTB |=" but still doesn't work. Some tips for simulating? I use AVRStudio4 because I've got an older programmer.

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

Can you confirm that you have read my volatile tutorial:

https://www.avrfreaks.net/index.p...

and therefore understand why:

unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000};

must be:

volatile unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000};

?

Also as the values are "int" (2 bytes wide) you are going to need to think about atomicity (see Kartman's tutorial for that one!).

Last Edited: Thu. Nov 14, 2013 - 01:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apart from the volatile and atomic issues, the code looks fine to me and it seems to do what it is supposed to do (tested on mega1284 after changing TIMSK to TIMSK1). You should declare the array volatile but avr-gcc usually don't throw away array-accesses.

The atomic issue can be fixed with something like this

#include 

void set_servo(uint8_t servo_number, uint16_t milliseconds)
{
    if (servo_number < 8)
    {
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
        {
            servo[servo_number] = milliseconds;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Clawson I understand now why I should use "volatile" for this array.

I have quoted a bit out of your tutorial:

Quote:

If it's required that this program behave as originally written it's possible to tell the compiler that variables 'i' and 'count' must not be ignored by using the word 'volatile' which means "this variable is possibly subject to use elsewhere so you must always read/write it when told to". With the modification as follows:
Code:
volatile char count;

int main(void) {
volatile char i;

I will look into the atomic issue. Snigelen nice to know that the code is working at your place still doesn't know why it is not working here. I really want to debug my code, but I have the same problem earlier mentioned on this forum: AvrStudio 4 crashes after starting debugging "AVR Simulator" :cry:

Good news it is doing something it goes back and forth between points not like it's specified in the code after a few turns it stops and doesn't start again.
This is after I entered "volatile" will dive now in the atomic question. Thanx everyone maybe I will fix this still today :)

Last Edited: Thu. Nov 14, 2013 - 01:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do the AVR and the servos have a common ground? It will not work without it.

If you have an oscilloscope you could look at the signals and see if they look as expected.

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

Snigelen,

I use a common ground by using the powerrails on my breadboard. Hopefully you can see it in this pic:

I wish I have an oscilloscope, tried to build one with an arduino and processing but doesn't give the result that I needed. :(

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

Guys I fixed it :) You guys are totally awesome ;) The problem was in the fact that I didn't used the keyword "volatile" and that I didn't assign the servo value inside an atomic block.

Thank you very much for your time and input :D

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

Quote:
and therefore understand why:

unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000};

must be:

volatile unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000};

In fact, this accomplishes nothing in this case. If you compile the code without volatile you will see that the values are in fact written to ram. Indeed the intermediate values are cached in registers, but the actual array values are written properly.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
But when I use the code above my servo wouldn't do anything.
...
Guys I fixed it... The problem was...that I didn't assign the servo value inside an atomic block.

I strongly doubt the problem was there.

Say we write the servo[1] non-atomicaly in main and an interrupt fires during this writing.
Say we have a bad luck and ISR is just reading servo[1].
In the ISR a wrong value for servo1 is assigned to OCR1A.
But at the next access to servo1 (i.e. after 20 ms) the value is corrected and remains correct for next 2 second.
So there may be a short glitch on the servo when changing its position but not that "my servo wouldn't do anything".

Edit:
Of course I agree that servo[] should be writen atomicaly in the main().