' ' WPS Model 13, Bakelite Clock. ' ' Clock software using MicroMint PICSTIC-2 with RTC. ' ' Tom Jennings ' mru 18 Jan 98 ' written 24 Nov 97 ' ' 18 Jan 98 ' Battery life bad -- is drawing 1.9mA due to PA4 grounded by NC ' 'SET' switch. Changed code for NO SET switch (added "+ 1" to "n/16"). ' Should then draw spec'd 0.5mA. ' ' 8 dec 97 ' First "final" code. ' Set seconds to 0 when changing minutes. Bit4 (LIGHT switch) was set as ' output in LED_dir; changed to be input at all times. ' ' 5 dec 97 ' First installed code. ' ' ' Copyright Tom Jennings, 1997. ' ' Awake time, seconds, = NAP_LOOP * (AWAKE_TIME + PWM time) (more or less) symbol IDLE_NAP_TIME= 2 ' sleep time during the long (idle loop) sleep symbol NAP_LOOP= 1 ' loop-slow-down nap time symbol AWAKE_TIME= 83 ' clock awake time, iterations symbol AUTODELAY= 10 ' key-repeat delay, iterations symbol AUTORATE= 3 ' key-repeat rate, iterations symbol LOOP_BLINK= 4 ' N iterations, toggle LEDs when SET+LIGHT symbol PWM_TIME= 4 ' 20 mS; approx. 2X RC time symbol NAP_18= 0 ' NAP time, 18 mS symbol NAP_36= 1 ' etc symbol NAP_72= 2 symbol NAP_144= 3 symbol NAP_288= 4 symbol NAP_576= 5 symbol NAP_1152= 6 ' PWM analog out bits are pins 0 and 1. symbol MS_out= 0 ' PWM analog out, coarse 0 - 5V symbol LS_out= 1 ' PWM analog out, fine 0 - 5V ' The front panel switches are on I/O pins 2 - 5. SET is on PA4 and ' checked explicitly. symbol TIME_bit= 2 ' TIME switch in/LED out symbol DATE_bit= 3 ' DATE switch in/LED out symbol LIGHT_bit= 4 ' LIGHT switch in/LED out symbol POWER_bit= 7 ' analog power out symbol switch_mask= %00011100 ' mask off unwanted bits ' LEDs are on I/O pins 2 - 7; many overlap switch bits. symbol AM_LED= %10000100 ' AM LED and analog power symbol PM_LED= %10100000 ' PM LED and analog power symbol MON_LED= %11000000 ' MONTH LED and analog power symbol DAY_LED= %10001000 ' DAY LED and analog power symbol SW_dir= %10000000 ' all pins except analog power are inputs symbol LED_dir= %11101100 ' MONTH, DAY, AM, PM LED bits are outputs symbol PU_dir= %10000011 ' power-up/shut down pin states ' RAM space, b0. Note that these three MUST be in this order, as they are ' used arithmetically in branch()'s and lookup()'s. symbol funcs= b0 ' all function bits symbol func_time= bit0 ' TIME switch was pressed (else date) symbol func_mode= bit1 ' toggled sub-state symbol func_set= bit2 ' LIGHT pressed symbol blink= bit8 ' symbol time_state= b2 ' button() command states symbol date_state= b3 symbol light_state= b4 symbol loop_count= b5 ' major loop counter ' b6-b7, our sole long int. symbol longint= w3 ' b7-b10 symbol i= b11 ' IJKLMN symbol j= b12 symbol k= b13 symbol n= b14 ' RAM used by clockget, B15 - B21. We name them for local convenience; ' the RTC routines always read/write into B15-B21. symbol year= b15 ' (0-99) symbol month= b16 ' (1-12) symbol day= b17 ' (1-31) symbol dywk= b18 ' (1-7) day of week symbol hours= b19 ' (0-23) symbol minutes= b20 ' (0-59) symbol seconds= b21 ' (0-59) ' Shut down all hardware, and go into low-power mode. force_idle: peek 133, n : n= n | 16 : poke 133, n ' make PA4 an input dirs= PU_dir ' set switches as inputs pins= 0 ' drain analog power, integrators, func_mode= 0 ' always awake in first state time_state= 0 : date_state= 0 : light_state= 0 ' We remain in low-power idle mode until a key is pressed. Battery life is ' mainly determined by nap vs. run code in this loop. idle: nap IDLE_NAP_TIME ' low-power sleep, n= pins & switch_mask if n = 0 then idle ' wait for key press, ' Process pressed switches. The logic is somewhat convoluted, as user-interfaces ' usually are. ' TIME and DATE set func_time to 1 (TIME) or 0 (DATE). The first pressing of either ' also sets func_mode, which indicates hour/minute or day/month handling: when ' set-mode criteria met (see below) tell which (h/m, d/m) should be incremented. ' SET and LIGHT pressed increments time/date (per above). SET is a simple on/off ' state; LIGHT auto-repeats once per iteration. i0: gosub getclock ' read the RTC, dirs= SW_dir ' set pins as inputs to read switches ' NIGHTMARE HEISENBUG!! Unless this command, or something that changes I/O? ' (such as debug code!) is here, func_mode toggles? Only in TIME mode. As if ' TIME switch were pressed repeatedly. 'high POWER_bit' is useful anways, to ' turn the opamp on before we set outputs, but sheesh, what a way to spend a ' dozen hours. high POWER_bit ' alleviate bug?, turn on analog power button DATE_bit, 1, 255, 0, date_state, 0, i1 func_mode= func_time + 1 & func_mode + 1 ' set if prev not date, else toggle func_time= 0 ' DATE pressed loop_count= 0 ' restart awake timer i1: button TIME_bit, 1, 255, 0, time_state, 0, i2 func_mode= func_time & func_mode + 1 ' set if prev not time, else toggle func_time= 1 ' TIME pressed loop_count= 0 ' restart awake timer ' SET and LIGHT are less straightforward. If both are pressed, we ' increment the appropriate value. If only LIGHT is pressed, then ' we repeat the previous TIME or DATE command (and LIGHT held down ' illuminates the meter card.) i2: peek 133, n : n= n | 16 : poke 133, n ' make PA4 an input peek 5, n ' read PA4 func_set= n / 16 + 1 ' set func_set accordingly i3: button LIGHT_bit, 1, AUTODELAY, AUTORATE, light_state, 0, disp loop_count= 0 ' restart awake timer if func_set = 0 then disp ' if SET+LIGHT, n= funcs & 3 ' dispatch to incrementer branch n, (emonth, emin, eday, ehour) emin: minutes= minutes + 2 // 60 : seconds= 0 :goto e9 ' increment minutes faster ehour: hours= hours + 1 // 24 : goto e9 emonth: month= month + 1 : if month < 13 then ed2 month= 1 : goto ed2 eday: lookup month, (0,31,28,31,30,31,30,31,31,30,31,30,31), i day= day + 1 : if day <= i then e9 ed2: day= 1 : year= 97 e9: gosub setclock ' update RTC with new time/date ' Handle blinking the LEDs first; while set is pressed, don't re-enable LED outputs ' every N loops (they are currently set to inputs so we can read the switches). disp: blink= loop_count / LOOP_BLINK + 1 ' toggles every N loops blink= func_set + 1 | blink ' 1 if not SET, or Nth loop if blink = 0 then disp2 ' dirs= LED_dir ' enable LEDs ' Display time or date. We use lookup to iron out non-linearities in analog side ' and the meter movement. Time and month display share code. disp2: if func_time = 1 then d2 ' if DATE pressed, pwm LS_out, 0, PWM_TIME ' not used; output 0 for loop timing if func_mode = 0 then d1 ' if day display, pins= DAY_LED lookup day, (0,7,13,19,25,31,38,44,51,57,63,70,76,82,88,95,101,107,113,119,125,131,135,140,145,149,154,159,163,168,173,178), n goto Out d1: n= month : i= MON_LED : goto d5 ' display month using 1-of-13 table ' Time is displayed with two PWM outputs to double resolution. The minutes PWM bit ' is tied to the - input on the opamp, to keep the load on the integrator capacitors low, ' and is therefore inverted in value; we run minutes 0 - 59 as +5V to 0 and ' display hours + 1. (eg. 2:45 is equiv. to 3:00 minus :15). The values here are ' hard coded; see the paper documentation. d2: longint= 60 - minutes * 265 / 100 ' PWM full scale is 0-158; 2.65= 158 / 60 pwm LS_out, longint, 10 ' do minutes, ' ---------- AM ------------- ------------- PM (+128) ----------------------- lookup hours, (13,2,3,4,5,6,7,8,9,10,11,12, 141,130,131,132,133,134,135,136,137,138,139,140), n d3: i= AM_LED : if n < 128 then d4 ' if flagged as PM, i= PM_LED : n= n & 127 ' turn on PM LED, clear MS bit d4: j= func_mode + 1 & func_set & 1 if j = 0 then d5 ' if SET pressed, and TIME pressed twice, i= AM_LED | PM_LED ' blink both LEDs d5: pins= i lookup n, (0,15,31,46,62,77,93,108,124,136,148,159,171,182), n Out: pwm MS_out, n, PWM_TIME ' output time, day or month ' End of (big) loop. e10: nap NAP_LOOP ' delay, slowing down loop, loop_count= loop_count + 1 ' count times through loop. if loop_count < AWAKE_TIME then i0 ' if last iteration, goto force_idle ' shut down ' -------------- ' These two routines use an ugly hack for compactness. ' Pack canonical to RTC packed hex, set the RTC, unpack again. setclock: for i= 15 to 21 ' Bxx register number n= i + 12 ' unknown magical result peek n, j ' read packed-hex k= j / 10 * 16 : j= j // 10 : k= k + j poke n, k next i call clockset goto gc1 ' Unpack the RTC packed hexadecimal to canonical. getclock: call clockget ' read the RTC gc1: for i= 15 to 21 ' Bxx register number n= i + 12 ' unknown magical result peek n, j ' read packed-hex k= j / 16 * 10 : j= j & $0f : k= k + j poke n, k next i return END