;****************************************************************************************************************** ; EEPROM interface subroutines and macros used by those subroutines ; Target processor is PIC16C74 ; EEPROM is 24C65 ; Pullup resistors are 1k on SDA and SCL ;------------------------------------------------------------------------------------------------------------------ ;transmit W to EEPROM and check for Ack ;(return from subroutine if no Ack -- must be used from within a top-level EEPROM subroutine) TxChkA MACRO CALL TX_EEP BSF INDF,3 ;raise SCL (high clock phase of ACK bit) BTFSC PORTC,4 ;test for ACK from EEPROM RETURN ;no ACK -- EEPROM is busy or not there ENDM ;------------------------------------------------------------------------------ ; Generate start condition and write address to EEPROM EEStrtSA MACRO BCF INDF,4 ;drop SDA to generate start condition MOVLW 0XA0 ;control byte for write to device with address 0 TxChkA MOVFW EEPaddr1 ;high byte of word address TxChkA MOVFW EEPaddr0 ;low byte of word address TxChkA ENDM ;------------------------------------------------------------------------------------------------------ ; Setup FSR so TRISC is accessed via INDF. Also set EEPfailed to true so early RETURN signals error. ; Also set PORTC<3> and PORTC<4> low so writing to TRISC bits act like open-drain outputs. EESetFSR MACRO MOVLW TRISC MOVWF FSR ;allow TRISC to be accessed via INDF rather than requiring page swaps MOVFW PORTC ANDLW ~0X18 MOVWF PORTC BSF MISCFLAGS,EEPfailed ENDM ;------------------------------------------------------------------------------------------------------ ; Setup FSR so TRISC is accessed via INDF. ; Also set PORTC<3> and PORTC<4> low so writing to TRISC bits act like open-drain outputs. EESetFSR1 MACRO MOVLW TRISC MOVWF FSR ;allow TRISC to be accessed via INDF rather than requiring page swaps MOVFW PORTC ANDLW ~0X18 MOVWF PORTC ENDM ;------------------------------------------------------------------------------------------------------- ; Check address. Return if address is out of range ( > 8k ) ;(must be used from within a top-level EEPROM subroutine) EEChkAddr MACRO MOVFW EEPaddr1 ;high byte of word address ANDLW 0XE0 ;make sure high 3 bits are 0s -- addresses above 8k aren't there and some high addresses can cause write-protect to be set (and thus destroy the EEPROM) BTFSS STATUS,Z RETURN ENDM ;------------------------------------------------------------------------------ ; Write EEPROM byte ; writes EEPdata0 to EEPaddr WRITE_EE_B EESetFSR EEChkAddr EEStrtSA MOVFW EEPdata0 ;byte of data to write WRITE_EE_FINISH TxChkA NOP NOP BCF INDF,3 ;drop SCL to end Ack bit (EEPROM stops driving SDA low) CALL EE_DLY ;allow time for line to change state before next BCF/BSF BCF INDF,4 ;drive SDA low CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL CALL EE_DLY ;setup time is 600ns minimum BSF INDF,4 ;raise SDA durring SCL high to generate stop condition BCF MISCFLAGS,EEPfailed ;Write operation was a success RETURN ;------------------------------------------------------------------------------ ; Set low byte of EEPaddr then write EEPROM page ; Writes W to EEPaddr0 then falls through to WRITE_EE_P WRITE_EE_P_AW MOVWF EEPaddr0 ;fallthru to WRITE_EE_P ;------------------------------------------------------------------------------ ; Write EEPROM page ; writes EEPdata0..EEPdata7 to EEPaddr ; will do nothing and return EEPfailed true if low 3 bits of EEPaddr are not 0 WRITE_EE_P EESetFSR EEChkAddr MOVFW EEPaddr0 ;low byte of word address ANDLW 0X07 ;check that low 3 bits are 0s BTFSS STATUS,Z RETURN EEStrtSA MOVFW EEPdata0 ;byte of data to write TxChkA MOVFW EEPdata1 TxChkA MOVFW EEPdata2 TxChkA MOVFW EEPdata3 TxChkA MOVFW EEPdata4 TxChkA MOVFW EEPdata5 TxChkA MOVFW EEPdata6 TxChkA MOVFW EEPdata7 GOTO WRITE_EE_FINISH ;------------------------------------------------------------------------------ ; Set low byte of EEPaddr then read EEPROM page ; Writes W to EEPaddr0 then falls through to READ_EE_P READ_EE_P_AW MOVWF EEPaddr0 ;fallthru to READ_EE_P ;------------------------------------------------------------------------------ ; Read EEPROM page ; reads EEPdata0..EEPdata7 from EEPaddr READ_EE_P CALL READ_EE_1 BTFSC MISCFLAGS,EEPfailed RETURN MOVWF EEPtemp3 CALL READ_EE MOVWF EEPdata1 CALL READ_EE MOVWF EEPdata2 CALL READ_EE MOVWF EEPdata3 CALL READ_EE MOVWF EEPdata4 CALL READ_EE MOVWF EEPdata5 CALL READ_EE MOVWF EEPdata6 CALL READ_EE_N MOVWF EEPdata7 MOVFW EEPtemp3 MOVWF EEPdata0 RETURN ;--------------------------------------------------------------------------------- ; Read EEPROM page ; Input: W = page index (address / 8) READ_EE_P_IW CALL CALCEEA GOTO READ_EE_P ;--------------------------------------------------------------------------------- ; Write EEPROM page ; Input: W = page index (address / 8) WRITE_EE_P_IW CALL CALCEEA GOTO WRITE_EE_P ;--------------------------------------------------------------------------------- ; Calculate EEPROM address from page index ; Input: W = page index (address / 8) ; Output: EEPaddr1:0 CALCEEA MOVWF EEPaddr0 CLRF EEPaddr1 BCF STATUS,C RLF EEPaddr0,F RLF EEPaddr1,F RLF EEPaddr0,F RLF EEPaddr1,F RLF EEPaddr0,F RLF EEPaddr1,F RETURN ;---------------------------------------------------------------------------------- ; Read a single byte of data from the EEPROM at address EEPaddr and return it ; in EEPdata0 and W. This sub terminates the read after the one byte. For multi- ; byte reads use READ_EE_1, READ_EE, and READ_EE_N. READ_EE_B EESetFSR EEStrtSA NOP NOP BCF INDF,3 ;drop SCL to end Ack bit (EEPROM stops driving SDA low) CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL CALL EE_DLY ;setup time is 600ns minimum BCF INDF,4 ;generate a start condition MOVLW 0XA1 ;control byte for read from device with address 0 TxChkA BCF MISCFLAGS,EEPfailed ;since EEPROM ack'd control bytes and address bytes, the read operation is ok (EEPROM does not generate Ack for reading bytes themselves) GOTO READ_EE_N2 ;sub entry point -- will read a byte and terminate the read sequence. ;------------------------------------------------------------------------------ ; Read 1st byte of read sequence from the EEPROM at address EEPaddr and return ; it in EEPdata0 and W. This sub leaves a read sequence in-progress. Subsequent ; bytes are read by calling READ_EE and the final byte is read via READ_EE_N. READ_EE_1 EESetFSR EEStrtSA NOP NOP BCF INDF,3 ;drop SCL to end Ack bit (EEPROM stops driving SDA low) CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL CALL EE_DLY ;setup time is 600ns minimum BCF INDF,4 ;generate a start condition MOVLW 0XA1 ;control byte for read from device with address 0 TxChkA BCF MISCFLAGS,EEPfailed ;since EEPROM ack'd control bytes and address bytes, the read operation is ok (EEPROM does not generate Ack for reading bytes themselves) ;fallthru GOTO READ_EE ;read a byte from the EEPROM. ;----------------------------------------------------------------------------------- ; Read next addressed byte of data from the EEPROM and return it in EEPdata0 and W. ; The read sequence must have been started via a prior call to READ_EE_1. To ; read the last byte in a sequence call READ_EE_N (terminates the sequence). ; This sub does not set EEPfailed -- if the call to READ_EE_1 succeeded then ; the EEPROM read operation is a go. READ_EE EESetFSR1 MOVLW 8 MOVWF EEPtemp2 BCF INDF,3 ;drop SCL CALL EE_DLY ;allow time for line to change state before next BCF/BSF BSF INDF,4 ;release SDA (SDA will be low after a call to READ_EE due to the need to generate an Ack) lprxeep CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BCF STATUS,C BSF INDF,3 ;raise SCL BTFSC PORTC,4 ;read databit BSF STATUS,C RLF EEPdata0,F CALL EE_DLY ;SCL high period is 600ns minimum BCF INDF,3 ;drop SCL DECFSZ EEPtemp2,F GOTO lprxeep BCF INDF,4 ;drop SDA to send an ACK -- EEPROM will increment its address register and have the next byte available to be clocked out CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL for ack bit MOVFW EEPdata0 RETURN ;note -- SDA is driven low. It must be released on the first clock of the next byte read. ;------------------------------------------------------------------------------------ ; Read final byte of data from the EEPROM and return it in EEPdata0 and W. ; This sub terminates a read by not generating an ack. The address must have been ; set via a prior call to READ_EE_1. This sub does not set EEPfailed -- if ; the call to READ_EE_1 succeeded then the EEPROM read operation is a go. READ_EE_N EESetFSR1 READ_EE_N2 MOVLW 8 MOVWF EEPtemp2 BCF INDF,3 ;drop SCL CALL EE_DLY ;allow time for line to change state before next BCF/BSF BSF INDF,4 ;release SDA (SDA will be low after a call to READ_EE due to the need to generate an Ack) lprxeepn CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BCF STATUS,C BSF INDF,3 ;raise SCL BTFSC PORTC,4 ;read databit BSF STATUS,C RLF EEPdata0,F BCF INDF,3 ;drop SCL DECFSZ EEPtemp2,F GOTO lprxeepn CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL for ack bit -- since SDA is not driven low this results in a not ack CALL EE_DLY ;SCL high period is 600ns minimum BCF INDF,3 ;drop SCL for stop condition CALL EE_DLY ;allow time for line to change state before next BCF/BSF BCF INDF,4 ;drop SDA CALL EE_DLY CALL EE_DLY ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL CALL EE_DLY ;setup time is 600ns minimum BSF INDF,4 ;raise SDA -- Stop condition MOVFW EEPdata0 RETURN ;------------------------------------------------------------------------------ ; Transmit a byte (control, address, or data) to the EEPROM ; Drops SCL 9th time (ack bit) before returning. Caller should check SDA ; for ack and then raise SCL. SCL is expected to be high upon entry and ; FSR is expected to point to TRISC. ; Uses EEPtemp and EEPtemp2. TX_EEP MOVWF EEPtemp MOVLW 8 MOVWF EEPtemp2 lptxeep BCF INDF,3 ;drop SCL CALL EE_DLY ;allow time for line to change state before port is read MOVFW INDF ANDLW 0XE7 ;default = SDA low BTFSC EEPtemp,7 IORLW 0X10 ;SDA high MOVWF INDF ;set SDA state RLF EEPtemp,f NOP NOP ;SCL low period is 1300ns minimum BSF INDF,3 ;raise SCL NOP NOP ;SCL high period is 600ns minimum DECFSZ EEPtemp2,f GOTO lptxeep NOP BCF INDF,3 ;drop SCL for ack bit CALL EE_DLY ;allow time for line to change state before next BCF/BSF BSF INDF,4 ;raise SDA (EEPROM will pull it low for an acknowledge or leave it high for no acknowledge) CALL EE_DLY NOP ;SCL low period is 1300ns minimum (calls to this sub are generally followed by immediate BSF INDF,3) ;fallthru to EE_DLY ;------------------------------------------------------------------------------ ; Delay 1 microsecond (including CALL and RETURN) EE_DLY NOP RETURN ;------------------------------------------------------------------------------------------------------- ; Clear EEPdata0..EEPdata7 CLR_EED CLRF EEPdata0 CLRF EEPdata1 CLRF EEPdata2 CLRF EEPdata3 CLRF EEPdata4 CLRF EEPdata5 CLRF EEPdata6 CLRF EEPdata7 RETURN