Servo dancing

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

I've graduated to trying to get servos to work.

 

I've taken a program from Elliot's book (Chapter 11) and hopefully distilled it to its essence.

 

It sets up a server to run off of pin 15 based on input from the user which is used for the servo pulse
length.  It basically works.  Values alternating between 1111/6666 move the servo to 'opposite' ends.

 

Eventually I want to run 4 servos, so I've tried to set up one running off of timer 2.  I discovered timer 2
is considerably different than timer 1.  As timer 2 only has 8 bits to work with, I set it up with /128 prescaling.

Values below 100 causes the servo arm to oscillate repeatedly.  Larger values cause larger oscillation.
I can get fairly consistent results with values < 10.

 

But why does it oscillate?  Putting a scope on the pin indicates repeated pulses, so I must be doing something trivially wrong,
but I can't see it.  Can any of you see it?

 

You guys have been most helpful before, and I'd again be grateful if someone would give it a look.  It's been kicking my butt around
for over a week;  I know a lot more than when I started but evidently not (quite?) enough :-)

 

The code:

                  /* Quick interactive demo running servo with Timer 1 */

// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#include "pinDefines.h"
#include "USART.h"

int timer1 = 0;
int timer2 = 0;

static inline uint16_t getNumber16(void);

static inline void initTimer1Servo(void) {
                   /* Set up Timer1 (16bit) to give a pulse every 20ms */
                             /* Use Fast PWM mode, counter max in ICR1 */
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
  TCCR1B |= (1 << CS10);  /* /1 prescaling -- counting in microseconds */
  ICR1 = 20000;                                    /* TOP value = 20ms */
  TCCR1A |= (1 << COM1A1);              /* Direct output on PB1 / OC1A */
  DDRB |= (1 << PB1);                            /* set pin for output */
}


/* new code for timer 2 */

static inline void initTimer2Servo(void) {
    TCCR2A |= (1 << WGM20); // Fast PWM mode
    TCCR2A |= (1 << WGM21); // Fast PWM mode, pt.2
    TCCR2B |= (1 << CS20);  // 20+22= /128
    TCCR2B |= (1 << CS22);  // 
    TCCR2A |= (1 << COM2A1);// PWM output on OCR2A
}

int main(void) {

  // -------- Inits --------- //
  timer1 = 1;
  timer2 = 0;
  uint16_t servoPulseLength;
  if (timer1)
	  initTimer1Servo();
  if (timer2)
	  initTimer2Servo();
  initUSART();
  printString("\r\nWelcome to the Servo Demo\r\n");

  // ------ Event loop ------ //
  while (1) {

    printString("\r\nEnter a four-digit pulse length:\r\n");
    servoPulseLength = getNumber16();

    printString("On my way....\r\n");
	if (timer1) {
		OCR1A = servoPulseLength;
		DDRB |= (1 << PB1);                        /* re-enable output pin */
		//while (TCNT1 < 3000)
		//	;
		_delay_ms(1000);
		DDRB &= ~(1 << PB1);                         /* disable output pin */
		printString("Releasing...\r\n");
    }                          /* delay until pulse part of cycle done */

	if (timer2) {
		DDRB |= (1 << PB3);                        /* re-enable output pin */
		OCR2A = servoPulseLength;
		loop_until_bit_is_set(TIFR2, TOV2);        /* wait for PWM cycle */
		TIFR2 |= (1 << TOV2);                  /* reset PWM overflow bit */
		//DDRB &= ~(1 << PB3);                     /* re-enable output pin */
		_delay_ms(1000);
	}

  }                                                  /* End event loop */
  return 0;                            /* This line is never reached */
}

static inline uint16_t getNumber16(void) {
  // Gets a PWM value from the serial port.
  // Reads in characters, turns them into a number
  char thousands = '0';
  char hundreds = '0';
  char tens = '0';
  char ones = '0';
  char thisChar = '0';

  do {
    thousands = hundreds;                        /* shift numbers over */
    hundreds = tens;
    tens = ones;
    ones = thisChar;
    thisChar = receiveByte();                   /* get a new character */
    transmitByte(thisChar);                                    /* echo */
  } while (thisChar != '\r');

  transmitByte('\n');                                       /* newline */
  return (1000 * (thousands - '0') + 100 * (hundreds - '0') +
          10 * (tens - '0') + ones - '0');
}

 

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

There must be buckloads of libraries on the 'net for handling multiple hobby servo's.

The way they work is that each servo needs a pulse between 500us and 1500us ever 20ms or so.

So they first send a pulse to servo_1, then a pulse to servo_2, etc...

Try:

https://github.com/search?utf8=%...

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

Last Edited: Sat. Jan 13, 2018 - 07:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll give that a look.  Thank you.

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

All of these seem to be built on top of Arduino or Rasp Pi.   Would be nice to see one on top of bare AVR hardware.
 

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

