'Program "84_1307_1.bas" written by Mark Rodgers 18,07,04. 'mark@rodgers.fsworld.co.uk '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 lcd_rspin porta.0 lcd_enpin portb.0 sda_pin porta.4 scl_pin porta.3 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 warnings on 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 year=0 month=1 date=1 day=0 hour=0 minute=0 '***************************************** '***************************************** 'this is the main routine that reads the time from the DS1307 and then displays it on the LCD start: cls if setup=0 then goto start 'debounce setup button on return from setting time display_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. Picbasic 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. start_clock: busout clockout,0,[0] return '***************************************** '***************************************** '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. set_time: 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] return '***************************************** '***************************************** get_time: 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 return 'Read the data from address pointed to by "ds1307_address" 'The data read is stored in the variable "bcd_to_bin_byte" read_time: busin clockin,ds1307_address,[bcd_to_bin_byte] gosub convert_to_bin return '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) convert_to_bin: select bcd_to_bin_byte case 16 to 25 bcd_to_bin_byte=bcd_to_bin_byte-6 case 32 to 41 bcd_to_bin_byte=bcd_to_bin_byte-12 case 48 to 57 bcd_to_bin_byte=bcd_to_bin_byte-18 case 64 to 73 bcd_to_bin_byte=bcd_to_bin_byte-24 case 80 to 89 bcd_to_bin_byte=bcd_to_bin_byte-30 case 96 to 105 bcd_to_bin_byte=bcd_to_bin_byte-36 case 112 to 121 bcd_to_bin_byte=bcd_to_bin_byte-42 case 128 to 137 bcd_to_bin_byte=bcd_to_bin_byte-48 case 144 to 153 bcd_to_bin_byte=bcd_to_bin_byte-54 endselect '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 return ' 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 convert_to_bcd: 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 'gtenth: 'Movlw 10 'Subwf LSD,w 'Btfss 3,0 'Goto Over 'Movwf LSD 'Incf MSD,f 'Goto Gtenth 'over: 'Swapf MSD,F 'Movf LSD,w 'Iorwf MSD,w 'Movwf bcd_to_bin_byte return '***************************************** '***************************************** '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. print_eeprom: repeat 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 return '***************************************** '***************************************** '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_time: print at 1,11,dec2 second print_min: print at 1,8,dec2 minute,":" print_hour: print at 1,5,dec2 hour,":" print_day: 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_date: print at 2,5,dec2 date print_month: 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_year: print at 2,12,"20",dec2 Year return '***************************************** '***************************************** '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). time_set: if setup=0 then goto time_set 'debounce button if still pressed from previous routine cls 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:gosub buttons:year=buttonvalue 'work on the YEAR first buttonvalue=month:index=1:maxvalue=12:minvalue=1:gosub 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 monthlength=30 case 2 'if it is FEB then set "monthlength" to 28 monthlength=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 endselect buttonvalue=date:index=2:maxvalue=monthlength:minvalue=1:gosub buttons:date=buttonvalue 'change the DATE buttonvalue=day:index=3:maxvalue=7:minvalue=1:gosub buttons:day=buttonvalue 'change the DAY buttonvalue=hour:index=4:maxvalue=23:minvalue=0:gosub 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" restart: '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 buttons: if up=0 then inc buttonvalue:delayms 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 buttonvalue:delayms 200 'if down button is pressed then decrease "buttonvalue" by one if buttonvalue