├── .gitignore ├── README.md ├── case.scad ├── datasheets ├── cc430.pdf ├── charliewatch.pdf ├── goodwatch22.pdf └── msp430-bsl.pdf ├── firmware ├── Makefile ├── adc.c ├── adc.h ├── animation.c ├── buildtime.c ├── button.c ├── button.h ├── clockset.c ├── led.c ├── led.h ├── main.c ├── memory.x ├── msp430.x ├── periph.x ├── power.c ├── power.h ├── rtc.c ├── rtc.h ├── rtcasm.S ├── rtcasm.h ├── stopwatch.c ├── ucs.c └── ucs.h ├── images ├── v0.1.jpg └── v0.2.jpg ├── pcb ├── charliewatch-0402.brd ├── charliewatch-0402.sch ├── charliewatch-bicolor.brd ├── charliewatch-bicolor.sch ├── charliewatch-norf.brd ├── charliewatch-norf.sch ├── charliewatch.brd ├── charliewatch.sch ├── oshw.bmp ├── routed-norf.brd ├── routed-norf.svg └── routed.brd └── watch.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.b\#* 2 | *.s\#* 3 | .*lck 4 | *.pro 5 | pcb/routed.sch 6 | .*.d 7 | *.o 8 | *.elf 9 | *.hex 10 | .*.swp 11 | *.stl 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Watch rendering](watch.png) 2 | 3 | "Analog" watch with 60+12 Charlieplexed LEDs. Based on Travis Goodspeed's 4 | GoodWatch21 schematic, using the CC430F5137IRGZR CPU, which doesn't 5 | have the LCD controller of the CC430F6xxx in the GoodWatch. There are 6 | also pads for a six pin EZfet cable with pogopins for programming or 7 | serial communication. 8 | 9 | ![v0.2 board, populated](images/v0.2.jpg) 10 | v0.2 works, although it has no radio parts. It is exactly 1 square inch 11 | of PCB and should fit in a 38mm or 36mm case. 12 | 13 | 14 | ![v0.1 board](images/v0.1.jpg) 15 | v0.1 works, although the radio parts have not been tested. It is a slightly 16 | larger board and fits in a 40mm case. 17 | 18 | [Charliewtch schematic](datasheets/charliewatch.pdf) 19 | 20 | Building the firmware 21 | ----- 22 | 23 | Note: some other system packages (boost libraries?) might also be needed. 24 | 25 | First, get the toolchain (msp430-gcc): 26 | 27 | sudo apt install gcc-msp430 mspdebug 28 | 29 | Then the all-important lib file (from https://www.ti.com/tool/MSPDS): 30 | 31 | sudo apt install libhidapi-dev 32 | mkdir MSPDebugStack 33 | wget https://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPDS/3_15_1_001/export/MSPDebugStack_OS_Package_3_15_1_1.zip 34 | 35 | Copy in the hidapi header (WTFBBQ): 36 | 37 | cp /usr/include/hidapi/hidapi.h ThirdParty/include/ 38 | 39 | Copy the following into a file 'hidapi.patch', so we can use the system hidapi library: 40 | 41 | --- Makefile 2020-12-18 18:01:18.602942502 +0100 42 | +++ Makefile.new 2020-12-18 18:02:58.472096541 +0100 43 | @@ -123,7 +123,7 @@ 44 | OBJS := $(patsubst %.cpp, %.o, $(SRC)) 45 | 46 | all: $(BSLLIB) $(OBJS) 47 | - $(CXX) $(CXXFLAGS) -shared $(OUTNAME)$(OUTPUT) -o $(OUTPUT) $(OBJS) $(HIDOBJ) $(LIBDIRS) $(BSTATIC) $(STATIC_LIBS) $(BDYNAMIC) $(LIBS) 48 | + $(CXX) $(CXXFLAGS) -shared $(OUTNAME)$(OUTPUT) -o $(OUTPUT) $(OBJS) -lhidapi-libusb $(LIBDIRS) $(BSTATIC) $(STATIC_LIBS) $(BDYNAMIC) $(LIBS) 49 | rm -f $(STATICOUTPUT).a 50 | ar -rs $(STATICOUTPUT).a $(OBJS) 51 | 52 | Then apply and make: 53 | 54 | patch < hidapi.patch 55 | make 56 | 57 | 58 | Finally, build and flash the firmware: 59 | 60 | git clone https://github.com/osresearch/charliewatch.git 61 | cd charliewatch/firmware 62 | make 63 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`../../MSPDebugStack/ 64 | mspdebug tilib "prog charliewatch.elf" 65 | 66 | 67 | Programming cable 68 | ----- 69 | 70 | From the top of the v0.1 board: 71 | * RST / TDIO 72 | * RXD on programmer 73 | * TXD on programmer 74 | * VCC 75 | * TST / TCK 76 | * GND 77 | 78 | v0.2: 79 | * greem 80 | * white 81 | * blue 82 | * purple 83 | * gray 84 | * black 85 | 86 | 87 | There is also a four-wire setup that might simplify the wiring. 88 | Travis documents it and the power consumption here: 89 | https://github.com/travisgoodspeed/goodwatch/wiki/EnergyTrace 90 | 91 | Bricked Launchpad 92 | ----- 93 | 94 | If you brick your launchpad by running Energia under Linux, it will no 95 | longer program the Charliewatch. The other weirdness is that `tilib` 96 | can detect the device, but can't write to it unless run as root. 97 | 98 | Energy Trace 99 | ---- 100 | * `libmsp430.so`: https://dlbeer.co.nz/articles/slac460y/index.html 101 | * https://github.com/carrotIndustries/energytrace-util 102 | * Have to update path to `MSP430.h` from the `slac/DLL430_v3/include` directory 103 | 104 | 105 | -------------------------------------------------------------------------------- /case.scad: -------------------------------------------------------------------------------- 1 | // v0.1 board is 0.622" radius 2 | // v0.2 board is 0.5642" radius (1 square inch) 3 | 4 | // 3d printed bracelet (default is nato strap) 5 | linked_bracelet = 0; 6 | 7 | // play between the links 8 | link_play = 1.4; 9 | 10 | // horizontal play between the teeth 11 | teeth_play = 0.2; 12 | 13 | // extra spacing for the dial 14 | dial_play = 0.4; 15 | 16 | band_height = 4; 17 | pin_ratio = 3/5; 18 | 19 | 20 | module link(w,d,h,w2, last=0, first=0) 21 | { 22 | render() difference() { 23 | intersection() { 24 | translate([0,0,-h/2-1]) 25 | linear_extrude(height=h+2) 26 | polygon([ 27 | [-w2/2,-(d+h+(last?h:0))/2], 28 | [+w2/2,-(d+h+(last?h:0))/2], 29 | [+w/2,+(d+h)/2], 30 | [-w/2,+(d+h)/2], 31 | ]); 32 | union() { 33 | 34 | cube([w2 > w ? w2 : w,d,h], center=true); 35 | 36 | // positive rounded edge A1, length w2 37 | translate([0,d/2,0]) 38 | rotate([0,90,0]) 39 | cylinder(d=h, h=w, $fn=32, center=true); 40 | 41 | // positive rounded edge B2 42 | translate([0,-d/2,0]) 43 | rotate([0,90,0]) 44 | cylinder(d=h, h=w2, $fn=32, center=true); 45 | 46 | // last one has a square clasp 47 | if(last) 48 | { 49 | translate([0,-h,0]) 50 | cube([w/3-2*teeth_play,h,h], center=true); 51 | 52 | translate([0,-h*3/2,0]) 53 | rotate([0,90,0]) 54 | cylinder(d=h, h=w/3-2*teeth_play, $fn=32, center=true); 55 | } 56 | } 57 | } 58 | 59 | // negative large rounded edge B1 60 | translate([w2/3/2-teeth_play,-d/2,0]) 61 | rotate([0,90,0]) 62 | cylinder(d=h+link_play, h=w2/3+4, $fn=32); 63 | 64 | translate([-w2/3/2+teeth_play,-d/2,0]) 65 | rotate([0,-90,0]) 66 | cylinder(d=h+link_play, h=w2/3+4, $fn=32); 67 | 68 | // negative large rounded edge A2 69 | translate([0,d/2,0]) 70 | rotate([0,90,0]) 71 | cylinder(d=h+link_play, h=w2/3+2*teeth_play, $fn=32, center=true); 72 | 73 | // pin hole 74 | translate([0,-d/2,0]) 75 | rotate([0,90,0]) 76 | cylinder(d=h*pin_ratio, h=w+2, $fn=32, center=true); 77 | 78 | // if this is the last one, 79 | if(last) { 80 | // make the hole open on the bottom 81 | translate([0,-d/2,-1]) 82 | //rotate([20,0,0]) 83 | cube([w,h*pin_ratio,h/2+1], center=true); 84 | 85 | translate([0,-d/2-h/3,0]) 86 | rotate([0,90,0]) 87 | cylinder(d=h*pin_ratio, h=w+2, $fn=32, center=true); 88 | 89 | translate([0,-d/2-h/3/2,0]) 90 | //rotate([20,0,0]) 91 | cube([w,h*pin_ratio,h*pin_ratio], center=true); 92 | } 93 | 94 | if (first) { 95 | // and make the clasp side even large 96 | translate([0,+h/3,-1]) 97 | cube([w2/3+2*teeth_play,h,h+2], center=true); 98 | } 99 | 100 | 101 | // maybe add some fun patterns 102 | if(0) { 103 | translate([w*1/6,d/2+h/4,-1]) cylinder(d=w/4, h=h+2, $fn=7); 104 | translate([w*3/6,d/2-h/4,-1]) cylinder(d=w/4, h=h+2, $fn=7); 105 | translate([w*5/6,d/2+h/4,-1]) cylinder(d=w/4, h=h+2, $fn=7); 106 | } 107 | } 108 | 109 | // pin 110 | translate([0,d/2,0]) 111 | rotate([0,90,0]) 112 | cylinder(d=h*pin_ratio-2*teeth_play, h=w-2, $fn=32, center=true); 113 | } 114 | 115 | module case(dial,lug_offset,band_width,height) 116 | { 117 | render() difference() 118 | { 119 | union() { 120 | intersection() { 121 | // 12-sided outside 122 | rotate([0,0,360/12/2]) cylinder(r=dial+2.5, h=height, $fn=12); 123 | // round off the corners slightly 124 | translate([0,0,-1]) cylinder(r=dial+2.5-0.1, h=height+2, $fn=180); 125 | // and round off the top slightly 126 | sphere(r=dial+2.5+0.8, $fn=60); 127 | } 128 | 129 | 130 | // lug 131 | if(linked_bracelet) 132 | { 133 | translate([+lug_offset+1,0,band_height/2]) 134 | rotate([0,0,90]) 135 | link(band_width, 8, band_height, w2=band_width-1); 136 | 137 | translate([-lug_offset-2,0,band_height/2]) 138 | rotate([0,0,90]) 139 | link(band_width, 8, band_height, w2=band_width); 140 | 141 | translate([-lug_offset,0,band_height/2]) 142 | rotate([0,0,90]) 143 | cube([band_width,5,band_height], center=true); 144 | } else { 145 | translate([+lug_offset,0,band_height/2]) 146 | rotate([0,0,90]) 147 | cube([band_width+2,8,band_height], center=true); 148 | 149 | translate([-lug_offset,0,band_height/2]) 150 | rotate([0,0,90]) 151 | cube([band_width+2,8,band_height], center=true); 152 | } 153 | 154 | // bump for button 155 | //translate([0,dial,height/4]) cylinder(r=3, h=height/2, $fn=30); 156 | } 157 | 158 | 159 | // tapered cylinder inside 160 | translate([0,0,height-1]) cylinder(r1=dial+dial_play, r2=dial-dial_play, h=1.1, $fn=60); 161 | 162 | // cutout for button 163 | translate([0,dial,1.5]) cylinder(r=3-0.1, h=2.5, $fn=30); 164 | 165 | // larger inside cylinder 166 | cylinder(r=dial+dial_play, h=height-1, $fn=180); 167 | 168 | // slot for strap if not using a linked bracelet 169 | if (!linked_bracelet) 170 | { 171 | translate([-14,0,1.5]) rotate([0,+30,0]) 172 | cube([20,band_width+0.2,2.5], center=true); 173 | 174 | translate([+14,0,1.5]) rotate([0,-30,0]) 175 | cube([20,band_width+0.2,2.5], center=true); 176 | } 177 | } 178 | } 179 | 180 | 181 | // v0.1 case 182 | if(0) 183 | translate([0,25,0]) case( 184 | dial=0.622 * 25.4, 185 | lug_offset = 16, 186 | band_width = 22.2, 187 | height=8 188 | ); 189 | 190 | 191 | // v0.2 case 192 | band_width = 18; 193 | band_length = 8; 194 | band_count = round(160 / band_length / 2); 195 | 196 | case( 197 | dial = 29/2, // 0.5642 * 25.4, 198 | lug_offset = 14, 199 | band_width = linked_bracelet ? band_width + band_count*0.5 : band_width, 200 | height = 7.5 201 | ); 202 | 203 | if(link_braclet) 204 | { 205 | for(i=[1:band_count]) 206 | { 207 | translate([-i*band_length-14-2,0,band_height/2]) 208 | rotate([0,0,90]) 209 | link( 210 | band_width+(band_count-i+1)*0.5, 211 | band_length, 212 | band_height, 213 | band_width+(band_count-i)*0.5, 214 | first=(i==band_count) 215 | ); 216 | } 217 | 218 | if(1) 219 | for(i=[1:band_count-1]) 220 | { 221 | translate([+i*band_length+14+1,0,band_height/2]) 222 | rotate([0,0,90]) 223 | link( 224 | band_width + (band_count-i)*0.5, 225 | band_length, 226 | band_height, 227 | band_width + (band_count-i-1)*0.5, 228 | last=(i==band_count-1) 229 | ); 230 | } 231 | } 232 | 233 | %translate([0,20,0]) cube([190,1,1], center=true); 234 | -------------------------------------------------------------------------------- /datasheets/cc430.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/datasheets/cc430.pdf -------------------------------------------------------------------------------- /datasheets/charliewatch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/datasheets/charliewatch.pdf -------------------------------------------------------------------------------- /datasheets/goodwatch22.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/datasheets/goodwatch22.pdf -------------------------------------------------------------------------------- /datasheets/msp430-bsl.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/datasheets/msp430-bsl.pdf -------------------------------------------------------------------------------- /firmware/Makefile: -------------------------------------------------------------------------------- 1 | # set default flashing serial port, dont override if passed in as an argument 2 | PORT ?= /dev/ttyACM0 3 | 4 | # The Charliewatch uses the CC430 5-variant with no LCD controller 5 | MCU ?= cc430f5137 6 | 7 | # Currently we're flashing with a launchpad board 8 | PROGRAMMER ?= tilib 9 | 10 | # Create an array of the current y/m/d h:m:s 11 | # The -100 is to avoid octal constant issues with leading zeros 12 | BUILDTIME := $(shell date "+%y,%m,1%d-100,1%H-100,1%M-100,1%S-100") 13 | BUILDSEC := $(shell date "+%s") 14 | 15 | #Standard Debian gcc-msp430 and msp430mcu packages. 16 | CC = msp430-gcc 17 | CFLAGS += \ 18 | -mmcu=$(MCU) \ 19 | -Wall \ 20 | -Werror \ 21 | -I. \ 22 | -Os \ 23 | -MMD \ 24 | -MF .$(notdir $@).d \ 25 | -DBUILDTIME=$(BUILDTIME) \ 26 | -DBUILDSEC=$(BUILDSEC) \ 27 | 28 | ASFLAGS += -mmcu=$(MCU) 29 | LDFLAGS += -mmcu=$(MCU) 30 | 31 | OBJS += main.o 32 | OBJS += led.o 33 | OBJS += button.o 34 | OBJS += animation.o 35 | OBJS += stopwatch.o 36 | OBJS += clockset.o 37 | OBJS += ucs.o 38 | OBJS += rtc.o 39 | OBJS += rtcasm.o 40 | OBJS += power.o 41 | OBJS += adc.o 42 | #OBJS += ref.o 43 | #OBJS += uart.o 44 | OBJS += buildtime.o 45 | 46 | all: charliewatch.hex 47 | 48 | charliewatch.elf: $(OBJS) 49 | $(CC) $(LDFLAGS) -T msp430.x -o $@ $^ 50 | 51 | buildtime.o: FORCE 52 | 53 | %.hex: %.elf 54 | msp430-objcopy -O ihex $< $@ 55 | 56 | clean: 57 | rm -rf *.hex *.elf *.o .*.d 58 | 59 | erase: 60 | $(BSL) -e 61 | 62 | flash: charliewatch.elf 63 | ../mspdebug/mspdebug \ 64 | $(PROGRAMMER) \ 65 | "prog $<" \ 66 | 67 | 68 | 69 | dmesg: 70 | $(BSL) -P goodwatch.hex -uD 71 | sbwdmesg: 72 | mspdebug tilib "save_raw 0x2400 2048 dmesg.bin" 73 | strings dmesg.bin 74 | 75 | #Same as dmesg, but it gives the target some time to boot first. 76 | run: 77 | sleep 5 78 | ../bin/cc430-bsl.py -p $(PORT) -P goodwatch.hex -uD 79 | 80 | codeplug.hex: codeplug.txt 81 | ../bin/goodwatch-txt2cp.py -i codeplug.txt -o codeplug.hex 82 | flashcp: codeplug.hex 83 | $(BSL) -Ef codeplug.hex 84 | 85 | dump: 86 | $(BSL) -ed 87 | 88 | 89 | 90 | energytrace.txt: goodwatch.hex sbwflash 91 | energytrace 60 > energytrace.txt #This will take a minute. 92 | gnuplot energytrace.gnuplot 93 | gnuplot energytrace-txt.gnuplot 94 | ../bin/batterylife.py 42 | #include 43 | #include "adc.h" 44 | #include "led.h" 45 | 46 | // ************************************************************************************************* 47 | // @fn adc12_single_conversion 48 | // @brief Init ADC12. Do single conversion. Turn off ADC12. 49 | // @param uint16_t ref Select reference 50 | // uint16_t sht Sample-and-hold time 51 | // uint16_t channel Channel of the conversion 52 | // @return uint16_t adc12_result Return ADC result 53 | // ************************************************************************************************* 54 | uint16_t adc12_single_conversion(uint16_t ref, uint16_t sht, uint16_t channel) 55 | { 56 | // Initialize the shared reference module 57 | REFCTL0 |= REFMSTR + ref + REFON; // Enable internal reference (1.5V or 2.5V) 58 | 59 | // Initialize ADC12_A 60 | ADC12CTL0 = sht + ADC12ON; // Set sample time 61 | ADC12CTL1 = ADC12SHP; // Enable sample timer 62 | ADC12MCTL0 = ADC12SREF_1 + channel; // ADC input channel 63 | ADC12IE = 0x001; // ADC_IFG upon conv result-ADCMEMO 64 | 65 | // Wait 2 ticks (66us) to allow internal reference to settle 66 | //Timer0_A4_Delay(2); 67 | //delay(1000); 68 | 69 | // Start ADC12 70 | ADC12CTL0 |= ADC12ENC; 71 | 72 | /* 73 | // Clear data ready flag 74 | adc12_data_ready = 0; 75 | */ 76 | 77 | // Sampling and conversion start 78 | ADC12CTL0 |= ADC12SC; 79 | 80 | //Start the conversion and fetch the result when ready. 81 | 82 | // Delay to get next ADC value 83 | //Timer0_A4_Delay(5); 84 | //delay(30); 85 | //delay(1000); 86 | //while (!adc12_data_ready) ; 87 | while ((ADC12IFG & BIT0)==0); 88 | uint16_t adc12_result = ADC12MEM0; 89 | 90 | // Shut down ADC12 91 | ADC12CTL0 &= ~(ADC12ENC | ADC12SC | sht); 92 | ADC12CTL0 &= ~ADC12ON; 93 | 94 | // Shut down reference voltage 95 | REFCTL0 &= ~(REFMSTR + ref + REFON); 96 | 97 | ADC12IE = 0; 98 | 99 | // Return ADC result 100 | return adc12_result; 101 | } 102 | -------------------------------------------------------------------------------- /firmware/adc.h: -------------------------------------------------------------------------------- 1 | /*! \file adc.h 2 | \brief ADC12 Driver for the GoodWatch. 3 | 4 | Mostly this uses the internal voltage reference in the ADC to 5 | measure the battery voltage. For now, we aren't very interested in 6 | the temperature or other analog inputs. 7 | 8 | */ 9 | 10 | 11 | uint16_t adc12_single_conversion(uint16_t ref, uint16_t sht, uint16_t channel); 12 | -------------------------------------------------------------------------------- /firmware/animation.c: -------------------------------------------------------------------------------- 1 | /*! \file animation.c 2 | 3 | \brief Drawing functions based on the time. 4 | 5 | Animations are drawn: 6 | * When the second hand hits the minute hand 7 | * When the second hand hits the minute hand on a 5 minut emark 8 | * When the second hand hits the hour hand 9 | * When all three hands line up 10 | * At midnight/noon 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "led.h" 19 | 20 | void delay(unsigned len) 21 | { 22 | #if 0 23 | const uint8_t p2 = P2OUT; 24 | const uint8_t p3 = P3OUT; 25 | 26 | while(len) 27 | { 28 | // toggle the LEDs back on 29 | P2OUT = p2; 30 | P3OUT = p3; 31 | len--; 32 | 33 | // now toggle them off 34 | P2OUT = 0; 35 | P3OUT = 0; 36 | } 37 | #else 38 | len *= 2; 39 | while(len) 40 | { 41 | asm(""); 42 | len--; 43 | } 44 | #endif 45 | } 46 | 47 | 48 | // In normal operation there are four LEDs powered on 49 | // The minute is full bright 50 | // The hour is half bright 51 | // the second is quarter bright 52 | // the previous second fades 53 | uint8_t led_display[] = {}; 54 | uint8_t led_bright[] = {}; 55 | 56 | 57 | void led_draw(void) 58 | { 59 | int i; 60 | 61 | for(i=0 ; i < NUM_DISPLAY ; i++) 62 | { 63 | const unsigned led = led_display[i]; 64 | const unsigned bright = led_bright[i]; 65 | 66 | #if 0 67 | if (bright > 0) 68 | led_on(led); 69 | 70 | if (bright > 1) 71 | delay(bright); 72 | else 73 | led_off(); 74 | #else 75 | led_dither(led, bright); 76 | #endif 77 | } 78 | 79 | led_off(); 80 | } 81 | 82 | 83 | static unsigned animation_counter = 0; 84 | static void (*animation)(unsigned counter) = NULL; 85 | 86 | static void sparkle_animation(unsigned count) 87 | { 88 | #if 0 89 | // should make the sparkles converge inward 90 | led_on((RTCSEC + rand() % (count/8) - count/16 + 240) % 60); 91 | led_on((RTCMIN + rand() % (count/8) - count/16 + 240) % 60); 92 | led_on((RTCHOUR*5 + rand() % (count/8) - count/16 + 240) % 60); 93 | led_on(rand() % 60); 94 | led_on(60 + (rand() % 12)); 95 | #else 96 | unsigned i; 97 | if (count > 72) 98 | count = 12; 99 | else 100 | count /= 8; 101 | 102 | for(i = 0 ; i < count ; i++) 103 | led_on(rand() % 72); 104 | 105 | // start turning on the hands 106 | if (count < 20) 107 | { 108 | led_on(led_display[0]); 109 | led_on(led_display[1]); 110 | led_on(RTCSEC); 111 | } 112 | #endif 113 | } 114 | 115 | // animation 1 for when all the hands align 116 | static void align_animation(unsigned count) 117 | { 118 | unsigned i; 119 | 120 | if (count > 180) 121 | { 122 | // draw out from the minute up to the count 123 | for(i=0 ; i < 240 - count ; i++) 124 | led_on((RTCMIN + i + 60) % 60); 125 | 126 | // draw out from the hour hand in reverse 127 | for(i=0 ; i < (240 - count)/5 ; i ++) 128 | led_on(60 + (RTCHOUR + i + 12) % 12); 129 | } else 130 | if (count > 120) 131 | { 132 | // start collapsing back to the minute 133 | for(i= 0 ; i < count - 120 ; i++) 134 | led_on((RTCMIN - i + 60) % 60); 135 | 136 | // collapse back to the hour hand 137 | for(i=0 ; i <= (count - 120)/5 ; i ++) 138 | led_on(60 + (RTCHOUR - i + 12) % 12); 139 | } else 140 | if (count > 60) 141 | { 142 | // draw out from the minute up to the count 143 | for(i=0 ; i < 120 - count ; i++) 144 | led_on((RTCMIN - i + 60) % 60); 145 | 146 | // draw out from the hour hand in reverse 147 | for(i=0 ; i <= (120 - count)/5 ; i ++) 148 | led_on(60 + (RTCHOUR + i + 12) % 12); 149 | } else { 150 | // start collapsing back to the minute 151 | for(i= 0 ; i < count ; i++) 152 | led_on((RTCMIN + i + 60) % 60); 153 | 154 | // collapse back to the hour hand 155 | for(i=0 ; i <= count/5 ; i ++) 156 | led_on(60 + (RTCHOUR - i + 12) % 12); 157 | } 158 | } 159 | 160 | // animation 2 for when all the hands align 161 | static void race_animation(unsigned count) 162 | { 163 | // second hand races at full speed 164 | led_display[2] = (RTCMIN + 360 - count) % 60; 165 | 166 | // minute hand at half speed 167 | led_display[0] = (RTCMIN + 360 - count/2) % 60; 168 | 169 | // hour hand at 1/3 speed 170 | led_display[1] = (RTCMIN + 360 - count/3) % 60; 171 | 172 | led_draw(); 173 | } 174 | 175 | // animation for when the hands align at 3 and 9 176 | // rotate start at max speed, rotate stop at half speed, 177 | // makes four revolutions 178 | // count goes from 240 to 0 179 | static void chase_animation(unsigned count) 180 | { 181 | unsigned i; 182 | 183 | // change the sense of the count 184 | count = 240 - count; 185 | 186 | if (count < 120) 187 | { 188 | // first two laps (0-59), go from slow to fast 189 | // at the end the entire watch is illuminated 190 | for(i = count/2 ; i < count ; i++) 191 | led_on((i + RTCSEC) % 60); 192 | } else { 193 | // second two laps, go from fast to slow, undrawing 194 | // at the end nothing is illuminated 195 | count -= 120; 196 | for(i = count ; i < count/2 + 60 ; i++) 197 | led_on((i + RTCSEC) % 60); 198 | } 199 | 200 | led_off(); 201 | } 202 | 203 | // when the second hand lines up with the hour and the minute 204 | // is 180 degrees away 205 | static void opposite_animation(unsigned count) 206 | { 207 | unsigned i; 208 | 209 | if (count >= 60) 210 | { 211 | // grow the bargraph between the two points until they meet 212 | count = (120 - count) / 2; 213 | for(i = 0 ; i < count ; i++) 214 | { 215 | led_on((RTCSEC + i) % 60); 216 | led_on((RTCMIN + i) % 60); 217 | } 218 | } else 219 | { 220 | // retract the bargraph between the two 221 | count /= 2; 222 | for(i = 0 ; i <= count ; i++) 223 | { 224 | led_on((RTCSEC - i + 60) % 60); 225 | led_on((RTCMIN - i + 60) % 60); 226 | } 227 | } 228 | 229 | // and keep the actual hour on 230 | led_on(led_display[1]); 231 | } 232 | 233 | // rotate all three together 234 | static void triangle_animation(unsigned count) 235 | { 236 | const unsigned hour = (RTCHOUR % 12) * 5; 237 | unsigned i; 238 | 239 | if (RTCSEC < RTCMIN) 240 | { 241 | // spin all three around twice 242 | led_on((RTCSEC + 120 - count) % 60); 243 | led_on((RTCMIN + 120 - count) % 60); 244 | led_on((hour + 120 - count) % 60); 245 | 246 | } else 247 | if (count >= 60) 248 | { 249 | // grow the bargraph between the three points 250 | // count goes from 0 - 20 251 | count = (120 - count) / 3; 252 | for(i = 0 ; i < count ; i++) 253 | { 254 | led_dither((RTCSEC + i) % 60, (20 - count + i)/8); 255 | led_dither((RTCMIN + i) % 60, (20 - count + i)/8); 256 | led_dither((hour + i) % 60, (20 - count + i)/8); 257 | } 258 | } else 259 | { 260 | // retract the bargraph to the three 261 | // count goes from 20 to 0 262 | count /= 3; 263 | for(i = 0 ; i <= count ; i++) 264 | { 265 | led_dither((RTCSEC - i + 60) % 60, (count - i)/8); 266 | led_dither((RTCMIN - i + 60) % 60, (count - i)/8); 267 | led_dither((hour - i + 60) % 60, (count - i)/8); 268 | } 269 | } 270 | 271 | // and keep the actual hour on 272 | led_on(led_display[1]); 273 | } 274 | 275 | static void hour_animation(unsigned count) 276 | { 277 | // run the hour backwards 278 | if (3 == (count % 5)) 279 | led_display[1] = 60 + (led_display[1] - 60 - 1 + 12) % 12; 280 | 281 | // and the second hand should match it 282 | led_display[2] = (led_display[2] + 1) % 60; 283 | 284 | led_draw(); 285 | } 286 | 287 | 288 | static void minute_animation(unsigned count) 289 | { 290 | // run the minute hand backwards at half speed 291 | if (0 == (count & 1)) 292 | led_display[0] = (led_display[0] + 60 - 1) % 60; 293 | 294 | // run the second hand forward at full speed 295 | led_display[2] = (led_display[2] + 1) % 60; 296 | 297 | // if this occured at the start of a new hour, 298 | // run an hour hand animation, too 299 | if (RTCMIN == 0 && (count % 5) == 0) 300 | led_display[1] = 60 + (led_display[1] - 60 - 1 + 12) % 12; 301 | 302 | led_draw(); 303 | } 304 | 305 | // count will be start at 60 306 | static void new_minute_animation(unsigned count) 307 | { 308 | // if earlier than 30 minute past the hour, 309 | // run the hand forwards to 59 310 | // otherwise 311 | // run the hard backwards to 1 312 | // reset animation counter when we hit either condition 313 | 314 | if (RTCMIN > 30) 315 | count = RTCMIN + count - 60; 316 | else 317 | count = RTCMIN - count + 60; 318 | 319 | if (count == 0 || count == 59) 320 | animation_counter = 0; 321 | 322 | led_on(count); 323 | led_off(); 324 | 325 | // ensure that the second hand advances 326 | led_display[2] = RTCSEC; 327 | 328 | led_draw(); 329 | } 330 | 331 | static void second_animation(unsigned count) 332 | { 333 | // run the second hand forward at full speed 334 | led_display[2] = (led_display[2] + 1) % 60; 335 | 336 | led_draw(); 337 | } 338 | 339 | static void diamond_animation(unsigned count) 340 | { 341 | // draw two pulses from the hour and minute hand to the second hand 342 | // count goes from 50 to 0 343 | if (count > 25) 344 | count = 50 - count; 345 | 346 | led_on((RTCHOUR*5 - count + 60) % 60); led_off(); 347 | led_on((RTCHOUR*5 + count + 60) % 60); led_off(); 348 | 349 | led_draw(); 350 | } 351 | 352 | 353 | static void 354 | check_animation(void) 355 | { 356 | // normalize so that hour and minute are relative to seconds 357 | const unsigned s = RTCSEC; 358 | const unsigned m = (RTCMIN - s + 60) % 60; 359 | const unsigned h = (RTCHOUR*5 - s + 60) % 60; 360 | 361 | if (s == 0 && h == 0 && m == 0) 362 | { 363 | // midnight or noon, draw the sparkles 364 | animation_counter = 240; 365 | animation = sparkle_animation; 366 | } else 367 | if (m == 0 && h == 0) 368 | { 369 | // when the hour, minute and second hands line up 370 | if (s == 30) 371 | { 372 | // special six o'clock animation 373 | animation_counter = 360; 374 | animation = race_animation; 375 | } else 376 | if (s == 15 || s == 45) 377 | { 378 | // special 3 and 9 animation 379 | animation_counter = 240; 380 | animation = chase_animation; 381 | } else { 382 | // normal hour animation 383 | animation_counter = 240; 384 | animation = align_animation; 385 | } 386 | } else 387 | if (m == 0 && (s % 5) == 0) 388 | { 389 | // when the minute and second line up 390 | // every five minutes 391 | // wrap the second hand twice 392 | // and the minute hand at half speed. 393 | animation_counter = 120; 394 | animation = minute_animation; 395 | } else 396 | if (m == 0) 397 | { 398 | // when the second hand hits the minute hand, 399 | // run the second hand forwards 400 | animation_counter = 60; 401 | animation = second_animation; 402 | } else 403 | if (m == 30 && h == 0) 404 | { 405 | // second and hour align, hour 180 degrees opposite 406 | animation_counter = 120; 407 | animation = opposite_animation; 408 | } else 409 | if (h == 0) 410 | { 411 | // second hand aligns with the hour hand 412 | animation_counter = 60; 413 | animation = hour_animation; 414 | } else 415 | if ((m == 20 && h == 40) || (m == 40 && h == 20)) 416 | { 417 | // all hands are 120 degrees out of phase 418 | animation_counter = 120; 419 | animation = triangle_animation; 420 | } else 421 | if ((h == 25 && m == 50) || (h == 35 && m == 10)) 422 | { 423 | animation_counter = 50; 424 | animation = diamond_animation; 425 | } else 426 | if (s == 0) 427 | { 428 | // new minute, re-wind the minute hand 429 | animation_counter = 60; 430 | animation = new_minute_animation; 431 | } 432 | } 433 | 434 | void animation_draw() 435 | { 436 | static int oldsec; 437 | static int hour_dir; 438 | static unsigned hour_bright = 0; 439 | 440 | 441 | if (animation_counter) 442 | { 443 | animation(animation_counter--); 444 | return; 445 | } 446 | 447 | // we're not doing an animation, check for a second update 448 | if (oldsec != RTCSEC) 449 | { 450 | oldsec = RTCSEC; 451 | 452 | led_display[0] = RTCMIN; 453 | led_display[1] = 60 + (RTCHOUR % 12); 454 | led_display[2] = RTCSEC; 455 | 456 | check_animation(); 457 | } 458 | 459 | // make the hour "breath" 460 | hour_bright += hour_dir; 461 | if (hour_bright == 16*8) 462 | hour_dir = -1; 463 | else 464 | if (hour_bright <= 8) 465 | hour_dir = +1; 466 | 467 | // set the default brightnesses for minute and second 468 | #define CONFIG_RED 469 | 470 | #ifdef CONFIG_PURPLE 471 | led_bright[0] = 32; 472 | led_bright[1] = (hour_bright / 4) + 1; 473 | led_bright[2] = 4; 474 | #endif 475 | 476 | #ifdef CONFIG_ORANGE 477 | led_bright[0] = 32; 478 | led_bright[1] = (hour_bright / 4) + 1; 479 | led_bright[2] = 1; 480 | #endif 481 | #ifdef CONFIG_RED 482 | led_bright[0] = 16; 483 | led_bright[1] = (hour_bright / 4) + 1; 484 | led_bright[2] = 1; 485 | #endif 486 | 487 | led_draw(); 488 | } 489 | -------------------------------------------------------------------------------- /firmware/buildtime.c: -------------------------------------------------------------------------------- 1 | // This file is rebuilt on every build to capture the build time 2 | #include 3 | 4 | const uint8_t romsavetime[] = { BUILDTIME }; 5 | const uint32_t buildtime = BUILDSEC; 6 | -------------------------------------------------------------------------------- /firmware/button.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "button.h" 4 | 5 | unsigned button_short; 6 | unsigned button_long; 7 | 8 | void button_init(void) 9 | { 10 | // configure button as input with internal pullup 11 | // (v0.1 is reusing the RX pin, 1.5) 12 | //#define BUTTON 5 13 | // (v0.2 has a separate button on 1.7) 14 | #define BUTTON 7 15 | P1SEL &= ~(1 << BUTTON); 16 | P1DIR &= ~(1 << BUTTON); 17 | P1REN |= (1 << BUTTON); 18 | P1OUT |= (1 << BUTTON); 19 | } 20 | 21 | 22 | // check the button status 23 | void button_update(void) 24 | { 25 | static unsigned button_counter; 26 | button_long = button_short = 0; 27 | 28 | // if button is pressed, increment hold counter 29 | if ((P1IN & (1 << BUTTON)) == 0) 30 | { 31 | button_counter++; 32 | 33 | // check for long hold (~2 seconds) 34 | // only have it true for one cycle 35 | if ((button_counter % 128) == 0) 36 | { 37 | button_long = 1; 38 | } 39 | 40 | return; 41 | } 42 | 43 | // button has been released 44 | // if held and released for the right amount of time 45 | if (3 < button_counter && button_counter < 60) 46 | button_short = 1; 47 | 48 | button_counter = 0; 49 | } 50 | -------------------------------------------------------------------------------- /firmware/button.h: -------------------------------------------------------------------------------- 1 | 2 | extern unsigned button_short; 3 | extern unsigned button_long; 4 | 5 | extern void button_init(void); 6 | extern void button_update(void); 7 | -------------------------------------------------------------------------------- /firmware/clockset.c: -------------------------------------------------------------------------------- 1 | /*! \file Clock setting functions. 2 | 3 | \brief Allow the user to set the clock via button presses. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "led.h" 12 | #include "button.h" 13 | #include "rtc.h" 14 | 15 | static unsigned temp_hour; 16 | static unsigned temp_min; 17 | static unsigned temp_sec; 18 | static unsigned timeout; 19 | static unsigned selected; 20 | extern unsigned watch_mode; 21 | 22 | // called every 16ms by the RTC interrupt 23 | void clockset_draw(void) 24 | { 25 | if (button_long) 26 | { 27 | // just entered this mode; capture the hour/min/sec 28 | temp_hour = RTCHOUR; 29 | temp_min = RTCMIN; 30 | temp_sec = RTCSEC; 31 | timeout = 0; 32 | 33 | // toggle which setting (minute or hour) we have selected 34 | selected = (selected + 1) % 2; 35 | } 36 | 37 | if (button_short) 38 | { 39 | timeout = 0; 40 | 41 | if (selected == 0) 42 | temp_min = (temp_min + 1) % 60; 43 | else 44 | temp_hour = (temp_hour + 1) % 24; 45 | 46 | SetRTCHOUR(temp_hour); 47 | SetRTCMIN(temp_min); 48 | SetRTCSEC(temp_sec); 49 | } 50 | 51 | // no button presses in 15 seconds => go back to clock mode 52 | if (++timeout > 15*60) 53 | { 54 | watch_mode = 0; 55 | return; 56 | } 57 | 58 | // the second always stays on 59 | led_on(temp_sec); 60 | led_off(); 61 | 62 | if (selected == 0) 63 | { 64 | // hour always on 65 | led_on((temp_hour % 12) + 60); 66 | delay(4); 67 | led_off(); 68 | 69 | // flash the minute on and off 70 | if ((timeout % 32) < 24) 71 | { 72 | led_on(temp_min); 73 | delay(4); 74 | } 75 | } else { 76 | // minute always on 77 | led_on(temp_min); 78 | delay(4); 79 | 80 | // flash the hour on and off 81 | if ((timeout % 32) < 24) 82 | { 83 | led_on((temp_hour % 12) + 60); 84 | delay(4); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /firmware/led.c: -------------------------------------------------------------------------------- 1 | /*! \file led.c 2 | 3 | \brief Charlieplexed LED driver 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "led.h" 12 | 13 | #define LED(x,y) ((x << 4) | (y << 0)) 14 | 15 | static const uint8_t leds[] = 16 | { 17 | // 0 - 59, the minutes 18 | LED(2,0), // led01, 00 19 | LED(3,0), // led02, 01 20 | LED(4,0), // led03, 02 21 | LED(5,0), // led04, 03 22 | LED(6,0), // led05, 04 23 | LED(7,0), // led06, 05 24 | LED(8,0), // led07, 06 25 | 26 | LED(8,1), // led15, 07 27 | LED(7,1), // led14, 08 28 | LED(6,1), // led13, 09 29 | LED(5,1), // led12, 10 30 | LED(4,1), // led11, 11 31 | LED(3,1), // led10, 12 32 | LED(2,1), // led09, 13 33 | LED(0,1), // led08, 14 34 | 35 | LED(0,2), // led16, 15 36 | LED(1,2), // led17, 16 37 | LED(3,2), // led18, 17 38 | LED(4,2), // led19, 18 39 | LED(5,2), // led20, 19 40 | LED(6,2), // led21, 20 41 | LED(7,2), // led22, 21 42 | LED(8,2), // led23, 22 43 | 44 | LED(8,3), // led31, 23 45 | LED(7,3), // led30, 24 46 | LED(6,3), // led29, 25 47 | LED(5,3), // led28, 26 48 | LED(4,3), // led27, 27 49 | LED(2,3), // led26, 28 50 | LED(1,3), // led25, 29 51 | LED(0,3), // led24, 30 52 | 53 | LED(0,4), // led32, 31 54 | LED(1,4), // led33, 32 55 | LED(2,4), // led34, 33 56 | LED(3,4), // led35, 34 57 | LED(5,4), // led36, 35 58 | LED(6,4), // led37, 36 59 | LED(7,4), // led38, 37 60 | LED(8,4), // led39, 38 61 | 62 | LED(8,5), // led47, 39 63 | LED(7,5), // led46, 40 64 | LED(6,5), // led45, 41 65 | LED(4,5), // led44, 42 66 | LED(3,5), // led43, 43 67 | LED(2,5), // led42, 44 68 | LED(1,5), // led41, 45 69 | LED(0,5), // led40, 46 70 | 71 | LED(0,6), // led48, 47 72 | LED(1,6), // led49, 48 73 | LED(2,6), // led50, 49 74 | LED(3,6), // led51, 50 75 | LED(4,6), // led52, 51 76 | LED(5,6), // led53, 52 77 | LED(7,6), // led54, 53 78 | LED(8,6), // led55, 54 79 | 80 | LED(8,7), // led63, 55 81 | LED(6,7), // led62, 56 82 | LED(5,7), // led61, 57 83 | LED(4,7), // led60, 58 84 | LED(1,0), // led0, 59 85 | 86 | // 60 - 71, the hours 87 | LED(1,7), // led57, 0 88 | LED(3,7), // led59, 1 89 | LED(2,7), // led58, 2 90 | LED(0,8), // led64, 3 91 | LED(2,8), // led66, 4 92 | LED(6,8), // led70, 5 93 | LED(0,7), // led56, 6 94 | LED(5,8), // led69, 7 95 | LED(7,8), // led71, 8 96 | LED(1,8), // led65, 9 97 | LED(3,8), // led67, 10 98 | LED(4,8), // led68, 11 99 | }; 100 | 101 | #define NUM_LEDS (sizeof(leds)/sizeof(*leds)) 102 | 103 | void led_on(uint8_t i) 104 | { 105 | // tri-state all LED lines, leaving the others driven 106 | P3DIR = ~0x1F; 107 | P3OUT = 0; 108 | P2DIR = ~0x0F; 109 | P2OUT = 0; 110 | 111 | const uint8_t x = (leds[i] >> 4) & 0xF; 112 | const uint8_t y = (leds[i] >> 0) & 0xF; 113 | 114 | // 0-4 are on P3.0 - P3.4 115 | // 5-8 are on P2.0 - P2.3 116 | if (x < 5) 117 | P3DIR |= 1 << (x - 0); 118 | else 119 | P2DIR |= 1 << (x - 5); 120 | 121 | if (y < 5) { 122 | P3DIR |= 1 << (y - 0); 123 | P3OUT |= 1 << (y - 0); 124 | } else { 125 | P2DIR |= 1 << (y - 5); 126 | P2OUT |= 1 << (y - 5); 127 | } 128 | } 129 | 130 | void led_off(void) 131 | { 132 | // set all the LED lines to output, drive to ground 133 | P2OUT = 0; 134 | P3OUT = 0; 135 | P3DIR = 0xFF; 136 | P2DIR = 0xFF; 137 | } 138 | 139 | 140 | void led_dither(uint8_t i, uint8_t bright) 141 | { 142 | const uint8_t x = (leds[i] >> 4) & 0xF; 143 | const uint8_t y = (leds[i] >> 0) & 0xF; 144 | 145 | // tri-state all LED lines, leaving the others driven 146 | P3DIR = ~0x1F; 147 | P2DIR = ~0x0F; 148 | 149 | // 0-4 are on P3.0 - P3.4 150 | // 5-8 are on P2.0 - P2.3 151 | if (x < 5) 152 | P3DIR |= 1 << (x - 0); 153 | else 154 | P2DIR |= 1 << (x - 5); 155 | 156 | if (y < 5) { 157 | const uint8_t mask = 1 << (y - 0); 158 | P3DIR |= mask; 159 | 160 | do { 161 | P3OUT = mask; 162 | P3OUT = 0; 163 | } while(bright--); 164 | } else { 165 | const uint8_t mask = 1 << (y - 5); 166 | P2DIR |= mask; 167 | 168 | do { 169 | P2OUT = mask; 170 | P2OUT = 0; 171 | } while(bright--); 172 | } 173 | 174 | led_off(); 175 | } 176 | 177 | void led_test(void) 178 | { 179 | int i, j; 180 | for(i = 0 ; i < 72 ; i++) 181 | { 182 | for(j = 0 ; j < 20 ; j++) 183 | { 184 | led_on(i); // delay(4); 185 | led_off(); 186 | delay(200); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /firmware/led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // "Framebuffer" for the four LEDs that are on, 4 | // with configurable brightness. 5 | #define NUM_DISPLAY 3 6 | extern uint8_t led_display[NUM_DISPLAY]; 7 | extern uint8_t led_bright[NUM_DISPLAY]; 8 | 9 | // Draw the "framebuffer" 10 | void led_draw(void); 11 | 12 | void animation_draw(void); 13 | 14 | // Raw method to turn on an LED 15 | void led_on(uint8_t i); 16 | void led_dither(uint8_t i, uint8_t bright); 17 | 18 | // Turn off all LEDs 19 | void led_off(void); 20 | 21 | // Spin loop 22 | void delay(unsigned len); 23 | 24 | // test all the LEDs 25 | void led_test(void); 26 | -------------------------------------------------------------------------------- /firmware/main.c: -------------------------------------------------------------------------------- 1 | /*! \file main.c 2 | 3 | \brief Main module. This version initializes the hardware and then 4 | drops to a low power mode, letting the WDT update the LEDs every 16ms 5 | 6 | Schematic and more at https://trmm.net/Charliewatch 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "rtc.h" 15 | #include "ucs.h" 16 | #include "led.h" 17 | #include "adc.h" 18 | #include "power.h" 19 | #include "button.h" 20 | 21 | //! Activates and configures the module, but does not turn on reference. 22 | void ref_init(){ 23 | //Setting the master bit disables legacy mode. 24 | REFCTL0|=REFMSTR; 25 | 26 | //Wait for register to not be busy before modifying voltage. 27 | while(REFCTL0 & REFGENBUSY); 28 | 29 | //2.5V reference. 30 | REFCTL0 &= ~BGMODE; 31 | REFCTL0|=REFVSEL_2; 32 | } 33 | 34 | 35 | //! Main method. 36 | int main(void) 37 | { 38 | WDTCTL = WDTPW + WDTHOLD; // Stop WDT 39 | 40 | ref_init(); 41 | //uart_init(); 42 | 43 | // drive port J and 1 to ground to avoid CMOS power drain 44 | PJDIR |= 0xF; 45 | PJOUT &= ~0xF; 46 | P1DIR |= 0xF; 47 | P1OUT &= ~0xF; 48 | 49 | button_init(); 50 | 51 | // turn off all the LEDs to reduce power 52 | led_off(); 53 | 54 | #if 1 55 | led_test(); 56 | #else 57 | while(1) { 58 | led_test(); 59 | delay(1000); 60 | } 61 | #endif 62 | 63 | rtc_init(); 64 | ucs_init(); // doesn't work if crystal isn't there? 65 | 66 | // Setup and enable WDT 16ms, ACLK, interval timer 67 | WDTCTL = WDT_ADLY_16; 68 | SFRIE1 |= WDTIE; 69 | 70 | // go into low power mode 71 | power_setvcore(0); 72 | ucs_slow(); 73 | 74 | __bis_SR_register(LPM3_bits + GIE); // Enter LPM3 75 | 76 | // flash the 0 hour so that we know something is wrong 77 | while(1) 78 | { 79 | led_on(60); delay(1); 80 | led_off(); delay(3); 81 | led_on(60); delay(1); 82 | led_off(); delay(6); 83 | } 84 | } 85 | 86 | 87 | extern void stopwatch_draw(void); 88 | extern void clockset_draw(void); 89 | extern void animation_draw(void); 90 | 91 | static void voltage_draw(void) 92 | { 93 | unsigned i; 94 | unsigned raw_volts = adc12_single_conversion( 95 | REFVSEL_1, ADC12SHT0_10, ADC12INCH_11); 96 | 97 | 98 | #if 0 99 | static unsigned bright = 0; 100 | bright = (bright + 1) % 128; 101 | 102 | for(i = 0 ; i < 12 ; i++) 103 | { 104 | if (0 == (raw_volts & (1< 64 ? 128 - bright : bright); 107 | } 108 | 109 | unsigned scaled = raw_volts / 68; 110 | for(i = 0 ; i < scaled ; i++) 111 | { 112 | led_on(i); 113 | } 114 | #else 115 | unsigned millivolts = (1250 * (uint32_t) raw_volts) / 1024; 116 | led_on(60 + (millivolts / 1000)); 117 | 118 | unsigned mv = ((millivolts % 1000) * 60) / 1000; 119 | for(i = 0 ; i < mv ; i++) 120 | { 121 | led_on(i); 122 | } 123 | #endif 124 | 125 | led_off(); 126 | } 127 | 128 | static void (*const modes[])(void) = { 129 | //voltage_draw, 130 | animation_draw, 131 | stopwatch_draw, 132 | clockset_draw, 133 | clockset_draw, 134 | voltage_draw, 135 | }; 136 | 137 | static const unsigned mode_count = sizeof(modes) / sizeof(*modes); 138 | unsigned watch_mode = 0; 139 | 140 | 141 | //! Watchdog Timer interrupt service routine, calls back to handler functions. 142 | void __attribute__ ((interrupt(WDT_VECTOR))) 143 | watchdog_timer(void) 144 | { 145 | ucs_fast(); 146 | 147 | button_update(); 148 | 149 | // long hold advances mode 150 | if (button_long) 151 | watch_mode = (watch_mode + 1) % mode_count; 152 | 153 | modes[watch_mode](); 154 | 155 | led_off(); 156 | ucs_slow(); 157 | } 158 | -------------------------------------------------------------------------------- /firmware/memory.x: -------------------------------------------------------------------------------- 1 | /*! \file memory.x 2 | \brief Fork of linker script to reserve 2kB for dmesg. 3 | */ 4 | 5 | MEMORY { 6 | sfr : ORIGIN = 0x0000, LENGTH = 0x0010 /* END=0x0010, size 16 */ 7 | peripheral_8bit : ORIGIN = 0x0010, LENGTH = 0x00f0 /* END=0x0100, size 240 */ 8 | peripheral_16bit : ORIGIN = 0x0100, LENGTH = 0x0100 /* END=0x0200, size 256 */ 9 | bsl : ORIGIN = 0x1000, LENGTH = 0x0800 /* END=0x1800, size 2K as 4 512-byte segments */ 10 | infomem : ORIGIN = 0x1800, LENGTH = 0x0200 /* END=0x1a00, size 512 as 4 128-byte segments */ 11 | infod : ORIGIN = 0x1800, LENGTH = 0x0080 /* END=0x1880, size 128 */ 12 | infoc : ORIGIN = 0x1880, LENGTH = 0x0080 /* END=0x1900, size 128 */ 13 | infob : ORIGIN = 0x1900, LENGTH = 0x0080 /* END=0x1980, size 128 */ 14 | infoa : ORIGIN = 0x1980, LENGTH = 0x0080 /* END=0x1a00, size 128 */ 15 | /*ram (wx) : ORIGIN = 0x1c00, LENGTH = 0x0ffe /* END=0x2bfe, size 4094 */ 16 | ram (wx) : ORIGIN = 0x1c00, LENGTH = 0x0800 /* We use half of RAM for C code. */ 17 | dmesg (wx) : ORIGIN = 0x2400, LENGTH = 0x0800 /* Second half just for dmesg. */ 18 | rom (rx) : ORIGIN = 0x8000, LENGTH = 0x7f80 /* END=0xff80, size 32640 */ 19 | vectors : ORIGIN = 0xff80, LENGTH = 0x0080 /* END=0x10000, size 128 as 64 2-byte segments */ 20 | /* Remaining banks are absent */ 21 | ram2 (wx) : ORIGIN = 0x0000, LENGTH = 0x0000 22 | ram_mirror (wx) : ORIGIN = 0x0000, LENGTH = 0x0000 23 | usbram (wx) : ORIGIN = 0x0000, LENGTH = 0x0000 24 | far_rom : ORIGIN = 0x00000000, LENGTH = 0x00000000 25 | } 26 | REGION_ALIAS("REGION_TEXT", rom); 27 | REGION_ALIAS("REGION_DATA", ram); 28 | REGION_ALIAS("REGION_DMESG", dmesg); 29 | REGION_ALIAS("REGION_FAR_ROM", far_rom); 30 | PROVIDE (__info_segment_size = 0x80); 31 | PROVIDE (__infod = 0x1800); 32 | PROVIDE (__infoc = 0x1880); 33 | PROVIDE (__infob = 0x1900); 34 | PROVIDE (__infoa = 0x1980); 35 | -------------------------------------------------------------------------------- /firmware/msp430.x: -------------------------------------------------------------------------------- 1 | /* Default linker script, for normal executables */ 2 | OUTPUT_FORMAT("elf32-msp430") 3 | OUTPUT_ARCH("msp430") 4 | INCLUDE memory.x 5 | INCLUDE periph.x 6 | SECTIONS 7 | { 8 | /* Read-only sections, merged into text segment. */ 9 | .hash : { *(.hash) } 10 | .dynsym : { *(.dynsym) } 11 | .dynstr : { *(.dynstr) } 12 | .gnu.version : { *(.gnu.version) } 13 | .gnu.version_d : { *(.gnu.version_d) } 14 | .gnu.version_r : { *(.gnu.version_r) } 15 | .rel.init : { *(.rel.init) } 16 | .rela.init : { *(.rela.init) } 17 | .rel.fini : { *(.rel.fini) } 18 | .rela.fini : { *(.rela.fini) } 19 | .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } 20 | .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } 21 | .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } 22 | .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } 23 | .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } 24 | .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } 25 | .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } 26 | .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } 27 | .rel.ctors : { *(.rel.ctors) } 28 | .rela.ctors : { *(.rela.ctors) } 29 | .rel.dtors : { *(.rel.dtors) } 30 | .rela.dtors : { *(.rela.dtors) } 31 | .rel.got : { *(.rel.got) } 32 | .rela.got : { *(.rela.got) } 33 | .rel.plt : { *(.rel.plt) } 34 | .rela.plt : { *(.rela.plt) } 35 | .text : 36 | { 37 | . = ALIGN(2); 38 | KEEP(*(.init .init.*)) 39 | KEEP(*(.init0)) /* Start here after reset. */ 40 | KEEP(*(.init1)) /* User definable. */ 41 | KEEP(*(.init2)) /* Initialize stack. */ 42 | KEEP(*(.init3)) /* Initialize hardware, user definable. */ 43 | KEEP(*(.init4)) /* Copy data to .data, clear bss. */ 44 | KEEP(*(.init5)) /* User definable. */ 45 | KEEP(*(.init6)) /* C++ constructors. */ 46 | KEEP(*(.init7)) /* User definable. */ 47 | KEEP(*(.init8)) /* User definable. */ 48 | KEEP(*(.init9)) /* Call main(). */ 49 | KEEP(*(.fini9)) /* Falls into here after main(). User definable. */ 50 | KEEP(*(.fini8)) /* User definable. */ 51 | KEEP(*(.fini7)) /* User definable. */ 52 | KEEP(*(.fini6)) /* C++ destructors. */ 53 | KEEP(*(.fini5)) /* User definable. */ 54 | KEEP(*(.fini4)) /* User definable. */ 55 | KEEP(*(.fini3)) /* User definable. */ 56 | KEEP(*(.fini2)) /* User definable. */ 57 | KEEP(*(.fini1)) /* User definable. */ 58 | KEEP(*(.fini0)) /* Infinite loop after program termination. */ 59 | KEEP(*(.fini .fini.*)) 60 | . = ALIGN(2); 61 | __ctors_start = . ; 62 | KEEP(*(.ctors)) 63 | __ctors_end = . ; 64 | __dtors_start = . ; 65 | KEEP(*(.dtors)) 66 | __dtors_end = . ; 67 | . = ALIGN(2); 68 | *(.text .text.* .gnu.linkonce.t.*) 69 | . = ALIGN(2); 70 | } > REGION_TEXT 71 | .rodata : 72 | { 73 | . = ALIGN(2); 74 | *(.rodata .rodata.* .gnu.linkonce.r.*) 75 | . = ALIGN(2); 76 | } > REGION_TEXT 77 | _etext = .; /* Past last read-only (loadable) segment */ 78 | .data : 79 | { 80 | . = ALIGN(2); 81 | PROVIDE (__data_start = .) ; 82 | *(.data .data.* .gnu.linkonce.d.*) 83 | . = ALIGN(2); 84 | _edata = . ; /* Past last read-write (loadable) segment */ 85 | } > REGION_DATA AT > REGION_TEXT 86 | PROVIDE (__data_load_start = LOADADDR(.data) ); 87 | PROVIDE (__data_size = SIZEOF(.data) ); 88 | .bss : 89 | { 90 | PROVIDE (__bss_start = .) ; 91 | *(.bss .bss.*) 92 | *(COMMON) 93 | . = ALIGN(2); 94 | PROVIDE (__bss_end = .) ; 95 | } > REGION_DATA 96 | PROVIDE (__bss_size = SIZEOF(.bss) ); 97 | .noinit : 98 | { 99 | PROVIDE (__noinit_start = .) ; 100 | *(.noinit .noinit.*) 101 | . = ALIGN(2); 102 | PROVIDE (__noinit_end = .) ; 103 | } > REGION_DATA 104 | . = ALIGN(2); 105 | _end = . ; /* Past last write (loadable) segment */ 106 | .infomem : 107 | { 108 | *(.infomem) 109 | . = ALIGN(2); 110 | *(.infomem.*) 111 | } > infomem 112 | .infomemnobits : 113 | { 114 | *(.infomemnobits) 115 | . = ALIGN(2); 116 | *(.infomemnobits.*) 117 | } > infomem 118 | .infoa : 119 | { 120 | *(.infoa .infoa.*) 121 | } > infoa 122 | .infob : 123 | { 124 | *(.infob .infob.*) 125 | } > infob 126 | .infoc : 127 | { 128 | *(.infoc .infoc.*) 129 | } > infoc 130 | .infod : 131 | { 132 | *(.infod .infod.*) 133 | } > infod 134 | .vectors : 135 | { 136 | PROVIDE (__vectors_start = .) ; 137 | KEEP(*(.vectors*)) 138 | _vectors_end = . ; 139 | } > vectors 140 | .fartext : 141 | { 142 | . = ALIGN(2); 143 | *(.fartext) 144 | . = ALIGN(2); 145 | *(.fartext.*) 146 | _efartext = .; 147 | } > REGION_FAR_ROM 148 | /* Stabs for profiling information*/ 149 | .profiler 0 : { *(.profiler) } 150 | /* Stabs debugging sections. */ 151 | .stab 0 : { *(.stab) } 152 | .stabstr 0 : { *(.stabstr) } 153 | .stab.excl 0 : { *(.stab.excl) } 154 | .stab.exclstr 0 : { *(.stab.exclstr) } 155 | .stab.index 0 : { *(.stab.index) } 156 | .stab.indexstr 0 : { *(.stab.indexstr) } 157 | .comment 0 : { *(.comment) } 158 | /* DWARF debug sections. 159 | Symbols in the DWARF debugging sections are relative to the beginning 160 | of the section so we begin them at 0. */ 161 | /* DWARF 1 */ 162 | .debug 0 : { *(.debug) } 163 | .line 0 : { *(.line) } 164 | /* GNU DWARF 1 extensions */ 165 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 166 | .debug_sfnames 0 : { *(.debug_sfnames) } 167 | /* DWARF 1.1 and DWARF 2 */ 168 | .debug_aranges 0 : { *(.debug_aranges) } 169 | .debug_pubnames 0 : { *(.debug_pubnames) } 170 | /* DWARF 2 */ 171 | .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } 172 | .debug_abbrev 0 : { *(.debug_abbrev) } 173 | .debug_line 0 : { *(.debug_line) } 174 | .debug_frame 0 : { *(.debug_frame) } 175 | .debug_str 0 : { *(.debug_str) } 176 | .debug_loc 0 : { *(.debug_loc) } 177 | .debug_macinfo 0 : { *(.debug_macinfo) } 178 | /* DWARF 3 */ 179 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 180 | .debug_ranges 0 : { *(.debug_ranges) } 181 | PROVIDE (__stack = ORIGIN(ram) + LENGTH(ram)); 182 | PROVIDE (__data_start_rom = _etext); 183 | PROVIDE (__data_end_rom = _etext + SIZEOF (.data)); 184 | } 185 | -------------------------------------------------------------------------------- /firmware/periph.x: -------------------------------------------------------------------------------- 1 | __ADC10CTL0_L = 0x0740; 2 | __ADC10CTL0_H = 0x0741; 3 | __ADC10CTL0 = 0x0740; 4 | __ADC10CTL1_L = 0x0742; 5 | __ADC10CTL1_H = 0x0743; 6 | __ADC10CTL1 = 0x0742; 7 | __ADC10CTL2_L = 0x0744; 8 | __ADC10CTL2_H = 0x0745; 9 | __ADC10CTL2 = 0x0744; 10 | __ADC10LO_L = 0x0746; 11 | __ADC10LO_H = 0x0747; 12 | __ADC10LO = 0x0746; 13 | __ADC10HI_L = 0x0748; 14 | __ADC10HI_H = 0x0749; 15 | __ADC10HI = 0x0748; 16 | __ADC10MCTL0_L = 0x074A; 17 | __ADC10MCTL0_H = 0x074B; 18 | __ADC10MCTL0 = 0x074A; 19 | __ADC10MEM0_L = 0x0752; 20 | __ADC10MEM0_H = 0x0753; 21 | __ADC10MEM0 = 0x0752; 22 | __ADC10IE_L = 0x075A; 23 | __ADC10IE_H = 0x075B; 24 | __ADC10IE = 0x075A; 25 | __ADC10IFG_L = 0x075C; 26 | __ADC10IFG_H = 0x075D; 27 | __ADC10IFG = 0x075C; 28 | __ADC10IV_L = 0x075E; 29 | __ADC10IV_H = 0x075F; 30 | __ADC10IV = 0x075E; 31 | __ADC12CTL0_L = 0x0700; 32 | __ADC12CTL0_H = 0x0701; 33 | __ADC12CTL0 = 0x0700; 34 | __ADC12CTL1_L = 0x0702; 35 | __ADC12CTL1_H = 0x0703; 36 | __ADC12CTL1 = 0x0702; 37 | __ADC12CTL2_L = 0x0704; 38 | __ADC12CTL2_H = 0x0705; 39 | __ADC12CTL2 = 0x0704; 40 | __ADC12IFG_L = 0x070A; 41 | __ADC12IFG_H = 0x070B; 42 | __ADC12IFG = 0x070A; 43 | __ADC12IE_L = 0x070C; 44 | __ADC12IE_H = 0x070D; 45 | __ADC12IE = 0x070C; 46 | __ADC12IV_L = 0x070E; 47 | __ADC12IV_H = 0x070F; 48 | __ADC12IV = 0x070E; 49 | __ADC12MEM0_L = 0x0720; 50 | __ADC12MEM0_H = 0x0721; 51 | __ADC12MEM0 = 0x0720; 52 | __ADC12MEM1_L = 0x0722; 53 | __ADC12MEM1_H = 0x0723; 54 | __ADC12MEM1 = 0x0722; 55 | __ADC12MEM2_L = 0x0724; 56 | __ADC12MEM2_H = 0x0725; 57 | __ADC12MEM2 = 0x0724; 58 | __ADC12MEM3_L = 0x0726; 59 | __ADC12MEM3_H = 0x0727; 60 | __ADC12MEM3 = 0x0726; 61 | __ADC12MEM4_L = 0x0728; 62 | __ADC12MEM4_H = 0x0729; 63 | __ADC12MEM4 = 0x0728; 64 | __ADC12MEM5_L = 0x072A; 65 | __ADC12MEM5_H = 0x072B; 66 | __ADC12MEM5 = 0x072A; 67 | __ADC12MEM6_L = 0x072C; 68 | __ADC12MEM6_H = 0x072D; 69 | __ADC12MEM6 = 0x072C; 70 | __ADC12MEM7_L = 0x072E; 71 | __ADC12MEM7_H = 0x072F; 72 | __ADC12MEM7 = 0x072E; 73 | __ADC12MEM8_L = 0x0730; 74 | __ADC12MEM8_H = 0x0731; 75 | __ADC12MEM8 = 0x0730; 76 | __ADC12MEM9_L = 0x0732; 77 | __ADC12MEM9_H = 0x0733; 78 | __ADC12MEM9 = 0x0732; 79 | __ADC12MEM10_L = 0x0734; 80 | __ADC12MEM10_H = 0x0735; 81 | __ADC12MEM10 = 0x0734; 82 | __ADC12MEM11_L = 0x0736; 83 | __ADC12MEM11_H = 0x0737; 84 | __ADC12MEM11 = 0x0736; 85 | __ADC12MEM12_L = 0x0738; 86 | __ADC12MEM12_H = 0x0739; 87 | __ADC12MEM12 = 0x0738; 88 | __ADC12MEM13_L = 0x073A; 89 | __ADC12MEM13_H = 0x073B; 90 | __ADC12MEM13 = 0x073A; 91 | __ADC12MEM14_L = 0x073C; 92 | __ADC12MEM14_H = 0x073D; 93 | __ADC12MEM14 = 0x073C; 94 | __ADC12MEM15_L = 0x073E; 95 | __ADC12MEM15_H = 0x073F; 96 | __ADC12MEM15 = 0x073E; 97 | __ADC12MCTL0 = 0x0710; 98 | __ADC12MCTL1 = 0x0711; 99 | __ADC12MCTL2 = 0x0712; 100 | __ADC12MCTL3 = 0x0713; 101 | __ADC12MCTL4 = 0x0714; 102 | __ADC12MCTL5 = 0x0715; 103 | __ADC12MCTL6 = 0x0716; 104 | __ADC12MCTL7 = 0x0717; 105 | __ADC12MCTL8 = 0x0718; 106 | __ADC12MCTL9 = 0x0719; 107 | __ADC12MCTL10 = 0x071A; 108 | __ADC12MCTL11 = 0x071B; 109 | __ADC12MCTL12 = 0x071C; 110 | __ADC12MCTL13 = 0x071D; 111 | __ADC12MCTL14 = 0x071E; 112 | __ADC12MCTL15 = 0x071F; 113 | __AESACTL0_L = 0x09C0; 114 | __AESACTL0_H = 0x09C1; 115 | __AESACTL0 = 0x09C0; 116 | __AESASTAT_L = 0x09C4; 117 | __AESASTAT_H = 0x09C5; 118 | __AESASTAT = 0x09C4; 119 | __AESAKEY_L = 0x09C6; 120 | __AESAKEY_H = 0x09C7; 121 | __AESAKEY = 0x09C6; 122 | __AESADIN_L = 0x09C8; 123 | __AESADIN_H = 0x09C9; 124 | __AESADIN = 0x09C8; 125 | __AESADOUT_L = 0x09CA; 126 | __AESADOUT_H = 0x09CB; 127 | __AESADOUT = 0x09CA; 128 | __CBCTL0_L = 0x08C0; 129 | __CBCTL0_H = 0x08C1; 130 | __CBCTL0 = 0x08C0; 131 | __CBCTL1_L = 0x08C2; 132 | __CBCTL1_H = 0x08C3; 133 | __CBCTL1 = 0x08C2; 134 | __CBCTL2_L = 0x08C4; 135 | __CBCTL2_H = 0x08C5; 136 | __CBCTL2 = 0x08C4; 137 | __CBCTL3_L = 0x08C6; 138 | __CBCTL3_H = 0x08C7; 139 | __CBCTL3 = 0x08C6; 140 | __CBINT_L = 0x08CC; 141 | __CBINT_H = 0x08CD; 142 | __CBINT = 0x08CC; 143 | __CBIV = 0x08CE; 144 | __RF1AIFCTL0_L = 0x0F00; 145 | __RF1AIFCTL0_H = 0x0F01; 146 | __RF1AIFCTL0 = 0x0F00; 147 | __RF1AIFCTL1_L = 0x0F02; 148 | __RF1AIFCTL1_H = 0x0F03; 149 | __RF1AIFCTL1 = 0x0F02; 150 | __RF1AIFCTL2_L = 0x0F04; 151 | __RF1AIFCTL2_H = 0x0F05; 152 | __RF1AIFCTL2 = 0x0F04; 153 | __RF1AIFERR_L = 0x0F06; 154 | __RF1AIFERR_H = 0x0F07; 155 | __RF1AIFERR = 0x0F06; 156 | __RF1AIFERRV_L = 0x0F0C; 157 | __RF1AIFERRV_H = 0x0F0D; 158 | __RF1AIFERRV = 0x0F0C; 159 | __RF1AIFIV_L = 0x0F0E; 160 | __RF1AIFIV_H = 0x0F0F; 161 | __RF1AIFIV = 0x0F0E; 162 | __RF1AINSTRW_L = 0x0F10; 163 | __RF1AINSTRW_H = 0x0F11; 164 | __RF1AINSTRW = 0x0F10; 165 | __RF1AINSTR1W_L = 0x0F12; 166 | __RF1AINSTR1W_H = 0x0F13; 167 | __RF1AINSTR1W = 0x0F12; 168 | __RF1AINSTR2W_L = 0x0F14; 169 | __RF1AINSTR2W_H = 0x0F15; 170 | __RF1AINSTR2W = 0x0F14; 171 | __RF1ADINW_L = 0x0F16; 172 | __RF1ADINW_H = 0x0F17; 173 | __RF1ADINW = 0x0F16; 174 | __RF1ASTAT0W_L = 0x0F20; 175 | __RF1ASTAT0W_H = 0x0F21; 176 | __RF1ASTAT0W = 0x0F20; 177 | __RF1ASTAT1W_L = 0x0F22; 178 | __RF1ASTAT1W_H = 0x0F23; 179 | __RF1ASTAT1W = 0x0F22; 180 | __RF1ASTAT2W_L = 0x0F24; 181 | __RF1ASTAT2W_H = 0x0F25; 182 | __RF1ASTAT2W = 0x0F24; 183 | __RF1ADOUT0W_L = 0x0F28; 184 | __RF1ADOUT0W_H = 0x0F29; 185 | __RF1ADOUT0W = 0x0F28; 186 | __RF1ADOUT1W_L = 0x0F2A; 187 | __RF1ADOUT1W_H = 0x0F2B; 188 | __RF1ADOUT1W = 0x0F2A; 189 | __RF1ADOUT2W_L = 0x0F2C; 190 | __RF1ADOUT2W_H = 0x0F2D; 191 | __RF1ADOUT2W = 0x0F2C; 192 | __RF1AIN_L = 0x0F30; 193 | __RF1AIN_H = 0x0F31; 194 | __RF1AIN = 0x0F30; 195 | __RF1AIFG_L = 0x0F32; 196 | __RF1AIFG_H = 0x0F33; 197 | __RF1AIFG = 0x0F32; 198 | __RF1AIES_L = 0x0F34; 199 | __RF1AIES_H = 0x0F35; 200 | __RF1AIES = 0x0F34; 201 | __RF1AIE_L = 0x0F36; 202 | __RF1AIE_H = 0x0F37; 203 | __RF1AIE = 0x0F36; 204 | __RF1AIV_L = 0x0F38; 205 | __RF1AIV_H = 0x0F39; 206 | __RF1AIV = 0x0F38; 207 | __RF1ARXFIFO_L = 0x0F3C; 208 | __RF1ARXFIFO_H = 0x0F3D; 209 | __RF1ARXFIFO = 0x0F3C; 210 | __RF1ATXFIFO_L = 0x0F3E; 211 | __RF1ATXFIFO_H = 0x0F3F; 212 | __RF1ATXFIFO = 0x0F3E; 213 | __CRCDI_L = 0x0150; 214 | __CRCDI_H = 0x0151; 215 | __CRCDI = 0x0150; 216 | __CRCDIRB_L = 0x0152; 217 | __CRCDIRB_H = 0x0153; 218 | __CRCDIRB = 0x0152; 219 | __CRCINIRES_L = 0x0154; 220 | __CRCINIRES_H = 0x0155; 221 | __CRCINIRES = 0x0154; 222 | __CRCRESR_L = 0x0156; 223 | __CRCRESR_H = 0x0157; 224 | __CRCRESR = 0x0156; 225 | __DMACTL0_L = 0x0500; 226 | __DMACTL0_H = 0x0501; 227 | __DMACTL0 = 0x0500; 228 | __DMACTL1_L = 0x0502; 229 | __DMACTL1_H = 0x0503; 230 | __DMACTL1 = 0x0502; 231 | __DMACTL2_L = 0x0504; 232 | __DMACTL2_H = 0x0505; 233 | __DMACTL2 = 0x0504; 234 | __DMACTL3_L = 0x0506; 235 | __DMACTL3_H = 0x0507; 236 | __DMACTL3 = 0x0506; 237 | __DMACTL4_L = 0x0508; 238 | __DMACTL4_H = 0x0509; 239 | __DMACTL4 = 0x0508; 240 | __DMAIV_L = 0x050E; 241 | __DMAIV_H = 0x050F; 242 | __DMAIV = 0x050E; 243 | __DMA0CTL_L = 0x0510; 244 | __DMA0CTL_H = 0x0511; 245 | __DMA0CTL = 0x0510; 246 | __DMA0SA = 0x0512; 247 | __DMA0DA = 0x0516; 248 | __DMA0SZ = 0x051A; 249 | __DMA1CTL_L = 0x0520; 250 | __DMA1CTL_H = 0x0521; 251 | __DMA1CTL = 0x0520; 252 | __DMA1SA = 0x0522; 253 | __DMA1DA = 0x0526; 254 | __DMA1SZ = 0x052A; 255 | __DMA2CTL_L = 0x0530; 256 | __DMA2CTL_H = 0x0531; 257 | __DMA2CTL = 0x0530; 258 | __DMA2SA = 0x0532; 259 | __DMA2DA = 0x0536; 260 | __DMA2SZ = 0x053A; 261 | __FCTL1_L = 0x0140; 262 | __FCTL1_H = 0x0141; 263 | __FCTL1 = 0x0140; 264 | __FCTL3_L = 0x0144; 265 | __FCTL3_H = 0x0145; 266 | __FCTL3 = 0x0144; 267 | __FCTL4_L = 0x0146; 268 | __FCTL4_H = 0x0147; 269 | __FCTL4 = 0x0146; 270 | __LCDBCTL0_L = 0x0A00; 271 | __LCDBCTL0_H = 0x0A01; 272 | __LCDBCTL0 = 0x0A00; 273 | __LCDBCTL1_L = 0x0A02; 274 | __LCDBCTL1_H = 0x0A03; 275 | __LCDBCTL1 = 0x0A02; 276 | __LCDBBLKCTL_L = 0x0A04; 277 | __LCDBBLKCTL_H = 0x0A05; 278 | __LCDBBLKCTL = 0x0A04; 279 | __LCDBMEMCTL_L = 0x0A06; 280 | __LCDBMEMCTL_H = 0x0A07; 281 | __LCDBMEMCTL = 0x0A06; 282 | __LCDBVCTL_L = 0x0A08; 283 | __LCDBVCTL_H = 0x0A09; 284 | __LCDBVCTL = 0x0A08; 285 | __LCDBPCTL0_L = 0x0A0A; 286 | __LCDBPCTL0_H = 0x0A0B; 287 | __LCDBPCTL0 = 0x0A0A; 288 | __LCDBPCTL1_L = 0x0A0C; 289 | __LCDBPCTL1_H = 0x0A0D; 290 | __LCDBPCTL1 = 0x0A0C; 291 | __LCDBPCTL2_L = 0x0A0E; 292 | __LCDBPCTL2_H = 0x0A0F; 293 | __LCDBPCTL2 = 0x0A0E; 294 | __LCDBPCTL3_L = 0x0A10; 295 | __LCDBPCTL3_H = 0x0A11; 296 | __LCDBPCTL3 = 0x0A10; 297 | __LCDBCPCTL_L = 0x0A12; 298 | __LCDBCPCTL_H = 0x0A13; 299 | __LCDBCPCTL = 0x0A12; 300 | __LCDBIV = 0x0A1E; 301 | __LCDM1 = 0x0A20; 302 | __LCDM2 = 0x0A21; 303 | __LCDM3 = 0x0A22; 304 | __LCDM4 = 0x0A23; 305 | __LCDM5 = 0x0A24; 306 | __LCDM6 = 0x0A25; 307 | __LCDM7 = 0x0A26; 308 | __LCDM8 = 0x0A27; 309 | __LCDM9 = 0x0A28; 310 | __LCDM10 = 0x0A29; 311 | __LCDM11 = 0x0A2A; 312 | __LCDM12 = 0x0A2B; 313 | __LCDM13 = 0x0A2C; 314 | __LCDM14 = 0x0A2D; 315 | __LCDM15 = 0x0A2E; 316 | __LCDM16 = 0x0A2F; 317 | __LCDM17 = 0x0A30; 318 | __LCDM18 = 0x0A31; 319 | __LCDM19 = 0x0A32; 320 | __LCDM20 = 0x0A33; 321 | __LCDM21 = 0x0A34; 322 | __LCDM22 = 0x0A35; 323 | __LCDM23 = 0x0A36; 324 | __LCDM24 = 0x0A37; 325 | __LCDBM1 = 0x0A40; 326 | __LCDBM2 = 0x0A41; 327 | __LCDBM3 = 0x0A42; 328 | __LCDBM4 = 0x0A43; 329 | __LCDBM5 = 0x0A44; 330 | __LCDBM6 = 0x0A45; 331 | __LCDBM7 = 0x0A46; 332 | __LCDBM8 = 0x0A47; 333 | __LCDBM9 = 0x0A48; 334 | __LCDBM10 = 0x0A49; 335 | __LCDBM11 = 0x0A4A; 336 | __LCDBM12 = 0x0A4B; 337 | __LCDBM13 = 0x0A4C; 338 | __LCDBM14 = 0x0A4D; 339 | __LCDBM15 = 0x0A4E; 340 | __LCDBM16 = 0x0A4F; 341 | __LCDBM17 = 0x0A50; 342 | __LCDBM18 = 0x0A51; 343 | __LCDBM19 = 0x0A52; 344 | __LCDBM20 = 0x0A53; 345 | __LCDBM21 = 0x0A54; 346 | __LCDBM22 = 0x0A55; 347 | __LCDBM23 = 0x0A56; 348 | __LCDBM24 = 0x0A57; 349 | __MPY_L = 0x04C0; 350 | __MPY_H = 0x04C1; 351 | __MPY = 0x04C0; 352 | __MPYS_L = 0x04C2; 353 | __MPYS_H = 0x04C3; 354 | __MPYS = 0x04C2; 355 | __MAC_L = 0x04C4; 356 | __MAC_H = 0x04C5; 357 | __MAC = 0x04C4; 358 | __MACS_L = 0x04C6; 359 | __MACS_H = 0x04C7; 360 | __MACS = 0x04C6; 361 | __OP2_L = 0x04C8; 362 | __OP2_H = 0x04C9; 363 | __OP2 = 0x04C8; 364 | __RESLO_L = 0x04CA; 365 | __RESLO_H = 0x04CB; 366 | __RESLO = 0x04CA; 367 | __RESHI_L = 0x04CC; 368 | __RESHI_H = 0x04CD; 369 | __RESHI = 0x04CC; 370 | __SUMEXT_L = 0x04CE; 371 | __SUMEXT_H = 0x04CF; 372 | __SUMEXT = 0x04CE; 373 | __MPY32L_L = 0x04D0; 374 | __MPY32L_H = 0x04D1; 375 | __MPY32L = 0x04D0; 376 | __MPY32H_L = 0x04D2; 377 | __MPY32H_H = 0x04D3; 378 | __MPY32H = 0x04D2; 379 | __MPYS32L_L = 0x04D4; 380 | __MPYS32L_H = 0x04D5; 381 | __MPYS32L = 0x04D4; 382 | __MPYS32H_L = 0x04D6; 383 | __MPYS32H_H = 0x04D7; 384 | __MPYS32H = 0x04D6; 385 | __MAC32L_L = 0x04D8; 386 | __MAC32L_H = 0x04D9; 387 | __MAC32L = 0x04D8; 388 | __MAC32H_L = 0x04DA; 389 | __MAC32H_H = 0x04DB; 390 | __MAC32H = 0x04DA; 391 | __MACS32L_L = 0x04DC; 392 | __MACS32L_H = 0x04DD; 393 | __MACS32L = 0x04DC; 394 | __MACS32H_L = 0x04DE; 395 | __MACS32H_H = 0x04DF; 396 | __MACS32H = 0x04DE; 397 | __OP2L_L = 0x04E0; 398 | __OP2L_H = 0x04E1; 399 | __OP2L = 0x04E0; 400 | __OP2H_L = 0x04E2; 401 | __OP2H_H = 0x04E3; 402 | __OP2H = 0x04E2; 403 | __RES0_L = 0x04E4; 404 | __RES0_H = 0x04E5; 405 | __RES0 = 0x04E4; 406 | __RES1_L = 0x04E6; 407 | __RES1_H = 0x04E7; 408 | __RES1 = 0x04E6; 409 | __RES2_L = 0x04E8; 410 | __RES2_H = 0x04E9; 411 | __RES2 = 0x04E8; 412 | __RES3_L = 0x04EA; 413 | __RES3_H = 0x04EB; 414 | __RES3 = 0x04EA; 415 | __MPY32CTL0_L = 0x04EC; 416 | __MPY32CTL0_H = 0x04ED; 417 | __MPY32CTL0 = 0x04EC; 418 | __PAIN_L = 0x0200; 419 | __PAIN_H = 0x0201; 420 | __PAIN = 0x0200; 421 | __PAOUT_L = 0x0202; 422 | __PAOUT_H = 0x0203; 423 | __PAOUT = 0x0202; 424 | __PADIR_L = 0x0204; 425 | __PADIR_H = 0x0205; 426 | __PADIR = 0x0204; 427 | __PAREN_L = 0x0206; 428 | __PAREN_H = 0x0207; 429 | __PAREN = 0x0206; 430 | __PADS_L = 0x0208; 431 | __PADS_H = 0x0209; 432 | __PADS = 0x0208; 433 | __PASEL_L = 0x020A; 434 | __PASEL_H = 0x020B; 435 | __PASEL = 0x020A; 436 | __PAIES_L = 0x0218; 437 | __PAIES_H = 0x0219; 438 | __PAIES = 0x0218; 439 | __PAIE_L = 0x021A; 440 | __PAIE_H = 0x021B; 441 | __PAIE = 0x021A; 442 | __PAIFG_L = 0x021C; 443 | __PAIFG_H = 0x021D; 444 | __PAIFG = 0x021C; 445 | __P1IV = 0x020E; 446 | __P2IV = 0x021E; 447 | __PBIN_L = 0x0220; 448 | __PBIN_H = 0x0221; 449 | __PBIN = 0x0220; 450 | __PBOUT_L = 0x0222; 451 | __PBOUT_H = 0x0223; 452 | __PBOUT = 0x0222; 453 | __PBDIR_L = 0x0224; 454 | __PBDIR_H = 0x0225; 455 | __PBDIR = 0x0224; 456 | __PBREN_L = 0x0226; 457 | __PBREN_H = 0x0227; 458 | __PBREN = 0x0226; 459 | __PBDS_L = 0x0228; 460 | __PBDS_H = 0x0229; 461 | __PBDS = 0x0228; 462 | __PBSEL_L = 0x022A; 463 | __PBSEL_H = 0x022B; 464 | __PBSEL = 0x022A; 465 | __PCIN_L = 0x0240; 466 | __PCIN_H = 0x0241; 467 | __PCIN = 0x0240; 468 | __PCOUT_L = 0x0242; 469 | __PCOUT_H = 0x0243; 470 | __PCOUT = 0x0242; 471 | __PCDIR_L = 0x0244; 472 | __PCDIR_H = 0x0245; 473 | __PCDIR = 0x0244; 474 | __PCREN_L = 0x0246; 475 | __PCREN_H = 0x0247; 476 | __PCREN = 0x0246; 477 | __PCDS_L = 0x0248; 478 | __PCDS_H = 0x0249; 479 | __PCDS = 0x0248; 480 | __PCSEL_L = 0x024A; 481 | __PCSEL_H = 0x024B; 482 | __PCSEL = 0x024A; 483 | __PJIN_L = 0x0320; 484 | __PJIN_H = 0x0321; 485 | __PJIN = 0x0320; 486 | __PJOUT_L = 0x0322; 487 | __PJOUT_H = 0x0323; 488 | __PJOUT = 0x0322; 489 | __PJDIR_L = 0x0324; 490 | __PJDIR_H = 0x0325; 491 | __PJDIR = 0x0324; 492 | __PJREN_L = 0x0326; 493 | __PJREN_H = 0x0327; 494 | __PJREN = 0x0326; 495 | __PJDS_L = 0x0328; 496 | __PJDS_H = 0x0329; 497 | __PJDS = 0x0328; 498 | __PMAPKEYID_L = 0x01C0; 499 | __PMAPKEYID_H = 0x01C1; 500 | __PMAPKEYID = 0x01C0; 501 | __PMAPCTL_L = 0x01C2; 502 | __PMAPCTL_H = 0x01C3; 503 | __PMAPCTL = 0x01C2; 504 | __P1MAP01_L = 0x01C8; 505 | __P1MAP01_H = 0x01C9; 506 | __P1MAP01 = 0x01C8; 507 | __P1MAP23_L = 0x01CA; 508 | __P1MAP23_H = 0x01CB; 509 | __P1MAP23 = 0x01CA; 510 | __P1MAP45_L = 0x01CC; 511 | __P1MAP45_H = 0x01CD; 512 | __P1MAP45 = 0x01CC; 513 | __P1MAP67_L = 0x01CE; 514 | __P1MAP67_H = 0x01CF; 515 | __P1MAP67 = 0x01CE; 516 | __P2MAP01_L = 0x01D0; 517 | __P2MAP01_H = 0x01D1; 518 | __P2MAP01 = 0x01D0; 519 | __P2MAP23_L = 0x01D2; 520 | __P2MAP23_H = 0x01D3; 521 | __P2MAP23 = 0x01D2; 522 | __P2MAP45_L = 0x01D4; 523 | __P2MAP45_H = 0x01D5; 524 | __P2MAP45 = 0x01D4; 525 | __P2MAP67_L = 0x01D6; 526 | __P2MAP67_H = 0x01D7; 527 | __P2MAP67 = 0x01D6; 528 | __P3MAP01_L = 0x01D8; 529 | __P3MAP01_H = 0x01D9; 530 | __P3MAP01 = 0x01D8; 531 | __P3MAP23_L = 0x01DA; 532 | __P3MAP23_H = 0x01DB; 533 | __P3MAP23 = 0x01DA; 534 | __P3MAP45_L = 0x01DC; 535 | __P3MAP45_H = 0x01DD; 536 | __P3MAP45 = 0x01DC; 537 | __P3MAP67_L = 0x01DE; 538 | __P3MAP67_H = 0x01DF; 539 | __P3MAP67 = 0x01DE; 540 | __PMMCTL0_L = 0x0120; 541 | __PMMCTL0_H = 0x0121; 542 | __PMMCTL0 = 0x0120; 543 | __PMMCTL1_L = 0x0122; 544 | __PMMCTL1_H = 0x0123; 545 | __PMMCTL1 = 0x0122; 546 | __SVSMHCTL_L = 0x0124; 547 | __SVSMHCTL_H = 0x0125; 548 | __SVSMHCTL = 0x0124; 549 | __SVSMLCTL_L = 0x0126; 550 | __SVSMLCTL_H = 0x0127; 551 | __SVSMLCTL = 0x0126; 552 | __SVSMIO_L = 0x0128; 553 | __SVSMIO_H = 0x0129; 554 | __SVSMIO = 0x0128; 555 | __PMMIFG_L = 0x012C; 556 | __PMMIFG_H = 0x012D; 557 | __PMMIFG = 0x012C; 558 | __PMMRIE_L = 0x012E; 559 | __PMMRIE_H = 0x012F; 560 | __PMMRIE = 0x012E; 561 | __RCCTL0_L = 0x0158; 562 | __RCCTL0_H = 0x0159; 563 | __RCCTL0 = 0x0158; 564 | __REFCTL0_L = 0x01B0; 565 | __REFCTL0_H = 0x01B1; 566 | __REFCTL0 = 0x01B0; 567 | __RTCCTL01_L = 0x04A0; 568 | __RTCCTL01_H = 0x04A1; 569 | __RTCCTL01 = 0x04A0; 570 | __RTCCTL23_L = 0x04A2; 571 | __RTCCTL23_H = 0x04A3; 572 | __RTCCTL23 = 0x04A2; 573 | __RTCPS0CTL_L = 0x04A8; 574 | __RTCPS0CTL_H = 0x04A9; 575 | __RTCPS0CTL = 0x04A8; 576 | __RTCPS1CTL_L = 0x04AA; 577 | __RTCPS1CTL_H = 0x04AB; 578 | __RTCPS1CTL = 0x04AA; 579 | __RTCPS_L = 0x04AC; 580 | __RTCPS_H = 0x04AD; 581 | __RTCPS = 0x04AC; 582 | __RTCIV = 0x04AE; 583 | __RTCTIM0_L = 0x04B0; 584 | __RTCTIM0_H = 0x04B1; 585 | __RTCTIM0 = 0x04B0; 586 | __RTCTIM1_L = 0x04B2; 587 | __RTCTIM1_H = 0x04B3; 588 | __RTCTIM1 = 0x04B2; 589 | __RTCDATE_L = 0x04B4; 590 | __RTCDATE_H = 0x04B5; 591 | __RTCDATE = 0x04B4; 592 | __RTCYEAR_L = 0x04B6; 593 | __RTCYEAR_H = 0x04B7; 594 | __RTCYEAR = 0x04B6; 595 | __RTCAMINHR_L = 0x04B8; 596 | __RTCAMINHR_H = 0x04B9; 597 | __RTCAMINHR = 0x04B8; 598 | __RTCADOWDAY_L = 0x04BA; 599 | __RTCADOWDAY_H = 0x04BB; 600 | __RTCADOWDAY = 0x04BA; 601 | __SFRIE1_L = 0x0100; 602 | __SFRIE1_H = 0x0101; 603 | __SFRIE1 = 0x0100; 604 | __SFRIFG1_L = 0x0102; 605 | __SFRIFG1_H = 0x0103; 606 | __SFRIFG1 = 0x0102; 607 | __SFRRPCR_L = 0x0104; 608 | __SFRRPCR_H = 0x0105; 609 | __SFRRPCR = 0x0104; 610 | __SYSCTL_L = 0x0180; 611 | __SYSCTL_H = 0x0181; 612 | __SYSCTL = 0x0180; 613 | __SYSBSLC_L = 0x0182; 614 | __SYSBSLC_H = 0x0183; 615 | __SYSBSLC = 0x0182; 616 | __SYSJMBC_L = 0x0186; 617 | __SYSJMBC_H = 0x0187; 618 | __SYSJMBC = 0x0186; 619 | __SYSJMBI0_L = 0x0188; 620 | __SYSJMBI0_H = 0x0189; 621 | __SYSJMBI0 = 0x0188; 622 | __SYSJMBI1_L = 0x018A; 623 | __SYSJMBI1_H = 0x018B; 624 | __SYSJMBI1 = 0x018A; 625 | __SYSJMBO0_L = 0x018C; 626 | __SYSJMBO0_H = 0x018D; 627 | __SYSJMBO0 = 0x018C; 628 | __SYSJMBO1_L = 0x018E; 629 | __SYSJMBO1_H = 0x018F; 630 | __SYSJMBO1 = 0x018E; 631 | __SYSBERRIV_L = 0x0198; 632 | __SYSBERRIV_H = 0x0199; 633 | __SYSBERRIV = 0x0198; 634 | __SYSUNIV_L = 0x019A; 635 | __SYSUNIV_H = 0x019B; 636 | __SYSUNIV = 0x019A; 637 | __SYSSNIV_L = 0x019C; 638 | __SYSSNIV_H = 0x019D; 639 | __SYSSNIV = 0x019C; 640 | __SYSRSTIV_L = 0x019E; 641 | __SYSRSTIV_H = 0x019F; 642 | __SYSRSTIV = 0x019E; 643 | __TA0CTL = 0x0340; 644 | __TA0CCTL0 = 0x0342; 645 | __TA0CCTL1 = 0x0344; 646 | __TA0CCTL2 = 0x0346; 647 | __TA0CCTL3 = 0x0348; 648 | __TA0CCTL4 = 0x034A; 649 | __TA0R = 0x0350; 650 | __TA0CCR0 = 0x0352; 651 | __TA0CCR1 = 0x0354; 652 | __TA0CCR2 = 0x0356; 653 | __TA0CCR3 = 0x0358; 654 | __TA0CCR4 = 0x035A; 655 | __TA0IV = 0x036E; 656 | __TA0EX0 = 0x0360; 657 | __TA1CTL = 0x0380; 658 | __TA1CCTL0 = 0x0382; 659 | __TA1CCTL1 = 0x0384; 660 | __TA1CCTL2 = 0x0386; 661 | __TA1R = 0x0390; 662 | __TA1CCR0 = 0x0392; 663 | __TA1CCR1 = 0x0394; 664 | __TA1CCR2 = 0x0396; 665 | __TA1IV = 0x03AE; 666 | __TA1EX0 = 0x03A0; 667 | __UCSCTL0_L = 0x0160; 668 | __UCSCTL0_H = 0x0161; 669 | __UCSCTL0 = 0x0160; 670 | __UCSCTL1_L = 0x0162; 671 | __UCSCTL1_H = 0x0163; 672 | __UCSCTL1 = 0x0162; 673 | __UCSCTL2_L = 0x0164; 674 | __UCSCTL2_H = 0x0165; 675 | __UCSCTL2 = 0x0164; 676 | __UCSCTL3_L = 0x0166; 677 | __UCSCTL3_H = 0x0167; 678 | __UCSCTL3 = 0x0166; 679 | __UCSCTL4_L = 0x0168; 680 | __UCSCTL4_H = 0x0169; 681 | __UCSCTL4 = 0x0168; 682 | __UCSCTL5_L = 0x016A; 683 | __UCSCTL5_H = 0x016B; 684 | __UCSCTL5 = 0x016A; 685 | __UCSCTL6_L = 0x016C; 686 | __UCSCTL6_H = 0x016D; 687 | __UCSCTL6 = 0x016C; 688 | __UCSCTL7_L = 0x016E; 689 | __UCSCTL7_H = 0x016F; 690 | __UCSCTL7 = 0x016E; 691 | __UCSCTL8_L = 0x0170; 692 | __UCSCTL8_H = 0x0171; 693 | __UCSCTL8 = 0x0170; 694 | __UCA0CTLW0_L = 0x05C0; 695 | __UCA0CTLW0_H = 0x05C1; 696 | __UCA0CTLW0 = 0x05C0; 697 | __UCA0BRW_L = 0x05C6; 698 | __UCA0BRW_H = 0x05C7; 699 | __UCA0BRW = 0x05C6; 700 | __UCA0MCTL = 0x05C8; 701 | __UCA0STAT = 0x05CA; 702 | __UCA0RXBUF = 0x05CC; 703 | __UCA0TXBUF = 0x05CE; 704 | __UCA0ABCTL = 0x05D0; 705 | __UCA0IRCTL_L = 0x05D2; 706 | __UCA0IRCTL_H = 0x05D3; 707 | __UCA0IRCTL = 0x05D2; 708 | __UCA0ICTL_L = 0x05DC; 709 | __UCA0ICTL_H = 0x05DD; 710 | __UCA0ICTL = 0x05DC; 711 | __UCA0IV = 0x05DE; 712 | __UCB0CTLW0_L = 0x05E0; 713 | __UCB0CTLW0_H = 0x05E1; 714 | __UCB0CTLW0 = 0x05E0; 715 | __UCB0BRW_L = 0x05E6; 716 | __UCB0BRW_H = 0x05E7; 717 | __UCB0BRW = 0x05E6; 718 | __UCB0STAT = 0x05EA; 719 | __UCB0RXBUF = 0x05EC; 720 | __UCB0TXBUF = 0x05EE; 721 | __UCB0I2COA_L = 0x05F0; 722 | __UCB0I2COA_H = 0x05F1; 723 | __UCB0I2COA = 0x05F0; 724 | __UCB0I2CSA_L = 0x05F2; 725 | __UCB0I2CSA_H = 0x05F3; 726 | __UCB0I2CSA = 0x05F2; 727 | __UCB0ICTL_L = 0x05FC; 728 | __UCB0ICTL_H = 0x05FD; 729 | __UCB0ICTL = 0x05FC; 730 | __UCB0IV = 0x05FE; 731 | __WDTCTL_L = 0x015C; 732 | __WDTCTL_H = 0x015D; 733 | __WDTCTL = 0x015C; 734 | -------------------------------------------------------------------------------- /firmware/power.c: -------------------------------------------------------------------------------- 1 | /*! \file power.c 2 | \brief Power management library for the CC430F6137. 3 | 4 | These library functions are responsible for powering up the CC430 5 | when necessary for the radio, and for dropping power at all other 6 | times. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | //! Returns non-zero if in anything but the lowest power mode. 13 | int power_ishigh(){ 14 | return 15 | ( PMMCTL0_L & PMMHPMRE_L) //High power mode. 16 | || (PMMCTL0 & PMMCOREV_3) //VCore in anything but 0 mode. 17 | ; 18 | } 19 | 20 | //! Step the core voltage up 21 | static unsigned int power_vcoreup (unsigned char level) { 22 | unsigned int PMMRIE_backup,SVSMHCTL_backup; 23 | 24 | // Open PMM registers for write access 25 | PMMCTL0_H = 0xA5; 26 | 27 | // Disable dedicated Interrupts to prevent that needed flags will be 28 | // cleared 29 | PMMRIE_backup = PMMRIE; 30 | PMMRIE &= ~(SVSMHDLYIE | SVSMLDLYIE | SVMLVLRIE | SVMHVLRIE | SVMHVLRPE); 31 | // Set SVM highside to new level and check if a VCore increase is 32 | // possible 33 | SVSMHCTL_backup = SVSMHCTL; 34 | PMMIFG &= ~(SVMHIFG | SVSMHDLYIFG); 35 | SVSMHCTL = SVMHE | SVMHFP | (SVSMHRRL0 * level); 36 | // Wait until SVM highside is settled 37 | while ((PMMIFG & SVSMHDLYIFG) == 0); 38 | // Disable full-performance mode to save energy 39 | SVSMHCTL &= ~SVSLFP ; 40 | 41 | // Check if a VCore increase is possible 42 | if ((PMMIFG & SVMHIFG) == SVMHIFG){ 43 | // Vcc is too low for a Vcore increase, so recover the previous 44 | // settings. 45 | 46 | //printf("VCC too low to raise core.\n"); 47 | 48 | PMMIFG &= ~SVSMHDLYIFG; 49 | SVSMHCTL = SVSMHCTL_backup; 50 | // Wait until SVM highside is settled 51 | while ((PMMIFG & SVSMHDLYIFG) == 0); 52 | // Clear all Flags 53 | PMMIFG &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG 54 | | SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG); 55 | // backup PMM-Interrupt-Register 56 | PMMRIE = PMMRIE_backup; 57 | 58 | // Lock PMM registers for write access 59 | PMMCTL0_H = 0x00; 60 | return 0;//Not set. 61 | } 62 | 63 | // Set also SVS highside to new level 64 | //-> Vcc is high enough for a Vcore increase 65 | SVSMHCTL |= SVSHE | (SVSHRVL0 * level); 66 | // Set SVM low side to new level 67 | SVSMLCTL = SVMLE | SVMLFP | (SVSMLRRL0 * level); 68 | // Wait until SVM low side is settled 69 | while ((PMMIFG & SVSMLDLYIFG) == 0); 70 | // Clear already set flags 71 | PMMIFG &= ~(SVMLVLRIFG | SVMLIFG); 72 | // Set VCore to new level 73 | PMMCTL0_L = PMMCOREV0 * level; 74 | // Wait until new level reached 75 | if (PMMIFG & SVMLIFG) 76 | while ((PMMIFG & SVMLVLRIFG) == 0); 77 | // Set also SVS/SVM low side to new level 78 | PMMIFG &= ~SVSMLDLYIFG; 79 | SVSMLCTL |= SVSLE | (SVSLRVL0 * level); 80 | // wait for lowside delay flags 81 | while ((PMMIFG & SVSMLDLYIFG) == 0); 82 | 83 | // Disable SVS/SVM Low 84 | // Disable full-performance mode to save energy 85 | SVSMLCTL &= ~(SVSLE+SVMLE+SVSLFP ); 86 | 87 | // Clear all Flags 88 | PMMIFG &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG 89 | | SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG); 90 | // backup PMM-Interrupt-Register 91 | PMMRIE = PMMRIE_backup; 92 | 93 | // Lock PMM registers for write access 94 | PMMCTL0_H = 0x00; 95 | return 1;//okay 96 | } 97 | 98 | //! Step the voltage core down. 99 | static unsigned int power_vcoredown (unsigned char level) { 100 | unsigned int PMMRIE_backup; 101 | 102 | // Open PMM registers for write access 103 | PMMCTL0_H = 0xA5; 104 | 105 | // Disable dedicated Interrupts to prevent that needed flags will be cleared 106 | PMMRIE_backup = PMMRIE; 107 | PMMRIE &= ~(SVSMHDLYIE | SVSMLDLYIE | SVMLVLRIE | SVMHVLRIE | SVMHVLRPE); 108 | 109 | // Set SVM high side and SVM low side to new level 110 | PMMIFG &= ~(SVMHIFG | SVSMHDLYIFG | SVMLIFG | SVSMLDLYIFG); 111 | SVSMHCTL = SVMHE | SVMHFP | (SVSMHRRL0 * level); 112 | SVSMLCTL = SVMLE | SVMLFP | (SVSMLRRL0 * level); 113 | // Wait until levels are settled. 114 | while ((PMMIFG & SVSMHDLYIFG) == 0 || (PMMIFG & SVSMLDLYIFG) == 0); 115 | 116 | 117 | // Set VCore to new level 118 | PMMCTL0_L = PMMCOREV0 * level; 119 | 120 | /* 121 | printf("Changing high and low sides.\n"); 122 | // Set also SVS highside and SVS low side to new level 123 | PMMIFG &= ~(SVSHIFG | SVSMHDLYIFG | SVSLIFG | SVSMLDLYIFG); 124 | 125 | printf("a\n"); 126 | SVSMHCTL |= SVSHE | SVSHFP | (SVSHRVL0 * level); 127 | printf("b\n"); 128 | SVSMLCTL |= SVSLE | SVSLFP | (SVSLRVL0 * level); 129 | */ 130 | 131 | 132 | // Wait until SVS high side and SVS low side is settled 133 | //while ((PMMIFG & SVSMHDLYIFG) == 0 || (PMMIFG & SVSMLDLYIFG) == 0); 134 | 135 | // Disable full-performance mode to save energy 136 | SVSMHCTL &= ~SVSLFP; 137 | // Disable SVS/SVM Low 138 | // Disable full-performance mode to save energy 139 | SVSMLCTL &= ~(SVSLE+SVMLE+SVSLFP); 140 | 141 | 142 | // Clear all Flags 143 | PMMIFG &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG 144 | | SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG); 145 | // backup PMM-Interrupt-Register 146 | PMMRIE = PMMRIE_backup; 147 | // Lock PMM registers for write access 148 | PMMCTL0_H = 0x00; 149 | 150 | if ((PMMIFG & SVMHIFG) == SVMHIFG) 151 | // Highside is still too low for the adjusted VCore Level 152 | return 0; 153 | else 154 | return 1; 155 | } 156 | 157 | 158 | //! Sets the core core voltage. 159 | int power_setvcore (int level) { 160 | int oldlevel, origlevel; 161 | int status = 1; 162 | 163 | //Mask off the level. 164 | level &= PMMCOREV_3; 165 | //Current core voltage. 166 | origlevel = oldlevel = (PMMCTL0 & PMMCOREV_3); 167 | 168 | // step by step increase or decrease 169 | while (((level != oldlevel) && (status)) || (level < oldlevel)) { 170 | if (level > oldlevel) 171 | status = power_vcoreup(++oldlevel); 172 | else 173 | status = power_vcoredown(--oldlevel); 174 | __delay_cycles(850); 175 | } 176 | 177 | /* 178 | printf("Power %d->%d.\n", 179 | origlevel, 180 | PMMCTL0 & PMMCOREV_3); 181 | */ 182 | (void) origlevel; 183 | 184 | return status; 185 | } 186 | -------------------------------------------------------------------------------- /firmware/power.h: -------------------------------------------------------------------------------- 1 | /*! \file power.h 2 | \brief Power management library for the CC430F6137. 3 | */ 4 | 5 | //! Returns non-zero if in anything but the lowest power mode. 6 | int power_ishigh(); 7 | 8 | //! Sets the core core voltage. 9 | int power_setvcore (int level); 10 | -------------------------------------------------------------------------------- /firmware/rtc.c: -------------------------------------------------------------------------------- 1 | /*! \file rtc.c 2 | \brief RTC driver for the GoodWatch/Charliewatch. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "rtc.h" 10 | #include "rtcasm.h" 11 | 12 | //#include "api.h" 13 | //#include "apps/calibrate.h" 14 | 15 | //Automatically generated, not a part of the git repo. 16 | //#include "buildtime.h" 17 | 18 | //! ROM copy of the manufacturing time is generated at build time 19 | extern const uint8_t romsavetime[]; 20 | extern const uint32_t buildtime; 21 | 22 | //! If this is 0xdeadbeef, the ram time is good. 23 | static unsigned long magicword __attribute__ ((section (".noinit"))); 24 | //! Time and date, in case of a reboot. 25 | static unsigned char ramsavetime[8] __attribute__ ((section (".noinit"))); 26 | 27 | #ifdef CONFIG_ALARM 28 | // Alarm tone status 29 | static unsigned int alarm_ringing = 0; 30 | #endif 31 | 32 | //! Save the times to RAM. Must be fast. 33 | static void rtc_savetime(){ 34 | ramsavetime[0]=RTCYEAR - 2000; 35 | ramsavetime[1]=RTCMON; 36 | ramsavetime[2]=RTCDAY; 37 | ramsavetime[3]=RTCHOUR; 38 | ramsavetime[4]=RTCMIN; 39 | ramsavetime[5]=RTCSEC; 40 | 41 | //Set the magic word, so we'll know the time is good. 42 | magicword=buildtime; 43 | } 44 | 45 | //! Load the time from RAM or ROM 46 | static void rtc_loadtime(){ 47 | //Use the RAM copy if it is reasonable. 48 | if(magicword!=buildtime){ 49 | memcpy(ramsavetime,romsavetime,sizeof(ramsavetime)); 50 | } 51 | 52 | /* We need to call these functions for safety, as there are some 53 | awful RTC errata to work around. */ 54 | SetRTCHOUR(ramsavetime[3] %24); 55 | SetRTCMIN(ramsavetime[4] %60); 56 | SetRTCSEC(ramsavetime[5] %60); 57 | 58 | SetRTCYEAR(ramsavetime[0] + 2000); 59 | SetRTCMON(ramsavetime[1]); 60 | SetRTCDAY(ramsavetime[2]); 61 | //printf("Setting RTCDAY to %d yielded %d.\n", 62 | //ramsavetime[7], RTCDAY); 63 | 64 | // opposite time 65 | //SetRTCHOUR(13); 66 | //SetRTCMIN(34); 67 | //SetRTCSEC(58); 68 | 69 | // align test 70 | if(0) { 71 | SetRTCHOUR(17); 72 | SetRTCMIN(25); 73 | SetRTCSEC(5); 74 | } 75 | 76 | // triangle test 77 | if(0) { 78 | SetRTCHOUR(20); 79 | SetRTCMIN(0); 80 | SetRTCSEC(15); 81 | } 82 | 83 | // chase alignment test 84 | if(0) { 85 | SetRTCHOUR(21); 86 | SetRTCMIN(45); 87 | SetRTCSEC(35); 88 | } 89 | 90 | // diamond test 91 | if(0) { 92 | SetRTCHOUR(13); 93 | SetRTCMIN(40); 94 | SetRTCSEC(25); 95 | } 96 | 97 | //SetRTCHOUR(5); 98 | //SetRTCMIN(59); 99 | //SetRTCSEC(45); 100 | } 101 | 102 | 103 | //! Initializes the clock with the timestamp from memory. 104 | void rtc_init(){ 105 | // Setup RTC Timer 106 | 107 | // Calendar Mode, RTC1PS, 8-bit ovf 108 | // overflow interrupt enable 109 | // alarm interrupt enable 110 | RTCCTL01 = RTCTEVIE + RTCSSEL_2 + RTCTEV_0 + RTCMODE + RTCAIE; 111 | RTCPS0CTL = RT0PSDIV_2; // ACLK, /8, start timer 112 | RTCPS1CTL = RT1SSEL_2 + RT1PSDIV_3; // out from RT0PS, /16, start timer 113 | 114 | #ifdef CALIBRATE_APP 115 | //Load the calibration routines. 116 | calibrate_enforce(); 117 | #endif 118 | 119 | rtc_loadtime(); 120 | rtc_setdow(); 121 | } 122 | 123 | 124 | //! Years div by 4, but not by 100 unless also by 400 are leap years. 125 | #define IS_LEAP_YEAR(year) ((year%400==0) || (year%4==0 && year%100!=0)) 126 | //! Number of leap years since 1984. Accurate until 2200, then off by one. 127 | #define LEAPS_SINCE_YEAR(year) (((year) - 1984) \ 128 | + ((year) - 1984) / 4 \ 129 | + (year>2100?-1:0) \ 130 | ); 131 | 132 | 133 | //! Returns the number of days for a given month. 134 | static unsigned int rtc_get_max_days(unsigned int month, unsigned int year) { 135 | switch (month) { 136 | //Thirty days hath November, April, June, and September. 137 | case 4: case 6: case 9: case 11: 138 | return 30; 139 | 140 | //With 28 or 29 there is but one 141 | case 2: 142 | if (IS_LEAP_YEAR(year)) 143 | return 29; 144 | else 145 | return 28; 146 | 147 | //All the rest have thirty one 148 | case 1: case 3: case 5: case 7: case 8: case 10: case 12: 149 | return 31; 150 | 151 | default: 152 | return 0; 153 | } 154 | } 155 | 156 | //! Sets the DOW from the calendar date. 157 | void rtc_setdow(){ 158 | unsigned int dow; 159 | 160 | //Begin with the offset for the current year. 161 | dow = LEAPS_SINCE_YEAR(RTCYEAR); 162 | 163 | //Subtract a day if this year is a leap year, but we haven't reached 164 | //the leap day yet. 165 | if ((29 == rtc_get_max_days(2, RTCYEAR)) && (RTCMON < 3)) 166 | dow--; 167 | 168 | //Add this month's offset. 169 | switch (RTCMON) { 170 | case 5: 171 | dow += 1; 172 | break; 173 | 174 | case 8: 175 | dow += 2; 176 | break; 177 | 178 | case 2: case 3: case 11: 179 | dow += 3; 180 | break; 181 | 182 | case 6: 183 | dow += 4; 184 | break; 185 | 186 | case 9: case 12: 187 | dow += 5; 188 | break; 189 | 190 | case 4: case 7: 191 | dow += 6; 192 | break; 193 | } 194 | 195 | //Add the day of the current month. 196 | dow += RTCDAY; 197 | 198 | //Write the result to the register. 199 | dow = dow % 7; 200 | SetRTCDOW(dow); 201 | } 202 | 203 | //! Real Time Clock interrupt handler. 204 | void __attribute__ ((interrupt(RTC_VECTOR))) RTC_ISR (void){ 205 | //Save the time once a minute, so that when we reboot, we loose just 206 | //a few seconds. We might later decide to call this in the 207 | //rendering loop. 208 | rtc_savetime(); 209 | 210 | //We don't really handly these, but might want to in the future. 211 | switch(RTCIV&~1){ 212 | case 0: break; // No interrupts 213 | case 2: break; // RTCRDYIFG 214 | case 4: break; // RTCTEVIFG 215 | case 6: // RTCAIFG Alarm 216 | #ifdef CONFIG_ALARM 217 | if (!alarm_ringing) { 218 | alarm_ringing = 1; 219 | printf("Sounding the alarm.\n"); 220 | //! Sound the alarm 221 | tone(NOTE_C6, 500); 222 | tone(NOTE_E6, 500); 223 | tone(NOTE_G6, 500); 224 | tone(NOTE_B7, 500); 225 | tone(NOTE_C7, 500); 226 | alarm_ringing = 0; 227 | } 228 | #endif 229 | break; 230 | case 8: break; // RT0PSIFG 231 | case 10: break; // RT1PSIFG 232 | case 12: break; // Reserved 233 | case 14: break; // Reserved 234 | case 16: break; // Reserved 235 | default: break; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /firmware/rtc.h: -------------------------------------------------------------------------------- 1 | /*! \file rtc.h 2 | \brief Real Time Clock library. 3 | */ 4 | 5 | //! Flash buffer containing the manufacturing time. 6 | extern const unsigned char romsavetime[]; 7 | 8 | //! Configure the Real Time Clock 9 | void rtc_init(); 10 | 11 | //! Sets the DOW from the calendar date. 12 | void rtc_setdow(); 13 | 14 | #include "rtcasm.h" 15 | 16 | -------------------------------------------------------------------------------- /firmware/rtcasm.S: -------------------------------------------------------------------------------- 1 | /* \file rtcasm.S 2 | \brief Hand assembly to set the RTC. 3 | 4 | I've modified this code from the CCE example to work with GCC, 5 | which mostly involved correcting the calling convention. (The first 6 | parameter is R12 is CCE but R15 in GCC.) 7 | 8 | This module ought to be 32-bit aligned, and every NOP counts. I've 9 | not yet verified that GNU LD isn't screwing up the alignment. 10 | 11 | --Travis 12 | */ 13 | 14 | ;****************************************************************************** 15 | ; MSP430FG5438 - Real Time Clock Workaround 16 | ; 17 | ; Description: This program demonstrates the RTC Workaround for the RTC 18 | ; Errata deailed in the XMS '5438 Errata under RTC3 19 | ; Assembly functions for the RTC workaround 20 | ; Allows safe access to the RTC registers that potentially have a write issue 21 | ; Code is aligned so that the lower 5 bits of the address of the instruction 22 | ; fetch following the RTC register write are equal 23 | ; Do not change the code in the ASM file (.s43) all nops in there are needed. 24 | ; 25 | ; Texas Instruments Inc. 26 | ; October 2008 27 | ; Built with CCE version 3.2.2 28 | ; 29 | ;******************************************************************************* 30 | ; !!!!!! Do not change the code. Every nop has a function !!!!!! 31 | ;******************************************************************************* 32 | 33 | ; .cdecls C,LIST,"msp430x54x.h" ; Include device header file 34 | #include 35 | ;------------------------------------------------------------------------------- 36 | .text ; Progam Start 37 | ;------------------------------------------------------------------------------- 38 | ; .align 32 39 | .balign 4 40 | ;------------------------------------------------------------------------------- 41 | .global SetRTCYEAR 42 | ; int SetRTCYEAR(int year) 43 | ; year will be set in the RTCYEAR register 44 | ; return value is read back from the RTCYEAR register 45 | ; Works only in calendar mode (RTCMODE = 1) 46 | ; argument year is in R15 47 | ; return value is in R15 48 | SetRTCYEAR: nop 49 | nop 50 | nop 51 | nop 52 | dint 53 | nop 54 | nop 55 | nop 56 | mov.w R15,&RTCYEAR ; Set RTCYEAR to year 57 | nop 58 | nop 59 | mov.w &RTCYEAR,R15 ; Return value 60 | eint 61 | ret 62 | 63 | 64 | 65 | ;------------------------------------------------------------------------------- 66 | .global SetRTCMON 67 | ; int SetRTCMON(int month) 68 | ; month will be set in the RTCMON register 69 | ; Workaround requires to write 16 bits into RTCDATE 70 | ; return value is read back from the RTCMON register 71 | ; argument month is in R15 72 | ; return value is in R15 73 | SetRTCMON: push.w R5 ; push R5 74 | mov.b &RTCDAY,R5 ; read RTCDAY into R5 75 | swpb R15 ; R15 holds month - swap lower and upper byte 76 | bis.w R15,R5 ; combine read RTCDAY and new RTCMON 77 | dint 78 | nop 79 | mov.w R5,&RTCDATE ; write to RTCDATE 80 | nop 81 | nop 82 | nop 83 | nop 84 | eint 85 | mov.b &RTCMON,R15 ; Read RTCMON register 86 | pop.w R5 ; pop R5 87 | ret 88 | 89 | ;------------------------------------------------------------------------------- 90 | .global SetRTCDAY 91 | ; int SetRTCDAY(int day) 92 | ; day will be set in the RTCDAY register 93 | ; Workaround requires to write 16 bits into RTCDATE 94 | ; return value is read back from the RTCDAY register 95 | ; argument day is in R15 96 | ; return value is in R15 97 | nop 98 | nop 99 | nop 100 | nop 101 | nop 102 | nop 103 | nop 104 | nop 105 | nop 106 | nop 107 | nop 108 | nop 109 | SetRTCDAY: ; R15 holds day in lower byte 110 | push.w R5 ; push R5 111 | mov.b &RTCMON,R5 ; read RTCMON into lower byte of R5 112 | swpb R5 ; month is now in higher byte of R5 113 | bic.w #0x00FF,R5 ; clear lower byte of R5 114 | bis.w R15,R5 ; combine read RTCDAY and new RTCMON 115 | dint 116 | nop 117 | mov.w R5,&RTCDATE ; write to RTCDATE 118 | nop 119 | eint 120 | mov.b &RTCDAY,R15 ; Read RTCDAY register 121 | pop.w R5 ; pop R5 122 | ret 123 | 124 | ;------------------------------------------------------------------------------- 125 | .global SetRTCDOW 126 | ; int SetRTCDOW(int dow) 127 | ; dow will be set in the RTCDOW register 128 | ; Workaround requires to write 16 bits into RTCTIM1 129 | ; return value is read back from the RTCDOW register 130 | ; argument dow is in R15 131 | ; return value is in R15 132 | SetRTCDOW: push.w R5 ; push R5 133 | mov.b &RTCHOUR,R5 ; read RTCHOUR into R5 134 | swpb R15 ; R15 holds dow - swap lower and upper byte 135 | bis.w R15, R5 ; combine read RTCHOUR and new RTCDOW 136 | dint 137 | nop 138 | mov.w R5,&RTCTIM1 ; write to RTCTIM1 139 | nop 140 | eint 141 | mov.b &RTCDOW,R15 ; Read RTCDOW register 142 | pop.w R5 ; pop R5 143 | ret 144 | 145 | ;------------------------------------------------------------------------------- 146 | .global SetRTCHOUR 147 | ; int SetRTCHOUR(int hour) 148 | ; hour will be set in the RTCHOUR register 149 | ; Workaround requires to write 16 bits into RTCTIM1 150 | ; return value is read back from the RTCHOUR register 151 | ; argument hour is in R15 152 | ; return value is in R15 153 | SetRTCHOUR: ; R15 holds day in lower byte 154 | push.w R5 ; push R5 155 | mov.b &RTCDOW,R5 ; read RTCDOW into lower byte of R5 156 | swpb R5 ; dow is now in higher byte of R5 157 | bis.w R15,R5 ; combine read RTCDOW and new RTCHOUR 158 | dint 159 | nop 160 | nop 161 | mov.w R5,&RTCTIM1 ; write to RTCTIM1 162 | nop 163 | eint 164 | mov.b &RTCHOUR,R15 ; Read RTCHOUR register 165 | pop.w R5 ; pop R5 166 | ret 167 | 168 | ;------------------------------------------------------------------------------- 169 | .global SetRTCMIN 170 | ; int SetRTCMIN(int min) 171 | ; min will be set in the RTCMIN register 172 | ; Workaround requires to write 16 bits into RTCTIM0 173 | ; return value is read back from the RTCDOW register 174 | ; argument min is in R15 175 | ; return value is in R15 176 | SetRTCMIN: push.w R5 ; push R5 177 | mov.b &RTCSEC, R5 ; read RTCSEC into R5 178 | swpb R15 ; R15 holds min - swap lower and upper byte 179 | bis.w R15,R5 ; combine read RTCMIN and new RTCSEC 180 | dint 181 | nop 182 | mov.w R5,&RTCTIM0 ; write to RTCTIM0 183 | nop 184 | eint 185 | mov.b &RTCMIN,R15 ; Read RTCDOW register 186 | pop.w R5 ; pop R5 187 | ret 188 | 189 | ;------------------------------------------------------------------------------- 190 | .global SetRTCSEC 191 | ; int SetRTCSEC(int sec) 192 | ; sec will be set in the RTCSEC register 193 | ; Workaround requires to write 16 bits into RTCTIM0 194 | ; return value is read back from the RTCSEC register 195 | ; argument sec is in R15 196 | ; return value is in R15 197 | SetRTCSEC: ; R15 holds day in lower byte 198 | push.w R5 ; push R5 199 | mov.b &RTCMIN, R5 ; read RTCMIN into lower byte of R5 200 | swpb R5 ; min is now in higher byte of R5 201 | bis.w R15, R5 ; combine read RTCMIN and new RTCSEC 202 | dint 203 | nop 204 | nop 205 | mov.w R5,&RTCTIM0 ; write to RTCTIM0 206 | nop 207 | eint 208 | mov.b &RTCSEC, R15 ; Read RTCSEC register 209 | pop.w R5 ; pop R5 210 | ret 211 | 212 | ;------------------------------------------------------------------------------- 213 | 214 | .global GetRTCTIM0 215 | ; Read the RTC registers RTCTIM0 216 | ; return the values 217 | GetRTCTIM0: 218 | mov.w &RTCTIM0,R15 219 | ret 220 | 221 | 222 | .global GetRTCTIM1 223 | ; Read the RTC register RTCTIM1 224 | ; return the values 225 | GetRTCTIM1: 226 | mov.w &RTCTIM1,R15 227 | ret 228 | 229 | 230 | .global GetRTCDATE 231 | ; Read the RTC register RTCDATE 232 | ; return the values 233 | GetRTCDATE: 234 | mov.w &RTCDATE,R15 235 | ret 236 | 237 | 238 | .global GetRTCYEAR 239 | ; Read the RTC registers RTCYEAR 240 | ; return the values 241 | GetRTCYEAR: 242 | mov.w &RTCYEAR, R15 243 | ret 244 | 245 | 246 | .global GetRTCMON 247 | ; Read the RTC register RTCMON 248 | ; return the values 249 | GetRTCMON: 250 | mov.b &RTCMON, R15 251 | ret 252 | 253 | 254 | .global GetRTCDOW 255 | ; Read the RTC register RTCDOW 256 | ; return the values 257 | GetRTCDOW: 258 | mov.b &RTCDOW, R15 259 | ret 260 | 261 | 262 | .global GetRTCDAY 263 | ; Read the RTC register RTCDOW 264 | ; return the values 265 | GetRTCDAY: 266 | mov.b &RTCDAY, R15 267 | ret 268 | 269 | 270 | .global GetRTCHOUR 271 | ; Read the RTC register RTCDOW 272 | ; return the values 273 | GetRTCHOUR: 274 | mov.b &RTCHOUR, R15 275 | ret 276 | 277 | 278 | .global GetRTCMIN 279 | ; Read the RTC register RTCDOW 280 | ; return the values 281 | GetRTCMIN: 282 | mov.b &RTCMIN, R15 283 | ret 284 | 285 | 286 | .global GetRTCSEC 287 | ; Read the RTC register RTCDOW 288 | ; return the values 289 | GetRTCSEC: 290 | mov.b &RTCSEC, R15 291 | ret 292 | 293 | 294 | -------------------------------------------------------------------------------- /firmware/rtcasm.h: -------------------------------------------------------------------------------- 1 | /*! \file rtcasm.h 2 | \brief Hand assembly to set the RTC. 3 | 4 | The CC430F6137 has an awful errata that makes setting the time a bit 5 | of a gamble, based on CPU alignment. These functions ought to work 6 | around it, if we've linked rtcasm.S properly and not one NOP has 7 | been changed. 8 | 9 | */ 10 | 11 | #ifndef RTC_H_ 12 | #define RTC_H_ 13 | 14 | //! Set the year. 15 | extern int SetRTCYEAR(int year); 16 | //! Set the month. 17 | extern int SetRTCMON(int month); 18 | //! Set the day. 19 | extern int SetRTCDAY(int day); 20 | //! Set the day of the week. 21 | extern int SetRTCDOW(int dow); 22 | //! Set the hour. 23 | extern int SetRTCHOUR(int hour); 24 | //! Set the minute. 25 | extern int SetRTCMIN(int min); 26 | //! Set the second. 27 | extern int SetRTCSEC(int sec); 28 | 29 | //! Get TIM0. 30 | extern int GetRTCTIM0(void); 31 | //! Get TIM1 32 | extern int GetRTCTIM1(void); 33 | //! Get the date. 34 | extern int GetRTCDATE(void); 35 | //! Get the year. 36 | extern int GetRTCYEAR(void); 37 | 38 | #endif /*RTC_H_*/ 39 | -------------------------------------------------------------------------------- /firmware/stopwatch.c: -------------------------------------------------------------------------------- 1 | /*! \file Stopwatch functions 2 | 3 | \brief Implement a timer function on the LEDs. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "led.h" 12 | #include "button.h" 13 | 14 | static unsigned running = 1; 15 | static uint32_t counter; // in ms 16 | static unsigned timeout; 17 | extern unsigned watch_mode; 18 | 19 | 20 | // called every 16ms by the RTC interrupt 21 | void stopwatch_draw() 22 | { 23 | if (button_short) 24 | { 25 | timeout = 0; 26 | 27 | if (running) 28 | { 29 | // stop the counter 30 | running = 0; 31 | } else { 32 | // reset the counter, restart it 33 | counter = 0; 34 | running = 1; 35 | } 36 | } 37 | 38 | if (button_long) 39 | { 40 | // we have just entered this mode 41 | running = 0; 42 | counter = 0; 43 | timeout = 0; 44 | } 45 | 46 | if (running) 47 | counter += 16; 48 | else 49 | timeout++; 50 | 51 | if (timeout > 30 * 60) 52 | watch_mode = 0; 53 | 54 | uint32_t c = counter; 55 | const unsigned ms = c % 1000; 56 | c /= 1000; 57 | const unsigned sec = c % 60; 58 | c /= 60; 59 | const unsigned min = c % 12; // wrap every 12 minutes 60 | 61 | led_on(sec); delay(2); 62 | led_on(min + 60); 63 | led_on(ms * 60 / 1000); led_off(); 64 | } 65 | -------------------------------------------------------------------------------- /firmware/ucs.c: -------------------------------------------------------------------------------- 1 | /*! \file ucs.c 2 | \brief Clocking functions. 3 | 4 | This module implements a minimal driver for the Unified Clock System 5 | of the CC430F6137 and related devices. In general, we try to run in 6 | slow mode whenever possible, jumping to fast mode only during 7 | diagnostics or if required (briefly) for a radio exchange. At all 8 | other times, we run the CPU at 32kHz for power efficiency. 9 | 10 | */ 11 | 12 | #include 13 | #include "ucs.h" 14 | 15 | //! Fast mode. 16 | void ucs_fast(){ 17 | UCSCTL4 = SELM_3 + SELS_0 + SELA_0; //XT1 for ACLK and SMCLK, MCLK from DCO. 18 | } 19 | 20 | //! Slow mode. 21 | void ucs_slow(){ 22 | UCSCTL4 = SELM_0 + SELS_0 + SELA_0; //XT1 for everything; very slow CPU. 23 | } 24 | 25 | //! Initialize the XT1 crystal, and stabilize it. 26 | void ucs_init(){ 27 | P5SEL |= BIT0 + BIT1; // Select XT1 28 | UCSCTL6 |= XCAP_1; // Internal load cap 29 | 30 | // Loop until XT1 & DCO stabilizes 31 | do{ 32 | UCSCTL7 &= ~(XT1LFOFFG + DCOFFG); 33 | // Clear LFXT1,DCO fault flags 34 | SFRIFG1 &= ~OFIFG; // Clear fault flags 35 | }while (SFRIFG1 & OFIFG); // Test oscillator fault flag 36 | 37 | UCSCTL6 &= ~(XT1DRIVE_3); // Xtal is now stable, reduce drive 38 | // strength 39 | //See page 125 of the family guide. 40 | ucs_fast(); 41 | } 42 | -------------------------------------------------------------------------------- /firmware/ucs.h: -------------------------------------------------------------------------------- 1 | /*! \file ucs.h 2 | \brief Clocking functions. 3 | */ 4 | 5 | //! Fast mode. 6 | void ucs_fast(); 7 | 8 | //! Slow mode. 9 | void ucs_slow(); 10 | 11 | //! Initialize the XT1 crystal, and stabilize it. 12 | void ucs_init(); 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/v0.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/images/v0.1.jpg -------------------------------------------------------------------------------- /images/v0.2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/images/v0.2.jpg -------------------------------------------------------------------------------- /pcb/oshw.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/pcb/oshw.bmp -------------------------------------------------------------------------------- /watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/charliewatch/dc0f546712d61c904ed42819b3df5c82dae89243/watch.png --------------------------------------------------------------------------------