// SilentTimer Action Figure software, v2.3
// main.c
// The main c file.
//
// 03.01.2021
// Copyright (C) 2014-2017, 2020-2021 Balthasar SzczepaĆski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
// #define NEW_DISPLAY
// #define OLD_HARDWARE
// #define DEBAG
#include "msp430.h"
#include "definitions.h"
unsigned char calcdata[8] __attribute__ ((aligned (2))); //data for calculator operation
unsigned char inbuf[8]; //SPI input buffer
unsigned char outbuf[8]; //SPI output buffer
unsigned char dispvalue[10]; //data to display
unsigned char forcedisp[5]={0,0,0,0,0}; //forcing the display
unsigned char time[8];
unsigned char leap; //leap year
unsigned char step; //constant count 0 1 2 3 0 1 2 3 ...
unsigned char settime[3]; //for editing values
unsigned char limit[3]; //limits for editing
unsigned char decimal; //if edited value is BCD
volatile unsigned char mode; //main state
unsigned char alarm[3];
unsigned char lastcdn[2]; //last countdown
unsigned char lastyear[5];
unsigned char digit; //currently selected digit
unsigned char changed; //changed value during edit
unsigned char run; //switch used in some states
unsigned char cont; //continued calculation
unsigned char buzzmode=0; //buzzer, 0-normal, 1-mute, 2-always on.
//for RS232 receiving
volatile unsigned char rxbyte=0;
volatile unsigned char rxcount=0;
volatile unsigned char rxind=0;
volatile unsigned char rxpin;
volatile unsigned char rxbuf[RXBUFSIZE];
unsigned char rxdata[RXDATASIZE];
unsigned char rxcmd;
volatile unsigned char txbyte;
volatile unsigned char txcount=0;
volatile unsigned char txindW=0;
volatile unsigned char txindR=0;
volatile unsigned char txpin=1;
volatile unsigned char txbuf[TXBUFSIZE];
volatile unsigned char txbuzz=0;
volatile unsigned char event=0;
unsigned char eventmask=0;
const unsigned char symbol[16] ={
SYMB_0,
SYMB_1,
SYMB_2,
SYMB_3,
SYMB_4,
SYMB_5,
SYMB_6,
SYMB_7,
SYMB_8,
SYMB_9,
SYMB_A,
SYMB_B,
SYMB_C,
SYMB_D,
SYMB_E,
SYMB_F};
__interrupt void P2_ISR();
__interrupt void TA0_ISR();
void gettime(void);
void exec(void);
void enter(void);
void leave(void);
void showvalue(void);
void send(unsigned char data);
void sendhex(unsigned char data);
void sendevent(unsigned char data);
void display(
unsigned char *data);
void spi(
unsigned char command,
unsigned char addr,
unsigned char *inbuf,
unsigned char *outbuf,
unsigned short n);
int main( void );
void send(unsigned char data)
{
if (data == '\n')
send ('\r');
while (((txindW - txindR) & TXBUFMASK) == TXBUFMASK) {} //wait for free buffer
//put byte to output buffer
txbuf[txindW] = data;
++txindW;
txindW &= TXBUFMASK;
if (!(TACCTL0 & CCIE))
{
TACCTL0 &= ~CCIFG;
TACCTL0 |= CCIE;
}
}
void sendhex (unsigned char data)
{
unsigned char i,j;
for (i=0; i<2; ++i)
{
j = i? (data & 0x0f) : (data>>4);
if (j>9)
j += 87;
else
j += 48;
send(j);
}
}
void sendevent (unsigned char data)
{
if (data & eventmask)
{
send('O');
sendhex(data);
send('\n');
}
}
void display(unsigned char *data)
{
unsigned char i,j;
for(i=0,j=0x80; i<4; ++i, j>>=1)
// for(i=0,j=0x80; i<2; ++i, j>>=1)
{
P1OUT = data[i];
P3OUT |= j;
P3OUT &= ~j;
}
// //lsd
// P1OUT =data[0];
// P3OUT |=0x80; //74hc373 latch enable
// P3OUT &=0x0f;
// P1OUT =data[1];
// P3OUT |=0x40;
// P3OUT &=0x0f;
// P1OUT =data[2];
// P3OUT |=0x20;
// P3OUT &=0x0f;
// //msd
// P1OUT =data[3];
// P3OUT |=0x10;
// P3OUT &=0x0f;
}
void spi(
unsigned char command,
unsigned char addr,
unsigned char *inbuf,
unsigned char *outbuf,
unsigned short n)
{
unsigned short i;
unsigned char nul;
unsigned char flags;
switch (command)
{
case RTC_EEREAD:
case RTC_IDREAD:
case RTC_READ:
flags= ADDRFLAG|READFLAG;
break;
case RTC_EEWRITE:
case RTC_IDWRITE:
n=(n>8)?8:n;
case RTC_WRITE:
flags= ADDRFLAG|WRITEFLAG;
break;
case RTC_EEWREN:
case RTC_EEWRDI:
flags=0;
break;
case RTC_SRREAD:
n=(n>1)?1:n;
flags=READFLAG;
break;
case RTC_SRWRITE: //1
case RTC_CLRRAM: //data ignored but has to be
case RTC_UNLOCK: //1
n=(n>1)?1:n;
flags=WRITEFLAG;
break;
default:
return;
}
P3OUT&=0xfe; //chip select down
#if !(defined DEBAG)
while((IFG2&UTXIFG0)==0); // wait until can be sent
#endif
U0TXBUF=command; // send command
#if !(defined DEBAG)
while((IFG2&UTXIFG0)==0); // wait until received
#endif
nul=U0RXBUF; //pretend to read "data"
if(flags&ADDRFLAG)
{
#if !(defined DEBAG)
while((IFG2&UTXIFG0)==0); // wait until can be sent
#endif
U0TXBUF=addr; // send address
#if !(defined DEBAG)
while((IFG2&UTXIFG0)==0); // wait until received
#endif
nul=U0RXBUF; //pretend to read "data"
}
if(flags&(READFLAG|WRITEFLAG))
{
for(i=0;i= 11) //stop bit
{
if (rxpin) //correct stop bit
{
if (rxbyte == 0x0d || rxbyte == 0x0a) // CR or NL, end of frame
{
if(rxind)
{
rxind = 0;
event |= EVENT_RS; //signal
}
}
else
{
if ((rxbyte >= 'g' && rxbyte <= 'z') || (rxbyte >= 'G' && rxbyte <= 'Z')) //new command
{
rxind=0;
}
if (rxind < RXBUFSIZE) //still place in buffer
{
rxbuf[rxind] = rxbyte; //put byte to buffer
++rxind;
}
}
}
rxcount = 0;
txcount = 0;
txindR = txindW;
P2OUT |= 0x04;
TACCTL0 &= ~CCIE; //stop receiving
}
else //receive single bit
{
rxbyte >>= 1;
rxbyte |= rxpin ? 0x80 : 0x00;
}
// }
if(event)
_BIC_SR_IRQ(OSCOFF|CPUOFF|SCG1|SCG0);
}
else if ((txindR != txindW) || txcount) //transmit mode
{
if(txcount)
--txcount;
if (!txcount) //reached end of byte
{
if (txindR != txindW) //bytes to send available
{
txbyte=txbuf[txindR];
++txindR; //next byte
txindR &= TXBUFMASK;
txcount = 10;
}
else if (txbuzz) //buzzer should be done
{
txbyte = 0x55;
txcount = 10;
}
}
// if (!(txcount & 0x01)) //every second count
// {
if ((txcount == 1) || (txcount == 0)) //stop bit
txpin = 1;
else if (txcount == 10) //start bit
txpin = 0;
else if (txcount < 10) //data bits
{
txpin = txbyte & 0x01;
txbyte >>= 1;
}
else //before start
txpin = 1;
// }
}
else if (txbuzz) //buzzer should be done
{
txcount = 2; //start buzzer from stop bit
}
else //nothing left to do
{
txpin = 1;
TACCTL0 &= ~CCIE;
}
// P3OUT &= ~0x10;
return;
}
#pragma vector=PORT2_VECTOR
__interrupt void P2_ISR()
{
if(P2IFG&0x08) // RTC
{
P2IFG &= ~0x08;
event |= EVENT_RTC;
}
else if(P2IFG&0x02) // button A
{
P2IFG &= ~0x02;
event |= EVENT_A;
}
else if(P2IFG&0x01) // button B
{
P2IFG &= ~0x01;
event |= EVENT_B;
}
else if(P2IFG&0x20) // button C
{
P2IFG &= ~0x20;
event |= EVENT_C;
}
else if(P2IFG&0x10) // RS232
{
// P3OUT |= 0x10;
P2IFG &= ~0x10;
if(!rxcount) //start bit detected
{
// TACTL |= TACLR; //reset timer
TACTL &= ~MC_3; //stop timer
TAR = BAUDHALF; //set timer to half count
TACTL |= MC_1; //start timer
TACCTL0 &= ~CCIFG; //clear any waiting timer interrupt
TACCTL0 |= CCIE; //enable timer interrupts
rxcount = 1;
rxbyte = 0;
txcount = 0; //cancel transmission
// txindW = 0;
txindR = txindW;
txpin = 1;
}
// P3OUT &= ~0x10;
}
else
P2IFG=0;
if(event)
_BIC_SR_IRQ(OSCOFF|CPUOFF|SCG1|SCG0); //escape low power mode
return;
}
void gettime(void)
{
P2IFG &= ~0x08;
//get latest time from RTC
spi(RTC_READ,0x00,inbuf,outbuf,8);
time[0]=inbuf[0]; //1/100 s
time[1]=inbuf[1]&0x7f; //seconds
time[2]=inbuf[2]&0x7f; //minutes
time[3]=inbuf[3]&0x3f; //hours
time[4]=inbuf[4]&0x07; //dow
time[5]=inbuf[5]&0x3f; //dom
time[6]=inbuf[6]&0x1f; //mon
time[7]=inbuf[7];//year
leap=inbuf[6]&0x20;
step = (time[1]&0x03)^((time[1]&0x10)?0x02:0x00);
spi(RTC_READ,0x0f,inbuf,outbuf,1); // <- because of RTC bug (probably not needed anymore?)
//check if trigger alarm
if(!((time[2]^alarm[1])|(time[3]^alarm[2])) && alarm[0])
{
mode=MODE_ALARM;
alarm[0]=0; //alarm removed after activated
spi(RTC_WRITE,0x20,inbuf,alarm,1);
sendevent(EVENT_ALARM);
}
//code to deal with centuries, functionality missing from RTC
//has to avoid accidentally reacting in case of transmission error
lastyear[4]=(time[7]==0x00 && lastyear[0]==0x99)?bcd_add_byte(lastyear[1],0x01):lastyear[1];
if (time[7]!=lastyear[0])
{
if(time[7]==lastyear[2])
++(lastyear[3]);
else
lastyear[3]=0;
lastyear[2]=time[7];
if(lastyear[3]>55)
{
lastyear[3]=0;
if(time[7]==0x00 && lastyear[0]==0x99)
lastyear[1]=bcd_add_byte(lastyear[1],0x01);
lastyear[0]=time[7];
outbuf[0]=ALARMVALUE;
outbuf[1]=lastyear[0];
outbuf[2]=lastyear[1];
spi(RTC_WRITE,0x26,inbuf,outbuf,3);
}
}
}
void leave (void) //to execute when leaving an edit state
{
switch (mode)
{
case MODE_EDIT | MODE_SETCALCOPER:
calcdata[6] &= ~0xc0;
calcdata[6] |= settime[0]<<6;
mode=MODE_EDIT | MODE_SETCALCNUM;
break;
case MODE_EDIT | MODE_SETCALCNUM:
if (run)
{
if(cont != 0xff) //skip if triggered externally
{
calcdata[2]=settime[0];
calcdata[3]=settime[1];
calcdata[6] &= ~0x0c; //clear old flags
if ((settime[1] & 0xf0)==0xa0) //minus sign encoded as 0xA
{
calcdata[6] |= 0x04;
calcdata[3] &= 0x0f;
}
if (settime[2]) //decimal point
calcdata[6] |= 0x08;
}
switch(calcdata[6]&0xc0)
{
case 0xc0:
calc_div(calcdata);
break;
case 0x80:
calc_mul(calcdata);
break;
case 0x40:
calcdata[6]^=0x04;
calc_add(calcdata);
calcdata[6]^=0x04;
break;
case 0x00:
calc_add(calcdata);
break;
default:
calcdata[7]=0xff;
}
mode=MODE_CALC;
}
else
{
calcdata[4]=settime[0];
calcdata[5]=settime[1];
calcdata[6] &= ~0x30;
if ((settime[1] & 0xF0)==0xA0) //minus sign encoded as 0xA
{
calcdata[6] |= 0x10;
calcdata[5] &= 0x0f;
}
if (settime[2]) //decimal point
calcdata[6] |= 0x20;
mode=MODE_EDIT | MODE_SETCALCOPER;
}
break;
case MODE_EDIT | MODE_SETDEBUGVAL:
spi(RTC_WRITE,settime[1],inbuf,settime,1);
mode=MODE_DEBUG;
break;
case MODE_EDIT | MODE_SETDEBUGADDR:
settime[1]=settime[0];
run=1;
mode=MODE_DEBUG;
break;
case MODE_EDIT | MODE_SETCALIB:
spi(RTC_WRITE,0x09,inbuf,settime,1);
outbuf[0]=time[3]|(settime[1]?0x80:0x00);
spi(RTC_WRITE,0x03,inbuf,outbuf,1);
mode=MODE_CALIB;
break;
case MODE_EDIT | MODE_SETDOWN:
lastcdn[0]=settime[0];
lastcdn[1]=settime[1];
outbuf[0]=ALARMVALUE;
outbuf[1]=settime[0];
outbuf[2]=settime[1];
spi(RTC_WRITE,0x23,inbuf,outbuf,3);
mode=MODE_DOWN;
break;
case MODE_EDIT | MODE_SETYEAR:
if(changed)
{
time[7]=settime[0];
spi(RTC_WRITE,0x07,inbuf,settime,1);
lastyear[0]=settime[0];
lastyear[1]=settime[1];
lastyear[2]=settime[1];
lastyear[3]=0;
lastyear[4]=settime[1];
outbuf[0]=ALARMVALUE;
outbuf[1]=settime[0];
outbuf[2]=settime[1];
spi(RTC_WRITE,0x26,inbuf,outbuf,3);
}
mode=MODE_DATE;
break;
case MODE_EDIT | MODE_SETDATE:
if (changed)
{
time[5]=settime[0]; //day
time[6]=settime[1]; //month
settime[2]=time[7]; //year
spi(RTC_WRITE,0x06,inbuf,settime+1,2); //write month and year first
spi(RTC_WRITE,0x05,inbuf,settime,1); //and only then the day
//otherwise rtc can reject the date
}
mode=MODE_DATE;
break;
case MODE_EDIT | MODE_SETALARM:
if(changed|| !alarm[0])
{
alarm[0]=ALARMVALUE;
alarm[1]=settime[0];
alarm[2]=settime[1];
spi(RTC_WRITE,0x20,inbuf,alarm,3);
}
else
{
alarm[0]=0;
spi(RTC_WRITE,0x20,inbuf,alarm,1);
}
mode=MODE_TIME;
break;
case MODE_EDIT | MODE_SETTIME:
if (changed)
{
outbuf[0]=0x00;
outbuf[1]=0x00;
outbuf[2]=settime[0]&0x7f;
outbuf[3]=settime[1]&0x3f;
spi(RTC_WRITE,0x01,inbuf,outbuf,1);//stop osc.
spi(RTC_READ,0x03,inbuf,outbuf,1);
outbuf[3]|=(inbuf[0]&0x80);//keep the calsgn bit.
spi(RTC_WRITE,0x00,inbuf,outbuf,4);//write new time
outbuf[0]=0x80;
spi(RTC_WRITE,0x01,inbuf,outbuf,1);//start osc;
time[1]=0;
time[2]=settime[0];
time[3]=settime[1];
}
mode = MODE_TIME;
break;
}
}
void enter (void) //to execute when entering a state
{
if (mode & MODE_EDIT)
{
digit = 3;
changed = 0;
limit[0]=0x59;
limit[1]=0x23;
decimal = 1;
switch (mode)
{
case MODE_EDIT | MODE_SETCALCOPER:
digit=0;
limit[0]=0x03;
settime[0] = cont ? (calcdata[6]>>6) : 0x00;
run = 1;
break;
case MODE_EDIT | MODE_SETCALCNUM:
digit=4;
limit[0]=0x99;
limit[1]=0xA9;
limit[2]=0x01;
if (run && cont)
{
settime[0] = calcdata[2];
settime[1] = calcdata[3];
if (calcdata[6]&0x04)
settime[1] |= 0xA0; //restore minus sign
settime[2] = (calcdata[6]&0x08)>>3; //restore decimal point
}
else
{
settime[0] = 0x00;
settime[1] = 0x00;
settime[2] = 0x00;
}
break;
case MODE_EDIT | MODE_SETDEBUGADDR:
if(run)
{
settime[2] = settime[0];
settime[0] = settime[1];
}
else
{
settime[0] = 0x00;
settime[2] = 0x00;
}
case MODE_EDIT | MODE_SETDEBUGVAL:
digit=1;
limit[0]=0xff;
decimal=0;
break;
case MODE_EDIT | MODE_SETYEAR:
limit[0]=0x99;
limit[1]=0x99;
settime[0] = time[7]; //year L
settime[1] = lastyear[4]; //year H
break;
case MODE_EDIT | MODE_SETDATE:
digit = 2;
// limit[0]=0x31;
// limit[1]=0x12; not needed will be dynamically done later
settime[0] = time[5]; //day
settime[1] = time[6]; //month
break;
case MODE_EDIT | MODE_SETCALIB:
limit[0]=0xff;
limit[1]=0x01;
decimal=0;
digit=2;
spi(RTC_READ,0x03,inbuf,outbuf,1);
settime[1]=(inbuf[0]&0x80)?0x01:0x00;
spi(RTC_READ,0x09,inbuf,outbuf,1);
settime[0]=inbuf[0];
break;
case MODE_EDIT | MODE_SETDOWN:
limit[1] = 0x99;
settime[0] = lastcdn[0];
settime[1] = lastcdn[1];
break;
case MODE_EDIT | MODE_SETALARM:
settime[0] = (alarm[1]>0x59 || (settime[1]&0x0f)>0x09) ? 0x00 : alarm[1];
settime[1] = (alarm[2]>0x59 || (settime[2]&0x0f)>0x09) ? 0x00 : alarm[2];
break;
case MODE_EDIT | MODE_SETTIME:
settime[0] = time[2]; //minute
settime[1] = time[3]; //hour
break;
}
}
else
{
switch(mode)
{
case MODE_CALC:
run=0;
break;
case MODE_DEBUG:
event |= EVENT_RTC;
break;
case MODE_VER:
run=0;
break;
case MODE_CALIB:
spi(RTC_READ,0x03,inbuf,outbuf,1);
settime[1]=(inbuf[0]&0x80)?0x01:0x00;
spi(RTC_READ,0x09,inbuf,outbuf,1);
settime[0]=inbuf[0];
break;
case MODE_DOWN:
run=0;
settime[0] = lastcdn[0];
settime[1] = lastcdn[1];
break;
case MODE_UP:
run=0;
settime[0] = 0;
settime[1] = 0;
break;
// default:
}
}
}
void exec (void) //execute every second
{
switch(mode)
{
case MODE_RESET:
if(P2IN&0x02 && P2IN&0x01 && P2IN&0x20) //perform a reset when no button pressed
WDTCTL = ALARMVALUE;
break;
case MODE_DEBUG:
spi(RTC_READ,settime[1],settime,outbuf,1);
event |= EVENT_RTC;
break;
case MODE_DOWN:
if(run && (settime[1] || settime[0]))
{
if(settime[0])
settime[0]=bcd_add_byte(settime[0],0x99);
else
{
settime[0]=0x59;
settime[1]=bcd_add_byte(settime[1],0x99);
}
if (!(settime[1] || settime[0]))
sendevent(EVENT_0);
}
else
run=0;
break;
case MODE_UP:
if(run)
{
if(settime[0]<0x59)
settime[0]=bcd_add_byte(settime[0],0x01);
else
{
settime[0]=0x00;
settime[1]=bcd_add_byte(settime[1],0x01);
}
}
break;
// default:
}
}
void showvalue () //determine what to show on display
{
unsigned char dot=0;
unsigned char zero=0;
unsigned char i;
dispvalue[4]=0;
dispvalue[5]=0;
dispvalue[6]=0;
dispvalue[7]=0;
if (mode == (MODE_EDIT | MODE_SETCALCOPER))
{
switch (settime[0])
{
case 3:
dispvalue[4] = SYMB_U;
dispvalue[5] = SYMB_I;
dispvalue[6] = SYMB_D;
dispvalue[7] = SYMB_NUL;
break;
case 2:
dispvalue[4] = SYMB_L;
dispvalue[5] = SYMB_U;
dispvalue[6] = SYMB_M2;
dispvalue[7] = SYMB_M1;
break;
case 1:
dispvalue[4] = SYMB_B;
dispvalue[5] = SYMB_U;
dispvalue[6] = SYMB_5;
dispvalue[7] = SYMB_NUL;
break;
case 0:
dispvalue[4] = SYMB_D;
dispvalue[5] = SYMB_D;
dispvalue[6] = SYMB_A;
dispvalue[7] = SYMB_NUL;
break;
}
}
else if (mode & MODE_EDIT)
{
dispvalue[8] = settime[0];
dispvalue[9] = settime[1];
switch (mode)
{
case MODE_EDIT | MODE_SETCALCNUM:
if ((settime[1]&0xF0) == 0xA0) //minus sign
dispvalue[7] = SYMB_MIN;
if (settime[2]) //decimal point
dot |= 0x02;
break;
case MODE_EDIT | MODE_SETDEBUGADDR:
dispvalue[6] = SYMB_A;
case MODE_EDIT | MODE_SETDEBUGVAL:
dispvalue[9]=0x00;
zero=2;
dot |= 0x03; //:
break;
case MODE_EDIT | MODE_CALIB:
if (settime[1] && (!(step&0x01) || digit==2))
dispvalue[6] = SYMB_MIN;
zero=2;
break;
case MODE_EDIT | MODE_SETDOWN:
dot |= ((step & 0x02)?0x00:002) | ((step==0 || step==3)?0x01:00);
break;
case MODE_EDIT | MODE_SETDATE:
dispvalue[8] = settime[1];
dispvalue[9] = settime[0];
dot |= 0x02;
break;
case MODE_EDIT | MODE_SETALARM:
if(!(step & 0x01))
dot |= 0x08;
case MODE_EDIT | MODE_SETTIME:
dot |= 0x03;
break;
}
if (step&0x01)
{
for(i=0; i<4; ++i)
{
if (i != digit)
{
if(mode== (MODE_EDIT | MODE_SETDATE))
{
switch(i)
{
case 2:
dispvalue[4] = SYMB_NUL;
dispvalue[5] = SYMB_NUL;
break;
case 1:
case 0:
dispvalue[6+i] = SYMB_NUL;
break;
}
}
else
dispvalue[i+4] = SYMB_NUL;
}
}
}
}
else
{
switch (mode)
{
case MODE_CALC:
if (calcdata[7])
{
dispvalue[4] = SYMB_E;
dispvalue[5] = SYMB_E;
dispvalue[6] = SYMB_E;
dispvalue[7] = SYMB_NUL;
dot |= 0x03; //:
}
else
{
dispvalue[8] = calcdata[0];
dispvalue[9] = calcdata[1];
if(calcdata[6] & 0x02) //decimal point
{
dot |= 0x02;
zero=1;
}
else
zero=3;
if(calcdata[6] & 0x01) //minus
dispvalue[7] = SYMB_MIN;
}
break;
case MODE_RESET:
dispvalue[4] = SYMB_NUL;
dispvalue[5] = SYMB_NUL;
dispvalue[6] = SYMB_NUL;
dispvalue[7] = SYMB_NUL;
break;
case MODE_VER:
dispvalue[4] = SYMB_NUL;
dispvalue[5] = SYMB_3;
dispvalue[6] = SYMB_2;
dispvalue[7] = SYMB_NUL;
dot |= 0x02; //.
break;
case MODE_CALIB:
dispvalue[8]=settime[0];
dispvalue[9]=settime[1];
if (settime[1])
dispvalue[6] = SYMB_MIN;
zero=2;
break;
case MODE_DOWN:
if (!settime[0] && !settime[1])
{
dot |= 0x80; //buzz.
if(!run) {
dispvalue[4] = SYMB_g;
dispvalue[5] = SYMB_n;
dispvalue[6] = SYMB_o;
dispvalue[7] = SYMB_NUL;
}
}
case MODE_UP:
zero=1;
case MODE_DEBUG:
dispvalue[8]=settime[0]; //second /value
dispvalue[9]=settime[1]; //minute /address
dot |= 0x03; //:
break;
case MODE_DATE:
if(step==3)
{
dispvalue[8]= time[7]; //year L
dispvalue[9]= lastyear[4]; //year H
zero=3;
}
else
{
dispvalue[8]= time[6]; //month
dispvalue[9]= time[5]; //day
dot=0x02; //.
zero=1;
}
break;
case MODE_ALARM:
dot |= 0x88;
if(step&0x01)
{
dispvalue[4] = SYMB_g;
dispvalue[5] = SYMB_n;
dispvalue[6] = SYMB_o;
dispvalue[7] = SYMB_NUL;
}
case MODE_TIME:
dispvalue[8] = time[2]; //minute
dispvalue[9] = time[3]; //hour
dot|=(step&0x01)?0x00:0x03; //:
zero=1;
break;
default:
dispvalue[8] = 0xEE;
dispvalue[9] = 0xEE;
}
}
dispvalue[0] = symbol[dispvalue[8]&0x0f];
dispvalue[1] = symbol[dispvalue[8]>>4];
dispvalue[2] = symbol[dispvalue[9]&0x0f];
dispvalue[3] = symbol[dispvalue[9]>>4];
if (alarm[0] && (mode != (MODE_EDIT | MODE_SETALARM)))
dot |= 0x08;
for(i=0; i 0; --i);
}
while ((IFG1 & OFIFG));
#endif
BCSCTL2 =
SELM_2| //MCLK from XT2
DIVM_1| //MCLK = 6/2 MHz
SELS| //SMCLK from XT2
DIVS_0; //SMCLK = 6/1 MHz
//init SPI
U0CTL =
SWRST| //reset
MM| //master
SYNC| //SPI mode
CHAR; //8 bit data
U0TCTL=
CKPH| //polarity and phase
SSEL0|SSEL1| //SMCLK
STC; //3 pin mode
U0BR1=0x00;
U0BR0=0x02; //baudrate = SMCLK/2 = 3MHz
U0MCTL=0x00;
ME2|=USPIE0;
U0CTL &= ~SWRST;
//init RTC
//Looks like sometimes the first SPI reads from the RTC may be incorrect.
//To be sure, let's do a few redundant reads before starting the real work
#if !(defined DEBAG)
for (i = 0xFF; i > 0; --i)
spi(RTC_READ,0x00,inbuf,outbuf,1);
#endif
//set 24 hour mode
spi(RTC_READ,0x03,inbuf,outbuf,1);
if(inbuf[0]&0x40)
{
outbuf[0]=inbuf[0]&(~0x40);
spi(RTC_WRITE,0x03,inbuf,outbuf,1);
}
//enable battery
spi(RTC_READ,0x04,inbuf,outbuf,1);
if(!(inbuf[0]&0x08))
{
outbuf[0]=inbuf[0]|0x08; //enable battery
spi(RTC_WRITE,0x04,inbuf,outbuf,1);
}
//set control register
//spi(RTC_READ,0x08,inbuf,outbuf,1);
outbuf[0]=
0x40| //squarewave enable
0x00; //1Hz
//no hardware alarms :(
spi(RTC_WRITE,0x08,inbuf,outbuf,1);
//get (soft) alarm from RTC SRAM
spi(RTC_READ,0x20,inbuf,outbuf,3);
alarm[0]=(inbuf[0]==ALARMVALUE)?ALARMVALUE:0;
alarm[1]=inbuf[1];
alarm[2]=inbuf[2];
spi(RTC_READ,0x2f,inbuf,outbuf,1); // <- because of RTC bug (?)
//get last countdown value from RTC SRAM
spi(RTC_READ,0x23,inbuf,outbuf,3);
if(inbuf[0]==ALARMVALUE)
{
lastcdn[0]=inbuf[1];
lastcdn[1]=inbuf[2];
}
else
{
lastcdn[0]=0;
lastcdn[1]=0;
}
spi(RTC_READ,0x2f,inbuf,outbuf,1); // <- because of RTC bug (?)
//get last year value from RTC SRAM
spi(RTC_READ,0x26,inbuf,outbuf,3);
if(inbuf[0]==ALARMVALUE)
{
lastyear[0]=inbuf[1];
lastyear[1]=inbuf[2];
}
else
{
lastyear[0]=0x00;
lastyear[1]=0x20;
//also clear calibration
outbuf[0]=0x00;
spi(RTC_WRITE,0x09,inbuf,outbuf,1);
}
lastyear[3]=0;
//start oscillator
spi(RTC_READ,0x01,inbuf,outbuf,1);
if(!(inbuf[0]&0x80))
{
outbuf[0]=inbuf[0]|0x80;
spi(RTC_WRITE,0x01,inbuf,outbuf,1);
}
//init timer:
TACCR0 = BAUDCOUNT - 1;
TACCTL0= //also, compare, not capture
// CCIE| //enable interupt
OUTMOD_4; //toggle
TACTL=
TASSEL_1| //ACLK, 6MHz
ID_0| //6MHz/1
MC_1| //up mode
TACLR; //reset
// P2SEL|=0x04; //P2.2 timer output.
// Init interrupts
P2IE=
0x10| //p2.4 - int. from RS232
0x08| //p2.3 - int. from RTC
0x02| //p2.1 - int. from button A
0x01| //p2.0 - int. from button B
0x20; //p2.5 - int. from button C
P2IES|=0x01|0x02|0x20|0x10; //button int. on falling edge
P2IES&= ~0x08; //RTC int. on rising edge
P2IFG=0x00;
if(!(P2IN&0x02)) //button A pressed at start?
mode=MODE_CALIB;
else if(!(P2IN&0x01)) //button B pressed at start?
{
for (i=0; i<8; ++i)
calcdata[i] = 0x00;
mode=MODE_CALC;
}
else if(!(P2IN&0x20)) //button C pressed at start?
mode=MODE_VER;
else
mode=MODE_TIME;
enter();
__enable_interrupt();
while(1)
{
event &= EVENTS; //to cancel undefined events
if(event)
{
i=0; //block state setup;
if (event & EVENT_A)
{
event &= ~ EVENT_A;
sendevent(EVENT_A);
if (mode & MODE_EDIT)
{
switch (mode)
{
case MODE_EDIT | MODE_SETCALCOPER:
case MODE_EDIT | MODE_SETCALCNUM:
mode = MODE_CALC;
break;
case MODE_EDIT | MODE_SETDEBUGADDR:
settime[0]=settime[2];
case MODE_EDIT | MODE_SETDEBUGVAL:
mode = run ? MODE_DEBUG : MODE_VER;
break;
case MODE_EDIT | MODE_SETYEAR:
case MODE_EDIT | MODE_SETDATE:
mode=MODE_DATE;
break;
case MODE_EDIT | MODE_SETDOWN:
if(changed)
break;
default:
mode=MODE_TIME;
}
enter();
}
else
{
switch (mode)
{
case MODE_CALC:
if (calcdata[7] || calcdata[1] || calcdata[0]) //if error on non-0
{
for (j=0; j<8; ++j)
calcdata[j] = 0x00;
}
else
mode = MODE_TIME;
break;
case MODE_RESET:
case MODE_DEBUG:
case MODE_VER:
mode = MODE_RESET;
break;
case MODE_DOWN:
mode = MODE_EDIT | MODE_SETDOWN;
case MODE_UP:
if(run || settime[0] || settime [1]) //reset
{}
else
mode = MODE_EDIT | MODE_SETDOWN;
break;
case MODE_DATE:
mode = MODE_UP;
break;
case MODE_TIME:
mode = MODE_DATE;
break;
default:
mode = MODE_TIME;
}
enter();
}
}
else if (event & EVENT_B)
{
event &= ~ EVENT_B;
sendevent(EVENT_B);
if (mode & MODE_EDIT)
{
if (digit)
{
switch(mode)
{
case MODE_EDIT | MODE_SETDATE:
if (digit==2)
{
limit[0] = (settime[1]==0x02) ?
(leap?0x29:0x28) :
(
(((settime[1]&0x18) && !(settime[1]&0x01)) || (!(settime[1]&0x18) && (settime[1]&0x01))) ? 0x31 : 0x30
);
if (settime[0] > (limit[0]|0xf))
settime[0] = 0x01;
}
break;
case MODE_EDIT | MODE_SETDOWN:
changed=1;
break;
}
--digit;
i = digit >> 1;
if(!(digit&0x01))
{
if ((settime[i] > limit [i]) && (mode!=(MODE_EDIT | MODE_SETDOWN))) //don't check for countdown!
settime[i] &= 0xf0;
}
if ((settime[i]==0) && (mode == (MODE_EDIT | MODE_SETDATE)))
settime[i] = 0x01;
}
else
{
leave();
enter();
}
}
else
{
switch (mode)
{
case MODE_CALC:
if(calcdata[7])
{
for (j=0; j<8; ++j)
calcdata[j] = 0x00;
}
else
{
calcdata[4]=calcdata[0];
calcdata[5]=calcdata[1];
calcdata[6]&= ~0x30;
calcdata[6]|=(calcdata[6]&0x03)<<4;
cont=1;
mode=MODE_EDIT | MODE_SETCALCOPER;
}
break;
case MODE_DEBUG:
case MODE_VER:
mode = MODE_EDIT | MODE_SETDEBUGADDR;
break;
case MODE_CALIB:
mode = MODE_EDIT | MODE_SETCALIB;
break;
case MODE_DOWN:
if(!settime[0] & !settime[1])
break;
case MODE_UP:
run = !run;
i = 1;
break;
case MODE_DATE:
mode = MODE_EDIT | MODE_SETDATE;
break;
case MODE_ALARM:
mode = MODE_TIME;
break;
case MODE_TIME:
mode = MODE_EDIT | MODE_SETTIME;
break;
default:
i = 1;
}
if(!i)
enter();
}
}
else if (event & EVENT_C)
{
event &= ~ EVENT_C;
sendevent(EVENT_C);
if (mode & MODE_EDIT)
{
changed = 1;
i = digit >> 1;
j = settime[i];
if (digit & 0x01)
{
j += 0x10;
if (j> (limit [i] | 0x0f))
j &= 0x0F;
}
else
{
if ((mode == (MODE_EDIT | MODE_SETDATE)) && (digit == 2))
{
j = bcd_add_byte(j,0x01);
if (j>0x12)
j=0x01;
}
else
{
j = (j&0xf0) | ((j+0x01)&0x0f);
if((j>limit[i]) || (decimal && ((j&0x0f)>0x09)))
j &= 0xF0;
}
if ((mode == (MODE_EDIT | MODE_SETDATE)) && (j == 0))
j = 0x01;
}
settime[i] = j;
}
else
{
switch (mode)
{
case MODE_CALC:
if(calcdata[7])
{
for (j=0; j<8; ++j)
calcdata[j] = 0x00;
}
else
{
cont=0;
mode=MODE_EDIT | MODE_SETCALCNUM;
}
break;
case MODE_DEBUG:
mode = MODE_EDIT | MODE_SETDEBUGVAL;
break;
case MODE_DOWN:
if(!run && !settime[0] && !settime[1])
run=1;
settime[0]=lastcdn[0];
settime[1]=lastcdn[1];
i = 1;
break;
case MODE_UP:
settime[0]=0;
settime[1]=0;
i = 1;
break;
case MODE_DATE:
mode = MODE_EDIT | MODE_SETYEAR;
break;
case MODE_ALARM:
mode = MODE_TIME;
break;
case MODE_TIME:
mode = MODE_EDIT | MODE_SETALARM;
break;
default:
i = 1;
}
if(!i)
enter();
}
}
else if (event & EVENT_RS) //received frame
{
event &= ~ EVENT_RS;
rxcmd = rxbuf[0]; //get command
for(i=0; i<(RXBUFSIZE-1); ++i) //decode arguments: hex digits
{
j=rxbuf[i+1];
if(j>='a') //decode digits
j-= 87;
else if(j>='A')
j-= 55;
else
j-= 48;
if(i&0x01)
rxdata[i>>1] |= j&0x0f;
else
rxdata[i>>1] = j<<4;
}
if(rxcmd & 0x20) //lowercase
{
switch (rxcmd)
{
case 'g': //force display value
for (i=0; i<4; ++i)
forcedisp[i]=rxdata[i];
forcedisp[4]=1;
break;
case 'h': //free display value
forcedisp[4]=0;
break;
case 'i': //trigger event
event |= rxdata[0];
break;
case 'j': //go to mode
mode = rxdata[0];
enter();
break;
case 'k': //select digit
digit = rxdata[0];
break;
case 'l': //value under digit
i = digit >> 1;
j = settime[i];
if (digit & 0x01)
{
j &= 0x0f;
j |= rxdata[0]<<4;
}
else
{
j &= 0xf0;
j |= rxdata[0]&0x0f;
}
settime[i] = j;
break;
case 'm': //confirm editing
changed=1;
leave();
enter();
break;
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v': //enter values
changed = 1;
settime[0] = rxdata[0];
settime[1] = rxdata[1];
settime[2] = rxdata[2];
switch (rxcmd)
{
case 'n': //set time
mode = MODE_EDIT | MODE_SETTIME;
break;
case 'o': //set alarm
mode = MODE_EDIT | MODE_SETALARM;
if(settime[0]==0xff && settime[1]==0xff)
{
changed=0;
alarm[0]=ALARMVALUE;
}
break;
case 'p': //set date
mode = MODE_EDIT | MODE_SETDATE;
break;
case 'q': //set year
mode = MODE_EDIT | MODE_SETYEAR;
break;
case 'r': //set countdown
mode = MODE_EDIT | MODE_SETDOWN;
run=0;
break;
case 's': //set calibration
mode = MODE_EDIT | MODE_SETCALIB;
break;
case 't': //set debug address
mode = MODE_EDIT | MODE_SETDEBUGADDR;
break;
case 'u': //set debug value
mode = MODE_EDIT | MODE_SETDEBUGVAL;
break;
case 'v': //perform calculation
calcdata[0]=0x00;
calcdata[1]=0x00;
for (i=0; i<5; ++i)
calcdata[i+2]=rxdata[i];
// calcdata[3]=rxdata[1];
// calcdata[4]=rxdata[2];
// calcdata[5]=rxdata[3];
// calcdata[6]=rxdata[4];
calcdata[7]=0;
run = 1;
cont = 0xFF;
mode = MODE_EDIT | MODE_SETCALCNUM;
}
leave();
break;
case 'w': //set value
settime[0]=rxdata[0];
settime[1]=rxdata[1];
settime[2]=rxdata[2];
break;
case 'x': //set run/pause
run = rxdata[0];
break;
case 'y': //set buzzer mode
buzzmode = rxdata[0];
break;
case 'z': //set memory
p = (unsigned char*)(*((unsigned short*)rxdata));
*p = rxdata[2];
break;
}
}
else //uppercase
{
send(rxcmd);
switch (rxcmd)
{
case 'G': //get time
gettime();
for (i=0; i<8; ++i)
{
if (i != 4)
sendhex(time[i]);
}
sendhex(lastyear[4]); //yH
break;
case 'H': //get state
sendhex(mode);
break;
case 'I': //get display
for (i=0; i<4; ++i)
sendhex(dispvalue[i]);
break;
case 'J': //get value
sendhex(settime[0]);
sendhex(settime[1]);
sendhex(settime[2]);
break;
case 'K': //get calculation
for (i=0; i<8; ++i)
sendhex(calcdata[i]);
break;
case 'L': //get run state
sendhex(run);
break;
case 'M': //get version
sendhex(3); //vL
sendhex(2); //vH
break;
case 'N': //get pinout
sendhex(PIN_AB);
sendhex(PIN_CD);
sendhex(PIN_EF);
sendhex(PIN_GH);
break;
case 'O': //subscribe for events
eventmask = rxdata[0];
sendhex(0);
break;
case 'P': //get alarm
if(alarm[0])
{
sendhex(alarm[1]);
sendhex(alarm[2]);
}
else
{
sendhex(0xFF);
sendhex(0xFF);
}
break;
case 'Q': //get diit
sendhex(digit);
break;
case 'R': //read memory
p = (unsigned char*)(*((unsigned short*)rxdata));
sendhex(*p);
break;
}
send('\n');
}
}
else if (event & EVENT_RTC)
{
event &= ~ EVENT_RTC;
sendevent(EVENT_RTC);
gettime();
exec();
}
showvalue();
display(forcedisp[4]?forcedisp:dispvalue);
}
else //go to low power mode if nothing's going on
{
lpm3();
}
}
}