Dial Clock Picaxe Basic Program

Declarations and Data

#rem dialClock.bas This uses interrupt on 1 Hz square wave and provides

clock pulses, program transmissioms and the time-date message.

Buttons provide for Daylight Saving Ttime selection and minute adjustment

memory assignment:

28-42 Transmitter message = txStart

45-65 Row0 time and date = row0Start, $80

70-90 Ror1 year, battery voltage, temperature, row1Start, $A0

95-115 Row2 Datlight Saving Time, message index 0 row2Start, $C0

120=140 Row3 soft key labels message index 1, row3Start, $E0

John Saunders 5/8/2022 Version with one clock access only, new memory locations, 

5/10/2022 change Tx dst code, do not display then, 7/1/2022 change timeout from 40 to 6

#endrem 

#Picaxe 18M2

rem NO_DATA


rem ports

symbol dispRst    = B.0            'Display reset, not used

symbol driveY    = B.2            'Alternate polarity clock motor drive pulses

symbol driveB    = B.3            'Alternate polarity clock motor drive pulses

symbol dispOff    = B.5            'Controls a power switch to de-power the display

symbol refPort    = B.7            'A reference diode to indirectly measure the battery voltage

symbol button1    = pinC.0        'Number 2 button from the left

symbol button0    = pinC.1        'Left button

symbol hzIn        = pinC.2        'Number 3 button from the left

symbol txOut    = C.3            'A delicate connection to pin 3 of thr DS3231 real-Time Clock chip

symbol microsw    = pinC.5

symbol button3    = pinC.6        'Right button

symbol button2    = pinC.7        'Number 3 button from the left


rem Flag variables

symbol dispOnFlag    = bit1        'Denotes display mode on

symbol dstFlag    = bit2        '1 is Daylight Saving time

symbol initFlag    = bit3        'Commands initializing the display

symbol oldOnFlag    = bit4        'To ensure initialzing only omce



rem interrupt variables

symbol phase    = bit7        'High and low of 1 Hz square wave

symbol oneHz    = bit8        'Alternating polarity of the clock drive pulses

symbol secCount    = b3

symbol ptrSave    = b4



rem local variables can be re-used but beware of nested subprograms

symbol decPoint    = bit9        'For numerical display

symbol repeatFlag    = bit10        'Transmissions are sent twich in case of interference

symbol overflow    = bit11

symbol analogVal    = w1            'For battery voltage calulation

symbol decVal    = b5

symbol charVal    = b6

symbol bcdval    = b7

symbol tens        = b8

symbol units    = b9

symbol row        = b10

symbol iter        = b11


rem addressing variabes, may be used in indirect addressing

symbol dataAddr    = b12            'In EEPROM

symbol memAddr    = b13            'In display buffer in RAM

symbol dsAddr    = b14            'In the RTC

symbol stringAddr    = b15            'In the RAM

symbol dispAddr    = b16            'In the display


rem global variables


symbol pgmIndx    = b17            'An index into the lights programming table

symbol toCount    = b18            'To automatically terminate the display

symbol stepCount    = b19            'Controls time/date and program transmissions            

symbol hour        = b20            'Current RTC value in binary

symbol minute    = b21            'Current RTC value in binary

symbol dispRow    = b22            'An index into the mster table of available display formats

symbol keyCode    = b23            'Receiver identifier in transmissions 



rem --------------------------------- constants --------------------------------


symbol pulselen    = 8             '30 milliseconds to clock drive

symbol maxToCount = 6            'Display timeout in seconds

symbol mainFreq    = k250        'Low power oopertion when the display is off

symbol dispFreq    = m4            'Faster operation necessary for button and display writing when om

symbol maxPgmIndx    = 21            'Size of the lights program table index

symbol refVal    = 63590        'Ref diode Volts = 2.484



rem memory addresses

symbol txStart    = 150            'Transmit message buffer 15 bytes 

symbol row0Start    = 45

symbol row1Start    = 70

symbol row2Start    = 95

symbol row3Start    = 120

symbol TDStart    = 34


rem Data locations

symbol row2STD    = 145            'EEPROM address of row 2 strings for STD

symbol row2DST    = 149            'EEPROM address of row 2 strings for DST

symbol row3data    = 153            'EEPROM address of row 3 strings

symbol dstSavAddr = 197            'Saving the Daylight Savings flag value

symbol dayStart    = 198            'Start of the day-of-week table in EEPROM

symbol monthStart    = 219            'Start of the month table in EEPROM




rem --------------------------------- Command table ---------------------------------

rem     hour minute flag keyCode button

DATA  0, (17,25,"7")    'String Lights ON

