Category: MSP430 Launchpad

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!

MSP430 Launchpad – ADC Primer

Posted by on August 19, 2011

I know why you are here! — I’ve been there!

Trying to find useful notes on how to accomplish anything is often a daunting task, mainly due to the fact that the Internet suffers of very low SNR and applications lately have been catering to dummies only.

While there are some very interesting documents out there, you still have to go through a lot of dirt to find a few gold pebbles!

 

Let’s jump right on it.

I won’t bother you with the technical aspects of ADCs and their different implementations, I will instead jump right on what you need to know in order to get started with them. You obviously understand what ADC stands for and you generally have a good idea of what they do.

First of all, the msp430g2131 has an 8 channel ADC port, the type of ADC used is  a 10bit SAR.

 

SAR ADC:

The letters stand for Successive Approximation Register – Analog to Digital Converter, this ADC actually uses a DAC to sweep and approximate the input using a comparator, for this reason a voltage reference is required.

 

Calculating the actual input voltage from the resulting sample:

Let’s assume your voltage reference is 2.5v, this means 2500mV. Now, we have 10 bits of resolution, that means ~1024 steps are available (2^nBits).

With this information, if you were given a sample with a value of “511”, you’d know the input voltage would be around 1.25v.

Thus, we can formulate a very simple approach to obtain a meaningful value out of the given sample:

input_millivolts = ( adc_sampled_value * reference_voltage ) / ( 2^adc_bits )

If we — for some reason — wanted to perform the inverse operation, one way of doing it would be:

adc_sampled_value = ( input_millivolts / reference_voltage ) * ( 2^adc_bits )

Of course, if we have a “compressed” range, we must expand it after conversion to obtain the actual voltage at the input of the circuit. You would simply multiply the input_millivolts variable by the ratio used in your input divider.

 

Accuracy vs Resolution:

The accuracy of your conversion will be dictated by process variance, component tolerances and circuit stability, whereas the resolution is defined by the overall available range of values; that is the amount of meaningful bits.

 

Using it…

So now you understand that the input voltage must be within the reference voltage. This means that we must perform input conditioning in order to achieve meaningful results.

For example, If you wanted to measure 0 to 10 volts, then of course we’ll be compressing a bigger voltage range into a smaller one and this tells us one thing: we’ll lose resolution. Now, there are ways to enhance our results by over-sampling for example, but that’s outside the scope of this guide.

Naturally, one way to “compress” this range would be to use the modest voltage divider: two resistors. This works just fine for low complexity circuits and low sampling rates. In this particular example we would obtain about a quarter of the resolution you’d have if you were working with a 1:1 input / reference ratio.

When you dive into higher sampling rates and resolution, suddenly other factors come into play, for example the accuracy and stability of your reference voltage (you may not use an internal reference anymore!) and noise, and…! — It gets harder than the pope at the candy store.

 

Closing up…

While I mentioned a specific device (it merely pertains to TI’s LaunchPad) the same principle applies to any platform as long as there is a SAR ADC being used. There are other topics involving ADCs such as dithering, operation modes, input conditioning (not only voltage scale but also sample and hold, etc).

However for the most part, this short text should provide you with the necessary tools to obtain a raging epiphany should you be a beginner.

Cheers.

 

PS: Comprehension comes first, this is why I didn’t bother to paste any code. One must first understand the basics.

 

References:

 

MSP430 Launchpad: LED as PhotoDiode

Posted by on February 28, 2011

Story:

I’ve been meaning to build a “line seeker” robot for a while now, however I can’t source LDRs (Light Dependent Resistor) due to RoHS nonsense, so I decided to use plain old LEDs instead. While you could use a couple analog amplifiers and comparators to do this I thought it would be a fun little microcontroller project.

Right now we’re just exploring how to utilize a regular LED as a photo-diode, ie. an input sensor.

There are a couple ways to accomplish this – I chose the simplest for the time being – The idea is to sample the voltage present on the LED anode (or cathode!) by using the ADC on the MSP430, this voltage will be relative to the ambient light. You could picture the LED as a solar cell in this case.

The code:

We’re going to use the on-board LEDs on the LaunchPad to simplify things. The Red LED will be our “sense” input and the Green LED will be used to depict the current state of the sensor.

You’ll have to modify the header (and your project target) depending on which MCU you’re using, I happen to have a G2452 currently populated for another project I’m working on, but you can use any value-line MCU as long as it’s got an ADC.

