Disassembling the TI ez430 chronos – an open source watch software development

Following procedure at:

http://karuppuswamy.com/wordpress/2010/10/07/debugging-ez430-chronos-with-mspdebug-tool-in-ubuntu-linux/

and

http://mspdebug.sourceforge.net/fet.html

I managed to extract out the firmware for the chronos:

mspdebug -R “hexout 0x8000 0xffff ez430-firmware.hex”

Trying to open interface 1 on 002
rf2500: warning: can’t detach kernel driver: No data available
FET protocol version is 30001000
Configured for Spy-Bi-Wire
Set Vcc: 3000 mV
Device ID: 0x6137
Device: CC430F6137
Code memory starts at 0x8000
Reading 128 bytes from 0x8000…
Reading 128 bytes from 0x8080…

(The reason ” -B /dev/ttyACM0″ is not used is because it gave the following errors:

mspdebug -B /dev/ttyACM0 “hexout 0x8000 0xffff ez430-firmware.hex”

bsl: couldn’t read bootloader transition acknowledgement)

It is possible to disassemble using the mspdebug tool, but using gdb remote feature is more powerful – as the full suite of features under gdb is available. To build the cross-compiled version of gdb the following is the procedure to build msp430-gdb:

http://zolertia.sourceforge.net/wiki/index.php/Howto_compile_mspgcc_from_the_source

