Wednesday, November 21, 2007

TI MSP430 MCU

Texas Instruments has a cool microcontroller in their MSP430 line. It's the eZ430-F2013. It's a USB programmer/debugger and a teeny detachable board that has an F2013 on it. It's not only incredibly convenient to play with, it's only US $20! To top it off, you can buy a batch of 3 more detachable T2012 boards for another US $10! How can you beat that?

As a programmer, I found their IDE from IAR Systems a bit scruffy, but I've been using Eclipse for some time which makes comparisons iffy to say the least. I understand there's an Eclipse project that uses GCC to create MSP430 binaries... that should be interesting to track down. One issue with that is possible problems exchanging code with fellow hams. There's a group over in western Massachusetts taking a class using these things and it might be a good idea to standardise for the benefit of all.

The little USB stick has an LED on it (P1.0) and it seemed logical that my first project would be to flash the little bugger to make CW (Morse Code). My C is rusty, so I did it in assembler, by brute force. Might as well record that effort here for posterity.


;*******************************************************************************
;
; Play CW by flashing the LED
; intended for the MSP430-F2013 on the EZ430 USB development board
; P1.0 is an output port with an LED attached
;
; IAR workbench 4.09A
; 2007-11-18
;*******************************************************************************
#include "msp430.h"
;-------------------------------------------------------------------------------
NAME main
PUBLIC main
; Interrupt Vector
ORG 0FFFEh ; MSP430 RESET Vector
dc16 RESET
; Progam code
RSEG CSTACK
RSEG CODE
;-------------------------------------------------------------------------------
RESET MOV #SFE(CSTACK), SP ; Set stackpointer (256B RAM)
main mov.w #WDTPW+WDTHOLD,&WDTCTL ; Stop watchdog timer
bis.b #001h,&P1DIR ; Set P1.0 to output direction
clr.b &P1OUT ; reset P1.0
;-------------------------------------------------------------------------------
; flash the LED using the message you want
; this is an initial non-repeating preface
call #space
call #space
call #v
call #v
call #v
call #space
; this message repeats
loop call #c
call #q
call #space
call #d
call #e
call #space
call #k
call #c
call #two
call #h
call #i
call #z
call #space
call #space
call #space
; now generate a long "carrier"
mov.b #001h,&P1OUT ; LED on
call #space
call #space
call #space
call #space
call #space
call #space
call #space
call #space
call #space
call #space
mov.b #000h,&P1OUT ; LED off
call #space
jmp loop
;-------------------------------------------------------------------------------
;
; This section forms the various CW characters
;
;-------------------------------------------------------------------------------
; space
space call #pause
call #pause
call #pause
call #pause
call #pause
ret
;-------------------------------------------------------------------------------
a call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
b call #dah
call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
c call #dah
call #dit
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
d call #dah
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
e call #dit
call #gap
ret
;-------------------------------------------------------------------------------
f call #dit
call #dit
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
g call #dah
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
h call #dit
call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
i call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
j call #dit
call #dah
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
k call #dah
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
l call #dit
call #dah
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
m call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
n call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
o call #dah
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
p call #dit
call #dah
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
q call #dah
call #dah
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
r call #dit
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
s call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
t call #dah
call #gap
ret
;-------------------------------------------------------------------------------
u call #dit
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
v call #dit
call #dit
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
w call #dit
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
x call #dah
call #dit
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
y call #dah
call #dit
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
z call #dah
call #dah
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
one call #dit
call #dah
call #dah
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
two call #dit
call #dit
call #dah
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
three call #dit
call #dit
call #dit
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
four call #dit
call #dit
call #dit
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
five call #dit
call #dit
call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
six call #dah
call #dit
call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
seven call #dah
call #dah
call #dit
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
eight call #dah
call #dah
call #dah
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
nine call #dah
call #dah
call #dah
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
zero call #dah
call #dah
call #dah
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
period call #dit
call #dah
call #dit
call #dah
call #dit
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
comma call #dah
call #dah
call #dit
call #dit
call #dah
call #dah
call #gap
ret
;-------------------------------------------------------------------------------
question call #dit
call #dit
call #dah
call #dah
call #dit
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
slash call #dah
call #dit
call #dit
call #dah
call #dit
call #gap
ret
;-------------------------------------------------------------------------------
;
; This section sets the individual elements and overall timing of each element
;
;-------------------------------------------------------------------------------
; dah
dah mov.b #001h,&P1OUT ; LED on
call #pause
call #pause
call #pause
mov.b #000h,&P1OUT ; LED off
call #pause
ret
;-------------------------------------------------------------------------------
; dit
dit mov.b #001h,&P1OUT ; LED on
call #pause
mov.b #000h,&P1OUT ; LED off
call #pause
ret
;-------------------------------------------------------------------------------
; inter-character gap
gap call #pause
call #pause
call #pause
call #pause
ret
;-------------------------------------------------------------------------------
; delay
; this sets the basic on or off time.
; Larger values mean slower CW
pause mov.w #040000,R15 ; max 65535
L1 dec.w R15
jnz L1
ret
END


Incredibly, this thing worked, and creates CW by flashing the LED!

I did a quick hack in C that looks like this:


// Flash the F2013 LED in CW
// IAR workbench 4.09A Kickstart

// TODO: translate character to CW

#include "msp430.h"
#include "string.h"
#define SHORT 3
#define LONG 7

int dot(void);
int dash(void);
int space(int);
int flash(char[]);

int main( void )
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= 0x01; // Set P1.0 to output direction

for (;;)
{
flash("...- ...- ...- -.-. --.- -.. . -.- -.-. ..--- .... .. --.. ");
}
}

// takes an array of char and flashes that message
int flash( char chars[])
{char c;

for (int i=0; i<=strlen(chars); i++) {
c=chars[i];
switch (c)
{
case '.':
dot();
break;
case '-':
dash();
break;
case ' ':
space(LONG);
space(LONG);
break;
default:
space(LONG);
space(LONG);
break;
}
}
return 0;
}

int dot(void)
{
P1OUT = 0x01; // LED on
space(SHORT);
P1OUT = 0x00; // LED off
space(SHORT);
return 0;
}

int dash(void)
{
P1OUT = 0x01; // LED on
space(LONG);
P1OUT = 0x00; // LED off
space(SHORT);
return 0;
}

int space(int time)
{unsigned int i;

do
{time--;
i = 10000;
do i--;
while (i != 0);
} while (time != 0);
return 0;
}


This too, works although I really don't think I like the dots & dashes. I figure my next step is to use actual letters and convert them via an intermediate function call.