Sunday, February 2, 2014

MSP430F5529 LaunchPad 'Project0' on Fedora 20

Recently TI launched another MSP430 LaunchPad, the MSP430F5529. While the "Project0"  (caution, PDF link) tutorial they provide is great if you intend to use Windows and CCS, it falls short if you intend to use mspgcc and Linux. This guide will hopefully fill in the blanks for doing the Project0 tutorial on Fedora 20.




Step 1 is just to make sure your LaunchPad works. With the original firmware on it, make sure it enumerates properly as both a mass-storage device, and a HID. Make sure pressing both buttons works as described on the TI page linked above. It's better to find out if your board works correctly now.

Once you have verified your LaunchPad functions, install mspgcc (and other required periphery):

# yum install msp430-gcc msp430-libc msp430mcu mspdebug dos2unix srecord

Now create a working directory to setup our Hello World project. Inside this directory, create a 'main.c' file and copy the Project0 source (shown below, with proper line break characters) into it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <msp430.h>
unsigned int i = 0;

// Initialize variables. This will keep count of how many cycles between LED toggles
void main(void) {
 WDTCTL = WDTPW + WDTHOLD;
 // Stop watchdog timer. This line of code is needed at the beginning of most MSP430 projects.
 // This line of code turns off the watchdog timer, which can reset the device after a certain period of time.
 P1DIR |= 0x01;
 // P1DIR is a register that configures the direction (DIR) of a port pin as an output or an input.
 // To set a specific pin as output or input, we write a '1' or '0' on the appropriate bit of the register.
 // P1DIR = <pin7><pin6><pin5><pin4><pin3><pin2><pin1><pin0>
 // Since we want to blink the on-board red LED, we want to set the direction of Port 1, Pin 0 (P1.0) as an output
 // We do that by writing a 1 on the PIN0 bit of the P1DIR register
 // P1DIR = <pin7><pin6><pin5><pin4><pin3><pin2><pin1><pin0>
 // P1DIR = 0000 0001
 // P1DIR = 0x01 <--this is the hexadecimal conversion of 0000 0001
 for(;;)
 // This empty for-loop will cause the lines of code within to loop infinitely
 {
  P1OUT ^= 0x01;
  // Toggle P1.0 using exclusive-OR operation (^=)
  // P1OUT is another register which holds the status of the LED.
  // '1' specifies that it's ON or HIGH, while '0' specifies that it's OFF or LOW
  // Since our LED is tied to P1.0, we will toggle the 0 bit of the P1OUT register
  for(i=0; i < 20000; i++);
  //Delay between LED toggles. This for-loop will run until the condition is met.
  //In this case, it will loop until the variable i increments to 20000.
 }
}

Now, since we aren't animals, let's setup a basic Makefile (modified slightly from the one provided on the wiki):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#
# Makefile for msp430
#
# 'make' builds everything
# 'make clean' deletes everything except source files and Makefile
# You need to set TARGET, MCU and SOURCES for your project.
# TARGET is the name of the executable file to be produced
# $(TARGET).elf $(TARGET).hex and $(TARGET).txt nad $(TARGET).map are all generated.
# The TXT file is used for BSL loading, the ELF can be used for JTAG use
#
TARGET     = project0
MCU        = msp430f5529
# List all the source files here
# eg if you have a source file foo.c then list it here
SOURCES = main.c
# Include are located in the Include directory
INCLUDES = -IInclude
# Add or subtract whatever MSPGCC flags you want. There are plenty more
#######################################################################################
CFLAGS   = -mmcu=$(MCU) -g -Os -Wall -Wunused $(INCLUDES)
ASFLAGS  = -mmcu=$(MCU) -x assembler-with-cpp -Wa,-gstabs
LDFLAGS  = -mmcu=$(MCU) -Wl,-Map=$(TARGET).map
########################################################################################
CC       = msp430-gcc
LD       = msp430-ld
AR       = msp430-ar
AS       = msp430-gcc
NM       = msp430-nm
OBJCOPY  = msp430-objcopy
RANLIB   = msp430-ranlib
STRIP    = msp430-strip
SIZE     = msp430-size
READELF  = msp430-readelf
MAKETXT  = srec_cat
CP       = cp -p
RM       = rm -f
MV       = mv
########################################################################################
# the file which will include dependencies
DEPEND = $(SOURCES:.c=.d)
# all the object files
OBJECTS = $(SOURCES:.c=.o)
all: $(TARGET).elf $(TARGET).hex $(TARGET).txt
$(TARGET).elf: $(OBJECTS)
 echo "Linking $@"
 $(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@
 echo
 echo ">>>> Size of Firmware <<<<"
 $(SIZE) $(TARGET).elf
 echo
%.hex: %.elf
 $(OBJCOPY) -O ihex $< $@
%.txt: %.hex
 $(MAKETXT) -O $@ -TITXT $< -I
 unix2dos $(TARGET).txt
#  The above line is required for the DOS based TI BSL tool to be able to read the txt file generated from linux/unix systems.
%.o: %.c
 echo "Compiling $<"
 $(CC) -c $(CFLAGS) -o $@ $<
# rule for making assembler source listing, to see the code
%.lst: %.c
 $(CC) -c $(ASFLAGS) -Wa,-anlhd $< > $@
# include the dependencies unless we're going to clean, then forget about them.
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPEND)
endif
# dependencies file
# includes also considered, since some of these are our own
# (otherwise use -MM instead of -M)
%.d: %.c
 echo "Generating dependencies $@ from $<"
 $(CC) -M ${CFLAGS} $< >$@
