Stampworks Experiment #33
Bullwinkle
Posts: 101
I've been playing with experiment #33, trying to extend the functionality to display ( and modify ) the date, month and year. To do this I had to expand the number of inputs to 8 ( well 6 ) so I shifted SDA to 9 and SCL to 10.
Suddenly I get squat. I think the program is looping, waiting for response the DS1307 chip that it never gets. I've examined the code carefully and the only bit that seems a mystery is the declaration of DS1307 as %1101 << 4. This equates to 11010000 but what does it relate to? These are no meaningful comments in the code and nothing in the doco explains it.
Am I on the right track, or have I screwed up somewhere else? I have included my code below. The only hardware changes I have made above #33 are to add 4 wires from the momentary contact switches 4-7 to the inputs 4-7 on the stamp. Also the original 4 input buttons were moved to 0-3. And, as I said, SDA was moved from 0 to 9 and SCL from 1 to 10.
Is my problem related to the Slave ID (write and read) mentioned in the comments? Should I dig up the datasheet for the DS1307 for more info?
Steve
' =========================================================================
'
' File....... SW20-EX33-DS1307.BS2
' Purpose.... Real-time-clock interfacing
' Author..... (C) 2000 - 2005, Parallax, Inc.
' E-mail..... support@parallax.com
' Started....
' Updated.... 01 SEP 2005
'
' {$STAMP BS2}
' {$PBASIC 2.5}
'
' =========================================================================
'
[noparse][[/noparse] Program Description ]
'
' This program demonstrates the access and control of an external real-
' time-clock chip, the DS1307.
'
[noparse][[/noparse] I/O Definitions ]
SDA PIN 9 ' I2C serial data line
SCL PIN 10 ' I2C serial clock line
BtnBus VAR INL ' eight inputs, pins 0 - 7
'
[noparse][[/noparse] Constants ]
Ack CON 0 ' acknowledge bit
Nak CON 1 ' no ack bit
DS1307 CON %1101 << 4
'
[noparse][[/noparse] Variables ]
slvAddr VAR Byte ' I2C slave address
devNum VAR Nib ' device number (0 - 7)
addrLen VAR Nib ' bytes in word addr (0 - 2)
wrdAddr VAR Word ' word address
i2cData VAR Byte ' data to/from device
i2cWork VAR Byte ' work byte for TX routine
i2cAck VAR Bit ' Ack bit from device
secs VAR Byte ' DS1307 time registers
mins VAR Byte
hrs VAR Byte
day VAR Byte ' weekday
date VAR Byte ' day in month, 1 - 31
month VAR Byte
year VAR Byte
control VAR Byte ' SQW I/O control
btns VAR Byte ' debounced button inputs
btnBack VAR btns.BIT5 ' roll back
btnYear VAR btns.BIT4 ' +/- month
btnMonth VAR btns.BIT3 ' +/- month
btnDate VAR btns.BIT2 ' +/- date
btnHr VAR btns.BIT1 ' +/- hours
btnMn VAR btns.BIT0 ' +/- minutes
idx VAR Nib ' loop control
pntr VAR Byte ' ee pointer
char VAR Byte ' character for display
'
[noparse][[/noparse] EEPROM Data ]
DayNames DATA "SunMonTueWedThuFriSat"
MonthNames DATA "JanFebMarAprMayJunJulAugSepOctNovDec"
'
[noparse][[/noparse] Initialization ]
Reset:
#IF ($STAMP >= BS2P) #THEN
#ERROR "Please use BS2p version: SW20-EX33-DS1307.BSP"
#ENDIF
Setup:
slvAddr = DS1307 ' 1 byte in word address
addrLen = 1
DEBUG LF,"DS1307 Demo", LF,
"
",LF,LF
Reset_Clock:
GOSUB Get_Buttons ' scan buttons
idx = btns & %0011 ' isolate hrs & mins
IF (idx = %11) THEN ' if both pressed, reset
secs = $00
mins = $00
hrs = $06 ' 6:00 AM
day = $07 ' Saturday
date = $01 ' 1st
month = $01 ' January
year = $05 ' 2005
control = 0 ' disable SQW output
' GOSUB Set_Clock ' block write clock regs
ENDIF
'
[noparse][[/noparse] Program Code ]
Main:
GOSUB Get_Clock ' read DS1307
hrs = hrs & $3F
GOSUB Print_Day
DEBUG " ",HEX2 date
GOSUB Print_Month
DEBUG " ",HEX2 year
DEBUG " ",HEX2 hrs, ":", HEX2 mins, ":", HEX2 secs,CR
PAUSE 100
GOSUB Get_Buttons
IF (btns > %0000) THEN ' button pressed?
IF (btns <> %1000) THEN ' ignore back only
hrs = hrs.NIB1 * 10 + hrs.NIB0 ' BCD to decimal
mins = mins.NIB1 * 10 + mins.NIB0
IF (btnBack = 0) THEN ' increment values
year = ((year - 1) + btnYear // 99) + 1 ' keep 1 - 99
month = ((month - 1) + btnMonth // 12) + 1 ' keep 1 - 12
date = ((date - 1) + btnDate // 31) + 1 ' keep 1 - 31
hrs = hrs + btnHr // 24 ' keep 0 - 23
mins = mins + btnMn // 60 ' keep 0 - 59
ELSE
year = ((year - 1) + (btnYear * 98) // 99) + 1
month = ((date - 1) + (btnMonth * 11) // 12) + 1
date = ((date - 1) + (btnDate * 30) // 31) + 1
hrs = hrs + (btnHr * 23) // 24
mins = mins + (btnMn * 59) // 60
ENDIF
hrs = (hrs / 10 << 4) + (hrs // 10) ' decimal to BCD
mins = (mins / 10 << 4) + (mins // 10)
secs = $00
GOSUB Set_Clock ' update DS1307
ENDIF
ENDIF
GOTO Main
'
[noparse][[/noparse] Subroutines ]
Get_Buttons:
btns = %1111 ' enable all four inputs
FOR idx = 1 TO 5
btns = btns & ~BtnBus ' test inputs
PAUSE 5 ' delay between tests
NEXT
RETURN
Print_Day:
pntr = DayNames + ((day - 1) * 3) ' point to 1st char
FOR idx = 0 TO 2 ' print 3 letters
READ (pntr + idx), char ' read letter
DEBUG char ' print it
NEXT
RETURN
Print_Month:
pntr = MonthNames + ((month - 1) * 3) ' point to 1st char
FOR idx = 0 TO 2 ' print 3 letters
READ (pntr + idx), char ' read letter
DEBUG char ' print it
NEXT
RETURN
' Do a block write to clock registers
Set_Clock:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Set_Clock ' wait until not busy
i2cWork = 0 ' point at secs register
GOSUB I2C_TX_Byte
FOR idx = 0 TO 7 ' write secs to control
i2cWork = secs(idx)
GOSUB I2C_TX_Byte
NEXT
GOSUB I2C_Stop
RETURN
' Do a block read from clock registers
Get_Clock:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Get_Clock ' wait until not busy
i2cWork = 0 ' point at secs register
GOSUB I2C_TX_Byte
GOSUB I2C_Start
i2cWork = slvAddr | %00000001 ' send slave ID (read)
GOSUB I2C_TX_Byte
FOR idx = 0 TO 6 ' read secs to year
GOSUB I2C_RX_Byte
secs(idx) = i2cWork
NEXT
GOSUB I2C_RX_Byte_Nak ' read control
control = i2cWork
GOSUB I2C_Stop
RETURN
' =====[noparse][[/noparse] High Level I2C Subroutines]=======================================
' Random location write
' -- pass device slave address in "slvAddr"
' -- pass bytes in word address (0, 1 or 2) in "addrLen"
' -- word address to write passed in "wrdAddr"
' -- data byte to be written is passed in "i2cData"
Write_Byte:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Write_Byte ' wait until not busy
IF (addrLen > 0) THEN
IF (addrLen = 2) THEN
i2cWork = wrdAddr.BYTE1 ' send word address (1)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = wrdAddr.BYTE0 ' send word address (0)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = i2cData ' send data
GOSUB I2C_TX_Byte
GOSUB I2C_Stop
RETURN
' Random location read
' -- pass device slave address in "slvAddr"
' -- pass bytes in word address (0, 1 or 2) in "addrLen"
' -- word address to write passed in "wrdAddr"
' -- data byte read is returned in "i2cData"
Read_Byte:
GOSUB I2C_Start ' send Start
IF (addrLen > 0) THEN
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Read_Byte ' wait until not busy
IF (addrLen = 2) THEN
i2cWork = wrdAddr.BYTE1 ' send word address (1)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = wrdAddr.BYTE0 ' send word address (0)
GOSUB I2C_TX_Byte
GOSUB I2C_Start
ENDIF
i2cWork = slvAddr | %00000001 ' send slave ID (read)
GOSUB I2C_TX_Byte
GOSUB I2C_RX_Byte_Nak
GOSUB I2C_Stop
i2cData = i2cWork
RETURN
'
[noparse][[/noparse] Low Level I2C Subroutines]
' *** Start Sequence ***
I2C_Start: ' I2C start bit sequence
INPUT SDA
INPUT SCL
LOW SDA
Clock_Hold:
DO : LOOP UNTIL (SCL = 1) ' wait for clock release
RETURN
' *** Transmit Byte ***
I2C_TX_Byte:
SHIFTOUT SDA, SCL, MSBFIRST, [noparse][[/noparse]i2cWork\8] ' send byte to device
SHIFTIN SDA, SCL, MSBPRE, [noparse][[/noparse]i2cAck\1] ' get acknowledge bit
RETURN
' *** Receive Byte ***
I2C_RX_Byte_Nak:
i2cAck = Nak ' no Ack = high
GOTO I2C_RX
I2C_RX_Byte:
i2cAck = Ack ' Ack = low
I2C_RX:
SHIFTIN SDA, SCL, MSBPRE, [noparse][[/noparse]i2cWork\8] ' get byte from device
SHIFTOUT SDA, SCL, LSBFIRST, [noparse][[/noparse]i2cAck\1] ' send ack or nak
RETURN
' *** Stop Sequence ***
I2C_Stop: ' I2C stop bit sequence
LOW SDA
INPUT SCL
INPUT SDA
RETURN
Suddenly I get squat. I think the program is looping, waiting for response the DS1307 chip that it never gets. I've examined the code carefully and the only bit that seems a mystery is the declaration of DS1307 as %1101 << 4. This equates to 11010000 but what does it relate to? These are no meaningful comments in the code and nothing in the doco explains it.
Am I on the right track, or have I screwed up somewhere else? I have included my code below. The only hardware changes I have made above #33 are to add 4 wires from the momentary contact switches 4-7 to the inputs 4-7 on the stamp. Also the original 4 input buttons were moved to 0-3. And, as I said, SDA was moved from 0 to 9 and SCL from 1 to 10.
Is my problem related to the Slave ID (write and read) mentioned in the comments? Should I dig up the datasheet for the DS1307 for more info?
Steve
' =========================================================================
'
' File....... SW20-EX33-DS1307.BS2
' Purpose.... Real-time-clock interfacing
' Author..... (C) 2000 - 2005, Parallax, Inc.
' E-mail..... support@parallax.com
' Started....
' Updated.... 01 SEP 2005
'
' {$STAMP BS2}
' {$PBASIC 2.5}
'
' =========================================================================
'
[noparse][[/noparse] Program Description ]
'
' This program demonstrates the access and control of an external real-
' time-clock chip, the DS1307.
'
[noparse][[/noparse] I/O Definitions ]
SDA PIN 9 ' I2C serial data line
SCL PIN 10 ' I2C serial clock line
BtnBus VAR INL ' eight inputs, pins 0 - 7
'
[noparse][[/noparse] Constants ]
Ack CON 0 ' acknowledge bit
Nak CON 1 ' no ack bit
DS1307 CON %1101 << 4
'
[noparse][[/noparse] Variables ]
slvAddr VAR Byte ' I2C slave address
devNum VAR Nib ' device number (0 - 7)
addrLen VAR Nib ' bytes in word addr (0 - 2)
wrdAddr VAR Word ' word address
i2cData VAR Byte ' data to/from device
i2cWork VAR Byte ' work byte for TX routine
i2cAck VAR Bit ' Ack bit from device
secs VAR Byte ' DS1307 time registers
mins VAR Byte
hrs VAR Byte
day VAR Byte ' weekday
date VAR Byte ' day in month, 1 - 31
month VAR Byte
year VAR Byte
control VAR Byte ' SQW I/O control
btns VAR Byte ' debounced button inputs
btnBack VAR btns.BIT5 ' roll back
btnYear VAR btns.BIT4 ' +/- month
btnMonth VAR btns.BIT3 ' +/- month
btnDate VAR btns.BIT2 ' +/- date
btnHr VAR btns.BIT1 ' +/- hours
btnMn VAR btns.BIT0 ' +/- minutes
idx VAR Nib ' loop control
pntr VAR Byte ' ee pointer
char VAR Byte ' character for display
'
[noparse][[/noparse] EEPROM Data ]
DayNames DATA "SunMonTueWedThuFriSat"
MonthNames DATA "JanFebMarAprMayJunJulAugSepOctNovDec"
'
[noparse][[/noparse] Initialization ]
Reset:
#IF ($STAMP >= BS2P) #THEN
#ERROR "Please use BS2p version: SW20-EX33-DS1307.BSP"
#ENDIF
Setup:
slvAddr = DS1307 ' 1 byte in word address
addrLen = 1
DEBUG LF,"DS1307 Demo", LF,
"
",LF,LF
Reset_Clock:
GOSUB Get_Buttons ' scan buttons
idx = btns & %0011 ' isolate hrs & mins
IF (idx = %11) THEN ' if both pressed, reset
secs = $00
mins = $00
hrs = $06 ' 6:00 AM
day = $07 ' Saturday
date = $01 ' 1st
month = $01 ' January
year = $05 ' 2005
control = 0 ' disable SQW output
' GOSUB Set_Clock ' block write clock regs
ENDIF
'
[noparse][[/noparse] Program Code ]
Main:
GOSUB Get_Clock ' read DS1307
hrs = hrs & $3F
GOSUB Print_Day
DEBUG " ",HEX2 date
GOSUB Print_Month
DEBUG " ",HEX2 year
DEBUG " ",HEX2 hrs, ":", HEX2 mins, ":", HEX2 secs,CR
PAUSE 100
GOSUB Get_Buttons
IF (btns > %0000) THEN ' button pressed?
IF (btns <> %1000) THEN ' ignore back only
hrs = hrs.NIB1 * 10 + hrs.NIB0 ' BCD to decimal
mins = mins.NIB1 * 10 + mins.NIB0
IF (btnBack = 0) THEN ' increment values
year = ((year - 1) + btnYear // 99) + 1 ' keep 1 - 99
month = ((month - 1) + btnMonth // 12) + 1 ' keep 1 - 12
date = ((date - 1) + btnDate // 31) + 1 ' keep 1 - 31
hrs = hrs + btnHr // 24 ' keep 0 - 23
mins = mins + btnMn // 60 ' keep 0 - 59
ELSE
year = ((year - 1) + (btnYear * 98) // 99) + 1
month = ((date - 1) + (btnMonth * 11) // 12) + 1
date = ((date - 1) + (btnDate * 30) // 31) + 1
hrs = hrs + (btnHr * 23) // 24
mins = mins + (btnMn * 59) // 60
ENDIF
hrs = (hrs / 10 << 4) + (hrs // 10) ' decimal to BCD
mins = (mins / 10 << 4) + (mins // 10)
secs = $00
GOSUB Set_Clock ' update DS1307
ENDIF
ENDIF
GOTO Main
'
[noparse][[/noparse] Subroutines ]
Get_Buttons:
btns = %1111 ' enable all four inputs
FOR idx = 1 TO 5
btns = btns & ~BtnBus ' test inputs
PAUSE 5 ' delay between tests
NEXT
RETURN
Print_Day:
pntr = DayNames + ((day - 1) * 3) ' point to 1st char
FOR idx = 0 TO 2 ' print 3 letters
READ (pntr + idx), char ' read letter
DEBUG char ' print it
NEXT
RETURN
Print_Month:
pntr = MonthNames + ((month - 1) * 3) ' point to 1st char
FOR idx = 0 TO 2 ' print 3 letters
READ (pntr + idx), char ' read letter
DEBUG char ' print it
NEXT
RETURN
' Do a block write to clock registers
Set_Clock:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Set_Clock ' wait until not busy
i2cWork = 0 ' point at secs register
GOSUB I2C_TX_Byte
FOR idx = 0 TO 7 ' write secs to control
i2cWork = secs(idx)
GOSUB I2C_TX_Byte
NEXT
GOSUB I2C_Stop
RETURN
' Do a block read from clock registers
Get_Clock:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Get_Clock ' wait until not busy
i2cWork = 0 ' point at secs register
GOSUB I2C_TX_Byte
GOSUB I2C_Start
i2cWork = slvAddr | %00000001 ' send slave ID (read)
GOSUB I2C_TX_Byte
FOR idx = 0 TO 6 ' read secs to year
GOSUB I2C_RX_Byte
secs(idx) = i2cWork
NEXT
GOSUB I2C_RX_Byte_Nak ' read control
control = i2cWork
GOSUB I2C_Stop
RETURN
' =====[noparse][[/noparse] High Level I2C Subroutines]=======================================
' Random location write
' -- pass device slave address in "slvAddr"
' -- pass bytes in word address (0, 1 or 2) in "addrLen"
' -- word address to write passed in "wrdAddr"
' -- data byte to be written is passed in "i2cData"
Write_Byte:
GOSUB I2C_Start ' send Start
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Write_Byte ' wait until not busy
IF (addrLen > 0) THEN
IF (addrLen = 2) THEN
i2cWork = wrdAddr.BYTE1 ' send word address (1)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = wrdAddr.BYTE0 ' send word address (0)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = i2cData ' send data
GOSUB I2C_TX_Byte
GOSUB I2C_Stop
RETURN
' Random location read
' -- pass device slave address in "slvAddr"
' -- pass bytes in word address (0, 1 or 2) in "addrLen"
' -- word address to write passed in "wrdAddr"
' -- data byte read is returned in "i2cData"
Read_Byte:
GOSUB I2C_Start ' send Start
IF (addrLen > 0) THEN
i2cWork = slvAddr & %11111110 ' send slave ID (write)
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Read_Byte ' wait until not busy
IF (addrLen = 2) THEN
i2cWork = wrdAddr.BYTE1 ' send word address (1)
GOSUB I2C_TX_Byte
ENDIF
i2cWork = wrdAddr.BYTE0 ' send word address (0)
GOSUB I2C_TX_Byte
GOSUB I2C_Start
ENDIF
i2cWork = slvAddr | %00000001 ' send slave ID (read)
GOSUB I2C_TX_Byte
GOSUB I2C_RX_Byte_Nak
GOSUB I2C_Stop
i2cData = i2cWork
RETURN
'
[noparse][[/noparse] Low Level I2C Subroutines]
' *** Start Sequence ***
I2C_Start: ' I2C start bit sequence
INPUT SDA
INPUT SCL
LOW SDA
Clock_Hold:
DO : LOOP UNTIL (SCL = 1) ' wait for clock release
RETURN
' *** Transmit Byte ***
I2C_TX_Byte:
SHIFTOUT SDA, SCL, MSBFIRST, [noparse][[/noparse]i2cWork\8] ' send byte to device
SHIFTIN SDA, SCL, MSBPRE, [noparse][[/noparse]i2cAck\1] ' get acknowledge bit
RETURN
' *** Receive Byte ***
I2C_RX_Byte_Nak:
i2cAck = Nak ' no Ack = high
GOTO I2C_RX
I2C_RX_Byte:
i2cAck = Ack ' Ack = low
I2C_RX:
SHIFTIN SDA, SCL, MSBPRE, [noparse][[/noparse]i2cWork\8] ' get byte from device
SHIFTOUT SDA, SCL, LSBFIRST, [noparse][[/noparse]i2cAck\1] ' send ack or nak
RETURN
' *** Stop Sequence ***
I2C_Stop: ' I2C stop bit sequence
LOW SDA
INPUT SCL
INPUT SDA
RETURN
Comments
Having posted my question I suddenly see the obvious mistake. Pins 0-7 and the input pins and pins 8 and 9 are now the SDA and SCL pins. Not 9 and 10! D'Oh!!!!
And I call myself a C programmer.
I must now go to the punishement room.....
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
csavage@parallax.com
I still have a question about this experiment, even though I now have it working. What is the DS1307 declaration for? Why is it set to 11010000 ?
DS1307 CON %1101 << 4
Steve
All I2C communications begin with the Slave ID (see page 185 of StampWorks 2)·and the upper four bits of that byte determine the device type.· Bits 1..3 of the Slave ID are almost always used for the device address, and bit0 determines read or write.·If you look in the DS1307 docs (hint, hint... ) you'll see that the device type for the DS1307 is %1101 -- Figure 6 shows a typical communication burst.· Since the DS1307 is not addressable, bits 1..3 are always zero.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Jon Williams
Applications Engineer, Parallax
I will stop being lazy now and actually read the 3107 doco....
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Jon Williams
Applications Engineer, Parallax
Stampworks is an excellent, well written book, thank you. I'm kinda jumping around doing experiments that grab my interest then extending them, rather than reading the book cover-to-cover. I figure the best way to learn this stuff is to just jump in. And reading the device datasheets could be a good idea too.
Tonight I plan to add the LCD display to the extended experiment #33. More fun!
Steve
www.parallax.com/detail.asp?product_id=27297
PAR
I have a ZIP file with a number of different DS-130X files in it which you are welcome to have if you'd like it. Send me your email address at the address below, and I'll send it along.
Regards,
Bruce Bates
bvbates @ comcast.net
·
·· Are you adding 4.7K pull-up resistors to the SDA/SCL lines?· If so that could be causing your problems.· It would help to know exactly what was connected where on the board.· I have attached known working code.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support