;Countdown clock. ; ;Copyright (c) 1999 Kendrick Lamont Webster (ken@webster.org, ;http://ken.webster.org). ; ;This device keeps track of the current time and a target ;countdown time. It communicates with a series of daisy- ;chained 2-wire serial-input 7-segment LED display drivers ;to display the time or countdown time (depending on the ;mode setting). It also communicates with a host computer ;over an RS232 serial connection for changing the time and ;settings. ; ; ;Target processor: ; ;PIC16C73B ;4096 x 14 EPROM ;192 x 8 RAM ;4MHz main oscillator ;32.768kHz Timer1 crystal ;WDT disabled ;Battery backup ; ; ;Pin assignments: ; ;Function Port Bit Pin ;---------------------------------------------------- ;Reset\ (tie to Vdd thru 10k) 1 ;CdS ambient light sensor A 0 2 * ;Alarm 1 output A 1 3 * ;Alarm 2 output A 2 4 ;Battery test input A 3 5 * ;"Mode" pushbutton input A 4 6 # ;Battery test control out A 5 7 ;Vss (GND) 8 ;OSC1/CLKIN (RC or crystal) 9 ;OSC2/CLKOUT (crystal) 10 ;Timer1 crystal (32768Hz) C 0 11 ;Timer1 crystal (32768Hz) C 1 12 ;Date serial port clock out C 2 13 ;Date serial port data out C 3 14 ;Days serial port clock out C 4 15 ;Days serial port data out C 5 16 ;Asynch (host) serial data out C 6 17 ;Asynch (host) serial data in C 7 18 ;Vss (GND) 19 ;Vdd 20 ;External power sense B 0 21 ;LED -- 1PP16S (batt) / 1PPS B 1 22 ;Time serial port clock out B 2 23 ;Time serial port data out B 3 24 ;Alarm 3 output B 4 25 ;Alarm 4 output B 5 26 ;Alarm 5 output B 6 27 ;"Set" pushbutton input B 7 28 ; ; * = all or none as A/D input vs digital I/O ; but these can be programmed as digital output ; even when they are functioning as A/D input ; ; # = open-drain output ; ; ; ;"Set" pushbutton useage: ; ;Press and hold for more than 1 second .. first digit starts flashing. ;Press the "mode/increment" pushbutton to increment that digit. Press ;the "set" pushbutton again to advance to the next digit. Note that ;all digits of the time and date will be visited, so, if your display ;doesn't have the hundreds place and thousands place of the year then ;the last two times you press the "set" pushbutton, the flashing digit ;will be off of your display. To allow all fields to be set on any ;display, a copy of the digit currently being set will appear in the ;first position on each chain (10s place of hours, 1s place of day of ;year, 1s place of day of month) and will flash rapidly unless it is ;the one being set (in which case it will flash at the slower rate). ;The digits are visited in the following order: s, 10s, m, 10m, h, 10h, ;day, 10day, month, 10month, y, 10y, 100y, 1000y. ; ;After the last digit is set, pressing the "set" pushbutton one more ;time will cause the entire display to flash rapidly. Pressing the ;"mode/increment" pushbutton will then accept the edited time and start ;the clock. This "accept time" mode may be entered at any time durring ;the set procedure by holding the "set" button down for more than 1 ;second. Pressing the "set" button in the "accept time" mode will ;advance the cursor to the first digit, thus re-starting the clock ;setting cycle. ; ;Note that the sequence of digits shown as the digit is 'incremented' ;includes an 'F' (for "flip") which, if selected by pressing the "set" ;pushbutton, will invert the digit and not advance the cursor (the ;digit which was just inverted remains flashing so that its value may ;then be set). This function can be used to set up digits which are ;intentionally placed upside-down in order to line up the decimal ;points of 2 adjacent digits to produce a colon. ; ;A 'd' also appears in the increment sequence. Selecting the 'd' will ;toggle whether the decimal point is off, on, or blinking. Since the ;digit blinks to indicate that it is selected, the decimal point will ;not blink with the digit but rather will blink only if configured to ;blink. As with the 'F', selecting 'd' will toggle the function but ;not advance the cursor to the next digit. Only selecting a number ;will advance the cursor. ; ;If the clock is being set (a digit is flashing) and no pushbutton ;activity occurs for a period of 30 seconds, the operation is aborted ;and the clock returns to normal operation without changing the time ;setting. No abort will occur in the "accept time" mode (the entire ;display is flashing) since the user may wish to enter a time several ;seconds ahead of the actual time and wait to start the clock. ; ; ;To set countdown time: ; ;Press and hold for more than 1 second .. first digit starts flashing .. ;continue to hold for another second .. display shows "A0" .. now ;release the button. The countdown time appears with the first digit ;flashing. Procede the same as with setting the actual time. ; ; ;To set alarm times: ; ;Press and hold for more than 1 second .. first digit starts flashing .. ;continue to hold for another second .. display shows "A0" .. continue ;to hold .. display shows "A1". If you want to set the Alarm1 time, then ;let go now, otherwise continue to hold until the desired alarm ;identifier ("A2" .. "A5") shows. Once you release the button, procede ;the same as with setting the actual time or countdown time. ; ; ; ;"Mode/increment" pushbutton useage: ; ;The mode/increment pushbutton toggles between displaying the current ;time and displaying the countdown timer. When setting the time, the ;mode/increment pushbutton increments the selected digit. When in the ;"accept time" mode, the mode/increment pushbutton accepts the setting ;and starts the clock. ; ; ;************************************************************************ ;Display drivers ; ;The display drivers with which this processor communicates are PIC16C505 ;MCUs programmed to accept a segment pattern and brightness level over ;a synchronous 2-wire serial input port. The display drivers are daisy- ;chained. There are 3 different chains, one for the time (hh:mm:ss.t), ;one for the date (mm/dd/yy), and one for the days (ddd). Each digit ;is identical (there are no addresses, etc.). To enable the user to ;leave off unimportant digits, the chains are ordered such that the least ;useful digits are furthest downstream. The chain orders are: ; ;Time chain: H10, H1, M10, M1, S10, S1, ST ;Date chain: MD1, MD10, N1, N10, Y1, Y10, Y100, Y1000 ;Days chain: D1, D10, D100, D1000 ; ;Where H = hours, M = minutes, S = seconds, ST = 10ths of seconds, ;MD = day of month, N = month, Y = year, D = day of year (or days ;in countdown mode). ; ; ;Excerpts from the comments from the display drivers' firmware relating ;to the communications protocol are copied below for reference: ; ;------------------------------------------------------------------------ ;Serial data communications protocol: ; ;The serial port is synchronous. Data is sampled shortly after the ;high-to-low transition of the clock signal. A special start condition ;is defined as a high-to-low transition on the data line when the clock ;is low and has been low for a sufficient amount of time. This start ;condition is used to mark the beginning of a data frame. ; ;Normal data bit: ;(X = don't care) ; ; --------------------- ----------- ; | | | ; Clk | | | ; | | | ; ---------- -------------------- ; ; | t[ch] | t[cl] | ; ; | t[cycle] | ; ; ---------- --------------------- -------------------- ----------- ; |XXXXXXXXXXXXXXXXXXXXX| |XXXXXXXXXXX ; Data |XXXXXXXXXXXXXXXXXXXXX| valid databit |XXXXXXXXXXX ; |XXXXXXXXXXXXXXXXXXXXX| |XXXXXXXXXXX ; ---------- --------------------- -------------------- ----------- ; ;The data line is sampled as early as 4 microseconds and as late as 29 ;microseconds after the high-to-low clock transition. The exact delay ;depends on the state of the software at the time of the transition. ; ;Received t[cl] must thus be greater than 29 microseconds in order to ;guarantee proper reception. Due to the downstream accumulation of clock ;jitter caused by the variable delay between a clock edge and a given ;controller detecting the edge, the t[cl] as transmitted by the master ;controller would need to be n * 29 microseconds (where n is the number ;of chained controllers) in order to guarantee operation under worst-case ;conditions (all of the controllers in the chain are in the software ;state at which the maximum delay occurs on one half cycle and minimum ;delay occurs on the opposite half cycle). In actual operation, this ;situation is extremely unlikely since each microcontroller is acting ;independently and the delays will tend to average out and be distributed ;evenly between the high and low clock phases. ; ;A shorter t[cl] may therefore be used if the possibility of an ;occasional miscommunication is acceptable. In a display such as this, ;such a miscommunication would result only in a brief (probably ;imperceptable to humans) flicker. ; ;Therefore, a transmitted t[cl] of 100 microseconds should be more than ;sufficient. The t[ch] requirements are slightly more relaxed (since the ;data bit is not being sampled) but should be aproximately the same ;duration since clock jitter accumulates here too. A minimum t[cycle] of ;200 microseconds with 50% duty is thus recommended. This cycle time ;would allow a 10-digit display to be refreshed 60 times per second ;(or refreshed with brightness information included at a rate of 30 ;updates per second). ; ; ; ;Start condition: ;(X = don't care) ; ; --------- -------- ; | | ; Clk | | ; | | ; ----------------------------------------------- ; ; | t[cldl] | t[dlch] | ; ; --------- ----------------------- -------- ; XXXXXXXXX| | |XXXXXXXX ; Data XXXXXXXXX| | |XXXXXXXX ; XXXXXXXXX| | |XXXXXXXX ; --------- ----------------------- -------- ; ;The t[cldl] and t[dlch] here should be at least 100 microseconds also due ;to the same reasons given (above) for choosing 100 microseconds as the ;minimum recommended clock low and clock high times. ; ; ; ;------------------------------------------------------------------------ ;Communications protocol ;Data frame format ; ;All data is sent with the least significant bit first. The first data ;bit (following the start condition) specifies the frame type: ; ; Type bit Frame contents (described below) ; --------------------------------------------- ; 0 Segment patterns ; 1 Brightness levels ; ; ; ;Data following the type bit is grouped into 8-bit bytes and organized ;into the following types of frames (as specified by the type bit): ; ; ;Segment patterns ;---------------- ;This frame consists of n data bytes where n is the number of chained ;controllers. One byte is targetted for each controller in the chain. ;Each controller accepts the first byte in the frame for its own use, ;removes it from the frame, and passes all subsequent bytes downstream. ; ;Removal of the first byte is accomplished by supressing the clock output ;(holding it in a high state) while receiving the byte. Input data and ;clock signals are echoed to the output port at all other times. ; ;The data byte each controller receives specifies the pattern to light up ;on the LED display. Bit 0 corresponds to segment 'a', bit 1 to segment ;'b', ... bit 7 corresponds to the decimal point. For each bit, a logic ;1 specifies that the segment is lit. ; ;Segment diagram: ; ; ; aaaaaaaaaaaaaaaa ; f b ; f b ; f b ; f b ; f b ; f b ; f b ; f b ; ggggggggggggggggg ; e c ; e c ; e c ; e c ; e c ; e c ; e c ; e c ; ddddddddddddddddd ; ; ; ;Brightness levels ;----------------- ;This frame follows the same chaining scheme as the segment patterns ;(with the target controller removing its own data byte and passing the ;remainder of the frame downstream). ; ;The byte a controller receives specifies a PWM brightness level where ;the value 0 corresponds to an almost doused (barely lit .. a 1/256 duty ;cycle) display and the value 255 corresponds to the maximum brightness ;level (100% duty cycle). ;------------------------------------------------------------------------ LIST p=16C73B, r=dec INCLUDE P16C73B.INC ;Application-specific general-purpose register useage: CBLOCK 0x20 REG_I ;Temporary register for calculations, etc. REG_J ;Ditto REG_K REG_L REG_M TEMP1 ;Temporary register for lower level subroutines TEMP2 TEMP3 TEMP4 TEMP5 RES4 ;Result register for multiply RES3 RES2 RES1 RES0 APP_FLAGS ;Application-specific flags .. bits are defined below RX_STATE ;Async (host) serial data receive state-machine state TX_STATE ;Async (host) serial data transmit state-machine state STX_TIMER ;Serial data transmit timer UDT_TIMER ;Time update timer SET_MODE ;0 if not setting time, 1 = set real time, 2 = set event time, 3..7 = set alarm 1..5 time SET_DIGIT ;0..14 = y1000, y100, y10, y1, m10, m1, d10, d1, h10, h1, n10, n1, s10, s1, accept SETSW_DEBOUNCE ;Debounce counter for the "set" pushbutton MODESW_DEBOUNCE ;Debounce counter for the "mode/increment" pushbutton SETSW_TIMER ;1/256th seconds .. timer for "long press" on "set" pushbutton MODESW_TIMER ;1/256th seconds .. timer for "long press" on "mode/increment" pushbutton BLINK_PHASE ;1/256th seconds .. cleared to phase 0 upon any pushbutton activity (so digit relights) STXS_TIME ;Serial data transmit state for time display chain STXD_TIME ;Serial data transmit digit index for time display chain STXB_TIME ;Serial data transmit bit index for time display chain STXY_TIME ;Serial data transmit byte for time display chain STXS_DATE ;Serial data transmit state for date display chain STXD_DATE ;Serial data transmit digit index for time display chain STXB_DATE ;Serial data transmit bit index for time display chain STXY_DATE ;Serial data transmit byte for time display chain STXS_DAYS ;Serial data transmit state for days display chain STXD_DAYS ;Serial data transmit digit index for days display chain STXB_DAYS ;Serial data transmit bit index for days display chain STXY_DAYS ;Serial data transmit byte for days display chain BRIGHTNESS ;Global brightness level for display (controlled by CdS input) BRIGHTNESSL ;fixed-point fraction for lowpass filtering PORTA_MIRROR ;Mirror for doing bitwise operations on PORTA outputs PORTB_MIRROR ;Ditto for PORTB PORTC_MIRROR ;Ditto for PORTC LAST_TMR1L ;The Timer1 value read on the prior subseconds update LAST_TMR1H ;... this allows a delta to be calculated and added to SUBSEC3..0 OSCCAL3 ;Least significant 8 bits of 32kHz oscillator calibration OSCCAL2 ;Next 8 bits of 32kHz oscillator calibration OSCCAL1 ;Next 8 bits (fractions of seconds) of 32kHz oscillator calibration OSCCAL0 ;Most significant 8 bits (whole seconds) of 32kHz oscillator calibration ;Note: OSCCAL is the number of seconds per increment of the high nybble of Timer1 ;with 8:1 prescaler .. ; ;It is thus 1.00000 with a perfect 32.768kHz crystal. OSCCAL0 holds the whole ;seconds and OSCCAL1 .. OSCCAL3 hold fractions of seconds. ; ;There is enough resolution to trim the frequency to +/- 1 part in 16,777,216. ;This amount of error corresponds to 1.8 seconds in 1 year (or about 5 milliseconds per ;day -- far less than one would expect from other variables such as the crystal's ;temperature coefficient and aging). SUBSEC3 ;Least significant 8 bits of subseconds SUBSEC2 ;Next 8 bits of subseconds SUBSEC1 ;Next 8 bits of subseconds SUBSEC0 ;Most significant 8 bits of subseconds SECONDS ;The name says it all .. plain binary format (not BCD) MINUTES ;(these next few registers represent the current time) HOURS MDAY ;Day of month -1 (0..30) MONTH ;0..11 YEARL ;Year mod 100 YEARH ;Year div 100 ;note: the above group of variables is treated like a structure .. the target time, ;editing time, and alarm times all follow the same pattern (are instances of the ;same structure) YDAYL ;(Day of year -1) mod 100 (derived from MM/DD/YY) YDAYH ;(Day of year -1) div 100 ESECONDS ;Edit seconds (for a time being edited via the pushbuttons) EMINUTES ;Next few registers are just like the realtime registers EHOURS ;except that they represent the edited time instead EMDAY ;of the current time EMONTH EYEARL EYEARH TSECONDS ;Target seconds (target = time to count down to) TMINUTES ;Next few registers are just like the realtime registers THOURS ;except that they represent the target time instead TMDAY ;of the current time TMONTH TYEARL TYEARH DSUBSEC3 ;Delta(subseconds) = 0 - Current_time DSUBSEC2 ;(borrowing from DSECONDS) DSUBSEC1 DSUBSEC0 DSECONDS ;Delta(seconds) = Target_time - Current_time DMINUTES ;Delta(minutes) DHOURS ;Delta(hours) DDAYL ;Delta(days) mod 100 DDAYH ;(Delta(days) mod 10000) div 100 DDAYH1 ;Delta(days) div 10000, low byte DDAYH0 ;Delta(days) div 10000, high byte ;May add this stuff later, time permitting ... ;Otherwise just blank out MM/DD/YYYY when in countdown mode ; DMDAY ; DMONTH ; DYEARL ; DYEARH ENDC ;Application-specific general-purpose register useage, high bank: CBLOCK 0xa0 A1SECONDS ;Alarm1 seconds (for Alarm1 output signal) A1MINUTES ;Next few registers are just like the realtime registers A1HOURS ;except that they represent the Alarm1 time instead A1MDAY ;of the current time A1MONTH A1YEARL A1YEARH A2SECONDS ;Alarm2 seconds (for Alarm2 output signal) A2MINUTES ;Next few registers are just like the realtime registers A2HOURS ;except that they represent the Alarm2 time instead A2MDAY ;of the current time A2MONTH A2YEARL A2YEARH A3SECONDS ;Alarm3 seconds (for Alarm3 output signal) A3MINUTES ;Next few registers are just like the realtime registers A3HOURS ;except that they represent the Alarm3 time instead A3MDAY ;of the current time A3MONTH A3YEARL A3YEARH A4SECONDS ;Alarm4 seconds (for Alarm4 output signal) A4MINUTES ;Next few registers are just like the realtime registers A4HOURS ;except that they represent the Alarm4 time instead A4MDAY ;of the current time A4MONTH A4YEARL A4YEARH A5SECONDS ;Alarm5 seconds (for Alarm5 output signal) A5MINUTES ;Next few registers are just like the realtime registers A5HOURS ;except that they represent the Alarm5 time instead A5MDAY ;of the current time A5MONTH A5YEARL A5YEARH ENDC ;Bits in the APP_FLAGS register: CBLOCK 0 PAST_TARGET ;set when the real time is beyond the countdown target time COUNTDOWN_MODE ;set if the display is showing countdown time, clear if real time CHECK_BATTERY ;set once per hour or on-demand via serial port .. triggers an A/D reading of the battery voltage SETSW_LONG ;signal from timer routine to pushbutton routine that the SETSW_TIMER expired SETSW_LONG1 ;sticky version of SETSW_LONG .. cleared only when button is released MODESW_LONG ;signal from timer routine to pushbutton routine that the MODESW_TIMER expired MODESW_LONG1 ;sticky version of MODESW_LONG .. cleared only when button is released BLINK_ALL_SET ;set if entire display should blink while in "set" mode (for "accept" mode) ENDC ;------------------------------------------------------------------------ ; Startup (coldstart) code ;------------------------------------------------------------------------ ;This code only executes when the battery is replaced or installed ;for the very first time. ;------------------------------------------------------------------------ ORG 0 ;Reset vector is at 0x0000 NOP ;The interrupt vector (0x0004) NOP ;is not used so make it do the NOP ;same thing as a reset. NOP CLRF PCLATH BCF STATUS,RP0 ;Clear all register file locations 0x20 .. 0x7f ;(Initialize all application-specific registers to zero) MOVLW 0x20 MOVWF FSR LPCLRREGS CLRF INDF INCF FSR,F BTFSS FSR,7 GOTO LPCLRREGS ;Clear all register file locations 0xa0 .. 0xff MOVLW 0xa0 MOVWF FSR LPCLRREGS2 CLRF INDF INCFSZ FSR,F GOTO LPCLRREGS2 ;Application-specific non-zero initializations MOVLW 0x64 MOVWF OSCCAL3 MOVLW 0xcf MOVWF OSCCAL2 MOVLW 0x8b MOVWF OSCCAL1 ; CLRF OSCCAL0 ;Scaling factor for using a 60kHz crystal (that is all I had on hand) assuming ;that the crystal is perfect. ;Enable Timer1, external oscillator, 8:1 prescale ;With a 32.768kHz crystal, an overflow will occur every 16 seconds MOVLW 0x3f MOVWF T1CON CALL DISABLE_PERIPHERALS ;power-down mode BSF STATUS,RP0 MOVLW (1 << TMR1IE) ;enable timer1 overflow as a MOVWF PIE1 ;peripheral interrupt trigger BCF STATUS,RP0 ;Enable peripheral interrupt trigger and external INT (power sense) interrupt ;triggers but don't set GIE .. the triggers will cause the processor to wake ;from SLEEP but we don't actually want any interrupts MOVLW (1 << PEIE) | (1 << INTE) MOVWF INTCON BTFSC PORTB,0 GOTO POWER_UP ;we do have external power POWER_DOWN CALL DISABLE_PERIPHERALS BCF INTCON,INTF CALL UPDATE_TIME ;------------------------------------------------------------------------------ ; Main loop for power-down mode ;------------------------------------------------------------------------------ BATTERY_POWER_LOOP SLEEP BTFSC INTCON,INTF ;external power? GOTO POWER_UP MOVLW 0xff MOVWF PORTB BSF STATUS,RP0 BCF TRISB,1 ;turn on "battery heartbeat" LED BCF STATUS,RP0 CALL UPDATE_TIME BTFSC PORTB,0 ;check for power GOTO POWER_UP BSF STATUS,RP0 BSF TRISB,1 ;turn off "battery heartbeat" LED BCF STATUS,RP0 GOTO BATTERY_POWER_LOOP POWER_UP CALL ENABLE_PERIPHERALS ;------------------------------------------------------------------------------ ; Main loop for external power mode ;------------------------------------------------------------------------------ EXTERNAL_POWER_LOOP CALL DRIVE_DISPLAY CALL HOST_COMM CALL CHECK_PUSHBUTTONS INCF UDT_TIMER,F BTFSS UDT_TIMER,7 GOTO EXTERNAL_POWER_LOOP ;only do this part of the loop once for every 128 iterations of the above CLRF UDT_TIMER CALL UPDATE_TIME BTFSS PORTB,0 ;do we still have power? GOTO POWER_DOWN ;no .. go to low power mode GOTO EXTERNAL_POWER_LOOP ;------------------------------------------------------------------------------ ;Enable the peripheral modules which are only used in powered-up mode. ;These peripheral modules are disabled in battery-power mode to save power. ENABLE_PERIPHERALS CLRF PORTA ;no alarms, no battery test MOVLW 0x0c MOVWF PORTB ;Time Clk and Data are high MOVLW 0x7c MOVWF PORTC ;Date and Days Clk and Data high, host Tx high BSF STATUS,RP0 ;Enable RA0..3 as analog inputs with the analog reference = Vdd MOVLW 2 MOVWF ADCON1 ;Set up asynchronous serial port MOVLW 25 ;9600 baud MOVWF SPBRG MOVLW 0x24 ;TxEn, high baud rate MOVWF TXSTA MOVLW 0x19 MOVWF TRISA ;RA0, RA3, and RA4 are the only inputs MOVLW 0x81 MOVWF TRISB ;RB0 and RB7 are the only inputs MOVLW 0x83 MOVWF TRISC ;RC7/Rx, RC0/T1OS0, and RC1/T1OS1 are only non-outputs BCF STATUS,RP0 ;Enable the A/D module and configure it to use fosc/8 as the clock. ;Select channel 0 (RA0) via the multiplexer MOVLW 0x41 MOVWF ADCON0 BSF RCSTA,SPEN ;Enable the serial port BSF RCSTA,CREN ;Enable reception RETURN ;------------------------------------------------------------------------------ ;Disable the peripheral modules which are only used in powered-up mode. ;These peripheral modules are disabled in battery-power mode to save power. DISABLE_PERIPHERALS BSF STATUS,RP0 MOVLW 7 MOVWF ADCON1 ;Configure RA0..3 as digital I/O CLRF TXSTA ;Disable asynchronous serial port transmit MOVLW 0xff MOVWF TRISA ;Configure all ports as inputs (they have external pullups) MOVWF TRISB MOVWF TRISC BCF STATUS,RP0 CLRF ADCON0 ;Disable the A/D module CLRF RCSTA ;Disable the serial port RETURN ;------------------------------------------------------------------------------ ;Read Timer1. Calculate the difference from the last time it was read. ;Scale the difference by the calibration factor and apply it to the time. UPDATE_TIME MOVF TMR1H,W MOVWF REG_J MOVF TMR1L,W MOVWF REG_I MOVF TMR1H,W SUBWF REG_J,W ;did a carry out of the low byte just occur? BTFSC STATUS,Z ;no, good to go GOTO UDTTIMEC1 MOVF TMR1H,W ;yes, reading it again now will get a stable value MOVWF REG_J MOVF TMR1L,W MOVWF REG_I ;Check for the count overflow flag to get the highest bit of the new value UDTTIMEC1 CLRF REG_K BTFSC PIR1,TMR1IF ;if the timer overflowed, INCF REG_K,F ;add 1 to next sig byte BTFSC REG_K,0 ;only clear the flag if it was read as a '1' BCF PIR1,TMR1IF ;(in case it just got set after it was read) ;Sampling TMR1IF here (after reading the counter) will not always ;produce correct results if UPDATE_TIME is not being called as a result ;of a timer1 interrupt .. the count could have been read at 0xffff and ;then the count rolled over and TMR1IF was set. If this is the case, ;simply increment the "value read" to 0x0000. MOVLW 0xff SUBWF REG_J,W BTFSS STATUS,Z GOTO UDT_NOROLL BTFSS REG_K,0 GOTO UDT_NOROLL CLRF REG_I ;yes, the count did roll over after CLRF REG_K ;the counter was read .. adjust the "value read" ;Ok, now REG_I .. REG_K contain the correct new count value complete with a 17th ;bit. Truncate the count to 13 bits (1/256 second resolution) to prevent rounding ;errors when multiplying by OSCCAL scale factor. Rounding errors could also be ;eliminated by extending the SUBSECONDS register but this would increase the time ;it takes to multiply and 1/256 second resolution is plenty for a display anyway. ; ;Throwing away the least sig 4 bits does not cause rounding errors because TMR1 ;is still keeping track of them. We are only discarding these bits from the ;delta used to update the real time registers. UDT_NOROLL MOVLW 0xf0 ANDWF REG_I,F ;Now save the (truncated) count read .. it will need to be copied to LAST_TMR1 ;after the subtract (below) MOVF REG_I,W MOVWF REG_L MOVF REG_J,W MOVWF REG_M ;Subtract the old count value from this new value to obtain the delta. MOVF LAST_TMR1L,W SUBWF REG_I,F MOVLW 1 BTFSS STATUS,C SUBWF REG_J,F BTFSS STATUS,C SUBWF REG_K,F MOVF LAST_TMR1H,W SUBWF REG_J,F BTFSS STATUS,C DECF REG_K,F ;now REG_I .. REG_K contain delta ;Copy this iteration's truncated count value to LAST_TMR1 to use as the ;"old value" on the next iteration MOVF REG_L,W MOVWF LAST_TMR1L MOVF REG_M,W MOVWF LAST_TMR1H ;Apply the counter delta to the time registers ... ;Scale the delta by OSCCAL to obtain a corrected time delta ... ;First, shift the delta 4 bits to the right since the least sig 4 bits were ;lopped off .. this will make the fixed-point alignment such that REG_J ;contains seconds and REG_I contains 1/256ths of seconds. SWAPF REG_I,W ANDLW 0x0f MOVWF REG_I SWAPF REG_J,W ANDLW 0xf0 IORWF REG_I,F SWAPF REG_J,W ANDLW 0x0f MOVWF REG_J SWAPF REG_K,W ANDLW 0xf0 IORWF REG_J,F ;Now the 13 bits are right-justified .. multiply by OSCCAL MOVF OSCCAL3,W MOVWF TEMP5 MOVF OSCCAL2,W MOVWF TEMP4 MOVF OSCCAL1,W MOVWF TEMP3 MOVF OSCCAL0,W ;whole seconds MOVWF TEMP2 CLRF TEMP1 MOVLW 13 MOVWF REG_K CLRF RES0 CLRF RES1 CLRF RES2 CLRF RES3 CLRF RES4 MULOSCCALLOOP RRF REG_J,F RRF REG_I,F BTFSS STATUS,C GOTO MULOSCCALSKIPADD MOVF TEMP5,W ADDWF RES4,F MOVLW 1 BTFSC STATUS,C ADDWF RES3,F BTFSC STATUS,C ADDWF RES2,F BTFSC STATUS,C ADDWF RES1,F BTFSC STATUS,C ADDWF RES0,F MOVF TEMP4,W ADDWF RES3,F MOVLW 1 BTFSC STATUS,C ADDWF RES2,F BTFSC STATUS,C ADDWF RES1,F BTFSC STATUS,C ADDWF RES0,F MOVF TEMP3,W ADDWF RES2,F MOVLW 1 BTFSC STATUS,C ADDWF RES1,F BTFSC STATUS,C ADDWF RES0,F MOVF TEMP2,W ADDWF RES1,F BTFSC STATUS,C INCF RES0,F MOVF TEMP1,W ADDWF RES0,F MULOSCCALSKIPADD BCF STATUS,C RLF TEMP5,F RLF TEMP4,F RLF TEMP3,F RLF TEMP2,F RLF TEMP1,F DECFSZ REG_K,F GOTO MULOSCCALLOOP ;Add the time-delta to the current time MOVF RES4,W ADDWF SUBSEC3,F MOVLW 1 BTFSC STATUS,C ADDWF SUBSEC2,F BTFSC STATUS,C ADDWF SUBSEC1,F BTFSC STATUS,C ADDWF SUBSEC0,F BTFSC STATUS,C ADDWF SECONDS,F MOVF RES3,W ADDWF SUBSEC2,F MOVLW 1 BTFSC STATUS,C ADDWF SUBSEC1,F BTFSC STATUS,C ADDWF SUBSEC0,F BTFSC STATUS,C ADDWF SECONDS,F MOVF RES2,W ADDWF SUBSEC1,F MOVLW 1 BTFSC STATUS,C ADDWF SUBSEC0,F BTFSC STATUS,C ADDWF SECONDS,F MOVF RES1,W ADDWF SUBSEC0,F BTFSC STATUS,C INCF SECONDS,F MOVF RES0,W ADDWF SECONDS,F ;seconds MOVLW 60 SUBWF SECONDS,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF SECONDS ;seconds rolled over INCF MINUTES,F ;increment minutes MOVLW 60 SUBWF MINUTES,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF MINUTES ;minutes rolled over INCF HOURS,F ;increment hours MOVLW 24 SUBWF HOURS,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF HOURS ;hours rolled over .. increment day BSF APP_FLAGS,CHECK_BATTERY ;check the backup battery once per hour (if in normal power mode) INCF YDAYL,F ;day of year MOVLW 100 SUBWF YDAYL,W BTFSS STATUS,C GOTO YDAYNRL MOVWF YDAYL ;day of year mod 100 rolled over INCF YDAYH,F ;increment 100s place YDAYNRL INCF MDAY,F ;day of month MOVF MONTH,W CALL GET_DAYS_IN_MONTH SUBWF MDAY,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF MDAY ;day of month rolled over INCF MONTH,F ;increment month MOVLW 12 SUBWF MONTH,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF MONTH ;month rolled over CLRF YDAYL ;day of year = 0 CLRF YDAYH ;(0 = first day of year) INCF YEARL,F ;increment year MOVLW 100 SUBWF YEARL,W BTFSS STATUS,C GOTO UPDATE_CDTIME MOVWF YEARL ;year mod 100 rolled over INCF YEARH,F ;increment century ;Subtract the time-delta from the countdown time UPDATE_CDTIME BTFSC APP_FLAGS,PAST_TARGET GOTO CHECK_ALARMS ;already past target MOVF RES4,W SUBWF DSUBSEC3,F MOVLW 1 BTFSS STATUS,C SUBWF DSUBSEC2,F BTFSS STATUS,C SUBWF DSUBSEC1,F BTFSS STATUS,C SUBWF DSUBSEC0,F BTFSS STATUS,C SUBWF DSECONDS,F MOVF RES3,W SUBWF DSUBSEC2,F MOVLW 1 BTFSS STATUS,C SUBWF DSUBSEC1,F BTFSS STATUS,C SUBWF DSUBSEC0,F BTFSS STATUS,C SUBWF DSECONDS,F MOVF RES2,W SUBWF DSUBSEC1,F MOVLW 1 BTFSS STATUS,C SUBWF DSUBSEC0,F BTFSS STATUS,C SUBWF DSECONDS,F MOVF RES1,W SUBWF DSUBSEC0,F BTFSS STATUS,C DECF DSECONDS,F MOVF RES0,W SUBWF DSECONDS,F ;seconds BTFSS DSECONDS,7 GOTO CHECK_ALARMS MOVLW 60 ;dseconds rolled over ADDWF DSECONDS,F DECF DMINUTES,F BTFSS DMINUTES,7 GOTO CHECK_ALARMS MOVLW 60 ADDWF DMINUTES,F ;dminutes rolled over DECF DHOURS,F BTFSS DHOURS,7 GOTO CHECK_ALARMS MOVLW 24 ;dhours rolled over ADDWF DHOURS,F DECF DDAYL,F BTFSS DDAYL,7 GOTO CHECK_ALARMS MOVLW 100 ;ddayl rolled over ADDWF DDAYL,F DECF DDAYH,F BTFSS DDAYH,7 GOTO CHECK_ALARMS MOVLW 100 ;ddayh rolled over ADDWF DDAYH,F MOVLW 1 SUBWF DDAYH1,F BTFSC STATUS,C GOTO CHECK_ALARMS SUBWF DDAYH0,F BTFSC STATUS,C GOTO CHECK_ALARMS BSF APP_FLAGS,PAST_TARGET ;we are past the target time CLRF DSUBSEC0 ;show 00:00:00 flashing CLRF DSECONDS CLRF DMINUTES CLRF DHOURS CLRF DDAYL CLRF DDAYH CLRF DDAYH1 CLRF DDAYH0 CHECK_ALARMS ;To do (time permitting), test for alarm times and set alarm outputs accordingly ;Update switch timers for detecting "long presses" MOVF RES1,W ADDWF SETSW_TIMER,F BTFSC STATUS,C BSF APP_FLAGS,SETSW_LONG ADDWF MODESW_TIMER,F BTFSC STATUS,C BSF APP_FLAGS,MODESW_LONG ;Update digit blink timer MOVF RES1,W ADDWF BLINK_PHASE,F RETURN ;------------------------------------------------------------------------------ ;Run the display driver state machines through one iteration ;This routine makes a state transition on each of the output busses every ;6 calls. The busses are staggered so that one bus makes a transition ;every other call. If the main loop takes 20 microseconds per minimum ;iteration, then each state machine state will last a minimum of 120 ;microseconds (about 20 microseconds more than the minimum requred for a ;half-cycle of synchronous serial data for the 16C505 display drivers). DRIVE_DISPLAY MOVLW HIGH(DDDISPTABLE) MOVWF PCLATH MOVF STX_TIMER,W INCF STX_TIMER,F ADDLW LOW(DDDISPTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL DDDISPTABLE GOTO DRIVE_DAYSDISP GOTO DRIVE_DATEDISP ;Drive the "time" display chain's serial output through 1 state DRIVE_TIMEDISP CLRF STX_TIMER ;this is the last driver called so reset STX_TIMER CALL DRIVE_TIMEDISP1 MOVF PORTB_MIRROR,W MOVWF PORTB INCF STXS_TIME,F RETURN DRIVE_TIMEDISP1 MOVLW HIGH(TXTIMETABLE) MOVWF PCLATH MOVF STXS_TIME,W ADDLW LOW(TXTIMETABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL TXTIMETABLE GOTO TXTIME0 GOTO TXTIME1 GOTO TXTIME2 GOTO TXTIME3 GOTO TXTIME4 GOTO TXTIME5 GOTO TXTIME6 GOTO TXTIME7 GOTO TXTIME8 GOTO TXTIME9 GOTO TXTIME10 GOTO TXTIME11 GOTO TXTIME12 GOTO TXTIME13 TXTIME0 BCF PORTB_MIRROR,2 ;Clk low BSF PORTB_MIRROR,3 ;Data high BSF ADCON0,GO ;Start an A/D conversion for brightness level sensor RETURN TXTIME1 BCF PORTB_MIRROR,3 ;Data low -- "start" condition RETURN TXTIME2 BSF PORTB_MIRROR,2 ;Clk high RETURN TXTIME3 BCF PORTB_MIRROR,2 ;Clk low -- frame type bit = 0 (segments frame) ; BCF PORTB_MIRROR,3 ;Data low (need't do agian since it was done for start condition) RETURN TXTIME4 BSF PORTB_MIRROR,2 ;Clk high CLRF STXD_TIME ;Digit 0 TXTGETNEXTDIG CALL GET_TIME_DIGIT ;Get the segment pattern MOVWF STXY_TIME ;this is the byte to transmit CLRF STXB_TIME ;Bit 0 TXTBITLOOP MOVLW 4 ;Next state = 5 to transmit byte MOVWF STXS_TIME RETURN TXTIME5 BCF PORTB_MIRROR,2 ;Clk low BCF PORTB_MIRROR,3 RRF STXY_TIME,F BTFSC STATUS,C BSF PORTB_MIRROR,3 ;databit RETURN TXTIME6 BSF PORTB_MIRROR,2 ;Clk high INCF STXB_TIME,F ;next bit index BTFSS STXB_TIME,3 GOTO TXTBITLOOP ;go back to state 5 to transmit another bit INCF STXD_TIME,F ;done with byte -- increment digit index MOVLW 7 SUBWF STXD_TIME,W BTFSS STATUS,C GOTO TXTGETNEXTDIG ;still more digits .. loop MOVF BRIGHTNESS,W ;no more digits, update brightness level SUBWF ADRES,W ;calculate difference between new level and lowpass accumulator CLRF REG_I BTFSS STATUS,C DECF REG_I,F ;REG_I = sign extension of difference ADDWF BRIGHTNESSL,F BTFSC STATUS,C INCF BRIGHTNESS,F MOVF REG_I,W ADDWF BRIGHTNESS,F RETURN ;continue to state 7 ;Generate another frame with brightness information TXTIME7 BCF PORTB_MIRROR,2 ;Clk low BSF PORTB_MIRROR,3 ;Data high RETURN TXTIME8 BCF PORTB_MIRROR,3 ;Data low -- "start" condition RETURN TXTIME9 BSF PORTB_MIRROR,2 ;Clk high RETURN TXTIME10 BCF PORTB_MIRROR,2 ;Clk low -- frame type bit = 1 (brightness frame) BSF PORTB_MIRROR,3 ;Data high RETURN TXTIME11 BSF PORTB_MIRROR,2 ;Clk high CLRF STXD_TIME ;Digit 0 TXTGETNEXTBRGT CALL GET_TIME_BRIGHTNESS ;Get the brightness level MOVWF STXY_TIME ;this is the byte to transmit CLRF STXB_TIME ;Bit 0 TXTBBITLOOP MOVLW 11 ;Next state = 12 to transmit byte MOVWF STXS_TIME RETURN TXTIME12 BCF PORTB_MIRROR,2 ;Clk low BCF PORTB_MIRROR,3 RRF STXY_TIME,F BTFSC STATUS,C BSF PORTB_MIRROR,3 ;databit RETURN TXTIME13 BSF PORTB_MIRROR,2 ;Clk high INCF STXB_TIME,F ;next bit index BTFSS STXB_TIME,3 GOTO TXTBBITLOOP ;go back to state 12 to transmit another bit INCF STXD_TIME,F ;done with byte -- increment digit index MOVLW 7 SUBWF STXD_TIME,W BTFSS STATUS,C GOTO TXTGETNEXTBRGT ;still more digits .. loop MOVLW 0xff ;done -- return to state 0 MOVWF STXS_TIME RETURN ;Get the segment pattern for the indexed digit to transmit to the time display chain ;Return with WREG = segment_pattern GET_TIME_DIGIT MOVF SET_MODE,F BTFSS STATUS,Z GOTO GETTIMEDIG_SET ;"set" mode is in effect BTFSC APP_FLAGS,COUNTDOWN_MODE GOTO GETTIMEDIG_CDM ;"countdown" mode MOVLW HIGH(GETTIMEDIGTABLE) ;"real time" mode MOVWF PCLATH MOVF STXD_TIME,W ADDLW LOW(GETTIMEDIGTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETTIMEDIG_CDM MOVLW HIGH(GETTIMEDIGCDMTABLE) MOVWF PCLATH MOVF STXD_TIME,W ADDLW LOW(GETTIMEDIGCDMTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETTIMEDIG_SET BTFSS PORTB,7 GOTO GTDSETSBND ;The "set" button is still down, show "A0, A1, etc., as appropriate" MOVF SET_MODE,W SUBLW 1 BTFSC STATUS,Z GOTO GTDSETSBND ;Setting the time .. don't show anything special MOVF STXD_TIME,W SUBLW 1 MOVLW 0 BTFSS STATUS,C RETURN MOVF SET_MODE,W ADDLW -2 ;SET_MODE 2 is A0 BTFSS STXD_TIME,0 MOVLW 10 ;'A' GOTO GET_SEGPATTERN ;"set" button is not down GTDSETSBND MOVLW HIGH(GETTIMEDIGSETTABLE) MOVWF PCLATH MOVF STXD_TIME,W ADDLW LOW(GETTIMEDIGSETTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETTIMEDIGTABLE GOTO GET_TIMED_10HOURS GOTO GET_TIMED_HOURS GOTO GET_TIMED_10MINUTES GOTO GET_TIMED_MINUTES GOTO GET_TIMED_10SECONDS GOTO GET_TIMED_SECONDS GOTO GET_TIMED_TENTHS GETTIMEDIGCDMTABLE GOTO GET_TIMED_10HOURCDM GOTO GET_TIMED_HOURCDM GOTO GET_TIMED_10MINCDM GOTO GET_TIMED_MINCDM GOTO GET_TIMED_10SECCDM GOTO GET_TIMED_SECCDM GOTO GET_TIMED_TNTCDM GETTIMEDIGSETTABLE GOTO GET_TIMED_10HOURSET GOTO GET_TIMED_HOURSET GOTO GET_TIMED_10MINSET GOTO GET_TIMED_MINSET GOTO GET_TIMED_10SECSET GOTO GET_TIMED_SECSET GOTO GET_TIMED_TNTSET GET_TIMED_10HOURS MOVF HOURS,W CALL SEGPTN_HIGHDIGIT ;To do: add decimal point if decimal point is enabled RETURN GET_TIMED_10HOURCDM MOVF DHOURS,W CALL SEGPTN_HIGHDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_10HOURSET MOVF EHOURS,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 8 BTFSC STATUS,Z GOTO BLINK_DIGIT GOTO SEGPTN_EDITFIELD ;Echo the digit being edited GET_TIMED_HOURS MOVF HOURS,W CALL SEGPTN_LOWDIGIT ;To do: DP RETURN GET_TIMED_HOURCDM MOVF DHOURS,W CALL SEGPTN_LOWDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_HOURSET MOVF EHOURS,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 9 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_TIMED_10MINUTES MOVF MINUTES,W CALL SEGPTN_HIGHDIGIT ;To do: DP RETURN GET_TIMED_10MINCDM MOVF DMINUTES,W CALL SEGPTN_HIGHDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_10MINSET MOVF EMINUTES,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 10 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_TIMED_MINUTES MOVF MINUTES,W CALL SEGPTN_LOWDIGIT ;To do: DP RETURN GET_TIMED_MINCDM MOVF DMINUTES,W CALL SEGPTN_LOWDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_MINSET MOVF EMINUTES,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 11 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_TIMED_10SECONDS MOVF SECONDS,W CALL SEGPTN_HIGHDIGIT ;To do: DP RETURN GET_TIMED_10SECCDM MOVF DSECONDS,W CALL SEGPTN_HIGHDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_10SECSET MOVF ESECONDS,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 12 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_TIMED_SECONDS MOVF SECONDS,W CALL SEGPTN_LOWDIGIT ;To do: DP RETURN GET_TIMED_SECCDM MOVF DSECONDS,W CALL SEGPTN_LOWDIGIT GOTO BLINK_COUNTDOWN GET_TIMED_SECSET MOVF ESECONDS,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 13 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_TIMED_TENTHS MOVLW 9 BTFSS SUBSEC0,7 MOVLW 4 MOVWF REG_I MOVF SUBSEC0,W IORLW 0x80 ADDLW 26 BTFSC STATUS,C GOTO GTDTENTHS_C1 ;.9 or .4 DECF REG_I,F ADDLW 25 BTFSC STATUS,C GOTO GTDTENTHS_C1 ;.8 or .3 DECF REG_I,F ADDLW 26 BTFSC STATUS,C GOTO GTDTENTHS_C1 ;.7 or .2 DECF REG_I,F ADDLW 25 BTFSS STATUS,C ;.6 or .1 DECF REG_I,F ;.5 or .0 GTDTENTHS_C1 MOVF REG_I,W CALL GET_SEGPATTERN ;To do: DP RETURN GET_TIMED_TNTCDM MOVLW 9 BTFSS DSUBSEC0,7 MOVLW 4 MOVWF REG_I MOVF DSUBSEC0,W IORLW 0x80 ADDLW 26 BTFSC STATUS,C GOTO DGTDTENTHS_C1 ;.9 or .4 DECF REG_I,F ADDLW 25 BTFSC STATUS,C GOTO DGTDTENTHS_C1 ;.8 or .3 DECF REG_I,F ADDLW 26 BTFSC STATUS,C GOTO DGTDTENTHS_C1 ;.7 or .2 DECF REG_I,F ADDLW 25 BTFSS STATUS,C ;.6 or .1 DECF REG_I,F ;.5 or .0 DGTDTENTHS_C1 MOVF REG_I,W CALL GET_SEGPATTERN GOTO BLINK_COUNTDOWN GET_TIMED_TNTSET MOVLW 0 CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF TEMP1,W RETURN ;Get the brightness level for the indexed digit to transmit to the time display chain ;Return with WREG = brightness_level GET_TIME_BRIGHTNESS MOVLW HIGH(GETTIMEBRGTTABLE) MOVWF PCLATH MOVF STXD_TIME,W ADDLW LOW(GETTIMEBRGTTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETTIMEBRGTTABLE GOTO GET_TIMEB_10HOURS GOTO GET_TIMEB_HOURS GOTO GET_TIMEB_10MINUTES GOTO GET_TIMEB_MINUTES GOTO GET_TIMEB_10SECONDS GOTO GET_TIMEB_SECONDS GOTO GET_TIMEB_TENTHS GET_TIMEB_10HOURS MOVF BRIGHTNESS,W ;To do: Individual digit brightness control RETURN GET_TIMEB_HOURS MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_TIMEB_10MINUTES MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_TIMEB_MINUTES MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_TIMEB_10SECONDS MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_TIMEB_SECONDS MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_TIMEB_TENTHS MOVF BRIGHTNESS,W ;To do: IDBC RETURN ;Drive the "date" display chain's serial output through 1 state DRIVE_DATEDISP CALL DRIVE_DATEDISP1 MOVF PORTC_MIRROR,W MOVWF PORTC INCF STXS_DATE,F RETURN DRIVE_DATEDISP1 MOVLW HIGH(TXDATETABLE) MOVWF PCLATH MOVF STXS_DATE,W ADDLW LOW(TXDATETABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL TXDATETABLE GOTO TXDATE0 GOTO TXDATE1 GOTO TXDATE2 GOTO TXDATE3 GOTO TXDATE4 GOTO TXDATE5 GOTO TXDATE6 GOTO TXDATE7 GOTO TXDATE8 GOTO TXDATE9 GOTO TXDATE10 GOTO TXDATE11 GOTO TXDATE12 GOTO TXDATE13 TXDATE0 BCF PORTC_MIRROR,2 ;Clk low BSF PORTC_MIRROR,3 ;Data high RETURN TXDATE1 BCF PORTC_MIRROR,3 ;Data low -- "start" condition RETURN TXDATE2 BSF PORTC_MIRROR,2 ;Clk high RETURN TXDATE3 BCF PORTC_MIRROR,2 ;Clk low -- frame type bit = 0 (segments frame) ; BCF PORTC_MIRROR,3 ;Data low (need't do agian since it was done for start condition) RETURN TXDATE4 BSF PORTC_MIRROR,2 ;Clk high CLRF STXD_DATE ;Digit 0 TXDGETNEXTDIG CALL GET_DATE_DIGIT ;Get the segment pattern MOVWF STXY_DATE ;this is the byte to transmit CLRF STXB_DATE ;Bit 0 TXDBITLOOP MOVLW 4 ;Next state = 5 to transmit byte MOVWF STXS_DATE RETURN TXDATE5 BCF PORTC_MIRROR,2 ;Clk low BCF PORTC_MIRROR,3 RRF STXY_DATE,F BTFSC STATUS,C BSF PORTC_MIRROR,3 ;databit RETURN TXDATE6 BSF PORTC_MIRROR,2 ;Clk high INCF STXB_DATE,F ;next bit index BTFSS STXB_DATE,3 GOTO TXDBITLOOP ;go back to state 5 to transmit another bit INCF STXD_DATE,F ;done with byte -- increment digit index MOVLW 8 SUBWF STXD_DATE,W BTFSS STATUS,C GOTO TXDGETNEXTDIG ;still more digits .. loop RETURN ;no more digits, continue to state 7 ;Generate another frame with brightness information TXDATE7 BCF PORTC_MIRROR,2 ;Clk low BSF PORTC_MIRROR,3 ;Data high RETURN TXDATE8 BCF PORTC_MIRROR,3 ;Data low -- "start" condition RETURN TXDATE9 BSF PORTC_MIRROR,2 ;Clk high RETURN TXDATE10 BCF PORTC_MIRROR,2 ;Clk low -- frame type bit = 1 (brightness frame) BSF PORTC_MIRROR,3 ;Data high RETURN TXDATE11 BSF PORTC_MIRROR,2 ;Clk high CLRF STXD_DATE ;Digit 0 TXDGETNEXTBRGT CALL GET_DATE_BRIGHTNESS ;Get the brightness level MOVWF STXY_DATE ;this is the byte to transmit CLRF STXB_DATE ;Bit 0 TXDBBITLOOP MOVLW 11 ;Next state = 12 to transmit byte MOVWF STXS_DATE RETURN TXDATE12 BCF PORTC_MIRROR,2 ;Clk low BCF PORTC_MIRROR,3 RRF STXY_DATE,F BTFSC STATUS,C BSF PORTC_MIRROR,3 ;databit RETURN TXDATE13 BSF PORTC_MIRROR,2 ;Clk high INCF STXB_DATE,F ;next bit index BTFSS STXB_DATE,3 GOTO TXDBBITLOOP ;go back to state 12 to transmit another bit INCF STXD_DATE,F ;done with byte -- increment digit index MOVLW 8 SUBWF STXD_DATE,W BTFSS STATUS,C GOTO TXDGETNEXTBRGT ;still more digits .. loop MOVLW 0xff ;done -- return to state 0 MOVWF STXS_DATE RETURN ;Get the segment pattern for the indexed digit to transmit to the date display chain ;Return with WREG = segment_pattern GET_DATE_DIGIT MOVF SET_MODE,F BTFSS STATUS,Z GOTO GETDATEDIG_SET ;"set" mode is in effect MOVLW HIGH(GETDATEDIGTABLE) MOVWF PCLATH MOVF STXD_DATE,W ADDLW LOW(GETDATEDIGTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDATEDIG_SET BTFSS PORTB,7 GOTO GDDSETSBND ;The "set" button is still down, show "A0, A1, etc., as appropriate" MOVF SET_MODE,W SUBLW 1 BTFSC STATUS,Z GOTO GDDSETSBND ;Setting the time .. don't show anything special MOVF STXD_DATE,W SUBLW 1 MOVLW 0 BTFSS STATUS,C RETURN MOVF SET_MODE,W ADDLW -2 ;SET_MODE 2 is A0 BTFSC STXD_DATE,0 MOVLW 10 ;'A' GOTO GET_SEGPATTERN ;"set" button is not down GDDSETSBND MOVLW HIGH(GETDATEDIGSETTABLE) MOVWF PCLATH MOVF STXD_DATE,W ADDLW LOW(GETDATEDIGSETTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDATEDIGTABLE GOTO GET_DATED_DOM GOTO GET_DATED_10DOM GOTO GET_DATED_MONTH GOTO GET_DATED_10MONTH GOTO GET_DATED_YEAR GOTO GET_DATED_10YEAR GOTO GET_DATED_100YEAR GOTO GET_DATED_1000YEAR GETDATEDIGSETTABLE GOTO GET_DATED_SDOM GOTO GET_DATED_S10DOM GOTO GET_DATED_SMONTH GOTO GET_DATED_S10MONTH GOTO GET_DATED_SYEAR GOTO GET_DATED_S10YEAR GOTO GET_DATED_S100YEAR GOTO GET_DATED_S1000YEAR GET_DATED_DOM INCF MDAY,W CALL SEGPTN_LOWDIGIT ;To do: add decimal point if decimal point is enabled ;To do: countdown mode RETURN GET_DATED_SDOM INCF EMDAY,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 7 BTFSC STATUS,Z GOTO BLINK_DIGIT CALL SEGPTN_EDITFIELD ;Echo the digit being edited RETURN GET_DATED_10DOM INCF MDAY,W CALL SEGPTN_HIGHDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_S10DOM INCF EMDAY,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 6 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_MONTH INCF MONTH,W CALL SEGPTN_LOWDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_SMONTH INCF EMONTH,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 5 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_10MONTH INCF MONTH,W CALL SEGPTN_HIGHDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_S10MONTH INCF EMONTH,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 4 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_YEAR MOVF YEARL,W CALL SEGPTN_LOWDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_SYEAR MOVF EYEARL,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 3 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_10YEAR MOVF YEARL,W CALL SEGPTN_HIGHDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_S10YEAR MOVF EYEARL,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 2 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_100YEAR MOVF YEARH,W CALL SEGPTN_LOWDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_S100YEAR MOVF EYEARH,W CALL SEGPTN_LOWDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W XORLW 1 BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN GET_DATED_1000YEAR MOVF YEARH,W CALL SEGPTN_HIGHDIGIT ;To do: DP ;To do: countdown mode RETURN GET_DATED_S1000YEAR MOVF YEARH,W CALL SEGPTN_HIGHDIGIT MOVWF TEMP1 BTFSC APP_FLAGS,BLINK_ALL_SET GOTO BLINK_DIGIT MOVF SET_DIGIT,W BTFSC STATUS,Z GOTO BLINK_DIGIT MOVF TEMP1,W RETURN ;Get the brightness level for the indexed digit to transmit to the date display chain ;Return with WREG = brightness_level GET_DATE_BRIGHTNESS MOVLW HIGH(GETDATEBRGTTABLE) MOVWF PCLATH MOVF STXD_DATE,W ADDLW LOW(GETDATEBRGTTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDATEBRGTTABLE GOTO GET_DATEB_DOM GOTO GET_DATEB_10DOM GOTO GET_DATEB_MONTH GOTO GET_DATEB_10MONTH GOTO GET_DATEB_YEAR GOTO GET_DATEB_10YEAR GOTO GET_DATEB_100YEAR GOTO GET_DATEB_1000YEAR GET_DATEB_DOM MOVF BRIGHTNESS,W ;To do: Individual digit brightness control RETURN GET_DATEB_10DOM MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_MONTH MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_10MONTH MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_YEAR MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_10YEAR MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_100YEAR MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DATEB_1000YEAR MOVF BRIGHTNESS,W ;To do: IDBC RETURN ;Drive the "days" display chain's serial output through 1 state DRIVE_DAYSDISP CALL DRIVE_DAYSDISP1 MOVF PORTC_MIRROR,W MOVWF PORTC INCF STXS_DAYS,F RETURN DRIVE_DAYSDISP1 MOVLW HIGH(TXDAYSTABLE) MOVWF PCLATH MOVF STXS_DAYS,W ADDLW LOW(TXDAYSTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL TXDAYSTABLE GOTO TXDAYS0 GOTO TXDAYS1 GOTO TXDAYS2 GOTO TXDAYS3 GOTO TXDAYS4 GOTO TXDAYS5 GOTO TXDAYS6 GOTO TXDAYS7 GOTO TXDAYS8 GOTO TXDAYS9 GOTO TXDAYS10 GOTO TXDAYS11 GOTO TXDAYS12 GOTO TXDAYS13 TXDAYS0 BCF PORTC_MIRROR,4 ;Clk low BSF PORTC_MIRROR,5 ;Data high RETURN TXDAYS1 BCF PORTC_MIRROR,5 ;Data low -- "start" condition RETURN TXDAYS2 BSF PORTC_MIRROR,4 ;Clk high RETURN TXDAYS3 BCF PORTC_MIRROR,4 ;Clk low -- frame type bit = 0 (segments frame) ; BCF PORTC_MIRROR,5 ;Data low (need't do agian since it was done for start condition) RETURN TXDAYS4 BSF PORTC_MIRROR,4 ;Clk high CLRF STXD_DAYS ;Digit 0 TXYGETNEXTDIG CALL GET_DAYS_DIGIT ;Get the segment pattern MOVWF STXY_DAYS ;this is the byte to transmit CLRF STXB_DAYS ;Bit 0 TXYBITLOOP MOVLW 4 ;Next state = 5 to transmit byte MOVWF STXS_DAYS RETURN TXDAYS5 BCF PORTC_MIRROR,4 ;Clk low BCF PORTC_MIRROR,5 RRF STXY_DAYS,F BTFSC STATUS,C BSF PORTC_MIRROR,5 ;databit RETURN TXDAYS6 BSF PORTC_MIRROR,4 ;Clk high INCF STXB_DAYS,F ;next bit index BTFSS STXB_DAYS,3 GOTO TXYBITLOOP ;go back to state 5 to transmit another bit INCF STXD_DAYS,F ;done with byte -- increment digit index MOVLW 4 SUBWF STXD_DAYS,W BTFSS STATUS,C GOTO TXYGETNEXTDIG ;still more digits .. loop RETURN ;no more digits, continue to state 7 ;Generate another frame with brightness information TXDAYS7 BCF PORTC_MIRROR,4 ;Clk low BSF PORTC_MIRROR,5 ;Data high RETURN TXDAYS8 BCF PORTC_MIRROR,5 ;Data low -- "start" condition RETURN TXDAYS9 BSF PORTC_MIRROR,4 ;Clk high RETURN TXDAYS10 BCF PORTC_MIRROR,4 ;Clk low -- frame type bit = 1 (brightness frame) BSF PORTC_MIRROR,5 ;Data high RETURN TXDAYS11 BSF PORTC_MIRROR,4 ;Clk high CLRF STXD_DAYS ;Digit 0 TXYGETNEXTBRGT CALL GET_DAYS_BRIGHTNESS ;Get the brightness level MOVWF STXY_DAYS ;this is the byte to transmit CLRF STXB_DAYS ;Bit 0 TXYBBITLOOP MOVLW 11 ;Next state = 12 to transmit byte MOVWF STXS_DAYS RETURN TXDAYS12 BCF PORTC_MIRROR,4 ;Clk low BCF PORTC_MIRROR,5 RRF STXY_DAYS,F BTFSC STATUS,C BSF PORTC_MIRROR,5 ;databit RETURN TXDAYS13 BSF PORTC_MIRROR,4 ;Clk high INCF STXB_DAYS,F ;next bit index BTFSS STXB_DAYS,3 GOTO TXYBBITLOOP ;go back to state 12 to transmit another bit INCF STXD_DAYS,F ;done with byte -- increment digit index MOVLW 4 SUBWF STXD_DAYS,W BTFSS STATUS,C GOTO TXYGETNEXTBRGT ;still more digits .. loop MOVLW 0xff ;done -- return to state 0 MOVWF STXS_DAYS RETURN ;Get the segment pattern for the indexed digit to transmit to the days display chain ;Return with WREG = segment_pattern GET_DAYS_DIGIT MOVF SET_MODE,F BTFSC STATUS,Z GOTO GYDNOTSETMODE ;"set" mode is in effect ... BTFSS PORTB,7 GOTO GYDSETSBND ;The "set" button is still down, show "A0, A1, etc., as appropriate" MOVF SET_MODE,W SUBLW 1 BTFSC STATUS,Z GOTO GYDSETSBND ;Setting the time .. don't show anything special MOVF STXD_DAYS,W SUBLW 1 MOVLW 0 BTFSS STATUS,C RETURN MOVF SET_MODE,W ADDLW -2 ;SET_MODE 2 is A0 BTFSC STXD_DAYS,0 MOVLW 10 ;'A' GOTO GET_SEGPATTERN ;"set" button is not down GYDSETSBND MOVLW 0 RETURN ;show noting .. can't set days ;"set" mode is not in effect ... GYDNOTSETMODE BTFSC APP_FLAGS,COUNTDOWN_MODE GOTO GETDAYSDIG_CDM ;"countdown" mode MOVLW HIGH(GETDAYSDIGTABLE) MOVWF PCLATH MOVF STXD_DAYS,W ADDLW LOW(GETDAYSDIGTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDAYSDIG_CDM MOVLW HIGH(GETDAYSDIGCDMTABLE) MOVWF PCLATH MOVF STXD_DAYS,W ADDLW LOW(GETDAYSDIGCDMTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDAYSDIGTABLE GOTO GET_DAYSD_1 GOTO GET_DAYSD_10 GOTO GET_DAYSD_100 GOTO GET_DAYSD_1000 GETDAYSDIGCDMTABLE GOTO GET_DAYSD_CDM1 GOTO GET_DAYSD_CDM10 GOTO GET_DAYSD_CDM100 GOTO GET_DAYSD_CDM1000 GET_DAYSD_1 INCF YDAYL,W CALL SEGPTN_LOWDIGIT ;To do: add decimal point if decimal point is enabled RETURN GET_DAYSD_CDM1 MOVF DDAYH0,W IORWF DDAYH1,W BTFSS STATUS,Z GOTO GDCDMOVERFLOW MOVF DDAYL,W CALL SEGPTN_LOWDIGIT GOTO BLINK_COUNTDOWN GET_DAYSD_10 INCF YDAYL,W CALL SEGPTN_HIGHDIGIT ;To do: DP RETURN GET_DAYSD_CDM10 MOVF DDAYH0,W IORWF DDAYH1,W BTFSS STATUS,Z GOTO GDCDMOVERFLOW MOVF DDAYL,W CALL SEGPTN_HIGHDIGIT GOTO BLINK_COUNTDOWN GET_DAYSD_100 MOVF YDAYL,W ADDLW 1 MOVF YDAYH,W BTFSC STATUS,C ADDLW 1 CALL SEGPTN_LOWDIGIT ;To do: DP RETURN GET_DAYSD_CDM100 MOVF DDAYH0,W IORWF DDAYH1,W BTFSS STATUS,Z GOTO GDCDMOVERFLOW MOVF DDAYH,W CALL SEGPTN_LOWDIGIT GOTO BLINK_COUNTDOWN GET_DAYSD_1000 MOVF YDAYL,W ADDLW 1 MOVF YDAYH,W BTFSC STATUS,C ADDLW 1 CALL SEGPTN_HIGHDIGIT ;To do: DP RETURN GET_DAYSD_CDM1000 MOVF DDAYH0,W IORWF DDAYH1,W BTFSS STATUS,Z GOTO GDCDMOVERFLOW MOVF DDAYH,W CALL SEGPTN_HIGHDIGIT GOTO BLINK_COUNTDOWN GDCDMOVERFLOW MOVLW 16 GOTO GET_SEGPATTERN ;Get the brightness level for the indexed digit to transmit to the days display chain ;Return with WREG = brightness_level GET_DAYS_BRIGHTNESS MOVLW HIGH(GETDAYSBRGTTABLE) MOVWF PCLATH MOVF STXD_DAYS,W ADDLW LOW(GETDAYSBRGTTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL GETDAYSBRGTTABLE GOTO GET_DAYSB_1 GOTO GET_DAYSB_10 GOTO GET_DAYSB_100 GOTO GET_DAYSB_1000 GET_DAYSB_1 MOVF BRIGHTNESS,W ;To do: Individual digit brightness control RETURN GET_DAYSB_10 MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DAYSB_100 MOVF BRIGHTNESS,W ;To do: IDBC RETURN GET_DAYSB_1000 MOVF BRIGHTNESS,W ;To do: IDBC RETURN ;------------------------------------------------------------------------------ ;Return the segment pattern for the field being edited .. blank it if the ;blink phase is appropriate .. this is called by the first digit in each chain ;so that they echo the field currently being edited and blink fast. SEGPTN_EDITFIELD MOVLW HIGH(EDITDIGSPTABLE) MOVWF PCLATH MOVF SET_DIGIT,W ADDWF SET_DIGIT,W ADDLW LOW(EDITDIGSPTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL EDITDIGSPTABLE MOVF EYEARH,W ;1000 years GOTO BLINK_FAST_HIGHDIG MOVF EYEARH,W ;100 years GOTO BLINK_FAST_LOWDIG MOVF EYEARL,W ;10 years GOTO BLINK_FAST_HIGHDIG MOVF EYEARL,W ;1 year GOTO BLINK_FAST_LOWDIG INCF EMONTH,W ;10 month GOTO BLINK_FAST_HIGHDIG INCF EMONTH,W ;1 month GOTO BLINK_FAST_LOWDIG INCF EMDAY,W ;10 day of month GOTO BLINK_FAST_HIGHDIG INCF EMDAY,W ;1 day of month GOTO BLINK_FAST_LOWDIG MOVF EHOURS,W ;10 hour GOTO BLINK_FAST_HIGHDIG MOVF EHOURS,W ;1 hour GOTO BLINK_FAST_LOWDIG MOVF EMINUTES,W ;10 minute GOTO BLINK_FAST_HIGHDIG MOVF EMINUTES,W ;1 minute GOTO BLINK_FAST_LOWDIG MOVF ESECONDS,W ;10 second GOTO BLINK_FAST_HIGHDIG MOVF ESECONDS,W ;1 second GOTO BLINK_FAST_LOWDIG RETLW 01111001b ;Accept -- return 'E' for "error" ;------------------------------------------------------------------------------ BLINK_FAST_HIGHDIG CALL SEGPTN_HIGHDIGIT GOTO BLINK_DIGIT_FAST ;------------------------------------------------------------------------------ BLINK_FAST_LOWDIG CALL SEGPTN_LOWDIGIT GOTO BLINK_DIGIT_FAST ;------------------------------------------------------------------------------ ;Do a binary-to-BCD conversion on W and return the LED segment pattern ;corresponding to the high digit SEGPTN_HIGHDIGIT MOVWF REG_I CALL BINARY_TO_BCD SWAPF REG_J,W ANDLW 0x0f GOTO GET_SEGPATTERN ;------------------------------------------------------------------------------ ;Do a binary-to-BCD conversion on W and return the LED segment pattern ;corresponding to the low digit SEGPTN_LOWDIGIT MOVWF REG_I CALL BINARY_TO_BCD MOVLW 0x0f ANDWF REG_J,W ; Fall through GOTO GET_SEGPATTERN ;------------------------------------------------------------------------------ ;Return the 7-segment display pattern corresponding to the digit in W GET_SEGPATTERN MOVWF REG_I MOVLW HIGH(SEGPTNTABLE) MOVWF PCLATH MOVF REG_I,W ADDLW LOW(SEGPTNTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL SEGPTNTABLE RETLW 00111111b ;0 RETLW 00000110b ;1 RETLW 01011011b ;2 RETLW 01001111b ;3 RETLW 01100110b ;4 RETLW 01101101b ;5 RETLW 01111101b ;6 RETLW 00000111b ;7 RETLW 01111111b ;8 RETLW 01101111b ;9 RETLW 01110111b ;A RETLW 01111100b ;b RETLW 00111001b ;C RETLW 01011110b ;d RETLW 01111001b ;E RETLW 01110001b ;F RETLW 01000000b ;- ;------------------------------------------------------------------------------ ;Blank out the segment pattern in TEMP1 if in the appropriate blink phase for ;slow-blinking digits. Return result in W. BLINK_DIGIT MOVF TEMP1,W BTFSC BLINK_PHASE,6 ;two cycles per second MOVLW 0 RETURN ;------------------------------------------------------------------------------ ;Blank out the segment pattern in W if in the appropriate blink phase for ;fast-blinking digits BLINK_DIGIT_FAST BTFSC BLINK_PHASE,5 ;four cycles per second MOVLW 0 RETURN ;------------------------------------------------------------------------------ ;Blank out the segment pattern in W if past the target time and in the ;appropriate blink phase BLINK_COUNTDOWN BTFSS APP_FLAGS,PAST_TARGET RETURN BTFSC BLINK_PHASE,6 ;two cycles per second MOVLW 0 RETURN ;------------------------------------------------------------------------------ ;Binary to BCD conversion - 8-bits ; ;Input ; REG_I - 8-bit binary number ;Outputs ; REG_K - the hundreds digit of the BCD conversion ; REG_J - the tens and ones digits of the BCD conversion BINARY_TO_BCD CLRF REG_K SWAPF REG_I,W ;Add the upper and lower nibbles ADDWF REG_I,W ;to get the one's digit ANDLW 0x0f SKPNDC ;Go through a binary to bcd ADDLW 0x16 ;conversion for just the one's SKPNDC ;digit ADDLW 0x06 ADDLW 0x06 SKPDC ADDLW -0x06 BTFSC REG_I,4 ;Bit 4 is a special case ADDLW 0x16 - 1 + 0x6 SKPDC ADDLW -0x06 ;Now adjust the ten's digit BTFSC REG_I,5 ;2^5 = 32, so add 3 to the ten's ADDLW 0x30 ;digit if bit 5 is set BTFSC REG_I,6 ;2^6 = 64, so add 6 ADDLW 0x60 ;if bit 6 is set BTFSC REG_I,7 ;2^7 = 128, so add 2 (the ten's ADDLW 0x20 ;digit) if bit 7 is set ADDLW 0x60 ;Convert the ten's digit to BCD RLF REG_K,F ;If there's a carry, then the input BTFSS REG_K,0 ;was greater than 99. ADDLW -0x60 MOVWF REG_J BTFSC REG_I,7 ;If msb is set then the hundred's INCF REG_K,F ;digit is a '2' RETURN ;------------------------------------------------------------------------------ ;BIN100_TO_BCD ; ;Converts W, assumed to be in the range 0..99, to BCD. BIN100_TO_BCD MOVWF REG_I CALL BINARY_TO_BCD MOVF REG_J,W RETURN ;------------------------------------------------------------------------------ ;BIN100_TO_BCDL ; ;Converts W, assumed to be in the range 0..99, to BCD. ;Returns the low digit of the result in W. BIN100_TO_BCDL MOVWF REG_I CALL BINARY_TO_BCD MOVF REG_J,W ANDLW 0x0f RETURN ;------------------------------------------------------------------------------ ;BIN100_TO_BCDH ; ;Converts W, assumed to be in the range 0..99, to BCD. ;Returns the high digit of the result in W. BIN100_TO_BCDH MOVWF REG_I CALL BINARY_TO_BCD SWAPF REG_J,W ANDLW 0x0f RETURN ;------------------------------------------------------------------------------ ;Check for and process RS232 input, send RS232 output HOST_COMM RETURN ;------------------------------------------------------------------------------ ;Check for and process button presses CHECK_PUSHBUTTONS BTFSC PORTB,7 CALL SET_PB_DOWN BTFSS PORTB,7 CALL SET_PB_UP BTFSC PORTA,4 CALL MODE_PB_DOWN BTFSS PORTA,4 CALL MODE_PB_UP RETURN SET_PB_UP INCF SETSW_DEBOUNCE,W BTFSS STATUS,Z ;was the debounce counter maxed out? GOTO SET_PB_UP_NOPRESS ; no, button was not pressed or not pressed long enough BTFSS APP_FLAGS,SETSW_LONG1 ; yes, was it pressed for a long time or a short time? CALL SET_PB_PRESS ;The button was pressed for a short duration SET_PB_UP_NOPRESS CLRF SETSW_DEBOUNCE CLRF SETSW_TIMER BCF APP_FLAGS,SETSW_LONG BCF APP_FLAGS,SETSW_LONG1 RETURN SET_PB_DOWN INCF SETSW_DEBOUNCE,F BTFSS STATUS,Z RETURN DECF SETSW_DEBOUNCE,F ;max out at 0xff BTFSS APP_FLAGS,SETSW_LONG RETURN CALL SET_PB_LONGPRESS BCF APP_FLAGS,SETSW_LONG ;clear this flag so that we can tell if the button stays down another second BSF APP_FLAGS,SETSW_LONG1 ;sticky flag .. stays set until button is released RETURN MODE_PB_UP INCF MODESW_DEBOUNCE,W BTFSS STATUS,Z ;was the debounce counter maxed out? GOTO MODE_PB_UP_NOPRESS ; no, button was not pressed or not pressed long enough BTFSS APP_FLAGS,MODESW_LONG1 ; yes, was it pressed for a long time or a short time? CALL MODE_PB_PRESS ;The button was pressed for a short duration MODE_PB_UP_NOPRESS CLRF MODESW_DEBOUNCE CLRF MODESW_TIMER BCF APP_FLAGS,MODESW_LONG BCF APP_FLAGS,MODESW_LONG1 RETURN MODE_PB_DOWN INCF MODESW_DEBOUNCE,F BTFSS STATUS,Z RETURN DECF MODESW_DEBOUNCE,F ;max out at 0xff BTFSS APP_FLAGS,MODESW_LONG RETURN CALL MODE_PB_LONGPRESS BCF APP_FLAGS,MODESW_LONG ;clear this flag so that we can tell if the button stays down another second BSF APP_FLAGS,MODESW_LONG1 ;sticky flag .. stays set until button is released RETURN ;When setting the real time or event time or an alarm time, pressing the "set" ;pushbutton advances the "cursor" to the next digit. The last "digit" is the ;"accept" mode where the whole display flashes. The "mode/increment" pushbutton ;must be pressed in the "accept" mode to accept the new settings. If the "set" ;pushbutton is pressed in this context, the cursor merely wraps around to the ;first digit in the sequence. ; ;A short press on the "set" pushbutton when not in "set mode" has no effect. ;To enter "set mode", the "set" pushbutton must be held down for one second. ; ;Note that this event handler is actually called when the button is released ;... this is done in order to differentiate between long presses and short ;presses (and not trigger a short press if the user is actually starting a ;long press). SET_PB_PRESS MOVF SET_MODE,F BTFSC STATUS,Z RETURN ;ignore a short press unless in "set" mode INCF SET_DIGIT,F MOVF SET_DIGIT,W SUBLW 15 BTFSC STATUS,Z CLRF SET_DIGIT ;wrap around to first digit BCF APP_FLAGS,BLINK_ALL_SET MOVLW 14 XORWF SET_DIGIT,W BTFSC STATUS,Z BSF APP_FLAGS,BLINK_ALL_SET ;"accept" mode CLRF BLINK_PHASE RETURN ;If the "set" pushbutton is pressed for one second then the clock will enter ;"set mode". If already in "set mode", pressing the "set" pushbutton for one ;second (or holding it in for another second) causes the next "set mode" to ;be entered. The "set mode"s are: real time, event time, alarm1, alarm2, ;alarm3, alarm4, alarm5. Incrementing beyond alarm5 set mode will exit from ;"set mode" (go back to SET_MODE = 0). SET_PB_LONGPRESS INCF SET_MODE,F BTFSC SET_MODE,3 CLRF SET_MODE ;cycle through choices 0..7 CLRF SET_DIGIT ;start at first digit in each "set mode" MOVF SET_MODE,F BTFSC STATUS,Z RETURN ;Copy the time being edited to the edit buffer CALL GET_EDITFIELD_ADDR ;get address of field to edit (based on SET_MODE) MOVWF FSR MOVF INDF,W ;seconds MOVWF ESECONDS INCF FSR,F MOVF INDF,W ;minutes MOVWF EMINUTES INCF FSR,F MOVF INDF,W ;hours MOVWF EHOURS INCF FSR,F MOVF INDF,W ;day of month MOVWF EMDAY INCF FSR,F MOVF INDF,W ;month MOVWF EMONTH INCF FSR,F MOVF INDF,W ;year modulo 100 MOVWF EYEARL INCF FSR,F MOVF INDF,W ;year div 100 MOVWF EYEARH CLRF BLINK_PHASE RETURN ;Pressing the "mode/increment" pushbutton for a short duration will increment ;the digit where the cursor is located if in "set mode". If the cursor location ;is "accept" (entire display is flashing), then pressing "mode/increment" accepts ;the changes. ; ;If not in "set mode", pressing "mode/increment" for a short duration has no effect. MODE_PB_PRESS MOVF SET_MODE,F BTFSC STATUS,Z RETURN CLRF BLINK_PHASE ;reset blink phase to "lit" MOVLW HIGH(EDITDIGSTABLE) MOVWF PCLATH MOVF SET_DIGIT,W ADDLW LOW(EDITDIGSTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL EDITDIGSTABLE GOTO EDIT_INC_YEAR1000 GOTO EDIT_INC_YEAR100 GOTO EDIT_INC_YEAR10 GOTO EDIT_INC_YEAR1 GOTO EDIT_INC_MONTH10 GOTO EDIT_INC_MONTH1 GOTO EDIT_INC_DOM10 GOTO EDIT_INC_DOM1 GOTO EDIT_INC_HOUR10 GOTO EDIT_INC_HOUR1 GOTO EDIT_INC_MINUTE10 GOTO EDIT_INC_MINUTE1 GOTO EDIT_INC_SECOND10 GOTO EDIT_INC_SECOND1 GOTO EDIT_ACCEPT EDIT_INC_YEAR1000 MOVLW 10 ADDWF EYEARH,F MOVLW 100 SUBWF EYEARH,W BTFSC STATUS,C MOVWF EYEARH RETURN EDIT_INC_YEAR100 MOVF EYEARH,W CALL BIN100_TO_BCDL SUBLW 9 MOVLW 1 BTFSC STATUS,Z MOVLW -9 ADDWF EYEARH,F RETURN EDIT_INC_YEAR10 MOVLW 10 ADDWF EYEARL,F MOVLW 100 SUBWF EYEARL,W BTFSC STATUS,C MOVWF EYEARL RETURN EDIT_INC_YEAR1 MOVF EYEARL,W CALL BIN100_TO_BCDL SUBLW 9 MOVLW 1 BTFSC STATUS,Z MOVLW -9 ADDWF EYEARL,F RETURN EDIT_INC_MONTH10 INCF EMONTH,W CALL BIN100_TO_BCD MOVWF REG_J ANDLW 0x0f MOVWF REG_I ;low digit SWAPF REG_J,W ANDLW 0x0f MOVWF REG_J ;high digit MOVLW 1 XORWF REG_J,F ;toggle 1 vs 0 BTFSS STATUS,Z CLRF REG_I ;limit to 10 (make sure month doesn't exceed 12) EIMCIJM MOVF REG_J,W CALL MUL10 ADDWF REG_I,W ADDLW -1 BTFSS STATUS,C ADDLW 1 ;(month+1) must be 1 or larger MOVWF EMONTH RETURN EDIT_INC_MONTH1 INCF EMONTH,W CALL BIN100_TO_BCD MOVWF REG_J ANDLW 0x0f MOVWF REG_I ;low digit SWAPF REG_J,W ANDLW 0x0f MOVWF REG_J ;high digit INCF REG_I,F MOVF REG_J,F BTFSC STATUS,Z GOTO EIMCIJM ;high digit is 0 so no overflow .. done MOVLW 3 SUBWF REG_I,W BTFSC STATUS,C CLRF REG_I ;cycle between 10, 11, 12 GOTO EIMCIJM EDIT_INC_DOM10 MOVF EMDAY,W ADDLW 10 MOVWF EMDAY ;try adding 10 MOVF EMONTH,W CALL GET_DAYS_IN_MONTH_E MOVWF REG_J SUBWF EMDAY,W BTFSS STATUS,C RETURN ;adding 10 was ok MOVF REG_J,W ;not ok, see if the high digit CALL BIN100_TO_BCDH ; alone is ok (if the low digit is reduced) MOVWF REG_K INCF EMDAY,W CALL BIN100_TO_BCDH XORWF REG_K,W BTFSC STATUS,Z GOTO EIDOM10RLD ;high digit is ok if low digit is reduced XORWF REG_K,W ;high digit too high -- wrap around CALL MUL10 SUBWF EMDAY,F BTFSS STATUS,C INCF EMDAY,F ;no such day as the '0th' .. inc to '1st' RETURN ;reduce the low digit so that the new high digit is legal EIDOM10RLD DECF REG_J,W MOVWF EMDAY RETURN EDIT_INC_DOM1 INCF EMDAY,W CALL BIN100_TO_BCD MOVWF REG_I SWAPF REG_I,W ANDLW 0x0f MOVWF REG_J ;Tens digit MOVLW 0x0f ANDWF REG_I,F ;Ones digit INCF REG_I,F ;increment the one's digit MOVLW 10 XORWF REG_I,W BTFSC STATUS,Z ;Incremented past 9? CLRF REG_I ;yes, wrap around MOVF REG_J,W ;Now reconstruct binary number from digits CALL MUL10 ADDWF REG_I,W BTFSS STATUS,Z ADDLW -1 ;adjust to base 0 unless already 0 (no such day as 00) MOVWF REG_K MOVF EMONTH,W CALL GET_DAYS_IN_MONTH_E SUBWF REG_K,W ;valid date? BTFSC STATUS,C GOTO EIDOM1WRAPDATE ;no, wrap around MOVF REG_K,W MOVWF EMDAY ;yes, go with it RETURN EIDOM1WRAPDATE CLRF REG_I MOVF REG_J,W ;Now reconstruct binary number from digits CALL MUL10 ADDWF REG_I,W BTFSS STATUS,Z ADDLW -1 ;adjust to base 0 unless already 0 (no such day as 00) MOVWF EMDAY RETURN EDIT_INC_HOUR10 MOVLW 10 ADDWF EHOURS,F MOVLW 60 SUBWF EHOURS,W BTFSC STATUS,C MOVWF EHOURS RETURN EDIT_INC_HOUR1 MOVF EHOURS,W CALL BIN100_TO_BCDL INCF EHOURS,F XORLW 9 MOVLW 10 BTFSC STATUS,Z SUBWF EHOURS,F RETURN EDIT_INC_MINUTE10 MOVLW 10 ADDWF EMINUTES,F MOVLW 60 SUBWF EMINUTES,W BTFSC STATUS,C MOVWF EMINUTES RETURN EDIT_INC_MINUTE1 MOVF EMINUTES,W CALL BIN100_TO_BCDL INCF EMINUTES,F XORLW 9 MOVLW 10 BTFSC STATUS,Z SUBWF EMINUTES,F RETURN EDIT_INC_SECOND10 MOVLW 10 ADDWF ESECONDS,F MOVLW 60 SUBWF ESECONDS,W BTFSC STATUS,C MOVWF ESECONDS RETURN EDIT_INC_SECOND1 MOVF ESECONDS,W CALL BIN100_TO_BCDL INCF ESECONDS,F XORLW 9 MOVLW 10 BTFSC STATUS,Z SUBWF ESECONDS,F RETURN ;----------------------------------------- ;Copy the edited time from the edit buffer ;Calculate day of year (in case real time was edited) and time delta in case real time ;or event time were edited EDIT_ACCEPT CALL GET_EDITFIELD_ADDR ;get address of field being edited (based on SET_MODE) MOVWF FSR MOVF ESECONDS,W MOVWF INDF ;seconds INCF FSR,F MOVF EMINUTES,W MOVWF INDF ;minutes MOVF EHOURS,W INCF FSR,F MOVWF INDF ;hours MOVF EMDAY,W INCF FSR,F MOVWF INDF ;day of month MOVF EMONTH,W INCF FSR,F MOVWF INDF ;month MOVF EYEARL,W INCF FSR,F MOVWF INDF ;year modulo 100 MOVF EYEARH,W INCF FSR,F MOVWF INDF ;year div 100 DECF SET_MODE,W BTFSS STATUS,Z ;realtime was edited? GOTO EDIT_ACCEPT_NOTRT CLRF SUBSEC0 ;yes, clear the subseconds CLRF SUBSEC1 CLRF SUBSEC2 CLRF SUBSEC3 EDIT_ACCEPT_NOTRT CLRF SET_MODE ;Calculate day of year CLRF YDAYL CLRF YDAYH MOVF MONTH,W MOVWF REG_I LPCALCDOY MOVF REG_I,W BTFSC STATUS,Z GOTO EXITLPCALCDOY DECF REG_I,F MOVF REG_I,W CALL GET_DAYS_IN_MONTH ADDWF YDAYL,F MOVLW 100 SUBWF YDAYL,W BTFSS STATUS,C GOTO LPCALCDOY MOVWF YDAYL INCF YDAYH,F GOTO LPCALCDOY EXITLPCALCDOY MOVF MDAY,W ADDWF YDAYL,F MOVLW 100 SUBWF YDAYL,W BTFSS STATUS,C GOTO CDOYADDDOMNC MOVWF YDAYL INCF YDAYH,F CDOYADDDOMNC ;Calculate difference between target time and real time BCF APP_FLAGS,PAST_TARGET ;Add up days until target MOVF YEARH,W SUBWF TYEARH,W BTFSS STATUS,C ;negative difference? GOTO CACLDELTAPASTTARG ;Yes, past target time BTFSS STATUS,Z GOTO DOCALCDYEARDAYS ;non-zero difference .. count years MOVF YEARL,W SUBWF TYEARL,W BTFSS STATUS,C ;negative difference? GOTO CACLDELTAPASTTARG ;Yes, past target time DOCALCDYEARDAYS CLRF DDAYL CLRF DDAYH CLRF DDAYH0 CLRF DDAYH1 MOVF YEARL,W MOVWF EYEARL MOVF YEARH,W MOVWF EYEARH LPCALCDDAYSADDYEARS MOVF EYEARL,W XORWF TYEARL,W MOVWF REG_I MOVF EYEARH,W XORWF TYEARH,W MOVWF REG_J MOVF REG_J,W IORWF REG_I,W BTFSC STATUS,Z GOTO EXITLPCDDADDYEARS CALL GET_EFEB_DAYS ADDLW 37 ADDWF DDAYL,F MOVLW 100 SUBWF DDAYL,W BTFSS STATUS,C GOTO LPADDYRSNOCY MOVWF DDAYL INCF DDAYH,F LPADDYRSNOCY MOVLW 3 ADDWF DDAYH,F MOVLW 100 SUBWF DDAYH,W BTFSS STATUS,C GOTO LPADDYRSNOCY2 MOVWF DDAYH INCF DDAYH0,F INCFSZ DDAYH1,F DECF DDAYH0,F LPADDYRSNOCY2 INCF EYEARL,F MOVLW 100 SUBWF EYEARL,W BTFSS STATUS,C GOTO LPCALCDDAYSADDYEARS MOVWF EYEARL INCF EYEARH,F GOTO LPCALCDDAYSADDYEARS ;Add days in target year up to target date EXITLPCDDADDYEARS MOVF TMONTH,W MOVWF REG_I LPADDDOY MOVF REG_I,W BTFSC STATUS,Z GOTO EXITLPADDDOY DECF REG_I,F MOVF REG_I,W CALL GET_DAYS_IN_MONTH_E ADDWF DDAYL,F MOVLW 100 SUBWF DDAYL,W BTFSS STATUS,C GOTO LPADDDOY MOVWF DDAYL INCF DDAYH,F MOVLW 100 SUBWF DDAYH,W BTFSS STATUS,C GOTO LPADDDOY MOVWF DDAYH INCF DDAYH0,F INCFSZ DDAYH1,F DECF DDAYH0,F GOTO LPADDDOY EXITLPADDDOY MOVF TMDAY,W ADDWF DDAYL,F MOVLW 100 SUBWF DDAYL,W BTFSS STATUS,C GOTO CDDADDDOMNC MOVWF DDAYL INCF DDAYH,F MOVLW 100 SUBWF DDAYH,W BTFSS STATUS,C GOTO CDDADDDOMNC MOVWF DDAYH INCF DDAYH0,F INCFSZ DDAYH1,F DECF DDAYH0,F CDDADDDOMNC ;Now subtract the current day of year from the day delta MOVF YDAYL,W SUBWF DDAYL,F BTFSC STATUS,C GOTO NBORROWCDDAYS MOVLW 100 ADDWF DDAYL,F MOVLW 1 SUBWF DDAYH,F BTFSC STATUS,C GOTO NBORROWCDDAYS MOVLW 100 ADDWF DDAYH,F MOVLW 1 SUBWF DDAYH1,F BTFSS STATUS,C DECF DDAYH0,F NBORROWCDDAYS MOVF YDAYH,W SUBWF DDAYH,F BTFSC STATUS,C GOTO NBORROWCDDAYS2 MOVLW 100 ADDWF DDAYH,F MOVLW 1 SUBWF DDAYH1,F BTFSS STATUS,C DECF DDAYH0,F NBORROWCDDAYS2 ;Done with day delta .. now calculate time delta CLRF REG_I ;day borrow accumulator MOVF HOURS,W SUBWF THOURS,W MOVWF DHOURS BTFSC STATUS,C GOTO NOBORHOURDELT MOVLW 24 ADDWF DHOURS,F DECF REG_I,F NOBORHOURDELT MOVF MINUTES,W SUBWF TMINUTES,W MOVWF DMINUTES BTFSC STATUS,C GOTO NOBORMINDELT MOVLW 60 ADDWF DMINUTES,F MOVLW 1 SUBWF DHOURS,F BTFSC STATUS,C GOTO NOBORMINDELT MOVLW 24 ADDWF DHOURS,F DECF REG_I,F NOBORMINDELT MOVF SECONDS,W SUBWF TSECONDS,W MOVWF DSECONDS BTFSC STATUS,C GOTO NOBORSECDELT MOVLW 60 ADDWF DSECONDS,F MOVLW 1 SUBWF DMINUTES,F BTFSC STATUS,C GOTO NOBORSECDELT MOVLW 60 ADDWF DMINUTES,F MOVLW 1 SUBWF DHOURS,F BTFSC STATUS,C GOTO NOBORSECDELT MOVLW 24 ADDWF DHOURS,F DECF REG_I,F NOBORSECDELT MOVF REG_I,W ;day borrow accumulator ADDWF DDAYL,F BTFSS DDAYL,7 GOTO NOBORDAYSLDELT MOVLW 100 ADDWF DDAYL,F MOVLW 1 SUBWF DDAYH,F BTFSC STATUS,C GOTO NOBORDAYSLDELT MOVLW 100 ADDWF DDAYH,F MOVLW 1 SUBWF DDAYH1,F BTFSS STATUS,C SUBWF DDAYH0,F NOBORDAYSLDELT BTFSS DDAYH0,7 ;negative delta? RETURN ;No, all done! ;Current time is past the target time ... set the delta to 0 CACLDELTAPASTTARG CLRF DDAYL CLRF DDAYH CLRF DDAYH0 CLRF DDAYH1 CLRF DHOURS CLRF DMINUTES CLRF DSECONDS CLRF DSUBSEC0 CLRF DSUBSEC1 CLRF DSUBSEC2 CLRF DSUBSEC3 RETURN ;Pressing the "mode/adjust" pushbutton for more than 1 second will abort a set ;operation, if one is occurring, or toggle between countdown mode and realtime ;mode, if no set operation is in effect. MODE_PB_LONGPRESS MOVF SET_MODE,F BTFSC STATUS,Z GOTO MPBLPNSM CLRF SET_MODE ;abort the set operation RETURN MPBLPNSM MOVLW 1 << COUNTDOWN_MODE XORWF APP_FLAGS,F ;toggle countdown mode RETURN ;------------------------------------------------------------------------------ ;Return the address of the field being edited (based on SET_MODE) GET_EDITFIELD_ADDR MOVLW HIGH(EDITFIELDSTABLE) MOVWF PCLATH DECF SET_MODE,W ADDLW LOW(EDITFIELDSTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL EDITFIELDSTABLE RETLW SECONDS ;real time RETLW TSECONDS ;"target" time (event time) RETLW A1SECONDS ;alarm1 time RETLW A2SECONDS RETLW A3SECONDS RETLW A4SECONDS RETLW A5SECONDS ;------------------------------------------------------------------------------ ;Multiply W by 10 ;W must be smaller than or equal to 25 MUL10 MOVWF TEMP1 BCF STATUS,C RLF TEMP1,F ;X2 MOVF TEMP1,W RLF TEMP1,F RLF TEMP1,F ;X8 ADDWF TEMP1,W RETURN ;------------------------------------------------------------------------------ ;Return the number of days in the month W (0..11) of the current year. ;non-leapyear days_in_months = (31,28,31,30,31,30,31,31,30,31,30,31); ;leapyear days_in_months = (31,29,31,30,31,30,31,31,30,31,30,31); ; ;Leap years are calculated based on the Gregorian calendar. ; GET_DAYS_IN_MONTH MOVWF TEMP1 MOVLW HIGH(DAYSINMONTHSTABLE) MOVWF PCLATH MOVF TEMP1,W ADDLW LOW(DAYSINMONTHSTABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL DAYSINMONTHSTABLE RETLW 31 ;JAN GOTO GET_FEB_DAYS ;FEB RETLW 31 ;MAR RETLW 30 ;APR RETLW 31 ;MAY RETLW 30 ;JUN RETLW 31 ;JUL RETLW 31 ;AUG RETLW 30 ;SEP RETLW 31 ;OCT RETLW 30 ;NOV RETLW 31 ;DEC ;Return the number of days in February of the current year based ;on the Gregorian calendar's method for calculating leap-years. GET_FEB_DAYS BTFSC YEARL,0 RETLW 28 ;year not divisible by 4 BTFSC YEARL,1 RETLW 28 ;year not divisible by 4 MOVF YEARL,F BTFSS STATUS,Z RETLW 29 ;year divisible by 4, not divisible by 100 BTFSC YEARH,0 RETLW 28 ;year divisible by 100, not divisible by 400 BTFSC YEARH,1 RETLW 28 ;year divisible by 100, not divisible by 400 RETLW 29 ;year divisible by 400 ;------------------------------------------------------------------------------ ;Return the number of days in the month W (0..11) of the edit year. ;non-leapyear days_in_months = (31,28,31,30,31,30,31,31,30,31,30,31); ;leapyear days_in_months = (31,29,31,30,31,30,31,31,30,31,30,31); ; ;Leap years are calculated based on the Gregorian calendar. ; GET_DAYS_IN_MONTH_E MOVWF TEMP1 MOVLW HIGH(DAYSINMONTHSETABLE) MOVWF PCLATH MOVF TEMP1,W ADDLW LOW(DAYSINMONTHSETABLE) BTFSC STATUS,C INCF PCLATH,F MOVWF PCL DAYSINMONTHSETABLE RETLW 31 ;JAN GOTO GET_EFEB_DAYS ;FEB RETLW 31 ;MAR RETLW 30 ;APR RETLW 31 ;MAY RETLW 30 ;JUN RETLW 31 ;JUL RETLW 31 ;AUG RETLW 30 ;SEP RETLW 31 ;OCT RETLW 30 ;NOV