.SILENT:
.PHONY: clean
clean:
 -$(RM) $(OBJECTS)
 -$(RM) $(TARGET).*
 -$(RM) $(SOURCES:.c=.lst)
 -$(RM) $(DEPEND)

Perform a make:

$ make
Generating dependencies main.d from main.c
Compiling main.c
main.c:5:6: warning: return type of 'main' is not 'int' [-Wmain]
Linking project0.elf

>>>> Size of Firmware <<<<
   text       data        bss        dec        hex    filename
    218          0          4        222         de    project0.elf

unix2dos: converting file project0.txt to DOS format ...

Finally, let's write our newly created binary to the LaunchPad with mspdebug. This is by far the most convoluted part of the process.

The first issue is that mspdebug does not ship with the necessary shared library for the MSP-FET430UIF. I suspect due to licensing, but who knows, I just want to be able to program my LaunchPad. Luckily for us, TI provides what we need to be able to build the library. Make sure the following are installed before continuing:

# yum install gcc-c++ boost-devel hidapi-devel libusb-devel

Download the source provided by TI here. Extract the source somewhere and navigate to the directory. We need to create a couple of symlinks due to how TI chose to handle the hid library (if you aren't using a 64 bit Fedora install, adjust accordingly):

$ ln -s /usr/include/hidapi/hidapi.h ThirdParty/include
$ ln -s /usr/lib64/libhidapi-libusb.so ThirdParty/lib/hid-libusb.o 

Additionally, we need to modify 'EnergyTraceProcessorId7.h' to include 'cstdio'. Simply add '<cstdio>' to the include section of 'DLL430_v3/src/TI/DLL430/EnergyTrace_TSPA/EnergyTraceProcessorId7.h'.

Perform a make, if everything completed successfully, the result should be a 'libmsp430.so' shared library file that we need to get into the linker path. Since I don't recommend blindly copying files into the system library path, I recommend creating a 'lib' directory in your home directory, copying 'libmsp430.so' to it, and adding the following lines to your '.bash_profile':

LD_LIBRARY_PATH=$HOME/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH 

At this point, mspdebug should work as root, but I don't condone running it as root. The last step to getting mspdebug to work as a user is to set a udev rule. This is necessary due to how the JTAG is no longer accessed over a USB TTY like previous debuggers, but instead appears as a raw USB node. As root, create the file '/etc/udev/rules.d/69-ti-launchpad.rules' and add the following to it:


ATTRS{idVendor}=="0451", ATTRS{idProduct}=="f432", MODE="0660", GROUP="dialout"

Also add your user to the 'dialout' group:

# usermod -a -G dialout YOURUSERNAME

Reboot.

Open a new terminal and navigate to your working directory. Attempt to attach mspdebug:

$ mspdebug tilib

If you are lucky, you'll be given a debug prompt. If you aren't, you'll probably be prompted to a firmware update:
 
$ mspdebug tilib

MSPDebug version 0.22 - debugging tool for MSP430 MCUs
Copyright (C) 2009-2013 Daniel Beer <dlbeer@gmail.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

MSP430_GetNumberOfUsbIfs
MSP430_GetNameOfUsbIf
Found FET: ttyACM0
MSP430_Initialize: ttyACM0
FET firmware update is required.

Re-run with --allow-fw-update to perform a firmware update.

tilib: device initialization failed

Just do what it says. Again, if you're lucky, you'll see:

$ mspdebug tilib --allow-fw-update

MSPDebug version 0.22 - debugging tool for MSP430 MCUs
Copyright (C) 2009-2013 Daniel Beer <dlbeer@gmail.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

MSP430_GetNumberOfUsbIfs
MSP430_GetNameOfUsbIf
Found FET: ttyACM0
MSP430_Initialize: ttyACM0

FET firmware update is required.
Starting firmware update (this may take some time)...
Initializing bootloader...
Programming new firmware...
    25 percent done
    50 percent done
    75 percent done
   100 percent done
   100 percent done
Update complete
Done, finishing...

MSP430_VCC: 3000 mV
MSP430_OpenDevice
MSP430_GetFoundDevice
 Device: MSP430F5529 (id = 0x0030)
8 breakpoints available

MSP430_EEM_Init
Chip ID data: 55 29 17
 
Available commands:
    =           erase       isearch     power       save_raw    simio       
    alias       exit        load        prog        set         step        
    break       fill        load_raw    read        setbreak    sym         
    cgraph      gdb         md          regs        setwatch    verify      
    delbreak    help        mw          reset       setwatch_r  verify_raw  
    dis         hexout      opt         run         setwatch_w  

Available options:
    color                       gdb_loop                    
    enable_bsl_access           gdbc_xfer_size              
    enable_locked_flash_access  iradix                      
    fet_block_size              quiet                       
    gdb_default_port            
 
Type "help <topic>" for more information.
Use the "opt" command ("help opt") to set options.
Press Ctrl+D to quit.

(mspdebug)

If you aren't, you won't. The most common error will be:

MSPDebug version 0.22 - debugging tool for MSP430 MCUs
Copyright (C) 2009-2013 Daniel Beer <dlbeer@gmail.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

MSP430_GetNumberOfUsbIfs
MSP430_GetNameOfUsbIf
Found FET: ttyACM0
MSP430_Initialize: ttyACM0
tilib: MSP430_Initialize: Interface Communication error (error = 35)
tilib: device initialization failed

This is a bug. There is currently no fix that I can find. The solution is to simply unplug and replug the LaunchPad and try again (and again, and again...) until the update works.

Once you are finally connected to the JTAG, we can write our LED blinking Project0 to our board (you do remember that, right?):

(mspdebug) prog project0.hex

Erasing...
Programming...
Writing   90 bytes at 4400...
Writing  128 bytes at ff80...
Done, 218 bytes total

Then run the program:

(mspdebug) run
Running. Press Ctrl+C to interrupt...

"But wait!"  I hear you exclaim. "Isn't the LED supposed to blink?" Oh, yeah. Let's examine our Makefile for a second (or, if you're hardcore, open the compiled binary in a hex editor). The "CFLAGS   = -mmcu=$(MCU) -g -Os -Wall -Wunused $(INCLUDES)", contains "-Os" which tells GCC to optimize for size. This is usually a reasonable default for compiling for a micro, but in the case of cheesy demo applications like this one, it tends to break things. Specifically, the loop we are using for a delay ("for(i=0; i < 20000; i++);") gets optimized right out, so the LED gets turned on and off so fast we don't see it.

