-
Notifications
You must be signed in to change notification settings - Fork 56
DeveloperGuide
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.
In 2020, we switched to the GCC8 for MSP430. We no longer support GCC4.
We do not support the GCC4 toolchain that ships with some Linux distributions.
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.
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.
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.
TODO
Coding for firmware can be frustrating, but here are some handy tips that might things go more smoothly.
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"));
...
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.
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.
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();
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.