/* LED as Photo-Sensor
 / By: Gustavo J. Fiorenza (GuShH - info@gushh.net)
 / No external components required, simply shine a light to the RED onboard LED!
 /
 / Note: Tested on a G2452 only, should also work with other value-line MCUs.
*/

#include "msp430g2452.h"	// Change the header to "msp430x20x2.h" if you're using the default MCU bundled with the LaunchPad.

#define LED_SENSE INCH_0 	// LED 1 (Red LED from LaunchPad).

unsigned int adcval = 0;

unsigned int analogRead(unsigned int pin) {

  ADC10CTL0 = ADC10ON + ADC10SHT_2 + SREF_1 + REFON + REF2_5V;
  ADC10CTL1 = ADC10SSEL_0 + pin;

  ADC10CTL0 |= ENC + ADC10SC;

  while (1) {
    if ((ADC10CTL1 ^ ADC10BUSY) & ((ADC10CTL0 & ADC10IFG)==ADC10IFG)) {
      ADC10CTL0 &= ~(ADC10IFG +ENC);
      break;
    }
  }

  return ADC10MEM;
}

void main(void) {
	unsigned int i, delay;

	WDTCTL = WDTPW + WDTHOLD;	// Hold the watchdog.
	P1DIR = BIT6; // LED 2 (Green LED from LaunchPad).

	while (1){

		// Multi-sampling (8 samples with delay for stability).
		adcval = 0;
		for ( i=0; i < 8; i++ ) {
			adcval += analogRead( LED_SENSE );	// Read the analog input.
			delay = 1000;
			while (--delay);
		}
		adcval >>= 3; // division by 8

		// Interpret the result
		if ( adcval < 500 ){
			P1OUT |= BIT6;	// Turn on the onboard Green LED.
		}else{
			P1OUT = 0x00;	// Turn off the entire Port 1, thus turning off the LED as well.
		}

	}

}

Why so vague?

Lately I've been suffering a serious mental block, I would really like to explain the theory behind all this but not only am I uninspired I also can't focus on writing... I would however recommend you Google "LED Sensor" (or similar query) to learn more about the subject.

Have fun.

MSP430 Launchpad: Using Buttons

Posted by on December 21, 2010

Intro:

Sooner or later you’ll want a user interface, whether you’re working on a game or just trying to figure things out one of the simplest methods for human interaction would be a button. We’ll discuss how to setup and read input bits in the following paragraphs.

The code:

// MSP430 Launchpad - Blink onboard LED using onboard Switch (SW1).
//	GuShH - info@gushh.net

#include  "msp430x20x2.h" // Include the necessary header for our target MCU

void delay_ms(unsigned int ms ){ 		// Not really a milliseconds delay!
      unsigned int i;
      for (i = 0; i < = ms; i++){ 		// The operator is <=, I've yet to fix this site bug.
        __delay_cycles(500);
      }
}

void do_led( int led, int delay ) {
	P1OUT = led;
	delay_ms( delay );
}

void main(void) {

	WDTCTL = WDTPW + WDTHOLD;		// Hold the WDT (WatchDog Timer)
	P1DIR |= BIT0|BIT6;       		// Setup the appropriate output bits	

	while(1) {

		if ( !(P1IN & BIT3) ){		// If not (port one input AND bit3 (sw1))
			do_led( BIT6, 2000 );	// Turn on LED 2
		}else{				// Otherwise...
			do_led( BIT0, 100 );	// Turn on LED 1
		}

	}

}

The quick explanation:

We'll deal with inputs very much the same way we would with an output, except we're reading instead of writing. Notice how we define the bits we want as "high" on P1OUT to light up our LEDs, provided the bits are set on P1DIR to "high" then those bits will act as outputs and every time P1OUT has one of these bits "high", your outputs will also go "high".

Likewise with our inputs, if P1DIR is set "low" on our target bit then all we have to do is read P1IN to know whether a button is being held down or not. Simple eh?

To recap: On port direction bits low = Inputs, bits high = Outputs

A word on de-bouncing:

For those of you unaware of switch bounce, I would like to give you a simple explanation of what it is: whenever you actuate a switch it's contacts bounce back and forth very rapidly as the metal vibrates after it's been bent and pushed onto the opposite contact. This poses a problem with false triggering; You may press a button once but the software may detect 3 or more presses.