DATA  3, (05,50,"7")    'String Lights ON

DATA  6, (19,40,"6")    'String Lights OFF

DATA  9, (07,00,"6")    'String Lights OFF

DATA 12, (17,26,"m")    'Clear Outlet ON

DATA 15, (05,53,"m")    'Clear Outlet ON

DATA 18, (06,56,"J")    'Clear Outlet OFF

DATA 21, (19,41,"J")    'Clear Outlet OFF

DATA 24, (20,37,"f")    'Floor Light OFF

DATA 27, (00,38,"f")    'Floor Light OFF

DATA 30, (25,00,"I")    'Hanging Lamp ON

DATA 33, (25,57,"I")    'Hanging Lamp ON

DATA 36, (19,42,"C")    'Hanging Lamp OFF

DATA 39, (06,58,"C")    'Hanging Lamp OFF

DATA 42, (17,30,"d")    'Ornaments ON

DATA 45, (19,38,"9")    'Ornaments OFF

DATA 48, (05,34,"d")    'Ornaments ON

DATA 51, (07,31,"9")    'Ornaments OFF

DATA 54, (17,31,"Y")    'Flashing ON

DATA 57, (19,37,"W")    'Flashing OFF

DATA 60, (05,35,"Y")    'Flashing ON

DATA 63, (07,37,"W")    'Flashing OFF


rem --------------------------------- Pages ---------------------------------


DATA  145,(189,182,164,0)        'Row 2 for STD

DATA  149,(193,182,164,0)        'Row 2 for DST

DATA  153,(189,193,172,177,0)        'Row 3


rem --------------------------------- Strings ---------------------------------


DATA 164,("Minutes")

DATA 172,("Incr")

DATA 177,("Decr");

DATA 182,("Select")

DATA 189,("STD")

DATA 193,("DST")

DATA 197,(0)            'Daylight Saving Time is 1

rem strings for abbreviations

DATA 198,("SunMonTueWedThuFriSat")

DATA 219,("JanFebMarAprMayJunJulAugSepOctNovDec")

Init and Main

init:    '--------------------------------- Init ---------------------------------


HIGH dispOFF

HIGH dispRst

LET pgmIndx = 0

LET stepCount = 0

LET toCount = 0

LET dispOnFlag = 0

LET oldOnFlag = 0

READ dstSavAddr,dstFlag            'Restore Daylight-Saving Time selectio

HI2CSETUP I2CMASTER,$D0,I2Cslow,I2cbyte

HI2COUT 0x0E,(0)                        'Sets the 1 Hz clock

'HI2COUT 0,(0x51,0x43,0x16,0x06,0x24,0x06,0x22)

SETFREQ mainFreq

SETINT %00000100,%00000100


main:


IF dispOnFlag = 1 AND stepCount <> 1 THEN                'Display on operations

    SETFREQ dispFreq

    IF initFlag = 1 THEN

        GOSUB initDisp

        LET dataAddr = row3Data

        LET memAddr = row3Start

        GOSUB storeFixed

        LET initFlag  = 0

    ENDIF    

    IF button0 = 0 tHEN

        LET dstFlag = 0

        WRITE dstSavAddr,dstFlag

    ENDIF

    IF button1 = 0 tHEN

        LET dstFlag = 1

        WRITE dstSavAddr,dstFlag

    ENDIF

    IF button2 = 0 OR button3 = 0 THEN

        GOSUB AdjMins

    ENDIF

    GOSUB storeRow0                'Get and store in ASCII RTC time and date

    GOSUB storeRow1                'store yesr; Measure battery voltage, temperature        

    IF dstFlag = 1 THEN            'Show current selection    

        LET dataAddr = row2DST

    ELSE

        LET dataAddr = row2STD

    ENDIF

    LET memAddr = row2Start

    GOSUB storeFixed    

    GOSUB DisplayBuffers

    SETFREQ mainFreq

ENDIF


