Proton BASIC Compiler - Three Button adjustment of a DS1307 RTC

  • Pic® Basic

  • Three Button adjustment of a DS1307 RTC

    The circuit uses a PIC® 16F84 and a standard 16xx 2 LCD (see Proton + manual under “print” for more details).

    The buttons are momentary push to make types.

    I have tested the program on the following PIC® chips, 16F84, 16F628(7), 18F1320(1220) and 18F452(252).

    It should work on any part with 1k or more memory.

    Read the notes in the program for more details.

    The software is called “84_1307_1.bas” and is fully commented with each routine having its own explanation.

    I have used versions of this code for two years now and it has proved to be very reliable.

    'Program "84_1307_1.bas" written by Mark Rodgers 18,07,04.
    'Compiled in Proton+ 2.1.4
    'Tested on 16F84,16F628,18F1320 and 18F452

    'It is presented as is with no guarantees or responsibilities implied.
    'It is not for any comercial use(I wish!) or to copy and pass any part as
    'your own, I would be happy if it was used to develop your own comercial free
    'code or as the basis for a club project with inclusion of author recognition.
    'Please feel free to contact me if you wish to use this code/hardware for any reason.

    Device 16F84 'better than an OC71

    XTAL 4



    WARNINGS off

    Dim bcd_to_bin_byte 'variable used in BCD and Binary conversion routines
    Dim low_bits 'low byte for Binary to BCD routine
    Dim high_bits 'high byte for Binary to BCD routine
    Dim ds1307_address 'DS1307 internal registers address(0=seconds,1=minutes etc.)
    Dim second 'seconds variable
    Dim minute 'minutes variable
    Dim hour 'hours variable
    Dim day 'days variable
    Dim date 'dates variable
    Dim month 'months variable
    Dim year 'years variable
    Dim buttonvalue 'variable used for "up" and "down" buttons values
    Dim maxvalue 'variable holding highest allowable current value in button routine
    Dim minvalue 'variable holding lowest current allowable value in button routine
    Dim leap 'variable used to set "maxvalue" in leapyear
    Dim monthlength 'variable used to set maximum number of days in the selected month
    Dim index 'variable used to select display options in "setup"
    Dim character 'variable holding data selected from eeprom or ldata tables to be printed on display
    Dim printposition 'start position on the LCD of first "character" in print routine
    Dim printend 'last "character" position to be printed on LCD
    Dim printaddress 'start position of data in eeprom or ldata table
    Dim row 'the line number you wish to display the info on the LCD
    Dim read_data 'tells the print routine,"print_eeprom" to use eeprom(0) or ldata(1) for its data reading


    Symbol up=PORTA.2 'use porta.2 for the "up" button
    Symbol down=PORTA.1 'use porta.1 for the "down" button
    Symbol setup=PORTB.3 'use portb.3 for the "setup" buton
    Symbol clockout=%11010000 'set the 1307 to receive data
    Symbol clockin=%11010001 'set the 1307 to transmit data

    TRISA=%00110 'set porta to use 2 bits as input
    TRISB.3=1 'set portb to input for "setup" button
    PORTA=0 'make sure porta is clear
    PORTB=0 'make sure portb is clear




    'this is the main routine that reads the time from the DS1307 and then displays it on the LCD
    If setup=0 Then GoTo start 'debounce setup button on return from setting time


    GoSub get_time 'read time from DS1307
    GoSub print_time 'print the clock display

    If setup=0 Then GoTo time_set 'if the setup button is pressed then goto the time setting routine

    GoTo display_time



    'This section of code is dealing with data from the DS1307 and converting it from the BCD used by
    'the chip, to Binary used by the real world.
    'These routines have been heavily borrowed from LES JOHNSON'S book "The L.E.T. PIC® basic Compiler
    'Unleashed" that was originaly provided with the old compiler. I have changed them to suit my own
    'conventions and re-written them to use purely Proton Basic commands (none of that horrid assembler
    'here thank you!!).
    'I have included the original assembler versions of the BCD to Binary conversion routines.
    'You can change the routines and see the different size of code produced when using the original
    'routines and my fat Basic ones.
    'I wrote the Basic versions in order to understand the conversion process and to keep a flag
    'flying for my "Basic Rules!" crusade!!.
    'Thanks for the inspiration LES.



    'This routine starts the clock running.
    '"clockout" is the alias for the DS1307 I2C buss address, the first zero is the DS1307 internal
    'register that holds the "seconds" data, and the second zero is the value to put in that register.
    'In this case, [0] will start the clock (reseting the seconds to zero)
    'and [1] (the default) will stop the clock.


    BusOut clockout,0,[0]


    'This routine uses the variable "bcd_to_bin_byte" to send the Binary data from the time variables minute,
    'hours etc.(set during the "time_set" routine) to the "convert_to_bcd" routine.
    'When the conversion is done the time variables, minutes, hours etc., will hold BCD values ready
    'to be sent to the DS1307 chip.
    'The "busout" line works in the following manner:
    '"clockout" is the alias used in setting the I2C of the DS1307 to receive data from the PIC®.
    '"1" is the address in the 1307 of the "minute" register, (the start of the data to follow).
    '[minute,hour,day,date,month,year] is the data sent to the 1307, the address is incremented by one
    '(as the values are bytes). Hour=address 2 etc.
    'The line "busout clockout,1,[minute,hour,day,date,month,year]" can be more easily understood
    '(by me at least) when written "busout clockout,1,[minute]" followed by "busout clockout,2,[hour]",
    '"busout clockout,3,[day]", and so on.
    'The "second" register can not be set to a value, it is set to zero when the clock is re-initialised
    'with the "start_clock" routine.


    bcd_to_bin_byte=minute:GoSub convert_to_bcd:minute=bcd_to_bin_byte
    bcd_to_bin_byte=hour:GoSub convert_to_bcd:hour=bcd_to_bin_byte
    bcd_to_bin_byte=day:GoSub convert_to_bcd:day=bcd_to_bin_byte
    bcd_to_bin_byte=date:GoSub convert_to_bcd:date=bcd_to_bin_byte
    bcd_to_bin_byte=month:GoSub convert_to_bcd:month=bcd_to_bin_byte
    bcd_to_bin_byte=year:GoSub convert_to_bcd:year=bcd_to_bin_byte

    BusOut clockout,1,[minute,hour,day,date,month,year]



    ds1307_address=0:GoSub read_time 'read seconds data from 1307
    second=bcd_to_bin_byte 'convert to binary

    'if a back up battery is missing use this line to stop the routine from locking up when power is switched off then on again
    'if second>59 then gosub start_clock:goto get_time

    ds1307_address=1:GoSub read_time 'read minute data from 1307
    minute=bcd_to_bin_byte 'convert to binary
    ds1307_address=2:GoSub read_time 'read hour data from 1307
    hour=bcd_to_bin_byte 'convert to binary
    ds1307_address=3:GoSub read_time 'read day data from 1307
    day=bcd_to_bin_byte 'convert to binary
    ds1307_address=4:GoSub read_time 'read date data from 1307
    date=bcd_to_bin_byte 'convert to binary
    ds1307_address=5:GoSub read_time 'read month data from 1307
    month=bcd_to_bin_byte 'convert to binary
    ds1307_address=6:GoSub read_time 'read year data from 1307
    year=bcd_to_bin_byte 'convert to binary


    'Read the data from address pointed to by "ds1307_address"
    'The data read is stored in the variable "bcd_to_bin_byte"

    BusIn clockin,ds1307_address,[bcd_to_bin_byte]
    GoSub convert_to_bin


    'BCD to BINARY conversion
    'The byte to be converted is loaded into the variable "bcd_to_bin_byte"
    'and is returned in the same variable "bcd_to_bin_byte."
    'if the value of bcd_to_bin_byte is 9 or less then do nothing,(Bin and BCD are equal at 9 and below)
    Select bcd_to_bin_byte

    Case 16 To 25

    Case 32 To 41

    Case 48 To 57

    Case 64 To 73

    Case 80 To 89

    Case 96 To 105

    Case 112 To 121

    Case 128 To 137

    Case 144 To 153


    'This following code does the same thing but in assembler

    'Movlw 0
    'Btfsc bcd_to_bin_byte,0
    'Addlw 1
    'Btfsc bcd_to_bin_byte,1
    'Addlw 2
    'Btfsc bcd_to_bin_byte,2
    'Addlw 4
    'Btfsc bcd_to_bin_byte,3
    'Addlw 8
    'Btfsc bcd_to_bin_byte,4
    'Addlw 10
    'Btfsc bcd_to_bin_byte,5
    'Addlw 20
    'Btfsc bcd_to_bin_byte,6
    'Addlw 40
    'Btfsc bcd_to_bin_byte,7
    'Addlw 80
    'Movwf bcd_to_bin_byte


    ' BINARY to BCD conversion
    ' The byte to be converted is loaded into the variable bcd_to_bin_byte
    ' and is returned in the same variable bcd_to_bin_byte

    low_bits=bcd_to_bin_byte//10 'get lsb,same for Bin and BCD
    high_bits=bcd_to_bin_byte/10 'get msb
    bcd_to_bin_byte=high_bits*16 'covert msb to BCD
    bcd_to_bin_byte=bcd_to_bin_byte+low_bits 'add BCD msb and lsb together

    'This following code does the same thing but in assembler

    'Movf bcd_to_bin_byte,w
    'Clrf MSD
    'Movwf LSD
    'Movlw 10
    'Subwf LSD,w
    'Btfss 3,0
    'Goto Over
    'Movwf LSD
    'Incf MSD,f
    'Goto Gtenth
    'Swapf MSD,F
    'Movf LSD,w
    'Iorwf MSD,w
    'Movwf bcd_to_bin_byte



    'this routine uses values set in other routines to print on the LCD a set of characters from either the eeprom or a ldata table
    'the ldata table is used in order to get the program into a 16F84, if you use a bigger chip the eedata can be used instead.
    'it is very simple, first it selects where to look for the data and then puts the data at the "printaddress" into the
    '"character" variable, that character is printed at the initial position ("row","printposition") on the LCD, this is repeated
    'until the "printposition" reaches the "printend" value.
    'All the required characters are now printed on the LCD and the routine is exited.
    If read_data=0 Then character=ERead printaddress 'use eeprom for data retrieval
    If read_data=1 Then character=LRead menu_list+printaddress 'use ldata for data retrieval

    Print At row,printposition,character 'print to the LCD

    Inc printposition 'move print position one to the right
    Inc printaddress 'point to the next position in the table

    Until printposition=printend+1 'end loop when all characters have been printed




    'this routine formats the screen display, it is set out in order to allow the same routine to be used (gosub print_time)
    'when displaying the time when the clock is running, and as the six diferent subroutines needed to display the time
    'options during the clock setting routine.

    Print At 1,11,DEC2 second

    Print At 1,8,DEC2 minute,":"

    Print At 1,5,DEC2 hour,":"

    read_data=0 'use eeprom for data reading
    row=2 'print on line 2 of LCD
    printposition=1 'initial position of "day" on LCD
    printend=3 'position of last letter to be printed
    printaddress=day*3 'work out address of data in the eeprom(minimum is 3)

    GoSub print_eeprom 'goto LCD print routine

    Print At 2,5,DEC2 date

    read_data=0 'use eeprom for data reading
    row=2 'print on line 2 of LCD
    printposition=8 'initial position of "month" on LCD
    printend=10 'position of last letter to be printed
    printaddress=month*3 'work out address of data in the eeprom(minimum is 3)
    printaddress=printaddress+21 'first month data is at address 24 (21+3)in eeprom

    GoSub print_eeprom 'goto LCD print routine

    Print At 2,12,"20",DEC2 year



    'This routine sets the time.
    'variables "minvalue" and "maxvalue" set the low and high values for year(0-99),month(1-12),
    'date(1-31,1-30,1-28 and leap year 1-29),day(1-7,mon to sun),hour(0-23)and minute(0-59).

    If setup=0 Then GoTo time_set 'debounce button if still pressed from previous routine
    second=0 'clears second display at end of setup

    Print At 1,1,"Set"
    'the setting of the clock is done "year" first and ends with the "minute", seconds can not be set by the user
    'the 1307 will start the clock with zero seconds whenever it is initialised after the time is sent to it.

    buttonvalue=year:index=0:maxvalue=99:minvalue=0:Go Sub buttons:year=buttonvalue 'work on the YEAR first
    buttonvalue=month:index=1:maxvalue=12:minvalue=1:G oSub buttons:month=buttonvalue 'next to the MONTH

    'before changing the date we must set the maximum number of days in the selected month and if we need
    'to wory about the leap year when it is February.

    monthlength=31 'if the months are JAN,MAR,MAY,JUL,AUG,OCT and DEC then "monthlength" will be 31

    Select month

    Case 4,6,9,11 'if the months are APR,JUN,SEP or NOV then "monthlength" will be 30

    Case 2 'if it is FEB then set "monthlength" to 28
    leap=year//4 'tests for a leap year
    If leap=0 Then monthlength=29 'if it is a leap year then set FEB "monthlength" to 29


    buttonvalue=date:index=2:maxvalue=monthlength:minv alue=1:GoSub buttons:date=buttonvalue 'change the DATE
    buttonvalue=day:index=3:maxvalue=7:minvalue=1:GoSu b buttons:day=buttonvalue 'change the DAY
    buttonvalue=hour:index=4:maxvalue=23:minvalue=0:Go Sub buttons:hour=buttonvalue 'change the HOUR
    buttonvalue=minute:index=5:maxvalue=59:minvalue=0: GoSub buttons:minute=buttonvalue 'change the MINUTE

    'restart system with new settings
    'NOTE:the SECONDS value is always set to zero when the clock is restarted,
    'always set the time 1 minute in advance and press "setup" when the reference clock gets to the displayed time!

    Print At 1,11,"00 " ,At 2,1,"'Setup' to Start"

    'wait until the "setup" button is pressed before starting the clock
    If setup=0 Then Cls:GoSub set_time:GoSub start_clock:GoTo start

    GoTo restart



    'this routine uses the "up" and "down" buttons to select the values you require when setting the clock and prints the setup menu

    If up=0 Then Inc buttonvalueelayMS 200 'if up button is pressed then increase "buttonvalue" by one
    If buttonvalue>maxvalue Then buttonvalue=minvalue 'if "buttonvalue" goes above its maximum then make it equal to "minvalue"
    If down=0 Then Dec buttonvalueelayMS 200 'if down button is pressed then decrease "buttonvalue" by one
    If buttonvalue<minvalue Or buttonvalue=255 Then buttonvalue=maxvalue 'if "buttonvalue" goes below its minimum, or 255 if the minimum is 0, then make it equal to "maxvalue"

    'print the time setting menu
    read_data=1 'use ldata table for print routine
    row=1 'print on line 1 of LCD
    printposition=13 'start position on LCD for the menu
    printend=16 'last character position on LCD for the menu
    printaddress=index*4 'work out start position of data in ldata table
    GoSub print_eeprom 'jump to LCD print routine

    'choose what is to be displayed on the LCD and which variable is to be changed by this "buttons" routine.
    'using if-then rather than case as it uses less memory in this routine
    If index=0 Then year=buttonvalue:GoSub print_year
    If index=1 Then month=buttonvalue:GoSub print_month
    If index=2 Then date=buttonvalue:GoSub print_date
    If index=3 Then day=buttonvalue:GoSub print_day
    If index=4 Then hour=buttonvalue:GoSub print_hour
    If index=5 Then minute=buttonvalue:GoSub print_min

    If setup=0 Then DelayMS 200:Return

    GoTo buttons



    EData 0,0,0,"MonTueWedThuFriSatSunJanFebMarAprMayJunJulA ugSepOctNovDec"

    menu_list:LData "YearMnthDateDay HourMin "




    contributed by Mark Rodgers.