13 July 2010

Tutorial 04: Stuck in a Loop

We now have enough to be able to write programs for the MSP430, so before we go into the peripherals that make the microcontroller useful, let's get started programming and debugging.  We'll start using the MSP430G2211 that comes with the LaunchPad.  If you're not using LaunchPad, it's easy to change this first program for whatever device you're using.  All it needs is an LED connected to one of the GPIO (General Purpose I/O) pins.  (Connect it with a resistor to prevent too much current draw out of the MSP.  A couple hundred ohms should be sufficient.)

In the previous tutorial, we mentioned the useful BITx definitions in the device-specific header file.  These were at one time variation specific (eg. msp430x2x01 as opposed to msp430g2001), but with the most recent version of Code Composer Studio, TI appears to be moving to device specific header files.  Whenever you write software for your MSP device, be sure to include the appropriate header file.  For this tutorial, if you're not using a LaunchPad or the G2211, change the header file to whatever device you're using.

So the start of any MSP430 program will have a line similar to this:
#include <msp430g2211.h>

For simple programs, this is often all that is needed before coding the main function, and that is certainly the case for the "Hello, world!" program of microcontrollers, blinky.  Blinky is a simple program that toggles an LED at a given period.  It's best to use timers, interrupts, and such to do this type of function, but you'll have to wait until we look at those types of peripherals and functions before we can do that.  The simplest way to get the program to work is with some type of delay loop.  The LaunchPad has a green LED connected to P1.0 and a red LED connected to P1.6.  We'll use the green LED for now.  Let's get started!

The beginning of our code looks like this:

#include <msp430g2211.h>


void main(void) {


} // main

