Skip to content

DeveloperGuide

Travis Goodspeed edited this page Feb 28, 2021 · 7 revisions

GoodWatch Developer's Guide

The GoodWatch firmware is developed in C on Debian GNU/Linux. This quick guide will show you some of the tricks of the trade when it comes to development, but see other pages of the wiki for explanations of how individual apps work.

In general, you will want at least a Linux workstation, two watches, and two MSP-EXP430FR4133 kits, for their JTAG programmers. You will develop on one watch, while periodically flashing firmware to the other watch, which you'll wear.

GCC8 Compiler Toolchain

In 2020, we switched to the GCC8 for MSP430. We no longer support GCC4.

GCC4 Compiler Toolchain

We do not support the GCC4 toolchain that ships with some Linux distributions.

Flashing with JTAG / SBW

The best way to wire up your hardware for development is through Spy-Bi-Wire (SBW), which is a reduced wiring version of JTAG.

You can save some money by ordering the MSP-EXP430FR4133 kit for $15 from Digikey, then running four jumper wires to your board. I keep one of these boards with a watch permanently wired to it, the band wrapped around the board, for developing my firmware. I also keep a second board, with test clips so that I can quickly attach a fresh board that will go into a watch, for flashing only after I've finished my latest firmware change.

As an added bonus, you'll be able to power trace your firmware, knowing just how much of the limited battery resources it will consume. Both wiring and power tracing are explained in detail on the EnergyTrace page.

Flashing with FTDI / UART

If you don't have a JTAG programmer, the BootStrapLoader that is built into the CC430's ROM can also be used to flash the firmware. We don't recommend this method because it requires six wires instead of four, but it still works perfectly well.

Kernel Debugging Messages (DMESG)

A small buffer of SRAM is used as a kernel dmesg buffer, which you may write to with printf() and read from either by the Dmesg app on a standalone watch, or through make dmesg over FTDI or make sbwdmesg over JTAG.

GDB with JTAG / SBW

TODO

Some Handy Tips

Coding for firmware can be frustrating, but here are some handy tips that might things go more smoothly.

Cross Compile for your Desktop

Earlier in this document, it was suggested that you ought to have at least two watches, one to wear and a second one permanently tethered to your workstation for development. Even better than that is to run the code directly on your desktop, so that you know it's correct before ever running it on real hardware.

In the GoodWatch firmware, this is done by libraries in the firmware/libs directory, which are either compiled for the host operating system with STANDALONE defined or compiled for the MSP430 microcontroller without it being defined. The test case can then be made of a main() method.

For example, this is how the disassembler library used by the hex editor tests itself.

#ifdef STANDALONE

#include<stdio.h>
#include<assert.h>

//! Print the current instruction to the unix console, for testing.
void asm_print(){/*...*/}

//! Unix command-line tool for testing.
int main(){
  printf("Testing the MSP430 disassembler:\n");

  //3FFF is an unconditional jump to self.
  asm_dis(0xdead, 0x3FFF, 0, 0);
  asm_print();
  assert(jumptarget==0xdead);
  assert(type==JUMPOP);

  //2002 is jnz     $+6 
  asm_dis(0x013a, 0x2002, 0, 0);
  asm_print();
  assert(jumptarget==0x0140);
  assert(type==JUMPOP);

  //430F is mov r14, r15
  asm_dis(0, 0x4e0F, 0, 0);
  asm_print();
  assert(type==TWOOP);
  assert(!strcmp(opstr,"mov"));

  ...

Use Types with Explicit Sizes

The MSP430 has a native word size of 16 bits, while your desktop likely has 32-bit ones. If you include stdint.h and use its integer definitions, your code will be more likely to run the same in testing as it does on the target hardware.

int a;      // Might be 16 bits or might be 32.
uint16_t b; // Always 16 bits.

Use SRAM Sparingly

In Unix, you might be used to simply declaring a variable as writable, even if you don't intend to write to it. In firmware, declaring a variable as const allows it to remain only in code memory (Flash), without a second copy being made in Data memory (SRAM).

uint16_t a = 0xdead;        //Two bytes of SRAM, two bytes of Flash.
const uint16_t b = 0xbeef;  //Just two bytes of Flash.

Use the CPU and Radio Sparingly

The GoodWatch only has 100mAh in its CR2016 battery, and you'll want to conserve these at all cost by paying attention to your applet's duty cycle and frequently measuring with EnergyTrace.

In general, non-radio apps should keep the CPU off as much as possible, do what little work they need to in the _draw() function. For example, the ClockApplet's clock_draw() function only calls draw_time() as needed, and that function only draws those digits which have recently changed. It is also careful to use int2bcd() with a ROM lookup table to avoid expensive divisions by 10.

Similarly, the radio takes quite a bit of power and ought to be turned off when it's not in use. pager.c, the POCSAG receiver, turns the radio off between timeslots to drop consumption from 16mA to 1mA. You might also notice that the LCD goes dim on CC430F6137 chips while the radio remains active, due to an undocumented errata involving the charge pump.

To prevent a poorly written applet from killing the battery, the watch will automatically revert to the clock after three minutes without a button press. If you know that the applet should remain in this mode, you should make the / button show the time and clear the timer during the draw phase.

//! Draw the stopwatch app and handle its input.
void stopwatch_draw(int forced){
  uint8_t subhex;
  
  /* The stopwatch is special in that it never times out.  Be very
     careful when doing this, because a minor bug might kill the
     battery.
   */
  app_cleartimer();

RTFM

Solid documentation exists in both code comments and this wiki for many of the applets. Before writing your own, read up on how the HexApplet allows exploration of memory, the ClockApplet shows the time, the CounterApplet measures a transmitter's frequency, while the MorseApplet transmits CW, the Jukebox app controls TouchTunes jukeboxes, and the POCSAG app can receive pager messages from DAPNET. The OOK_Example shows you how to clone a cheap 433MHz remote control.

Around the wiki, you will also find detailed descriptions of how the LCD works, a MemoryMap to decipher pointers, and a detailed explanation of the Keypad driver and the original Casio3208 module whose pinout we clone.