from Tsvetan Usunov [usunov at OLIMEX.COM]
PC_OFFSET equ 0x1A ;EEPROM variables
EEADDRHI equ 0x1B ;
EEADDRLO equ 0x1C ;
EEDATA equ 0x1D ;
EEBYTE equ 0x1E ;
COUNTER equ 0x1F ;
#define SCL port_c,6 ;EEPROM SCL
#define SDA port_c,7 ;EEPROM SDA
#define SDAindef b'10110100' ;port_c
#define SDAoutdef b'00110100' ;
#define EE_OK PC_OFFSET,7 ;Bit 7 in PC_OFFSET used as OK flag for EE
#define OK 1 ;
#define NO 0 ;
;-----------------------------------------------
; EEPROM PROCEDURES
;-----------------------------------------------
EE_readcurrent movlw b'10000101' ;
movwf PC_OFFSET ;
goto Init_read_control
EE_write ;
movlw b'10000000' ;PC_OFFSET.7=OK, write sequence
goto Init_write_control
EE_read ;
movlw b'10000100' ;PC_OFFSET.7=OK, read sequence
Init_write_control movwf PC_OFFSET ;
movlw b'10100000' ;control byte 1010 device 000 address 0(Write)
Start_bit bcf SDA ;Start bit, SDA and SCL preset to '1'
Prep_transfer_byte movwf EEBYTE ;
movlw SDAoutdef ;
tris port_c ;
movlw .8 ;Counter to transfer 8 bits
movwf COUNTER ;
Output_byte bcf SCL ;Set clock low during data set-up
rlf EEBYTE,1 ;Rotate left, high order bit into carry bit
bcf SDA ;Set data low, if rotated carry bit is
SKPNC ; a '1', then:
bsf SDA ;reset data pin to a one, otherwise leave low
bsf SCL ;clock data into EEPROM
goto $+1 ;
goto $+1 ;
decfsz COUNTER,1 ;Repeat until entire byte is sent
goto Output_byte ;
movlw SDAindef ;
SKPNC ;if SDA=1 then tristate port to allow
tris port_c ;pullup to hold '1', avoiding bus contention
;if EEPROM acks in < 1us after clock goes low
bcf SCL ;Set SCL low, 0.5us < ack valid < 3us
tris port_c ;If SDA = '0' wait until SCL is low to set SDA to
;input. If done above, could have sent STOP bit
nop ;May be necessary for SCL Tlow at low voltage,
goto $+1 ;also give resistor time to pull up bus if last
goto $+1 ;bit written = '0' and there is no ack from slave
bsf SCL ;Raise SCL, EEPROM acknowledge still valid
btfsc SDA ;Check SDA for acknowledge (low)
bcf EE_OK ;If SDA not low (no ack), set error flag
bcf SCL ;Lower SCL, EEPROM release bus
btfss EE_OK ;If no error continue, else stop bit
goto Stop_bit
movfw PC_OFFSET
andlw b'00001111'
addwf pc,1
goto Init_addressHi ;PC offset=0, write control done, send address hi
goto Init_addressLo ;PC offset=1, write address low
goto Init_write_data ;PC offset=2, write address done, send data
goto Stop_bit ;PC offset=3, write done, send stop bit
goto Init_addressHi ;PC offset=4, write control done, send address hi
goto Init_addressLo ;PC offset=5, write address low
goto Init_read_control;PC offset=6, send read control
goto Read_bit_counter;PC offset=7, set counter and read byte
goto Stop_bit ;PC offset=8, random read done, send stop
Init_addressHi incf PC_OFFSET,1 ;Increment PC offset to 2 (write) or to 4 (read)
movfw EEADDRHI ;Put EEPROM address in W, ready to send to EEPROM
goto Prep_transfer_byte
Init_addressLo incf PC_OFFSET,1 ;Increment PC offset to 2 (write) or to 4 (read)
movfw EEADDRLO ;Put EEPROM address in W, ready to send to EEPROM
goto Prep_transfer_byte
Init_write_data incf PC_OFFSET,1 ;Increment PC offset to go to STOP_BIT next
movfw EEDATA ;Put EEPROM data in W, ready to send to EEPROM
goto Prep_transfer_byte
Init_read_control bsf SCL ;Raise SCL
incf PC_OFFSET,1 ;Increment PC offset to go to READ_BIT_COUNTER next
movlw b'10100001' ;Set up read control byte, ready to send to EEPROM
goto Start_bit ;bit 0 = '1' for read operation
Read_bit_counter movlw .8 ;Set counter so 8 bits will be read into EEDATA
movwf COUNTER
Input_byte bsf SCL ;Raise SCL, SDA valid. SDA still input from ack
bsf carryf ;Assume bit to be read = 1
btfss SDA ;Check if SDA = 1
bcf carryf ;if SDA not = 1 then clear carry bit
rlf EEDATA,1 ;rotate carry bit (=SDA) into EEDATA;
bcf SCL ;Lower SCL
decfsz COUNTER,1 ;Decrement counter
goto Input_byte ;Read next bit if not finished reading byte
Stop_bit bcf SDA ;SDA=0, on TRIS, to prepare for transition to '1'
movlw SDAoutdef ;SDA and SCL set to outputs, Bit0 and Bit1 ' input
tris port_c ;
bsf SCL ;SCL = 1 to prepare for STOP bit
goto $+1 ;4 NOPs neccessary for I2C spec Tsu:sto = 4.7us
goto $+1
nop
bsf SDA ;Stop bit, SDA transition to '1' while SCL high
btfss EE_OK ;Check for error
retlw NO ;if error, send back NO
retlw OK ;if no error, send back OK