However, beginning wider search :-)

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

For libraries such are this there are probably only a few differences.

First you have to get rid of the redicilous "DigitalWrite()" and replace it with normal bit manipulations for pins.

Then you have to locate the initialisation code of the timer and make it arduino indepentent.

Add a few #include's for header files.

 

Note that "arduino" is not a language. "arduino" is just C++ with some obfucation added.

Studying well written code can be very educational.

Studying badly written code can also be very educational, but don't waste too much time on it.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

kujira wrote:
Elliot's book

Who is Elliot?

 

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

awneil wrote:
Elliot

Elliot, Elliot, who the heck is Elliot!? ;-)

 

I'm not living next door to him, but I guess he is the author of this: https://www.amazon.com/AVR-Progr...

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

I just had a look at:

https://github.com/arduino-libra...

 

It seems to be a real good atempt at obfuscation. (But not meant in a negative way).

It is an example of the stuff you run in if you want to make a source code library compatible with different uC's From AVR's tot ARM's, run it on different timers hardware and try hard to make it robust  so beginners can use it without giving any thought to how it works.

 

Then I had a peek at:

https://github.com/nabontra/Serv...

 

It's just written for Timer2 on an AVR without all the compatibility obfuscation layers.

Dependency on "arduino" also seems minimal.

It also works in the way I predicted. the servo channels are "scanned" from an array after each other.

Code is fairly compact (About 130 lines) and looks well written as a handfull of small functions which each have a well defined small amount fo  functgionality.

 

Looks like a good point to start for you.

 

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

Apart from the "Elliot" question, which microcontroller chip are you using?

Ross McKenzie ValuSoft Melbourne Australia

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

Ah, microcontroller chip is the 382Mega.  Bedtime here in Blighty.  Look at this stuff tomorrow.  Appreciate all the pointers.

 

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

Hmmm, presumably that is supposed to be mega328....  but 328P or 328PB ?

 

Ross McKenzie ValuSoft Melbourne Australia

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

who the heck is Elliot!?

Ness of course. https://en.wikipedia.org/wiki/El...

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Where is your circuit & the size of your support caps at the servo...servos can use up sudden surges of amps, so rock solid supply & plenty of caps ( say 1000uf) are in order on the servo supply.

A sturdy bench supply might be ok for testing.  

Your control signal should be clean and glitch free...a small res-cap filter might be in order as well.   If using 5V control, check that your servo can tolerate a 5v control signa.

 

Did you use your scope to check for jitter or glitches?

When in the dark remember-the future looks brighter than ever.

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

I'm using a 328P - PU chip.  Powering it from the Arduino UNO I use for the programmer. 

 

The Elliot book was properly identified by AW Neil above. 

 

I've used a scope to inspect the signal but am no expert on interpretation. 

 

I was hoping that flip flopping back and forth with be a common beginner mistake :-( 

 

I'll take a look at the pointers you all have provided above.

 

 

 

 

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

Check your choice of prescaler.

 

it looks like you are running at 1MHz. Am I correct?

 

So 1MHz/128/256 = 30.5Hz for a frame rate of 32ms. Seems a bit slow; is your servo happy with this?

 

But your resolution/step size is just 1MHz/128 = 128us.

 

Consider the range of valid values, 500us would need a count of just 4 and 1500us a count of just 12. So end to end your servo control range is 4 to 12. Which seems a bit coarse. But it ties in with you saying you get consistent result at value <10.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

Last Edited: Sun. Jan 14, 2018 - 10:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
It was a matter of getting the TCCR defines in the right places.  #ifdef FIXED code works.

static inline void initTimer2Servo(void) {
#ifdef OLD
    TCCR2A |= (1 << WGM20); // Fast PWM mode
    TCCR2A |= (1 << WGM21); // Fast PWM mode, pt.2
    TCCR2B |= (1 << CS20);  // 20+21= /32
    TCCR2B |= (1 << CS21);  // 
    TCCR2A |= (1 << COM2A1);// PWM output on OCR2A
#endif
#define FIXED
#ifdef FIXED
    TCCR2B |= (1 << WGM20); // Fast PWM mode
    TCCR2B |= (1 << WGM21); // Fast PWM mode, pt.2
    TCCR2A |= (1 << CS20);  // 20+21= /32
    TCCR2A |= (1 << CS21);  // 
    TCCR2A |= (1 << COM2A1);// PWM output on OCR2A
#endif

The DataSheet differentiates TCCR2A from TCCR2B as locations of the WGM bits...and checking here....says WGM20 and WGM21 go into TCCR2A.  So now I am mucho confused.  I wonder if the header file defines are wrong....

I've got #define TCCR2A _SFR_MEM8(0xB0)   and #define TCCR2B _SFR_MEM8(0xB1)  which look right to me.

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

kujira wrote:
The Elliot book was properly identified by AW Neil above.   

It was Johan who identified it.