Tag: msp430 servo

MSP430 Launchpad: Controlling a Servo with hardware PWM

Posted by on March 7, 2012

RC Servos are pretty useful devices, they allow us to move things in a mechanical manner. They are cheap and readily available, so it only makes sense to use them!

A quick intro:

Servos are actually rather simple to interface with, they merely require a pulse of a certain width to determine the next position.

Because of this, we can use hardware PWM to generate these pulses. The frequency is typically 50Hz, which means we’ll be able to obtain a great deal of granularity with regard to the duty cycle, given our clock speed of 1MHz. Which is required since we’ll be dealing with pulses ranging ~1mS to ~2mS (typically)

Different servos will require a different minimum and maximum duty cycle, however in general the given code should just-work, in case your servo goes off range simply find the best minimum and maximum values for it.

#include "msp430g2553.h" // make sure you change the header to suit your particular device.

// Connect the servo SIGNAL wire to P1.2 through a 1K resistor.

#define MCU_CLOCK			1000000
#define PWM_FREQUENCY		46		// In Hertz, ideally 50Hz.

#define SERVO_STEPS			180		// Maximum amount of steps in degrees (180 is common)
#define SERVO_MIN			650		// The minimum duty cycle for this servo
#define SERVO_MAX			2700	// The maximum duty cycle

unsigned int PWM_Period		= (MCU_CLOCK / PWM_FREQUENCY);	// PWM Period
unsigned int PWM_Duty		= 0;							// %

void main (void){

	unsigned int servo_stepval, servo_stepnow;
	unsigned int servo_lut[ SERVO_STEPS+1 ];
	unsigned int i;

	// Calculate the step value and define the current step, defaults to minimum.
	servo_stepval 	= ( (SERVO_MAX - SERVO_MIN) / SERVO_STEPS );
	servo_stepnow	= SERVO_MIN;

	// Fill up the LUT
	for (i = 0; i < SERVO_STEPS; i++) {
		servo_stepnow += servo_stepval;
		servo_lut[i] = servo_stepnow;
	}

	// Setup the PWM, etc.
	WDTCTL	= WDTPW + WDTHOLD;     // Kill watchdog timer
	TACCTL1	= OUTMOD_7;            // TACCR1 reset/set
	TACTL	= TASSEL_2 + MC_1;     // SMCLK, upmode
	TACCR0	= PWM_Period-1;        // PWM Period
	TACCR1	= PWM_Duty;            // TACCR1 PWM Duty Cycle
	P1DIR	|= BIT2;               // P1.2 = output
	P1SEL	|= BIT2;               // P1.2 = TA1 output

	// Main loop
	while (1){

		// Go to 0°
		TACCR1 = servo_lut[0];
		__delay_cycles(1000000);

		// Go to 45°
		TACCR1 = servo_lut[45];
		__delay_cycles(1000000);

		// Go to 90°
		TACCR1 = servo_lut[90];
		__delay_cycles(1000000);

		// Go to 180°
		TACCR1 = servo_lut[179];
		__delay_cycles(1000000);

		// Move forward toward the maximum step value
		for (i = 0; i < SERVO_STEPS; i++) { 			TACCR1 = servo_lut[i]; 			__delay_cycles(20000); 		} 		 		// Move backward toward the minimum step value 		for (i = SERVO_STEPS; i > 0; i--) {
			TACCR1 = servo_lut[i];
			__delay_cycles(20000);
		}

   }

}

The actual code was written for a quick and dirty servo testing unit I put together, however it could easily be expanded to other uses.

By using a LUT (Look-Up Table) we are able to position the servo on a per-degree basis, which makes the code a lot more intuitive and readable.

A bunch of notes and tips:

  1. Filter your servo power supply properly; servos are electrically noisy!
  2. Make sure your micro-controller is protected from external devices, such as the servos. For instance never connect the signal wire directly to the GPIO pins. Instead, use a buffer or at least a resistor. For this example we used a 1k Resistor, but since the “signal” interface is relatively high impedance, we could even use 4k7 or higher without much hassle.
  3. As long as you keep sending a signal to the servo, the servo will keep it’s position due to it’s internal feedback. However this consumes power so if you’ve got a very lightweight load or no load at all, you can always stop the PWM output for the periods of inactivity to save power.
  4. If you aren’t sure about your servo connections, here’s a small diagram to help you find the pinouts. In my case I had a couple cheap “Tower Pro” blue servos that use the JR pinout — These work fine at both 5v and 3.3v, however do not power them from the Launchpad power supply pins! — Servos consume a lot of current, always use an external PSU.
  5. Only one connection is required to interface the servo with the launchpad, find the pin P1.2 and connect it to the SIGNAL wire from your servo.

 

That’s it for now, enjoy!