IF stepCount = 1 THEN                    'Get the current hour and minute in binary for program comparison            

    LET dsAddr = TDStart

    PEEK dsAddr,units,tens                'in bcd            

    LET bcdVal = units / 16

    LET minute = 10 * bcdVal 

    LET minute = units & 0x0F + minute

    LET bcdVal = tens / 16

    LET hour = 10 * bcdVal 

    LET hour = tens & 0x0F + hour    + dstFlag

    IF hour > 23 THEN

        LET hour = 0

    ENDIF

    LET memAddr = txStart             

    FOR iter = 0 TO 3                    'Populate the transmit buffer time and date and commas in ASCII

        LOOkUP iter,(5,4,2,1),dsAddr

        LET DataAddr = TDStart + dsAddr - 1

        PEEK DataAddr,bcdval

        BCDTOASCII bcdval,tens,units

        IF  dsAddr = 2 AND dstFlag = 1 THEN

            IF units < "9" THEN

                INC units

            ELSE

                LET units = "0"

                IF tens < "2" THEN

                    INC tens

                ELSE

                    LET tens = "0"

                ENDIF

            ENDIF

        ENDIF

        LET memAddr = 3 * iter + txStart 

        POKE memAddr,tens, units, ","

    NEXT 


    LET decVal = 0                    'Calculate the checksum

    LET bptr = txStart 

    FOR iter = 0 TO 10                'Don't include the last comma! 

        LET decVal = decVal + @bptrinc

    NEXT


    LET memAddr = txStart + 12            'Add the 2 checksum characters to the transmit buffer                

    LET charVal = decVal / 16

    IF charVal < 10 THEN

        LET charVal = charVal + "0"

    ELSE

        LET charVal = charVal + "7"

    ENDIF

    POKE memAddr,charVal    

    INC memAddr                    

    LET charVal = decVal & $F

    IF charVal < 10 THEN

        LET charVal = charVal + "0"

    ELSE

        LET charVal = charVal + "7"

    ENDIF

    POKE memAddr,charVal    

    LET stepCount = 2

    PAUSE 2000

ENDIF


IF stepCount = 3 THEN

rem Look for a hour and minute match in the lights table

    dataAddr = 3* PgmIndx

    READ dataAddr, tens, units, keyCode

    IF stepCount > 3 THEN

        FOR iter = 0 TO 10

            PAUSE 200

        NEXT

    ENDIF

    IF hour = tens AND minute = units THEN

        LET stepCount = 4

    ENDIF

    IF stepCount = 3 THEN

        IF pgmIndx < maxPgmIndx THEN

            INC pgmIndx

        ELSE

            LET stepCount = 0

            LET pgmIndx = 0

        ENDIF

    ENDIF

ENDIF    


GOTO main

Sub-programs

rem --------------------------------- Top level subroutines which call other subroutines ------------


storeRow0:

LET bptr = row0Start

FOR iter = 0 TO 4

    LOOKUP iter,(2,1,3,5,4),dsAddr

    LET dsAddr = dsAddr + TDStart - 1

    PEEK dsAddr,bcdVal                            

    LET tens = bcdVal / 16 + "0"

    LET units = bcdVal & 0x0F + "0"

    SELECT iter

        CASE 0                        'Hour

            IF dstFlag = 1 THEN 

                IF units < "9" THEN

                    INC units

                ELSE

                    LET units = "0"

                    INC tens

                ENDIF

            ENDIF

            IF tens > "2" THEN

                LET tens = "0"

            ENDIF

            LET @bptrinc = tens

            LET @bptrinc = units

            LET @bptrinc = ":"

            LET @bptrinc = " "

        CASE 1                        'Minute

            LET @bptrinc = tens

            LET @bptrinc = units

            LET @bptrinc = " "

            LET @bptrinc = " "

        CASE 2                        'Day of week

            LET decVal = bcdVal & 0x0F

            LET dataAddr = 3 * decVal + dayStart - 3

            READ dataAddr,@bptrinc

            INC dataAddr

            READ dataAddr,@bptrinc

            INC dataAddr

            READ dataAddr,@bptrinc

            LET @bptrinc = " "

            LET @bptrinc = " "

        CASE 3                        'Month

            LET decVal = bcdVal / 16

            LET decVal = 10 * decVal

            LET decVal = bcdVal & 0x0F + decVal

            LET dataAddr = 3 * decVal + monthStart - 3

            READ dataAddr,@bptrinc

            INC dataAddr

            READ dataAddr,@bptrinc

            INC dataAddr

            READ dataAddr,@bptrinc    

            LET @bptrinc = " "

            LET @bptrinc = " "        

        CASE 4                        'Date

            LET @bptrinc = tens

            LET @bptrinc = units                    

    ENDSELECT    

NEXT

RETURN


storeRow1:

LET dsAddr = TDStart + 5

LET bptr = row1Start

PEEK dsAddr,bcdVal                    'Store year

LET @bptrinc = "2"

LET @bptrinc = "0"

LET tens = bcdVal / 16 + "0"

LET units = bcdVal & 0x0F + "0"

LET @bptrinc = tens

LET @bptrinc = units

LET @bptrinc = " "

LET @bptrinc = " "

ADCCONFIG 0

READADC refPort,decVal                'Store battery voltage

LET analogVal = refVal/decVal    

LET decPoint = 1