Unfortunately, we do need one step that we can't explain completely yet before we do anything else.  The MSP430 includes a special timer called the Watchdog Timer (WDT).  Its function is to reset the CPU if it gets stuck (ie. the watchdog timer runs out).  This timer is on by default when the chip powers up.  Though we could use this timer to give us our delay, for now we don't want our program to reset on us, so we need to first turn this timer off.  (We'll look at using the WDT later.)

The WDT has a 16 bit register called WDTCTL, but only the first 8 bits have any control.  The upper byte is used as a security measure; to prevent your code from accidentally changing the WDT, you must provide a password.  The password turns out to be 0x5a (in the upper byte, so actually 0x5a00), but the header file helps you out by defining that value as the much easier to remember WDTPW.  Add this value to the 1-byte configuration value you want to assign to WDTCTL.  To turn off the WDT, we can use the header-defined value WDTHOLD:
WDTCTL = WDTPW + WDTHOLD;

You'll get into the habit of starting all of your first programs with this line, but keep in mind the WDT has a lot of useful functions.  We'll discuss those when we talk about the WDT in depth.

Ok, now that we've disabled the watchdog, we can continue writing the code.  We want to light an LED on P1.0, so we can use the last tutorial to set that up:
P1OUT = 0;
P1DIR = BIT0;

Now we want to toggle the LED and delay for some given amount of time.  I'm using a for loop, though a while or do-while loop would be just as effective.  We'll need a counter for the loop, so remember to define it at the start of the code:

#include <msp430g2211.h>


void main(void) {
    unsigned int count;
    WDTCTL = WDTPW + WDTHOLD;


    P1OUT = 0;
    P1DIR = BIT0;


    P1OUT |= BIT0;
    for (count = 0; count < 60000; count++);
    P1OUT &= ~BIT0;
    for (count = 0; count < 60000; count++);
} // main

What happens when we get to the end of the code?  In a computer, it would just exit.  In a microcontroller, however, it would just move on to the next address to read its next "instruction".  There could be just about anything in there, from random noise to leftover code from older (larger) programs.  In any case, it would just continue stepping merrily through the address space until it gets to the end and then... well, I don't know what happens.  It's not what is intended to happen in any case, so we need to prevent the processor from ever moving beyond the actual code.  We'll do this now by putting the code into an infinite loop.  That's usually a bad idea in computer code, but essential here.


#include <msp430g2211.h>

void main(void) {
    unsigned int count;
    WDTCTL = WDTPW + WDTHOLD;


    P1OUT = 0;
    P1DIR = BIT0;

    for (;;) {
        P1OUT |= BIT0;
        for (count = 0; count < 60000; count++);
        P1OUT &= ~BIT0;
        for (count = 0; count < 60000; count++);
    }
// main


Again, you could easily do the same thing with a while(1) loop or something similar instead.

So there's blinky.  The light turns on, it waits, turns off the light, waits again, then goes back to the beginning to do it all over ad nauseum.

Note there's a cleaner way to accomplish this task:

#include <msp430g2211.h>

void main(void) {
    unsigned int count;        // loop counter
    WDTCTL = WDTPW + WDTHOLD;  // disable watchdog


    P1OUT = 0;                 // initialize LED off
    P1DIR = BIT0;              // P1.0 output

    for (;;) {
        P1OUT ^= BIT0;         // toggle LED on P1.0
        for (count = 0; count < 60000; count++);  //delay
    }
// main

Now you only have to change one loop length to adjust the frequency of the blink.  (Though if you want to play with different duty cycles, the first method works well.)  I've also added some comments to the code. Programming teachers seem to always stress the importance of good commenting habits; it'll save your hairline in microcontrollers.  Comments don't add to the code size at all, so be sure to document everything you do well.

Next time we'll put this into our G2211 and learn how to use CCS to load programs onto the LaunchPad.


Reader Exercise:  Write a program like blinky that alternates the green and red LEDs on the LaunchPad (the red LED is on P1.6).  Then modify the program so that it alternates flashing the green LED, then the red LED, with short pulses and a delay between the individual flashes.

Disclaimer:  I don't have my LaunchPad yet, so I haven't technically tested this code.  After doing similar programs for three or four other MSP430's, I'm confident it works, but until I can get a video posted of my LaunchPad blinking its LED with this code, I reserve the right to have made mistakes.  =)


Disclaimer 2:  When I first typed up this post, I declared the count variable as just int count;.  In the MSP430 compiler, a 16-bit plain int cannot count up as high as 60,000; it reaches its highest value then wraps around to a negative value.  Using a plain int for this code will never leave the delay loop.  An unsigned int, rather, is non-negative.  Using an unsigned int allows us to count up higher.  Longer delays may require an unsigned long.  An alternative would be to use a hex notation; 0xFFFF is the highest value an int can count to.  In plain int decimal, this corresponds to 32,767.  In unsigned int decimal, it's 65,535.

16 comments:

OCY said...

1) Making P1.0 an output pin

You mentioned that you need to set:
P1DIR = BIT0;

But later on, you forgot to include that line in the code you shown.

2) After main finishes

I think actually execution always start from "cstart". It sets the stack-pointer, initializes some of the variables, and then calls your "main". If and when "main" returns, it calls "exit" which usually just loops forever doing nothing.

3) Device specific header file

Aside from the c-compiler, the linker and the debugger also needs to know what specific device you are using.

I use KickStart IDE which requires you to set it up for one of the devices it supports. But if this setup is not consistent with the device specific header file you #included in the c-source code, you may end up with a mess.

One way to work around this potential problem is to use a special header file "msp430.h", and not the device specific header file such as "msp430g2211". Doing this forces the c-compiler to automatically #include the device specific header file that is consistent with the IDE setting.

NJC said...

David,

I'm really looking forward to your future posts after you finish the basic tutorial(specifically about the WDT). I've used microcomputers for years, academically, professionally, and for hobby projects; yet I've never used the WDT! There are a few things I haven't had the time to look up, but would like to know.

Keep up the good work. You do a great job working through things simply btw. It's a problem that many people who have an EE background struggle with.

-NJC

Unknown said...

Sorry about the error in the code; the count variable needs to be an unsigned int to be able to count to 60000.

Unknown said...

OCY, thanks for the comments! I've fixed the broken code now to include the port declarations.

Unknown said...