While we could turn off optimization for size, you are probably going to blindly copy this Makefile someday and wonder why your binaries are so huge. Instead, let's move our delay to a function, and tell GCC to not optimize it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <msp430.h>

void main(void) {
 WDTCTL = WDTPW + WDTHOLD;
 // Stop watchdog timer. This line of code is needed at the beginning of most MSP430 projects.
 // This line of code turns off the watchdog timer, which can reset the device after a certain period of time.
 P1DIR |= 0x01;
 // P1DIR is a register that configures the direction (DIR) of a port pin as an output or an input.
 // To set a specific pin as output or input, we write a '1' or '0' on the appropriate bit of the register.
 // P1DIR = <pin7><pin6><pin5><pin4><pin3><pin2><pin1><pin0>
 // Since we want to blink the on-board red LED, we want to set the direction of Port 1, Pin 0 (P1.0) as an output
 // We do that by writing a 1 on the PIN0 bit of the P1DIR register
 // P1DIR = <pin7><pin6><pin5><pin4><pin3><pin2><pin1><pin0>
 // P1DIR = 0000 0001
 // P1DIR = 0x01 <--this is the hexadecimal conversion of 0000 0001
 for(;;)
 // This empty for-loop will cause the lines of code within to loop infinitely
 {
  P1OUT ^= 0x01;
  // Toggle P1.0 using exclusive-OR operation (^=)
  // P1OUT is another register which holds the status of the LED.
  // '1' specifies that it's ON or HIGH, while '0' specifies that it's OFF or LOW
  // Since our LED is tied to P1.0, we will toggle the 0 bit of the P1OUT register
  delay();
 }
}

//Tell GCC to not optimize out the delay loop
#pragma GCC push_options
#pragma GCC optimize ("0")
void delay(void) {
 unsigned int i = 0;

 //Delay between LED toggles. This for-loop will run until the condition is met.
 //In this case, it will loop until the variable i increments to 20000.
 for(i=0; i < 20000; i++);
}
#pragma GCC pop_options

What we've done here is tell GCC to save whatever compile options are defined, do not optimize for the upcoming function, and when finished compiling the function, restore any compile options. Redo the make, reprogram, and watch the light blink!

This completes our Fedora 20 version of the 'Project0' tutorial. My next MSP430 article will discuss mspdebug in a bit more depth.

No comments:

Post a Comment