decided as my code was growing to split the source up into modules... and added a few tweaks along the way, i think that was my downfall, i should have just split it and re-tested it before doing the tweaks.
my i2c bit banger is ugly as heck, it was nice and readable full of calls but was incredibly slow so i replaced most of the calls with macros. and then decided to use af' instead of a memory byte to store the current bus state, but decided that was too slow still so unrolled the macros too
when i am sending a byte it's pretty much as fast as it'll go, the DS1307 is only rated at 100Khz speed and my clock pusle is 18t-states, 100khz been 16.5t-states per half cycles. of course it takes much longer to prepare the next bit of data around 60 t-states but at around 80t-states per bit thats getting on for a therectical speed of 5KBs
Code: Select all
;DO_TX
;
;SEND A BYTE TO I2C BUS
;
;ON ENTRY A=BYTE TO SEND
;
;USES BC,A
;
DO_TX:
LD B,8 ;COUNTER FOR 8 BITS
LD C,A ;BYTE TO SEND IN C
NEXT_TX_BIT:
SLA C ;SHIFT BIT OT SEND INTO CF
JR C,TX_BIT_HIGH
TX_BIT_LOW:
EX AF,AF'
AND %11101111
OUT (PORT08),A ;SDA LOW
EX AF,AF'
JR TX_DATA_SET
TX_BIT_HIGH:
EX AF,AF'
OR %00010000
OUT (PORT08),A ;SDA HIGH
EX AF,AF'
TX_DATA_SET:
EX AF,AF' ;USE ALTERNATIVE ACCUMULATOR
OR %00001000
OUT (PORT08),A ;SCL HIGH
;with macros there would have been an extra EX AF,AF'\ EX AF,AF' here....
AND %11110111
OUT (PORT08),A ;SCL LOW
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
DJNZ NEXT_TX_BIT ;MORE BITS ?
TX_ACK:
;TX_ACK IS A REALLY BAD NAME FOR THIS SECTION...
;DATA LINE IS FREE'D AND THE ACK BIT IF ANY
;IS RECIEVED FROM THE SLAVE DEVICE
;BUT IS CLOCKED FROM THE MASTER
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
OR %00010000
OUT (PORT08),A ;FREE SDA (HIGH)
;with macros there would have been an extra EX AF,AF'\ EX AF,AF' here....
OR %00001000
OUT (PORT08),A ;SCL HIGH
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
IN A,(PORT08) ;READ THE SDA LINE.
;DO NOTHING WITH IT !
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
AND %11110111
OUT (PORT08),A ;SCL LOW
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
RET
Code: Select all
;DO_RX
;
;RECIEVE A BYTE FROM I2C BUS
;
;ON ENTRY IF A<>0 ACK WILL BE SENT
;ELSE NO ACK
;
;BYTE RETURNED IN A
;
;USES BC, A
;
DO_RX:
PUSH AF ;SAVE AF (A NONZERO will send ACK bit)
LD B,8 ;COUNTER FOR 8 BITS
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
OR %00010000
OUT (PORT08),A ;SDA FREE (HIGH)
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
NEXT_RX_BIT:
SLA C ;SHIFT A ZERO INTO BIT 0
;WILL BE ALTERED LATER IF RECIEVED
;BIT IS 1
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
OR %00001000
OUT (PORT08),A ;SCL HIGH
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
;I MIGHT TRY IT WITHOUT THIS WAITING TO SEE IF THE SLAVE DEVICE
;IS CLOCK STRECTING AND SEE IF IT STILL WORKS OK
CLK_STRETCH:
IN A,(PORT08) ;WAIT FOR THE OTHER
AND SCL_IN ;DEVICE WHICH MAY
JR Z,CLK_STRETCH ;BE CLOCK STRECTHING
READ_BIT:
IN A,(PORT08)
AND SDA_IN ;ONLY TEST THE DATA BIT
JR Z,BIT_DONE
BIT_ONE:
SET 0,C ;RX BIT WAS ONE; BIT 0 OF C ALREADY CONTAINS 0
BIT_DONE:
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
AND %11110111
OUT (PORT08),A ;SCL LOW
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
DJNZ NEXT_RX_BIT
BYTE_DONE: ;SEND AN ACK BIT ?
;NO ACK TELLS SLAVE DEVICE THAT WAS THE
;LAST BYTE TO SEND
POP AF
AND A ;REMEBER NON ZERO = DO ACK
JR Z,NO_ACK
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
AND %11101111
OUT (PORT08),A ;SDA LOW (ACK BIT)
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
JR DO_9TH ;JUMP TO 9TH CLOCK PULSE
NO_ACK:
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
OR %00010000
OUT (PORT08),A ;SDA HIGH (IT'S PROBABLY ALREADY HIGH.)
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
DO_9TH:
EX AF,AF' ;USE ALTERNATE ACCUMULATOR
OR %00001000
OUT (PORT08),A ;SCL HIGH
AND %11110111
OUT (PORT08),A ;SCL LOW
OR %00010000
OUT (PORT08),A ;SDA HIGH
EX AF,AF' ;BACK TO NORMAL ACCUMULATOR
LD A,C ;RECIEVED BYTE IN A
RET
i hope to get some video on youtube over the weekend
Regards Andy