Friday, November 12, 2010

Breathing LED effect with the LaunchPad!

Have you noticed how the blinking LED of the MacBooks causes a soothing, kind of hypnotic effect? No wonder why...

Apparently, the behavior of the LED while the computer is sleeping is tuned to resemble the human breathing rhythm at rest! Things like these give a better understanding of Apple's meticulous attention to detail and how much of your MacBook's $999 went into patent attorne-- ALL GLORY TO THE BREATHING LED!

The breathing effect is a natural step forward from the dull and classic "hello world" blinking LED. If you want to add it to your project, you need a spare Timer and a few lines of code. Here is how:

The guys at Adafruit reversed the waveform and, to me, it resembles something like this:

We are going to use PWM to drive the LED and change its brightness according to the above graph.

Since math operations are expensive on such a limited instruction set, we'll start off with a pre-calculated sine table. We only need the "breath in" portion of the wave (between -PI/2 and 0) as we can easily calculate the "breath out" side by mirroring the wave:

#include <io.h>
#include <signal.h>

int index = 0;

const unsigned char curve[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13,
13, 13, 14, 14, 15, 15, 15, 16,
16, 17, 17, 18, 18, 18, 19, 19,
20, 20, 21, 21, 22, 22, 23, 23,
24, 24, 25, 25, 26, 26, 27, 27,
28, 29, 29, 30, 30, 31, 31, 32,
33, 33, 34, 34, 35, 36, 36, 37,
38, 38, 39, 39, 40, 41, 41, 42,
43, 43, 44, 45, 46, 46, 47, 48,
48, 49, 50, 50, 51, 52, 53, 53,
54, 55, 56, 56, 57, 58, 59, 59,
60, 61, 62, 62, 63, 64, 65, 66,
66, 67, 68, 69, 70, 70, 71, 72,
73, 74, 75, 75, 76, 77, 78, 79,
80, 80, 81, 82, 83, 84, 85, 86,
87, 87, 88, 89, 90, 91, 92, 93,
94, 95, 95, 96, 97, 98, 99, 100,
101, 102, 103, 104, 105, 106, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115,
116, 117, 118, 119, 120, 121, 122, 122,
123, 124, 125, 126, 127, 128, 129, 130,
131, 132, 133, 134, 135, 136, 137, 138,
139, 140, 141, 142, 143, 144, 145, 146,
147, 148, 149, 150, 151, 152, 153, 154,
155, 156
};

 

Now, let's do all the necessary set-up. This is common to most projects: stop the watchdog and set MCLK at 1 MHz:

int main(void)
{
WDTCTL = WDTPW + WDTHOLD;
DCOCTL= 0;
BCSCTL1= CALBC1_1MHZ;
DCOCTL= CALDCO_1MHZ;

We are going to use SMCLK as the clock source. We'll set it up so that it uses MCLK / 8, that's 125 KHz (see SLAU144E p.5-15):

  BCSCTL2 |= DIVS_3;

Next, we'll make pin 1.6, which is hooked up to the green LED, an output (SLAU144E p.8-3). We also set pin 1.6 to follow TA0.1 (timer A's output, more on it below). Per SLAS694C p.41:

  P1DIR |= BIT6;
P1SEL |= BIT6;

Now we'll set up Timer A to count up to 625. At 125 KHz, that will give us a period of 5 milliseconds, or a frequency of 200 Hz. 200 Hz is high enough to avoid flicker. Higher values will flicker less, but waste more power since we're waking up the CPU more often. So... set TACCR0 to 625:

  TACCR0 = 625;

Ok, let's start Timer A. We'll source it from SMCLK (by setting TASSEL_2) and run it in "up" mode (MC_1). In "up" mode, the timer counts up to TACCR0 and then starts again from 0 (SLAU144E p.12-20):

  TACTL = TASSEL_2 | MC_1;

This is where the PWM magic happens: as it turns out, the LED brightness is proportional to its duty cycle (ie. the ratio of time it stays lit on each period). Remember we set pin 1.6 to follow "timer A's output"? Now, here's where we define how that output behaves. We configure it so that it's high (set) when it counts to TACCR1 and low (reset) when it counts to TACCR0. That's what OUTMOD_7 does. So, by giving TACCR1 values within the [0..625] range, we'll make the LED glow more or less (the higher, the brighter). CCIE enables interrupts every time the timer hits TACCR1. We will use the interrupt to update the TACCR1 value according to the breathing curve.

  TACCTL1 = OUTMOD_7 | CCIE;

