mirror of
https://github.com/travisgoodspeed/goodwatch
synced 2024-11-22 08:09:13 +00:00
307 lines
7.5 KiB
C
307 lines
7.5 KiB
C
/*! \file clock.c
|
|
\brief Main clock application, that runs by default.
|
|
|
|
This is the main clock application, which is seen by default
|
|
whenever the watch has nothing else to do. In addition to telling
|
|
the time, holding a button allows you to see additional
|
|
information, such as the date and git revision.
|
|
|
|
By default, the time is drawn. Hold 7 to run a self-test, 8 to
|
|
display the callsign, 9 for the day of the week, / for the date, 4
|
|
for the git tag, 5 for the date of flashing, 6 to toggle the CPU
|
|
load indicator, 0 for the name of the current working channel.
|
|
Hold 1 for the voltage, * for the CPU model number.
|
|
*/
|
|
|
|
#include <msp430.h>
|
|
#include <stdio.h>
|
|
|
|
#include "api.h"
|
|
|
|
#include "applist.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define RTCAE (0x80) /* Real Time Clock Alarm enable */
|
|
|
|
//! If non-zero, we need to redraw the whole time.
|
|
static int redraw=0;
|
|
|
|
//! Last measured voltage.
|
|
static unsigned int vcc;
|
|
|
|
//! This hold the last character pressed.
|
|
static char lastchar=0;
|
|
|
|
//! Draws the time.
|
|
void draw_time(int always){
|
|
/* So at first glance, this might seem a bit complicated. Why draw
|
|
the seconds first, and why take so many opportunities to abort
|
|
and return without drawing anything?
|
|
|
|
The reason is that the vast majority of CPU time is spent drawing
|
|
the time to the LCD, and that anything we can do to shorten that
|
|
time in each quarter-second frame will increase the watch's
|
|
battery life. Therefore, we only draw the seconds if they have
|
|
changed, which lets us do nothing on three quarters of the
|
|
frames. Then we only draw the minutes and hours if the minutes
|
|
have changed, saving two thirds of the digit drawing on each
|
|
frame, and we only draw the tens digit if the ones digit is zero,
|
|
saving us half the LCD memory writes and trable lookups during
|
|
those frames.
|
|
|
|
This could be further improved by testing hours, but as that only
|
|
applies to one frame in 3600, it won't impact battery life very
|
|
much. When a button has been pressed or the mode changed, it is
|
|
set non-zero to force the whole frame to be drawn.
|
|
|
|
int2bcd() works by a ROM table lookup in bcd.o. The '6147 also
|
|
has a hardware BCD calculator, but as this isn't present in the
|
|
'6137 and only costs us 64 bytes of ROM, it isn't a very high
|
|
priority.
|
|
|
|
All the digits are drawn out of order for these fancy reasons.
|
|
Sorry about that.
|
|
*/
|
|
|
|
unsigned int hour=RTCHOUR;
|
|
static unsigned int min;
|
|
static unsigned int sec;
|
|
unsigned int lsec; //Lower digit of the seconds.
|
|
|
|
//Only draw once a second, unless a button is pressed.
|
|
if(sec==RTCSEC && !always)
|
|
return;
|
|
sec=RTCSEC;
|
|
lcd_digit(0,lsec=int2bcd(sec)&0xf); //Lower digit.
|
|
if(lsec && !always) //Only need to draw tens digit if ones is zero.
|
|
return;
|
|
lcd_digit(1,int2bcd(sec)>>4);
|
|
|
|
|
|
//If the minute hasn't changed, don't bother drawing it or the hour.
|
|
if(min==RTCMIN && !always)
|
|
return;
|
|
min=RTCMIN;
|
|
|
|
lcd_cleardigit(2); //Space between seconds and minutes.
|
|
|
|
lcd_digit(7,int2bcd(hour)>>4);
|
|
lcd_digit(6,int2bcd(hour)&0xf);
|
|
lcd_cleardigit(5); //Space between hours and minutes.
|
|
setcolon(1);
|
|
lcd_digit(4,int2bcd(min)>>4);
|
|
lcd_digit(3,int2bcd(min)&0xf);
|
|
|
|
setam(hour<12);
|
|
setpm(hour>=12);
|
|
|
|
// get alarm status
|
|
if (RTCAHOUR & RTCAE && RTCAMIN & RTCAE)
|
|
setplus(1);
|
|
else
|
|
setplus(0);
|
|
}
|
|
|
|
//! Draws the date as yyyy.mm.dd
|
|
void draw_date(){
|
|
unsigned int year=RTCYEAR;
|
|
unsigned int month=RTCMON;
|
|
unsigned int day=RTCDAY;
|
|
|
|
lcd_digit(7,(year/1000)%10);
|
|
lcd_digit(6,(year/100)%10);
|
|
lcd_digit(5,(year/10)%10);
|
|
lcd_digit(4,year%10);
|
|
setperiod(4,1);
|
|
setcolon(0);
|
|
lcd_digit(3,month/10);
|
|
lcd_digit(2,month%10);
|
|
setperiod(2,1);
|
|
lcd_digit(1,day/10);
|
|
lcd_digit(0,day%10);
|
|
|
|
setam(0);
|
|
setpm(0);
|
|
}
|
|
|
|
static const char *daysofweek[7]={
|
|
" sunday",
|
|
" monday",
|
|
" tuesday",
|
|
"wednesdy",
|
|
"thursday",
|
|
" friday",
|
|
"saturday"
|
|
};
|
|
|
|
//! Draws the day of the week.
|
|
static void draw_dow(){
|
|
lcd_string(daysofweek[RTCDOW]);
|
|
}
|
|
|
|
//! Draws the date as yyyy.mm.dd
|
|
static void draw_date_rom(){
|
|
unsigned int year=(romsavetime[4]+(romsavetime[5]<<8)) % 4096;;
|
|
unsigned int month=romsavetime[6];
|
|
unsigned int day=romsavetime[7];
|
|
|
|
lcd_digit(7,(year/1000)%10);
|
|
lcd_digit(6,(year/100)%10);
|
|
lcd_digit(5,(year/10)%10);
|
|
lcd_digit(4,year%10);
|
|
setperiod(4,1);
|
|
setcolon(0);
|
|
lcd_digit(3,month/10);
|
|
lcd_digit(2,month%10);
|
|
setperiod(2,1);
|
|
lcd_digit(1,day/10);
|
|
lcd_digit(0,day%10);
|
|
|
|
setam(0);
|
|
setpm(0);
|
|
}
|
|
|
|
//! Exit clock when the sidebutton is pressed.
|
|
int clock_exit(){
|
|
return 0;
|
|
}
|
|
|
|
//! Plays the time as audio.
|
|
void clock_playtime(int hold){
|
|
char buf[12];
|
|
sprintf(buf,"%02d %02d",
|
|
RTCHOUR, RTCMIN);
|
|
lcd_string(buf);
|
|
//Little delay, so we can quit early with dignity on accidental keypresses.
|
|
audio_morse(" ", 1);
|
|
audio_morse(buf, //Buffer to play.
|
|
hold //Play it only so long as the button is held down.
|
|
);
|
|
}
|
|
|
|
|
|
//! Draws the clock face in the main application.
|
|
void clock_draw(int forced){
|
|
//Use the SET button to reconfigure the time.
|
|
if(sidebutton_set()){
|
|
//Wait for the button to be released.
|
|
while(sidebutton_set());
|
|
//Switch to the setting applet.
|
|
app_set(&setting_applet);
|
|
}
|
|
|
|
if(!lastchar){
|
|
// Draw the time by default, but only if no buttons pushed.
|
|
draw_time(redraw);
|
|
if(redraw)
|
|
redraw--;
|
|
}
|
|
}
|
|
|
|
|
|
//! Entry to the clock app.
|
|
void clock_init(){
|
|
lastchar=0;
|
|
redraw=0;
|
|
lcd_zero();
|
|
draw_time(1);
|
|
}
|
|
|
|
|
|
|
|
//! A button has been pressed for the clock.
|
|
int clock_keypress(char ch){
|
|
lastchar=ch;
|
|
|
|
/* This function is called *once* per keypress event, while the
|
|
handlers in clock_draw() are called once per frame. It is very
|
|
important that anything that takes more than a frame be handled
|
|
here, rather than in clock_draw(), which is rerun for every frame.
|
|
*/
|
|
|
|
lcd_zero();
|
|
redraw++;
|
|
|
|
switch(ch){
|
|
case '7':
|
|
//Hold 7 to run the self-test after startup. Response codes try
|
|
//to roughly describe the fault.
|
|
post();
|
|
break;
|
|
case '8':
|
|
//8 shows the callsign.
|
|
lcd_string(CALLSIGN);
|
|
break;
|
|
case '9':
|
|
//Hold 9 to draw the day of the week.
|
|
draw_dow();
|
|
break;
|
|
case '/':
|
|
//Hold / to draw the date.
|
|
draw_date();
|
|
break;
|
|
|
|
case '4':
|
|
//4 shows the git revision.
|
|
lcd_hex(GITHASH);
|
|
lcd_cleardigit(7);
|
|
break;
|
|
case '5':
|
|
//5 shows the git date from Flash.
|
|
draw_date_rom();
|
|
break;
|
|
case '6':
|
|
//6 toggles the CPU load indicator.
|
|
flickermode=(flickermode?0:-1);
|
|
lcd_string(flickermode?"FLICK ON":"FLICKOFF");
|
|
break;
|
|
case '*':
|
|
//* shows the chip model number.
|
|
lcd_string(DEVICEID==DEVICEID6137?"430F6137":"430F6147");
|
|
break;
|
|
|
|
|
|
case '1':
|
|
/* 1 shows the the voltage. To save on power, the reference is
|
|
not active when idling. */
|
|
ref_on();
|
|
vcc=adc_getvcc();
|
|
ref_off();
|
|
lcd_number(vcc);
|
|
lcd_string("volt ");
|
|
setperiod(2,1);
|
|
break;
|
|
|
|
|
|
case '0':
|
|
//0 shows the name of the working channel.
|
|
if(has_radio)
|
|
lcd_string(codeplug_name());
|
|
else
|
|
lcd_string("NO RADIO");
|
|
break;
|
|
case '.':
|
|
//. shows the frequency of the working channel.
|
|
if(has_radio){
|
|
lcd_number(codeplug_getfreq()/10);
|
|
setperiod(5,1);
|
|
setperiod(2,1);
|
|
}else{
|
|
lcd_string("NO RADIO");
|
|
}
|
|
break;
|
|
|
|
|
|
case '+':
|
|
/* + beeps the time as morse code, which might be handy
|
|
for learning the code or when it's too dark to see the display.
|
|
*/
|
|
clock_playtime(1);
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|