GOSUB storeAnalog

LET @bptrinc = " "

LET @bptrinc = " "                        'Measure temperature in degrees Celcius

HI2CSETUP I2CMASTER,$D0,I2Cslow,I2cbyte        

HI2CIN $11,(tens,units)

LET analogVal = tens & $7F

LET analogVal = 10 * analogVal

LET units = units / 64

LET units = 5 * units

LET analogVal = units/2  + analogVal

LET decPoint = 0

GOSUB storeAnalog

RETURN


rem --------------------------------- Subprograms for the display buffer ----------------


AdjMins:                        'DO NOT USE IF THERE WILL BE OVERFLOW!

LET overflow = 0

LET dsAddr = TDStart

PEEK dsAddr,bcdVal    

LET charVal = bcdVal & $F0

LET decVal = charVal/16

LET charVal = bcdVal & $0F

LET decVal = 10*decVal + charVal

IF button2 = 0 THEN

    IF decVal < 59 THEN

        INC decVal

    ELSE

    LET decVal = 0

    LET overflow = 1

    ENDIF

ENDIF

IF button3 = 0 THEN

    IF decVal > 0 THEN

        DEC decVal

    ELSE

        LET decVal = 59

        LET overflow = 1

    ENDIF

ENDIF

LET tens = decVal / 10                'Convert back to BCD

LET units = decVal // 10

LET bcdVal = 16*tens + units

IF overflow = 0 THEN

    Hi2COUT 1,(bcdVal)            'minutes

    LET dsAddr = TDStart

    POKE dsAddr,bcdval

ENDIF

RETURN


storeAnalog:                    'Set row and col and analogVal before calling

LET charVal  = analogVal/100        

LET charVal  = charVal + "0"            'Ascii

LET @bptrinc = charVal                'Digit #1

IF decPoint = 1 THEN        

    LET @bptrinc = "."            'Digit 3 if period    

ENDIF                

LET AnalogVal  = AnalogVal//100        

LET charVal  = AnalogVal/10        

LET charVal  = charVal + "0"            'Ascii

LET @bptrinc = charVal                'Digit #2 w/o period else digit 4

IF decPoint = 0 THEN        

    LET @bptrinc = "."            'Digit 3 if period    

ENDIF            

LET charVal  = AnalogVal //10            'Least significant digit

LET charVal  = charVal + "0"            'Ascii

LET @bptrinc = charVal                'Digit #3 w/o period else digit 5

LET @bptrinc = " "

IF decPoint = 1 THEN        

    LET @bptrinc = "V"

ELSE            

    LET @bptrinc = "C"

ENDIF

RETURN


storeFixed:                    'Set row and data start addresses before calling

LET bptr = memAddr

DO

    READ dataAddr,stringAddr


    IF stringAddr > 100 THEN

    DO

        READ stringAddr,charVal

        IF charVal > 0 THEN

            LET @bptrinc = charVal

        ENDIF

        INC stringAddr

    LOOP WHILE charVal > 0

    LET @bptrinc = " "

    LET @bptrinc = " "

    

    ENDIF

    INC dataAddr

LOOP WHILE stringAddr > 0    

RETURN




rem --------------------------------- Subprograms for the display itself ----------------


initDisp:                    'From the display datasheet 


HI2CSETUP I2CMASTER,$78,i2cfast,i2cbyte            'The OLED Display

HIGH dispRst

PAUSE 1

HI2COUT (0,0x2A);  //function set (extended command set)  

HI2COUT (0,0x71);  //function selection A  

HI2COUT (0,0x00);  // data(00) = disable regulator (5V I/O)  

HI2COUT (0,0x28);  //function set (fundamental command set)  

HI2COUT (0,0x08);  //display off, cursor off, blink off  

HI2COUT (0,0x2A);  //function set (extended command set)  

HI2COUT (0,0x79);  //OLED command set enabled  

HI2COUT (0,0xD5);  //set display clock divide ratio/oscillator frequency  

HI2COUT (0,0x70);  //set display clock divide ratio/oscillator frequency  

HI2COUT (0,0x78);  //OLED command set disabled 

HI2COUT (0,0x09);  //extended function set (4-lines) 

HI2COUT (0,0x06);  //COM SEG direction  

HI2COUT (0,0x72);  //function selection B  

HI2COUT ($40,0x00);  //ROM CGRAM selection  A

HI2COUT (0,0x2A);  //function set (extended command set)  

HI2COUT (0,0x79);  //OLED command set enabled  

HI2COUT (0,0xDA);  //set SEG pins hardware configuration  

HI2COUT (0,0x10);  //set SEG pins hardware configuration  