Set the initial TACCR1 to 0 (the LED is fully off):

  TACCR1 = 0;

Now put the CPU to sleep. We are just waiting for interrupts here. The return is kind of silly (we're never returning anywhere) but keeps gcc happy.

  __bis_SR_register(CPUOFF | GIE);
return 0;
}

Finally, our ISR (interrupt service routine). It is called once every 5 ms period, right after timer A hits TACCR1. We just count to 1000. From 0..499 we read the table forward and from 500-999 we read it backward. 1000 times 5 milliseconds gives a 5 seconds breathing rate, which is quite close to people's rate at rest:

// This will be called when timer counts to TACCR1.
interrupt(TIMERA1_VECTOR) ta1_isr(void)
{
int new_ccr1 = 1;

// Clear interrupt flag
TACCTL1 &= ~CCIFG;

if (index < 500) {
new_ccr1 = curve[index++ >> 1];
} else if (index < 1000) {
new_ccr1 = curve[(999 - index++) >> 1];
} else {
index = 0;
}

We are almost done. When setting a new TACCR1 value, we need to wait until TAR (the timer counter) has gone past it, otherwise we'll hit TACCR1 twice or more in the same period!

  while (TAR <= new_ccr1) ;
TACCR1 = new_ccr1;
}

That's it. I hope this helps anyone getting started with timers and PWM on the MSP430 family!

The whole source code for the file is here:

http://code.google.com/p/osx-launchpad/downloads/list

Unzip it. Then, run 'make install' to download it to your LaunchPad.

 

Launchpad's toolchain version 20101112 released!

A new version of the toolchain is ready for download. What's new:

  • mspdebug no longer needs replugging to detect the board.

You can download it from:

http://code.google.com/p/osx-launchpad/downloads/list

Just download, unzip and install.

 

Tuesday, November 2, 2010

mspgcc4-20101006 with gcc-4.4.3, gdb-7.0.1 and mspdebug-0.12

Hello!

I'm writing this blog hoping to provide a quick and painless procedure to get started with Texas Instrument's MSP430 LaunchPad board on Mac OS X. Their support for OSX is quite poor to say the least. I'm providing a self-contained package so that you don't have to waste your time like I did all day. Just download the following file, unzip and install:

Note that this is a set of command-line tools in the spirit of the equivalent CrossPack tools for AVR MCUs.
To test it:
  1. Download their temperature demo. Unzip.
  2. Open a Terminal, cd to the folder you just unzipped, then run
    make

    If you don't have 'make', just install Xcode (I know, it's overkill, but...).
  3. You should get a 'main.elf', now upload it to the LaunchPad with:
    sudo mspdebug rf2500 "prog main.elf"
  4. Done! You won't notice much of a change since you just uploaded the same blinking red/green demo that came pre-loaded, but you get the idea :)
Caveats:
  • You don't get a serial interface like Windows/Linux users do. The official wiki(yes, the official wiki) will tell you to install this driver, but you'll just waste your time yet again. This information is not only misleading, but absolutely wrong, that driver was written for the TUSB3410UARTPDK and will not work with the LaunchPad.
  • You can only program the board once, and then it won't get recognized again by mspdebug until you unplug it and plug it back in.Edit: fixed! download the lastest package.
What's under the hood?
The package will install a few things:
  • The mspgcc4 toolchain (current version: 20101006), which includes GCC/G++ 4.4.3, GDB 7.0.1 and related bintuils.
  • mspdebug (current version: 0.12). This is a programmer/debugger for the MSP430 devices. It's actually very neat, you can perform on-chip disassembly, debugging and step-by-step execution. I think you can piggyback gdb on it and debug from gdb or any of its front-ends, but I haven't actually tried it. Again, I included the binaries so that you don't have to deal with the homebrew/darwinports/libusb stuff yourself.
  • An especially crafted driver by Bill Westfield (westfw). This is sort of a "null" driver so that OSX doesn't take exclusive control over the LaunchPad when you plug it in.
I hope this helps. It goes without saying that this is a barely tested build of the toolchain and it might not even work for you. I just tested it on Snow Leopard (10.6.4), but it should work with Tiger and Leopard, too. Edit:. So far, the toolchain only works on Snow Leopard. Please drop your flames on the comment area below. If you get it to work, let me know as well.