Notice how we don't perform any sort of de-bouncing in the code, this is for simplicity and in the future we will tackle this, but for now we're keeping it simple and down to the basics.

One simple way to de-bounce in software is to sample the input multiple times, hardware de-bounce is often proffered but it comes with it's costs. For cheap consumer products more often than not this action is carried in software.

Where do we go from here?

Now that you can manipulate both outputs and inputs; it is time to create something useful. In the next lesson we'll design a full blown bicycle safety light with what we've learnt!

For the next lesson you'll need:

  • 3x General Purpose NPN Transistor ( PN2222, BC337, 3904, etc. Almost any will do )
  • 3x 4k7 Ohm 1/4W Resistors
  • 3x 100 1/4W Resistors ( Could be up to 470 Ohms, or even 1K if high brightness LEDs are used )
  • 1x 10K Ohms 1/4W Resistor (Optional)
  • 1x Tact Switch (Optional)
  • 3x 5mm or 3mm LEDs, Could be all of the same colour or one Red, Green and Blue. Your choice.
  • 1x Breadboard or any other prototyping method of your choice.

Something to download:

The "Using Buttons" project files.


Stay tuned.

MSP430 Launchpad: Random Software Delays

Posted by on December 13, 2010

The goal: to “randomly” flash the on-board LEDs at P1.0 and P1.6

The weapons: CCS, Launchpad, Cookies (you may choose your favourite ones)

The reason: To familiarize yourself with the coding environment, or just for the heck of it.

The library:

#ifndef GRAND_H_
#define GRAND_H_

static unsigned long int rand_next = 1;

int gRand( void ) {
	rand_next = rand_next * 1103515245 + 12345;
	return (unsigned int)(rand_next/65536) % 32768;
}

void gSRand( unsigned int seed ) {
	rand_next = seed;
}

#define gRandom()		((gRand() & 0x7fff) / ((float)0x7fff)) 	// random in the range [0, 1]
#define gCRandom()		(2.0 * (gRandom() - 0.5)) 			// random in the range [-1, 1]
#define gMax(x,y)		(x > y ? x : y)				// minimum
#define gMin(x,y)		(x < y ? x : y)				// maximum

#endif /*GRAND_H_*/

That’s our random library, it’s grand.h

The “g” prefix is one I often use privately, it’s simply the first letter of my name. However because there may be other routines in the future with a similar naming convention, having “g” prefixed is not a bad idea; without having to fall into namespace gibberish.

The PRNG is an old standard. No need to discuss it.

You may recognize those macros, yes! They’re from Quake3! — Although we aren’t using them I left them there for future reference on how to obtain usable value ranges from the PRNG. There’s a lot to be said about floating point values and whatnot, But I’m going to restrain myself in this case.

Now to the main code:

// MSP430 Launchpad - Blink onboard LEDs using random delays.
//	GuShH - info@gushh.net

#include  "msp430x20x2.h"	// Include the necessary header for our target MCU
#include  "grand.h"		// Include our simplistic prng lib.

void delay_ms(unsigned int ms ) { // This function simply performs a software delay, hard from efficient but it's practical in this case.
	unsigned int i;
	for (i = 0; i < = ms; i++) { // Make sure your < and = has no space in between (the syntax parser seems to be messing things up)
		__delay_cycles(500); // This should be dependent on clock speed... but what the hell, even the loop itself should be taken into account...
	}
}

void set_and_wait( int led ) {
	P1OUT = led;			// Set the bits
	delay_ms( gRand() * 0.01 );	// Delay a "random" amount of time
}

void main(void) {

	WDTCTL = WDTPW + WDTHOLD; // Hold the WDT (WatchDog Timer)
	P1DIR |= BIT0|BIT6;       // Enable the appropriate output

	while(1) {
		set_and_wait( BIT0 );	// Set BIT0, that's our first LED.
		set_and_wait( BIT6 );	// Set BIT6, The other LED.
	}

}

No external hardware is required, just make sure both P1.0 and P1.6 jumpers are set.

As you can see we're simply toggling the LEDs with a random delay, the delay_ms(); function was taken from here.

Like I said there are quite a few topics to explain, however I decided to keep this one as simple as possible (Alright, I'm in a rush!)

So... Compile, run and enjoy!

Once I get the time I'll put together some utilitarian code libraries and lengthier explanations, promise.

For those interested, you may download the entire project directory from here: Random Software Delays.