MSP430 assembly part 1: Blinking LED hello world

Lets blink an LED on a MSP430 launchpad the old fashioned way! Assembly! Don't worry, MSP430 has a nice, small RISC instruction set. It's only true 24 instructions, with a total of 27 higher level ones emulated by the assembler.

The full user's guide for the MSP430 microcontroller i will be covering (on an older launchpad) is available here ,
grab a copy because it covers the instruction set and microcontroller completely.

First, I will present the source with the CCS generated boiler plate for ELF sections and stack setup:

            .cdecls C,LIST,"msp430.h"       ; Include device header file
            .def    RESET                   ; Export program entry-point to
                                            ; make it known to linker.
            .text                           ; Assemble into program memory.
            .retain                         ; Override ELF conditional linking
                                            ; and retain current section.
            .retainrefs                     ; And retain any sections that have
                                            ; references to current section.

    RESET       mov.w   #__STACK_END,SP         ; Initialize stackpointer
    StopWDT     mov.w   #WDTPW|WDTHOLD,&WDTCTL  ; Stop watchdog timer

            ; our code will go here moving forward

    .global __STACK_END
    .sect   .stack
    .sect   ".reset"                ; MSP430 RESET Vector
    .short  RESET

For all intents and purposes we can ignore the setup scaffolding, it just turns off the watchdog timer and setups the stack and reset vector. This lets you press the reset button to .. jump to our RESET label.

To blink an LED on a microcontroller you need to do 2 things:

On the Ti launchpad, there are two LEDs on PORT1. For simplicity we can set the entire port to output. To do this, mov an immediate value into the named mapping like so:

mov #0FFh, P1DIR ; P1DIR is port1 direction control. '#0FFh' is an immediate value in hex

Now the entire port is set as output. To check, you could just try to lightup all the LEDs by turning all the pins on:

mov #0FFh, P1OUT

However this isnt very exciting because they just stay lit. We need to toggle. The easiest way is an exclusive or (xor) of the bits.

xor #0FFh, P1OUT  ; flip all bits in P1OUT

However, we need to loop over this so we dont just change the output once and fall through:

    xor #0FFh, P1OUT  ; flip all bits in P1OUT
    jmp LOOP

Astute readers know what's coming next. if you run this code the LEDs will appear to be solid. It's because the loop's executing so fast the human eye cannot see the difference. In fact there probably is no difference, as the rate this loop executes is many 1000s of instructions per second.

I lied when I said we only need to do 2 things. We need a delay loop. Unfortunately we do not have something as convenient as wiring's delay(). We need to make a delay loop by hand! This is much easier than it sounds. There are a few ways to do so, we will do the naive approach first:

The tricky part about this approach is figuring out how big of a number you need, which can depend on the MCU clock rate, and what instructions you effectively execute within the loop. Precise timing can be achieved with some math and nop sleds. But for our purpose, we can ballpark some visible delay.

Lets stuff our countdown in the first general register R4. The MSP430 is a 16-bit cpu so lets try the biggest 16-bit unsigned value to start with:

mov #0FFFFh, R4

Now lets run it down to zero. We can check if it hit zero using the status register SR, this is like the x86 EFLAGS register. It will have bits set automatically after certain instructions, which are documented for every instruction that affects status flags. Alternatively, there is a conditional jump when the zero flag is set, so we will use it - jnz.

    SUB #1h, R4 
    ; we fall through here once delay loop is done 

This will spinwait until R4 hits zero and then fall through, now all that's left is to jump to top loop again:

    jmp LOOP 

This will start the whole process over. On a MSP430G2553, this first try at a delay loop works out to around 250ms, making for a nice blinking rate.

For completeness, the entire body of our effective code looks like this (with some added comments):

            mov #0FFh, P1DIR    ; setup - set port1 to output
            xor #0FFh, P1OUT    ; flip port 1 bits
            mov #0FFFFh, R4     ; R4 will be our delay counter

            SUB #1h, R4         ; subtract 1 from R4...
            jnz DELAYLOOP       ; if we hit zero we're done with delay loop
            jmp LOOP

This code can be simplified even further, do you see how? This is an exercise left to the reader. It's also midnight and I'm starting to get tired in my old age.

In part two we will use the built in timer peripherals of the microcontroller and use interrupts for a cleaner approach. After that it will be on to PWM (Pulse Width Modulation) duty cycles and controlling a servo using an ADC input.

March 12, 2018 · asm · embedded · msp430 · blinky · assembly

Previous:Simple sampling with Box-Muller transforms pt 1
Next:Waterboarding your brain / becoming a BSD curmudgeon