Launchpad LED Chaser
LED chasers are the Hello World of microcontrollers. Here’s mine for the TI Launchpad.
Download source and Makefile here.
The MSP430G2231 doesn’t have enough timers or hardware Pulse Width Modulation channels for a good chaser effect. I want brightness control of 8 independent channels.
My solution is to use a single timer and Binary Code Modulation. BCM uses less processor time than PWM but doesn’t provide a single on and a single off period per cycle. Instead, it uses several bursts. This is fine for LEDs, where given a fast enough period the human eye can’t tell.
The reason for BCM’s efficiency is that the on/off periods are directly related to the binary representation of the desired duty cycle. Here’s a great tutorial.
LEDs really need a diffuser. I’m using an opaque plastic bowl.
(it looks like this without the bowl)
The circuit is simple. 8 LEDs to P1 (via resistors).
#include#include #define LED_DIR P1DIR #define LED_OUT P1OUT #define TICKS_PER_SEC (1000000 / TACCR0) // 1MHz clock #define LED_WRAP8(n) (((n)+8)%8) // #define PINGPONG 1 // Do the Knight Rider thing volatile static uint8_t led_brightness[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; volatile static uint32_t sys_ticks = 0; static void cpu_init(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT BCSCTL1 = CALBC1_1MHZ; // Set range DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz } // Binary Code Modulation static void bcm_tick(uint8_t led_ticks) { uint8_t bcm = 0x00; switch(led_ticks) { case 0x1: case 0x2: case 0x4: case 0x8: case 0x10: case 0x20: case 0x40: case 0x80: // led_ticks is a power of 2 if (led_brightness[0] & led_ticks) bcm |= BIT0; if (led_brightness[1] & led_ticks) bcm |= BIT1; if (led_brightness[2] & led_ticks) bcm |= BIT2; if (led_brightness[3] & led_ticks) bcm |= BIT3; if (led_brightness[4] & led_ticks) bcm |= BIT4; if (led_brightness[5] & led_ticks) bcm |= BIT5; if (led_brightness[6] & led_ticks) bcm |= BIT6; if (led_brightness[7] & led_ticks) bcm |= BIT7; LED_OUT = bcm; } } // Timer0 ISR interrupt(TIMERA0_VECTOR) TIMERA0_ISR(void) { sys_ticks++; bcm_tick(sys_ticks); } int main(void) { uint32_t t = 0; // time of last pattern change uint8_t pos = 0; // LED index of head of pattern int8_t dir = -1; // direction of motion cpu_init(); // Setup LED pins LED_DIR |= 0xFF; // All LED pins as outputs LED_OUT = 0x00; // Turn off LEDs // TimerA SMCLK in UP mode TACTL = TASSEL_2 | MC_1; // Enable interrupt for TACCR0 match TACCTL0 = CCIE; // Set TACCR0, starts timer TACCR0 = 80; // This must be short enough to look good and long enough for TIMER0_ISR to complete eint(); while(1) { // to set an LED to a brightness level, set led_brightness[index] = 0 to 255 if (sys_ticks - t > (TICKS_PER_SEC/8)) // pattern will repeat at 1Hz (8 LEDs) { t = sys_ticks; led_brightness[pos] = 0xFF; led_brightness[LED_WRAP8(pos-dir)] = 0x7F; led_brightness[LED_WRAP8(pos-(dir*2))] = 0x3F; led_brightness[LED_WRAP8(pos-(dir*3))] = 0x1F; led_brightness[LED_WRAP8(pos-(dir*4))] = 0xF; led_brightness[LED_WRAP8(pos-(dir*5))] = 0x7; led_brightness[LED_WRAP8(pos-(dir*6))] = 0x3; led_brightness[LED_WRAP8(pos-(dir*7))] = 0x1; pos = LED_WRAP8(pos+dir); #ifdef PINGPONG if (pos == 0) dir = -dir; if (pos == 7) dir = -dir; pos += dir; #endif } } }