(the errors of “error: ignoring return value of ‘getcwd’, declared with attribute warn_unused_result” will be encountered many times over. Just modify the line by wrapping it with “assert( getcwd()!= NULL);” and insert the line “#include <assert.h>” at the top of the C file – and subsequently everything will compile successfully.)

The following are the steps for connection:

First connect direct to the device (implicitly via /dev/ttyACM0) using mspdebug and “gdb” as an option:

In one terminal enter:

mspdebug -R
Code memory starts at 0x8000
(mspdebug) gdb
Bound to port 2000. Now waiting for connection…

And in another terminal issued the msp430-gdb command:

msp430-gdb
(gdb) target remote localhost:2000
(gdb) info registers
pc/r0: e958 sp/r1: 0000 sr/r2: 0000 r3: 0000
fp/r4: 0000 r5: 0000 r6: 0000 r7: 0000
r8: 0000 r9: 0000 r10: 0000 r11: 0000
r12: 0000 r13: 0000 r14: 0000 r15: 0000
(gdb) x /10i $pc
0xe958: mov #11260, r1 ;#0x2bfc
0xe95c: .word 0x1800; ????
0xe95e: mov.b #-2932, &0x1ea8 ;#0xf48c
0xe964: .word 0x1800; ????
0xe966: mov.b #-2932, &0x1eac ;#0xf48c
0xe96c: .word 0x13b0; ????
0xe96e: and.b @r4, r12
0xe970: tst r12
0xe972: jz $+6 ;abs 0xe978
0xe974: .word 0x13b0; ????

Ok, disassembled successfully. On the other hand, dumping the firmware is not really necessary really – as the source code for the ez430 Chronos is openly available:

http://focus.ti.com/lit/sw/slac388/slac388.zip

http://www.lengal.net/git/?p=chronos;a=summary

http://code.google.com/p/rit-ti-ez430/source/detail?r=21

As mentioned here:

http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/t/32220.aspx

the source code compiled is identical to that in the firmware – but only when when compiled using the “Full edition” of the IAR tool.

Reference TI documentation:

http://focus.ti.com/lit/ds/symlink/cc430f5133.pdf
http://focus.ti.com/lit/ds/symlink/cc430f6137.pdf
http://focus.ti.com/lit/ug/slau292c/slau292c.pdf
http://focus.ti.com/docs/toolsw/folders/print/ez430-chronos.html?DCMP=Chronos&HQS=Other+OT+chronos

After doing a checkout of the source code:

svn checkout http://rit-ti-ez430.googlecode.com/svn/trunk/ rit-ti-ez430-read-only

Here are my analysis of the major features of the source codes.

What is the main functions and flow?

extern void display_update() is called from 1 sites in this file.
It appears to be inlineable (size = 189 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
display_symbol() (3 times)
is_bluerobin() (1 times)
display_chars() (2 times)
memcpy() (5 times)
clear_line() (3 times)
? () (7 times)

extern void idle_loop() is called from 1 sites in this file.
It appears to be inlineable (size = 5 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
to_lpm() (1 times)

extern void init_application() is called from 1 sites in this file.
It appears to be inlineable (size = 113 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
ps_init() (1 times)
Timer0_Init() (1 times)
init_buttons() (1 times)
lcd_init() (1 times)
as_init() (1 times)
radio_powerdown() (1 times)
radio_reset() (1 times)
_enable_interrupts() (1 times)
_disable_interrupts() (1 times)
SetVCore() (1 times)

extern void init_global_variables() is called from 1 sites in this file.
It appears to be inlineable (size = 53 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
read_calibration_values() (1 times)
battery_measurement() (1 times)
reset_batt_measurement() (1 times)
reset_temp_measurement() (1 times)
reset_rf() (1 times)
reset_bluerobin() (1 times)
reset_acceleration() (1 times)
reset_altitude_measurement() (1 times)
reset_stopwatch() (1 times)
reset_buzzer() (1 times)
reset_alarm() (1 times)
reset_date() (1 times)
reset_clock() (1 times)

extern int main() is called from 0 sites in this file.
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
display_update() (1 times)
process_requests() (1 times)
wakeup_event() (1 times)
idle_loop() (1 times)
test_mode() (1 times)
init_global_variables() (1 times)
init_application() (1 times)

extern void process_requests() is called from 1 sites in this file.
It appears to be inlineable (size = 49 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
start_buzzer() (1 times)
battery_measurement() (1 times)
do_acceleration_measurement() (1 times)
do_altitude_measurement() (1 times)
temperature_measurement() (1 times)

extern void read_calibration_values() is called from 1 sites in this file.
It appears to be inlineable (size = 116 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
<NONE>

extern void to_lpm() is called from 1 sites in this file.
It appears to be inlineable (size = 4 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
<NONE>

extern void wakeup_event() is called from 1 sites in this file.
It appears to be inlineable (size = 241 units)
It has 3 non-trivial scope blocks nested 3 deep.
It calls these functions:
clear_display() (1 times)
? () (6 times)

What are the ISR routines (interrupt service request handler)?

./ez430_chronos/driver/radio.c:
__interrupt void radio_ISR(void)

./ez430_chronos/driver/timer.c:
__interrupt void TIMER0_A0_ISR(void)
__interrupt void TIMER0_A1_5_ISR(void)

./ez430_chronos/driver/adc12.c:
__interrupt void ADC12ISR (void)

./ez430_chronos/driver/ports.c:
__interrupt void PORT2_ISR(void)

./ez430_chronos_datalogger/driver/radio.c:
__interrupt void radio_ISR(void)

./ez430_chronos_datalogger/driver/timer.c:
__interrupt void TIMER0_A0_ISR(void)
__interrupt void TIMER0_A1_5_ISR(void)

./ez430_chronos_datalogger/driver/adc12.c:
__interrupt void ADC12ISR (void)

./ez430_chronos_datalogger/driver/ports.c:
__interrupt void PORT2_ISR(void)

Listed below is the snapshot of ./ez430_chronos/main.c:

int main(void)
{
// Init MCU
init_application();

// Assign initial value to global variables
init_global_variables();

// Branch to welcome screen
test_mode();

// Main control loop: wait in low power mode until some event needs to be processed
while(1)
{
// When idle go to LPM3
idle_loop();

// Process wake-up events
if (button.all_flags || sys.all_flags) wakeup_event();

// Process actions requested by logic modules
if (request.all_flags) process_requests();

// Before going to LPM3, update display
if (display.all_flags) display_update();
}
}

The DMA declaration for the buttons (driver/ports.h):

// Port, pins and interrupt resources for buttons
#define BUTTONS_IN (P2IN)
#define BUTTONS_OUT (P2OUT)
#define BUTTONS_DIR (P2DIR)
#define BUTTONS_REN (P2REN)
#define BUTTONS_IE (P2IE)
#define BUTTONS_IES (P2IES)
#define BUTTONS_IFG (P2IFG)
#define BUTTONS_IRQ_VECT2 (PORT2_VECTOR)

And the P2IN value above are documented here (page 31):

http://focus.ti.com/lit/ds/symlink/cc430f5133.pdf

Port P1 input P1IN 00h
Port P1 output P1OUT 02h
Port P1 direction P1DIR 04h
Port P1 pullup/pulldown enable P1REN 06h
Port P1 drive strength P1DS 08h
Port P1 selection P1SEL 0Ah
Port P1 interrupt vector word P1IV 0Eh
Port P1 interrupt edge select P1IES 18h
Port P1 interrupt enable P1IE 1Ah
Port P1 interrupt flag P1IFG 1Ch
Port P2 input P2IN 01h
Port P2 output P2OUT 03h
Port P2 direction P2DIR 05h
Port P2 pullup/pulldown enable P2REN 07h
Port P2 drive strength P2DS 09h
Port P2 selection P2SEL 0Bh
Port P2 interrupt vector word P2IV 1Eh
Port P2 interrupt edge select P2IES 19h
Port P2 interrupt enable P2IE 1Bh
Port P2 interrupt flag P2IFG 1Dh

The following are the functions for display (display.h):

// Physical LCD memory write
extern void write_lcd_mem(u8 * lcdmem, u8 bits, u8 bitmask, u8 state);

// Display init / clear
extern void lcd_init(void);
extern void clear_display(void);
extern void clear_display_all(void);
extern void clear_line(u8 line);

// Blinking function
extern void start_blink(void);
extern void stop_blink(void);
extern void clear_blink_mem(void);
extern void set_blink_rate(u8 bits);

// Character / symbol draw functions
extern void display_char(u8 segment, u8 chr, u8 mode);
extern void display_chars(u8 segments, u8 * str, u8 mode);
extern void display_symbol(u8 symbol, u8 mode);

// Time display function
extern void DisplayTime(u8 updateMode);
extern void display_am_pm_symbol(u8 timeAM);

// Set_value display functions
extern void display_value1(u8 segments, u32 value, u8 digits, u8 blanks);
extern void display_hours1(u8 segments, u32 value, u8 digits, u8 blanks);

// Integer to string conversion
extern u8 * itoa(u32 n, u8 digits, u8 blanks);

// Segment index helper function
extern u8 switch_seg(u8 line, u8 index1, u8 index2);

Generally, it would be interesting to know:

a. How are the radio access point implemented? If there are multiple recipient how can they be distinguished from another? How to pair with the RF transceiver? How secure is the communication, or rather, how easy it is to perform man-in-the-middle attacks? How to access all the other sensors via the RF access point?

b. How to access the pressure sensor?

c. How to access the three-axis accelerometer?

d. How to access the temperature sensor?

e. How to access the battery voltage sensor?

f. It would be interesting to understand how the watch is able to trigger a USB CDC communication on the linux host, so that /dev/ttyACM0 from the linux host can be used to access the embedded device – performing debugging, putting it into single-stepping mode, accessing sensors etc?

Advertisements

2 responses to this post.

  1. Here is a link with source codes on how to access the accelerometer:

    http://ruditronics.wordpress.com/category/msp430/

    Summarizing the functions gave a good overview of the internal of the source code component:

    logic/acceleration.c:// @fn reset_acceleration
    logic/acceleration.c:// @fn is_acceleration_measurement
    logic/acceleration.c:// @fn do_acceleration_measurement
    logic/acceleration.c:// @fn acceleration_value_is_positive
    logic/acceleration.c:// @fn convert_acceleration_value_to_mgrav
    logic/acceleration.c://// @fn display_acceleration
    logic/altitude.c:// @fn reset_altitude_measurement
    logic/altitude.c:// @fn conv_m_to_ft
    logic/altitude.c:// @fn conv_ft_to_m
    logic/altitude.c:// @fn start_altitude_measurement
    logic/altitude.c:// @fn stop_altitude_measurement
    logic/altitude.c:// @fn is_altitude_measurement
    logic/altitude.c:// @fn do_altitude_measurement
    logic/altitude.c:// @fn display_altitude
    logic/battery.c:// @fn reset_batt_measurement
    logic/battery.c:// @fn battery_measurement
    logic/bluerobin.c:// @fn reset_bluerobin
    logic/bluerobin.c:// @fn mx_rfblue
    logic/bluerobin.c:// @fn sx_bluerobin
    logic/bluerobin.c:// @fn display_heartrate
    logic/bluerobin.c:// @fn is_bluerobin
    logic/bluerobin.c:// @fn is_bluerobin_searching
    logic/bluerobin.c:// @fn get_bluerobin_data
    logic/bluerobin.c:// @fn start_bluerobin
    logic/bluerobin.c:// @fn stop_bluerobin
    logic/bluerobin.c:// @fn bluerobin_flash_write_window
    logic/clock.c:// @fn reset_clock
    logic/clock.c:// @fn clock_tick
    logic/clock.c:// @fn convert_hour_to_12H_format
    logic/clock.c:// @fn is_hour_am
    logic/clock.c:// @fn sx_time
    logic/clock.c:// @fn display_time
    logic/datalog.c:// @fn reset_datalog
    logic/datalog.c:// @fn sx_alarm
    logic/datalog.c:// @fn start_datalog
    logic/datalog.c:// @fn stop_datalog
    logic/datalog.c:// @fn display_datalog
    logic/datalog.c:// @fn do_datalog
    logic/datalog.c:// @fn datalog_add_to_buffer
    logic/datalog.c:// @fn datalog_sm
    logic/date.c:// @fn reset_date
    logic/date.c:// @fn get_NumberOfDays
    logic/date.c:// @fn add_day
    logic/date.c:// @fn sx_date
    logic/date.c:// @fn display_date
    logic/rfbsl.c:// @fn sx_rfbsl
    logic/rfbsl.c:// @fn display_rfbsl
    logic/rfsimpliciti.c:// @fn reset_rf
    logic/rfsimpliciti.c:// @fn sx_sync
    logic/rfsimpliciti.c:// @fn display_sync
    logic/rfsimpliciti.c:// @fn is_rf
    logic/rfsimpliciti.c:// @fn start_simpliciti_sync
    logic/rfsimpliciti.c:// @fn simpliciti_sync_decode_ap_cmd_callback
    logic/rfsimpliciti.c:// @fn simpliciti_sync_get_data_callback
    logic/temperature.c:// @fn convert_C_to_F
    logic/temperature.c:// @fn convert_F_to_C
    logic/temperature.c:// @fn display_temperature
    logic/user.c:// @fn dummy
    logic/user.c:// @fn set_value
    tteikhua-laptop:/root/download/msp430/ez430_chronos/datalogger>grep fn */*.c
    driver/adc12.c://// @fn adc12_single_conversion
    driver/adc12.c:// @fn adc12_single_conversion
    driver/adc12.c:// @fn ADC12ISR
    driver/buzzer.c:// @fn reset_buzzer
    driver/buzzer.c:// @fn start_buzzer
    driver/buzzer.c:// @fn toggle_buzzer
    driver/buzzer.c:// @fn stop_buzzer
    driver/buzzer.c:// @fn is_buzzer
    driver/buzzer.c:// @fn countdown_buzzer
    driver/display.c:// @fn lcd_init
    driver/display.c:// @fn clear_display_all
    driver/display.c:// @fn clear_display
    driver/display.c:// @fn clear_line
    driver/display.c:// @fn write_segment
    driver/display.c:// @fn itoa
    driver/display.c:// @fn display_value1
    driver/display.c:// @fn display_hours
    driver/display.c:// @fn display_am_pm_symbol
    driver/display.c:// @fn display_symbol
    driver/display.c:// @fn display_char
    driver/display.c:// @fn display_chars
    driver/display.c:// @fn switch_seg
    driver/display.c:// @fn start_blink
    driver/display.c:// @fn stop_blink
    driver/display.c:// @fn stop_blink
    driver/display.c:// @fn set_blink_rate
    driver/ports.c:// @fn init_buttons
    driver/ports.c:// @fn PORT2_ISR
    driver/ports.c:// @fn button_repeat_on
    driver/ports.c:// @fn button_repeat_off
    driver/ports.c:// @fn button_repeat_function
    driver/radio.c:// @fn radio_reset
    driver/radio.c:// @fn radio_powerdown
    driver/radio.c:// @fn radio_sxoff
    driver/radio.c:// @fn open_radio
    driver/radio.c:// @fn close_radio
    driver/radio.c:// @fn GDOx_ISR
    driver/rf1a.c:// @fn Strobe
    driver/rf1a.c:// @fn ResetRadioCore
    driver/rf1a.c:// @fn ReadSingleReg
    driver/rf1a.c:// @fn WriteSingleReg
    driver/rf1a.c:// @fn ReadBurstReg
    driver/rf1a.c:// @fn WriteBurstReg
    driver/rf1a.c:// @fn WritePATable
    driver/timer.c:// @fn Timer0_Init
    driver/timer.c:// @fn Timer0_Start
    driver/timer.c:// @fn Timer0_Stop
    driver/timer.c:// @fn Timer0_A3_Start
    driver/timer.c:// @fn Timer0_A3_Stop
    driver/timer.c:// @fn Timer0_A4_Delay
    driver/timer.c:// @fn TIMER0_A0_ISR
    driver/timer.c:// @fn Timer0_A1_5_ISR
    driver/vti_as.c:// @fn as_init
    driver/vti_as.c:// @fn as_start
    driver/vti_as.c:// @fn as_stop
    driver/vti_as.c:// @fn as_read_register
    driver/vti_as.c:// @fn as_write_register
    driver/vti_as.c:// @fn as_get_data
    driver/vti_ps.c:// @fn ps_init
    driver/vti_ps.c:// @fn ps_start
    driver/vti_ps.c:// @fn ps_stop
    driver/vti_ps.c:// @fn ps_twi_sda
    driver/vti_ps.c:// @fn twi_delay
    driver/vti_ps.c:// @fn ps_twi_write
    driver/vti_ps.c:// @fn ps_twi_read
    driver/vti_ps.c:// @fn as_write_register
    driver/vti_ps.c:// @fn ps_read_register
    driver/vti_ps.c:// @fn ps_get_pa
    driver/vti_ps.c:// @fn ps_get_temp
    driver/vti_ps.c:// @fn init_pressure_table
    driver/vti_ps.c:// @fn update_pressure_table
    driver/vti_ps.c:// @fn conv_pa_to_meter
    logic/acceleration.c:// @fn reset_acceleration
    logic/acceleration.c:// @fn is_acceleration_measurement
    logic/acceleration.c:// @fn do_acceleration_measurement
    logic/acceleration.c:// @fn acceleration_value_is_positive
    logic/acceleration.c:// @fn convert_acceleration_value_to_mgrav
    logic/acceleration.c://// @fn display_acceleration
    logic/altitude.c:// @fn reset_altitude_measurement
    logic/altitude.c:// @fn conv_m_to_ft
    logic/altitude.c:// @fn conv_ft_to_m
    logic/altitude.c:// @fn start_altitude_measurement
    logic/altitude.c:// @fn stop_altitude_measurement
    logic/altitude.c:// @fn is_altitude_measurement
    logic/altitude.c:// @fn do_altitude_measurement
    logic/altitude.c:// @fn display_altitude
    logic/battery.c:// @fn reset_batt_measurement
    logic/battery.c:// @fn battery_measurement
    logic/bluerobin.c:// @fn reset_bluerobin
    logic/bluerobin.c:// @fn mx_rfblue
    logic/bluerobin.c:// @fn sx_bluerobin
    logic/bluerobin.c:// @fn display_heartrate
    logic/bluerobin.c:// @fn is_bluerobin
    logic/bluerobin.c:// @fn is_bluerobin_searching
    logic/bluerobin.c:// @fn get_bluerobin_data
    logic/bluerobin.c:// @fn start_bluerobin
    logic/bluerobin.c:// @fn stop_bluerobin
    logic/bluerobin.c:// @fn bluerobin_flash_write_window
    logic/clock.c:// @fn reset_clock
    logic/clock.c:// @fn clock_tick
    logic/clock.c:// @fn convert_hour_to_12H_format
    logic/clock.c:// @fn is_hour_am
    logic/clock.c:// @fn sx_time
    logic/clock.c:// @fn display_time
    logic/datalog.c:// @fn reset_datalog
    logic/datalog.c:// @fn sx_alarm
    logic/datalog.c:// @fn start_datalog
    logic/datalog.c:// @fn stop_datalog
    logic/datalog.c:// @fn display_datalog
    logic/datalog.c:// @fn do_datalog
    logic/datalog.c:// @fn datalog_add_to_buffer
    logic/datalog.c:// @fn datalog_sm
    logic/date.c:// @fn reset_date
    logic/date.c:// @fn get_NumberOfDays
    logic/date.c:// @fn add_day
    logic/date.c:// @fn sx_date
    logic/date.c:// @fn display_date
    logic/rfbsl.c:// @fn sx_rfbsl
    logic/rfbsl.c:// @fn display_rfbsl
    logic/rfsimpliciti.c:// @fn reset_rf
    logic/rfsimpliciti.c:// @fn sx_sync
    logic/rfsimpliciti.c:// @fn display_sync
    logic/rfsimpliciti.c:// @fn is_rf
    logic/rfsimpliciti.c:// @fn start_simpliciti_sync
    logic/rfsimpliciti.c:// @fn simpliciti_sync_decode_ap_cmd_callback
    logic/rfsimpliciti.c:// @fn simpliciti_sync_get_data_callback
    logic/temperature.c:// @fn convert_C_to_F
    logic/temperature.c:// @fn convert_F_to_C
    logic/temperature.c:// @fn display_temperature
    logic/user.c:// @fn dummy
    logic/user.c:// @fn set_value

    Reply

  2. Posted by trandi on September 4, 2011 at 7:41 pm

    Nice post !
    Here’s another quick but slightly advanced Hello World example, that deals with how to display some scrolling text on the watch itself:
    http://trandi.wordpress.com/2011/09/03/ti-ez430-watch-unbox-hworld/

    Dan

    Reply

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: