'Dallas Semiconductor DS1307 Real Time Clock Application Program Interface 'By Anthony Rhodes August 27, 2005 'Using Functions Native to the Z-Basic Family of Processors '(other processors will need to have the I2C Functions written as a user-supplied Function) '(Except for the native I2C Functions, this code should be directly portable to the Netmedia BasicX Family 'copyright Anthony Rhodes 2005 'This code may be used freely in all software, but no charge may be made for this software. 'ZX time format is always 24 hr, and so it will be for the RTC as well. Public Const I2C50K as integer = 137 Public Const I2C100K as byte = 66 '100k is "Standard Speed", 400k is "High Speed" Public Const I2C200K as integer = 29 'not a formally defined I2C speed Public Const I2C400K as integer = 11 'actually 388K, but anything else exceeds 400K limit Public Const I2CMAX as integer = 10 '409.6K: the max the ZX will do. 'N.B. DS1307 is rated ONLY at standard speed 100Kbps '........but in bench testing it works all the way to 400Kbps 'N.B. DS1337 works at BOTH standard and high speeds 'N.B. Ramtron devices usually work at 1Mbps! 'Pin 11 = I2C SDA for hardware port 'Pin 12 = I2C SCL for hardware port Public Const RTC_Write as Byte = bx1101_0000 Public Const RTC_Read as Byte = bx1101_0001 Public Const RTC_ID as Byte = RTC_Write Public Const RTC_SQW_Out_High as Boolean = TRUE Public Const RTC_SQW_Out_Low as Boolean = FALSE Public Const RTC_SQW_Enable as Boolean = TRUE Public Const RTC_SQW_Disable as Boolean = FALSE Public Const RTC_SQW_1HZ as Byte = 0 Public Const RTC_SQW_4KHZ as Byte = 1 Public Const RTC_SQW_8KHZ as Byte = 2 Public Const RTC_SQW_32KHZ as Byte = 3 'N.B. numbers are Binary Coded Decimal! Private Const DS1307_Addr_Seconds as Byte = &H00 '00-59: bit7 = clock halt, bit6:4 = tens, Bit3:0 = ones Private Const DS1307_Addr_Minutes as Byte = &H01 '00-59: bit6:4 = tens, Bit3:0 = ones Private Const DS1307_Addr_Hours as Byte = &H02 'Hours: 1-12/1-23: Bit6=1 for 12hour, 0 for 24hour, bit5:4 = tens or PM/AM(1/0)+tens, bit 3:0 = ones Private Const DS1307_Addr_WDay as Byte = &H03 '1-7 arbitrary Bit2:0 = ones Private Const DS1307_Addr_MDay as Byte = &H04 '01-31: Bit5:4 = tens, Bit3:0 = ones Private Const DS1307_Addr_Month as Byte = &H05 '01-12: Bit4 = tens, Bit3:0 = ones Private Const DS1307_Addr_Year as Byte = &H06 '00-99: Bit7:4 = tens, Bit3:0 = ones Private Const DS1307_Addr_Ctrl as Byte = &H07 'Bit7 = SQW_PULL (1/0), Bit4 = SQWE(1/0), Bit1:0 = Rate Select (0:0=1hz, 0:1=4.096KHz, 1:0=8.192KHz, 1:1=32.768KHz) Private Const DS1307_Ram_Start as Byte = &H08 'ram start Private Const DS1307_Ram_End as Byte = &H3F 'ram End Private Const DS1307_Ram_Amt as Byte = 56 'bytes of ram 'block data for the day of week function 'dim month_code as new bytevectordata ({6,2,2,5,0,3,5,1,4,6,2,4}) 'this code is ZX specific. For BX, the data needs to be moved to a datafile, one entry per line 'then use this command: Call month_code.source("filename.txt") Private RTC_Channel as Byte Private RTC_SQW_Out as Boolean Private RTC_SQWE as Boolean Private RTC_Rate_Select as Byte '_______________________________________________________________________________ Public Sub RTC_Initialize ( ByVal Channel as Byte, _ ByVal Set_RTC_SQW_Out as Boolean, _ ByVal Set_RTC_SQW_Enable as Boolean, _ ByVal Set_RTC_SQW_Freq as Byte) 'args: Channel = 1 to 4 ' Set_RTC_SQW_Out = RTC_SQW_Out_High | RTC_SQW_Out_Low ' Set_RTC_SQW_Enable = RTC_SQW_Enable | RTC_SQW_Disable ' Set_RTC_SQW_Freq = RTC_SQW_1HZ | RTC_SQW_4KHZ | RTC_SQW_8KHZ | RTC_SQW_32KHZ Dim hours as Byte Dim AMPM as Boolean Dim Success as Integer 'set local module level variables and Then call set time with 8/27/05 midnight RTC_SQW_Out = Set_RTC_SQW_Out RTC_SQWE = SET_RTC_SQW_Enable RTC_Rate_Select = Set_RTC_SQW_Freq RTC_Channel = Channel '8/27/05 12AM exactly 'Call RTC_Put_TimeStamp (5,8,27,0,0,0, success) 'defaults to the date this was written (for fun) End Sub '________________________________________________________________ Public Sub RTC_Put_TimeStamp ( ByVal year as Byte, _ ByVal month as Byte, _ ByVal MDay as Byte, _ ByVal hour as Byte, _ ByVal minute as Byte, _ ByVal second as Byte, _ ByRef Success as Integer) Dim Write_Data(1 to 9) as Byte Write_Data(1) = DS1307_Addr_Seconds Write_Data(2) = DS1307_Second(second) Write_Data(3) = DS1307_Minute(minute) Write_Data(4) = DS1307_Hour(hour) 'Write_Data(5) = DS1307_WDay(Wday) Write_Data(5) = RTC_Calc_Day_of_Week(year,month,Mday) Write_Data(6) = DS1307_MDay(Mday) Write_Data(7) = DS1307_Month(month) Write_Data(8) = DS1307_Year(year) Write_Data(9) = DS1307_Control() 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 9, write_data(1), 0, 0) End Sub '_____________________________________________________________________ Public Sub RTC_Get_TimeStamp(ByRef year as Byte, _ ByRef month as Byte, _ ByRef MDay as Byte, _ ByRef hour as Byte, _ ByRef minute as Byte, _ ByRef second as Byte, _ ByRef Success as Integer) Dim Read_Data(1 to 7) as Byte Dim address as Byte address = DS1307_Addr_Seconds 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 1, address, 7, Read_Data(1)) Read_Data(1) = Read_Data(1) AND bx0111_1111 'mask off the Clock Halt bit at the top Second = RTC_BCDtoBin(Read_Data(1)) Minute = RTC_BCDtoBin(Read_Data(2)) Hour = RTC_BCDtoBin(Read_Data(3)) 'Wday = RTC_BCDtoBin(Read_Data(4)) Mday = RTC_BCDtoBin(Read_Data(5)) Month = RTC_BCDtoBin(Read_Data(6)) Year = RTC_BCDtoBin(Read_Data(7)) End Sub '________________________________________________________________ Public Sub RTC_Put_Time ( ByVal hour as Byte, _ ByVal minute as Byte, _ ByVal second as Byte, _ ByRef Success as Integer) Dim Write_Data(1 to 4) as Byte Write_Data(1) = DS1307_Addr_Seconds Write_Data(2) = DS1307_Second(second) Write_Data(3) = DS1307_Minute(minute) Write_Data(4) = DS1307_Hour(hour) 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 4, write_data(1), 0, 0) End Sub '_____________________________________________________________________ Public Sub RTC_Get_Time ( ByRef hour as Byte, _ ByRef minute as Byte, _ ByRef second as Byte, _ ByRef Success as Integer) Dim Read_Data(1 to 3) as Byte Dim address as Byte address = DS1307_Addr_Seconds 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 1, address, 3, Read_Data(1)) Read_Data(1) = Read_Data(1) AND bx0111_1111 'mask off the Clock Halt bit at the top Second = RTC_BCDtoBin(Read_Data(1)) Minute = RTC_BCDtoBin(Read_Data(2)) Hour = RTC_BCDtoBin(Read_Data(3)) End Sub '________________________________________________________________ Public Sub RTC_Put_Date ( ByVal year as Byte, _ ByVal month as Byte, _ ByVal MDay as Byte, _ ByRef Success as Integer) Dim Write_Data(1 to 5) as Byte Write_Data(1) = DS1307_Addr_Wday Write_Data(2) = RTC_Calc_Day_of_Week(year,month,Mday) Write_Data(3) = DS1307_MDay(Mday) Write_Data(4) = DS1307_Month(month) Write_Data(5) = DS1307_Year(year) 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 5, write_data(1), 0, 0) End Sub '_____________________________________________________________________ Public Sub RTC_Get_Date(ByRef year as Byte, _ ByRef month as Byte, _ ByRef MDay as Byte, _ ByRef Success as Integer) Dim Read_Data(1 to 3) as Byte Dim address as byte address = DS1307_Addr_MDay 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 1, address, 3, Read_Data(1)) Mday = RTC_BCDtoBin(Read_Data(1)) Month = RTC_BCDtoBin(Read_Data(2)) Year = RTC_BCDtoBin(Read_Data(3)) End Sub '_________________________________________________________________ Public Sub RTC_Put_Ram( byref data() as byte, _ byVal Ram_Addr as byte, _ byval Num_Bytes as byte, _ byref Success as integer) 'rtc_ram address base is 1, not zero, so we have to adjust the start address down 1 dim RTC_address as Byte dim index as Byte if (Ram_addr = 0) then success = -999 Exit Sub end if RTC_Address = DS1307_Ram_Start + Ram_addr -1 success = 0 If ((RTC_Address + Num_bytes - 1) > DS1307_Ram_End) then 'check if the data will stay in bounds success = -1000 Exit Sub End If 'have to do this one manually, unfortunately I2CStart(RTC_Channel) 'start the transfer if Not I2cPutByte(RTC_Channel, RTC_write) then success = -1001 Exit Sub End If if NOT I2cPutByte(RTC_Channel, RTC_Address) then 'set the address for the data success = -1002 Exit Sub End If for index = 1 to num_bytes if NOT I2CPutByte(RTC_Channel,data(index)) then 'send the data success = success - 1 End If next 'index if (success = 0) then success = Cint(num_bytes) End If I2CStop(RTC_Channel) 'end the transfer End Sub '_________________________________________________________________ Public Sub RTC_Get_Ram( byref data() as byte, _ byVal Ram_Addr as byte, _ byval Num_Bytes as byte, _ byref Success as integer) 'rtc_ram address base is 1, not zero, so we have to adjust the start address down 1 dim RTC_address as Byte if (Ram_addr = 0) then success = -999 Exit Sub end if RTC_Address = DS1307_Ram_Start + Ram_addr -1 If ((RTC_Address + Num_bytes - 1) > DS1307_Ram_End) then 'check if the data will stay in bounds success = -1000 Exit Sub End If 'for this we can use the built-in function 'I2CCmd(channel, slave_id, write_count, write_data, read_count, read_data) success = I2CCmd(RTC_Channel, RTC_ID, 1, RTC_address, Num_Bytes, Data(1)) End Sub '______________________________________________________________________ public Sub RTC_Set_Control( ByVal Set_RTC_SQW_Out as Boolean, _ ByVal Set_RTC_SQW_Enable as Boolean, _ ByVal Set_RTC_SQW_Freq as Byte, _ ByRef Success as integer) dim Write_Data(1 to 2) as byte RTC_SQW_Out = Set_RTC_SQW_Out RTC_SQWE = SET_RTC_SQW_Enable RTC_Rate_Select = Set_RTC_SQW_Freq Write_data(1) = DS1307_Addr_Ctrl Write_Data(2) = DS1307_Control() success = I2CCmd(RTC_Channel, RTC_ID, 2, write_data(1), 0, 0) End Sub '______________________________________________________________________ Private Function RTC_BinToBCD(ByVal data as Byte) as Byte RTC_BinToBCD = (data \10) * 16 'shift left the tens by 4 bits to the high nibble RTC_BinToBCD = RTC_BinToBCD + (data MOD 10) 'low nibble is the ones of the data End Function '______________________________________________________________________ Private Function RTC_BCDToBin(ByVal data as Byte) as Byte RTC_BCDToBin = (data \ 16) *10 RTC_BCDToBin = RTC_BCDToBin + (data AND bx0000_1111) End Function '_______________________________________________________________________ Private Function DS1307_Year (ByVal year as Byte) as Byte If (year >99) Then year = 0 End If DS1307_Year = RTC_BinToBCD(year) End Function '__________________________________________________________________________ Private Function DS1307_Month (ByVal month as Byte) as Byte If (month <1) Then month = 1 End If If (month >12) Then month = 12 End If DS1307_Month = RTC_BinToBCD(month) End Function '__________________________________________________________________________ Private Function DS1307_Mday (ByVal Mday as Byte) as Byte If (mday <1) Then Mday = 1 End If If (Mday >31) Then Mday = 31 End If DS1307_Mday = RTC_BinToBCD(Mday) End Function '__________________________________________________________________________ Private Function DS1307_Wday (ByVal Wday as Byte) as Byte If (wday <1) Then wday = 1 End If If (wday >7) Then Wday = 7 End If DS1307_Wday = RTC_BinToBCD(Wday) End Function '__________________________________________________________________________ Private Function DS1307_Hour (ByVal Hour as Byte) as Byte 'always 24 hour format to match ZX time functions If (hour >23) Then hour = 23 End If DS1307_Hour = RTC_BinToBCD(hour) End Function '_________________________________________________________________________ Private Function DS1307_Minute (ByVal minute as Byte) as Byte If (minute >59) Then minute = 59 End If DS1307_Minute = RTC_BinToBCD(minute) End Function '_________________________________________________________________________ Private Function DS1307_Second (ByVal Second as Byte) as Byte If (second > 59) Then second = 59 End If DS1307_Second = RTC_BinToBCD(second) 'DS1307_Second = DS1307_Second + bx1000_0000 'disable the oscillator End Function '_________________________________________________________________________ Private Function DS1307_Control() as Byte If (RTC_rate_select > 3) Then RTC_rate_select = 0 End If DS1307_Control = RTC_Rate_Select 'bits 1:0 range 0-3 If (RTC_SQW_Out) Then DS1307_Control = DS1307_Control + bx1000_0000 'Bit7 End If If (RTC_SQWE) Then DS1307_Control = DS1307_Control + bx0001_0000 'Bit4 End If End Function '____________________________________________________________________________ 'calculate the day of the week in the 21st century Public Function RTC_Calc_Day_of_Week(byval year as byte, _ byval month as byte, _ byval mday as byte) as byte 'eventually delete this when bytebvectordata is working dim month_code (1 to 12) as byte '({6,2,2,5,0,3,5,1,4,6,2,4}) month_code(1) = 6 month_code(2) = 2 month_code(3) = 2 month_code(4) = 5 month_code(5) = 0 month_code(6) = 3 month_code(7) = 5 month_code(8) = 1 month_code(9) = 4 month_code(10) = 6 month_code(11) = 2 month_code(12) = 4 'returns: 1=Sunday, 2=Monday.... 7=Saturday RTC_Calc_Day_of_Week = year + year\4 'max:102 min:0 RTC_Calc_Day_of_Week = RTC_Calc_Day_of_Week + month_code(month) 'max:6 min:0 RTC_Calc_Day_of_Week = RTC_Calc_Day_of_Week + mday 'max:31 min 1 'if Jan/Feb in a leapyear then subtract 1 if NOT CBool(year AND 3) then 'bitwise OR to determine if either bit0 or bit1 are set if ((month = 1) or (month = 2)) then ' correct for jan & feb in a leapyear RTC_Calc_Day_of_Week = RTC_Calc_Day_of_Week - 1 end if end if RTC_Calc_Day_Of_Week = RTC_Calc_Day_of_Week mod 7 +1 End Function