NJC, yeah, I'd like to get to WDT as well. I think there are a couple of things I should cover on the way, but hopefully it won't take me too long to get to that peripheral. It would make sense to do it soon, since it's one of the peripherals that's on all of the MSP430's, but I think the BCS+ and Timers should come first. We'll get there, though.

Unknown said...

OCY: I just did some looking into your comment on the header file; the msp430.h file is fantastic! Writing code with this header definitely makes projects more portable; as long as you're using a chip with the same peripheral configurations, the code will work the same on one chip as the other without any changes.

Essentially, msp430 reads which device is active in the linker, and loads the appropriate header file; with no added overhead in code size. I'm curious why it's not used in TI code examples, though. I didn't know about it, because the sources I learned from are relatively old in comparison to the newer versions of CCS.

Joerg said...

Thank you for your great tutorial! I am using your examples with the mspgcc4 under Ubuntu and it works, but there is a little pitfall: I like to compile the sources using compiler optimization, e.g. -Os. But then your example will not work as expected. The optimized compiler will drop out the delay loop!

One simple solution is to declare the counter in the following way: volatile unsigned int count. This will prevent the compiler from dropping the delay loop.

Unknown said...

That's an excellent point. I'm curious if there's any hit to code size for declaring a variable as volatile; I wouldn't think so, but I'm not sure. Might be an interesting thing to check!

Marcus said...

I'm a beginner at embedded development, but not at software development, however I am confused by this and another example I saw here, where a delay loop is used, as opposed to interrupts or timers.

On a general purpose computer, that sort of timing loop is never done, except in perhaps as a mistake in the most extreme novice case. However, in embedded programming is this normal? My assumption is that it's not a good practice at all, but I'd like to know for sure which is why I'm asking.

Thanks, btw, for your posts, they have been very helpful to me starting out.

Unknown said...

Marcus,

I'm glad they've been helpful to you! As for your assessment of using loops, you're quite right. But interrupts aren't the simplest concept at first, and often it's easier to just get started. Be patient; you'll switch to interrupts soon enough. =)

There may actually be reasons why in embedded systems you'll use loops instead of interrupts, but it will be very dependent on the exact situation you're in. Perhaps all the timers in your device are already being used for something else. Or perhaps you just need to code something up really quickly; a loop takes a lot less time to code and is much easier to debug than an interrupt. So these loops do happen in embedded programming, but usually only at the beginning stages or under unusual circumstances.

Sathyan said...

David,
Is it possible to add any program to MSP430G2231 without over writing the demo program viz.," Intrenal Temprature Measurement".
Is an "ORG" declaration required, then what should this be and where is introduced
Thanks.
Sathyan

Unknown said...

Sathyan,

I believe there is a way to write to a specific portion of the flash, but then your device would run the demo program anyway. There's no need to be concerned for over-writing the demo-- you can download the code for it from TI and reflash it at any time!

In terms of an "ORG" declaration, I'll be honest and admit I don't know what that is. So I expect it's not required. =) Perhaps another reader might be able to chime in on that?

Sedo said...

Hi~

I'm learning many things from your post.
And I have a question.

With the first example, I tried to make LED to blink.
However, once LED was turned on, it does not blink with just "for" loop.

I put below code in for loop, then it works.
for(count=0;count<500;count++)
__delay_cycles(2000);

So, my question is that there is any reason why it does not work with just for loop(I mean without __delay_cycles(2000);)

Any comment will be appreciate.

Thanks.

Unknown said...

It may be how your compiler is set up; an empty for loop is something some compilers may ignore, since they don't actually do anything. The for loop method exploits compilers that aren't as smart about optimizing the code. As such, it's not the best method of introducing a delay, but it's a very helpful way for beginners as it's easy to understand what's happening.

zelu said...

Thank you so much for your great tutorial!
And I want to mention that for the MSP430 G2 launchpad, however, P1.0 corresponds to the red led and P1.6 corresponds to the green led.

autumnleaf said...

David, these are excellent tutorials. I've been using the arduino and recently made the switch to MSP430, and I'm shocked at how much concepts the Arduino tried to hide. Feels a lot better to know what's going on under the hood.