HI2COUT (0,0xDC);  //function selection C  

HI2COUT (0,0x00);  //function selection C  

HI2COUT (0,0x81);  //set contrast control  

HI2COUT (0,0x7F);  //set contrast control  

HI2COUT (0,0xD9);  //set phase length  

HI2COUT (0,0xF1);  //set phase length  

HI2COUT (0,0xDB);  //set VCOMH deselect level  

HI2COUT (0,0x40);  //set VCOMH deselect level 

HI2COUT (0,0x78);  //OLED command set disabled  

HI2COUT (0,0x28);  //function set (fundamental command set) 

HI2COUT (0,0x01);  //clear display  

HI2COUT (0,0x80);  //set DDRAM address to 0x00  

HI2COUT (0,0x0C);  //display ON 

PAUSE 10

RETURN


DisplayBuffers:        'Done all at once to avoid flickering

HI2CSETUP I2CMASTER,$78,i2cfast,i2cbyte        'The OLED Display. 

FOR row = 0 TO 3

    LOOKUP row,(row0Start,row1Start,row2Start,row3Start),bptr            'Row addressess

    LOOKUP row,($80, $A0, $C0, $E0),dispAddr

    HI2COUT 0,(dispAddr)                    'Set cursor to the beginning of the row

    FOR Iter = 0 TO 19

        LET charVal = @bptrinc

        HI2COUT $40,(charVal)

    NEXT

NEXT

PAUSE 2000

RETURN


interrupt:    'rem ------------------------------------ Interrupt --------------------------------------

LET ptrSave = bptr


LET phase = hzIn                    

IF phase = 0 THEN        

    SETFREQ m1            

    IF oneHz = 1 THEN             'Drive the clock

        LET oneHz = 0

        LOW driveY    '    

        PAUSE pulseLen    

        LOW driveB

    ELSE

        LET oneHz = 1

        HIGH driveY

        PAUSE pulseLen

        HIGH driveB

    ENDIF

    IF dispOnFlag = 1 THEN

        SETFREQ dispFreq

    ELSE

        SETFREQ mainfreq

    ENDIF

    SETINT %00000100,%00000100    

ELSE

rem Timed operations,Positive transition, clock is not updating

    SETFREQ dispFreq

    IF toCount > 0 THEN

        DEC toCount

    ELSE

        LET dispOnFlag = 0

        HIGH dispOFF

    ENDIF

    

    HI2CSETUP I2CMASTER,$D0,I2Cslow,I2cbyte

    HI2CIN 0,(secCount)


    IF secCount = 0 THEN

        LET pgmIndx = 0

        LET stepCount = 1

        LET bptr = TDStart

        HI2CIN 1,(@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptr)            

    ENDIF

    

    IF stepCount = 2 THEN            'Transmit a time and date message

        SETFREQ m4

        HIGH TxOut

        PAUSE 20

        LOW TxOut

        PAUSE 10

        LET bptr = txStart

        SEROUT TxOut,N2400_4,("14L1776t,G,",@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc, @bptrinc,@bptrinc,  @bptrinc,@bptrinc,@bptrinc,13,10)

        LET stepCount = 3

    ENDIF

    IF stepCount = 5 THEN        'Transmit a light programmessage

        SETFREQ m4

        HIGH TxOut

        PAUSE 20

        LOW TxOut

        PAUSE 10

        SEROUT TxOut,N2400_4,("14L1776",keyCode,13,10)        'Lights program command message    

        IF pgmIndx < maxPgmIndx THEN

            INC pgmIndx

            LET stepCount = 3

        ELSE

            LET pgmIndx = 0

            LET stepCount = 0

        ENDIF

    ENDIF

        IF stepCount = 4 THEN        'Transmit a light programmessage

        SETFREQ m4

        HIGH TxOut

        PAUSE 20

        LOW TxOut

        PAUSE 10

        SEROUT TxOut,N2400_4,("14L1776",keyCode,13,10)        'Lights program command message    

        LET stepCount = 5

    ENDIF

    IF microsw = 0 THEN

        IF dispOnFlag = 0 THEN

            LET initFlag = 1

            LET toCount = maxToCount

        ENDIF    

        LET dispOnFlag = 1

        LOW dispOFF 

    ENDIF

    IF button0 = 0 OR button1 = 0 OR button2 = 0 OR button3 = 0 THEN

        LET toCount = maxToCount

    ENDIF

    

    IF dispOnFlag = 1 THEN

        SETFREQ dispFreq

    ELSE

        SETFREQ mainfreq

    ENDIF

    SETINT %00000000,%00000100

ENDIF                            

LET bptr = ptrSave

RETURN