📄 i2c.asm
字号:
;#define TWENTYMHZ
;#define TENMHZ
#define FOURMHZ
; If the operation is successful, bit 7 of PC_OFFSET will be set, and
; the functions will return W=1. If the memory is busy with a write
; cycle, it will not ACK the command. The functions will return with
; bit 7 of PC_OFFSET cleared and and W will be set to 0.
;
; Must reside on the lower half of code page (address 0-FF).
;
; Conditional assembly delays are included to meet standard mode timing
; specs. For 4Mhz, define FOURMHZ at top of file. For 10 Mhz, define ; TENMHZ. Applications running at slower clock rates & those operating
; within 4.5-5.5V may be able to remove some of the NOPs/Delay calls.
;
;**********************************************************************
;*********************** EEPROM Subroutines *************************
;**********************************************************************
; Communication for EEPROM based on I2C protocol, with Acknowledge.
;
; Byte_Write: Byte write routine
; Inputs: EEPROM Address EEADDR
; EEPROM Data EEDATA
; Outputs: Return 01 in W if OK, else return 00 in W
;
; Page_Write: Page write routine - writes up to 8 bytes at a time
; Inputs: FSR points to beginning of RAM buffer.
; W number of bytes to write
; EEPROM Address EEADDR
; EEPROM Data EEDATA
; Outputs: Return 01 in W if OK, else return 00 in W
;
; Read_Current: Read EEPROM at address currently held by EE device.
; Inputs: NONE
; Outputs: EEPROM Data EEDATA
; Return 01 in W if OK, else return 00 in W
;
; Read_Random: Read EEPROM byte at supplied address
; Inputs: EEPROM Address EEADDR
; Outputs: EEPROM Data EEDATA
; Return 01 in W if OK, else return 00 in W
;
; Note: EEPROM subroutines will set bit 7 in PC_OFFSET register if the
; EEPROM acknowledged OK, else that bit will be cleared. This
; bit can be checked instead of refering to value returned in W
;
; EEinterface file registers (EEAddress, EEDATA) are in common ram.
; EEINTF file register is on Register Page 1. Upon exit, Register
; page is set to 0.
;**********************************************************************
;
; OPERATION:
; Byte Write:
; load EEADDR and EEDATA
; then CALL WRITE_BYTE
;
; Page Write:
; Load EEADDR
; Load FSR with address of 1st byte to transfer
; Load W with number of bytes to transfer (8 bytes max)
; then CALL WRITE_PAGE
;
; Read Random:
; Load EEADDR
; then CALL READ_RANDOM
; data read returned in EEDATA
;
; Read Current
; no setup necessary
; CALL READ_CURRENT
; data read returned in EEDATA
;
;**********************************************************************
;************************ Variable Listing **************************
;**********************************************************************
OK EQU 01H
NO EQU 00H
EE_OK EQU 07H ; Bit 7 in PC_OFFSET used as OK flag
cram udata
EEADDR res 1 ; EEPROM Address
EEDATA res 1 ; EEPROM Data
EEBYTE res 1 ; Byte sent to or received from
; EEPROM (control, address, or data)
bytecount res 1 ; # of bytes to write
COUNTER res 1 ; Bit counter for serial transfer
PC_OFFSET RES 1 ; PC offset register (low order 4 bits),
; value based on operating mode of EEPROM.
; Also, bit 7 used for EE_OK flag
;******************* Set up EEPROM control bytes **********************
;**********************************************************************
READ_CURRENT
MOVLW B'10000100' ; PC offset for read current addr.
MOVWF PC_OFFSET ; Load PC offset
BSF STATUS,RP0 ; set register page 1
clrf PCLATH
GOTO INIT_READ_CONTROL
WRITE_BYTE
MOVLW B'10000000' ; PC offset for write byte.
GOTO INIT_WRITE_CONTROL
WRITE_PAGE
movwf bytecount ; save off number of bytes to send
MOVLW B'10000111' ; PC offset for write page. EE_OK bit = 1
goto INIT_WRITE_CONTROL
READ_PAGE
movwf bytecount ; save off number of bytes to send
MOVLW B'10001010' ; PC offset for read page. EE_OK bit = 1
goto INIT_WRITE_CONTROL
READ_RANDOM
MOVLW B'10000011' ; PC offset for read random.
INIT_WRITE_CONTROL
MOVWF PC_OFFSET ; Load PC offset register,value preset in W
MOVLW B'10100000' ; Control byte with write bit, bit 0 = '0'
START_BIT
BSF STATUS,RP0 ; set register page 1
clrf PCLATH
BCF EEINTF,EESDA ; Start bit, EESDA and EESCL preset to '1'
;******* Set up output data (control, address, or data) and counter
;**********************************************************************
PREP_TRANSFER_BYTE
MOVWF EEBYTE ; Byte to transfer to EEPROM already in W
MOVLW .8 ; Counter to transfer 8 bits
MOVWF COUNTER
;************ Clock out data (control, address, or data) byte
;**********************************************************************
OUTPUT_BYTE
#ifdef FOURMHZ
NOP
#endif
#ifdef TENMHZ
call delay8
#endif
#ifdef TWENTYMHZ
call delay16
#endif
RLF EEBYTE, F ; Rotate left,high order bit into carry
BCF EEINTF,EESCL ; Set clock low during data set-up
BCF EEINTF,EESDA ; Set data low, if rotated carry bit is
SKPNC ; a '1', then:
BSF EEINTF,EESDA ; reset data pin to one, else leave low
#ifdef FOURMHZ
NOP
#endif
#ifdef TENMHZ
call delay8 ; Tlow 4700 nS (add 6 cycles at 10 Mhz)
#endif
#ifdef TWENTYMHZ
call delay16 ; Tlow 4700 nS (add 6 cycles at 10 Mhz)
#endif
BSF EEINTF,EESCL ; clock data into EEPROM
DECFSZ COUNTER, F ; Repeat until entire byte is sent
GOTO OUTPUT_BYTE
#ifdef FOURMHZ
NOP ; Needed to meet Timing (Thigh=4000nS)
#endif
#ifdef TENMHZ
call delay8
#endif
#ifdef TWENTYMHZ
call delay16 ; Tlow 4700 nS (add 6 cycles at 10 Mhz)
#endif
;*********************** Acknowledge Check ***************************
;**********************************************************************
BCF EEINTF,EESCL ; Set EESCL low, 0.5us < ack valid < 3us
#ifdef FOURMHZ
NOP ; Needed to meet Timing (Tlow= 4700nS)
#endif
#ifdef TENMHZ
goto $+1
#endif
#ifdef TWENTYMHZ
call delay4
#endif
BSF EEINTF,EESDA ; set data line high to check for ack
#ifdef FOURMHZ
GOTO $+1
#endif
#ifdef TENMHZ
call delay6 ; Necessary for EESCL Tlow at low voltage
#endif
#ifdef TWENTYMHZ
call delay12
#endif
BSF EEINTF,EESCL ; Raise EESCL, EEPROM ack still valid
#ifdef FOURMHZ
NOP ; Tsu:dat (allow time for ack setup)
#endif
#ifdef TENMHZ
call delay4
#endif
#ifdef TWENTYMHZ
call delay8
#endif
BTFSC EEINTF,EESDA ; Check EESDA for acknowledge (low)
BCF PC_OFFSET,EE_OK ; If EESDA high (no ack), set err flag
#ifdef TENMHZ
call delay4
#endif
#ifdef TWENTYMHZ
call delay8
#endif
BCF EEINTF,EESCL ; Lower EESCL, EEPROM release bus
BTFSS PC_OFFSET,EE_OK ; If no error continue, else stop bit
GOTO STOP_BIT
;***** Set up program counter offset, based on EEPROM operating mode
;**********************************************************************
STATEMACHINE
MOVF PC_OFFSET,W
ANDLW B'00001111'
ADDWF PCL, F
GOTO INIT_ADDRESS ;PC offset=0, write ctrl done,send addr
GOTO INIT_WRITE_DATA ;PC offset=1, write addr done,send data
GOTO STOP_BIT ;PC offset=2, write done, send stop bit
GOTO INIT_ADDRESS ;PC offset=3, write ctrl done,send addr
GOTO INIT_READ_CONTROL ;PC offset=4, send read control
GOTO READ_BIT_COUNTER ;PC offset=5, set counter and read byte
GOTO STOP_BIT ;PC offset=6, random rd done,send stop
GOTO INIT_ADDRESS ;PC offset=7, write ctrl done,send addr
GOTO INIT_WRITE_PAGE_DATA ;PC offset=8, wrt addr done,send dat
GOTO STOP_BIT ;PC offset=9, write done, send stop bit
GOTO INIT_ADDRESS ;PC offset=A, write ctrl done,send addr
GOTO INIT_READ_PAGE_CONTROL;PC offset=B,wrt addr done,send dat
GOTO READ_PAGE_BIT_COUNTER;PC offset=C, set counter &read byte
;********** Initalize EEPROM data (address, data, or control) bytes
;**********************************************************************
INIT_ADDRESS
INCF PC_OFFSET, F ; Incr PC offset to 2 (write) or 4 (read)
MOVF EEADDR,W ; Put EEPROM address in W, ready to send to EEPROM
GOTO PREP_TRANSFER_BYTE
INIT_WRITE_DATA
INCF PC_OFFSET, F ; Increment PC offset to go to STOP_BIT next
MOVF EEDATA,W ; Put EEPROM data in W, ready to send to EE
GOTO PREP_TRANSFER_BYTE
INIT_WRITE_PAGE_DATA
DECFSZ bytecount,f ; count byte tx'd
GOTO $+2 ;
INCF PC_OFFSET, F ; Increment PC offset, goto STOP_BIT next
movf INDF,W
INCF FSR,F ; bump pointer
GOTO PREP_TRANSFER_BYTE
INIT_READ_CONTROL
BSF EEINTF,EESCL ; Raise EESCL
BSF EEINTF,EESDA ; raise EESDA
INCF PC_OFFSET, F ; Increment PC offset to go to READ_BIT_COUNTER next
MOVLW B'10100001' ; Set up read control byte, ready send to EE
GOTO START_BIT ; bit 0 = '1' for read operation
INIT_READ_PAGE_CONTROL
BSF EEINTF,EESCL ; Raise EESCL
BSF EEINTF,EESDA ; raise EESDA
INCF PC_OFFSET, F ; Increment PC offset to go to READ_BIT_COUNTER next
MOVLW B'10100001' ; Set up read control byte,ready send to EE
GOTO START_BIT ; bit 0 = '1' for read operation
;*********************** Read EEPROM data ***************************
;**********************************************************************
READ_PAGE_BIT_COUNTER
BSF EEINTF,EESDA ; set bit to 1, don't pull bus down.
MOVLW .8 ; Set counter,8 bits will be read in EEDATA
MOVWF COUNTER
READ_BYTE_RPC
#ifdef TENMHZ
call delay6
#endif
#ifdef TWENTYMHZ
call delay12
#endif
BSF EEINTF,EESCL ; Raise SCL,SDA valid. SDA input from ack
SETC ; Assume bit to be read = 1
#ifdef TENMHZ
call delay6
#endif
#ifdef TWENTYMHZ
call delay12
#endif
BTFSS EEINTF,EESDA ; Check if EESDA = 1
CLRC ; if EESDA not = 1 then clear carry bit
RLF EEDATA, F ; rotate carry bit (=EESDA) into EEDATA;
BCF EEINTF,EESCL ; Lower EESCL
NOP
BSF EEINTF,EESDA ; reset EESDA
DECFSZ COUNTER, F ; Decrement counter
GOTO READ_BYTE_RPC; Read next bit if not finished reading byte
movf EEDATA,w
movwf INDF ; write data to buffer
incf FSR,f ; increment buffer pointer
decfsz bytecount,f
GOTO SEND_ACK
GOTO SEND_NAK ; skip next 2 instructions
SEND_ACK
BCF EEINTF,EESDA ; Send an ACK (More reads to come)
BSF EEINTF,EESCL ;
NOP
BCF EEINTF,EESCL
GOTO READ_PAGE_BIT_COUNTER
SEND_NAK
BSF EEINTF,EESDA ; Send an ACK (More reads to come)
BSF EEINTF,EESCL ;
NOP
BCF EEINTF,EESCL
GOTO STOP_BIT ; skip next 2 instructions
; end read page bit control
READ_BIT_COUNTER
BSF EEINTF,EESDA ; set data bit to 1, don't pull bus down.
MOVLW .8 ; Set counter so 8 bits will be read into EEDATA
MOVWF COUNTER
READ_BYTE_RBC
#ifdef TENMHZ
call delay6
#endif
#ifdef TWENTYMHZ
call delay12
#endif
BSF EEINTF,EESCL ; Raise SCL,SDA valid. SDA input from ack
SETC ; Assume bit to be read = 1
#ifdef TENMHZ
call delay6
#endif
#ifdef TWENTYMHZ
call delay12
#endif
BTFSS EEINTF,EESDA ; Check if EESDA = 1
CLRC ; if EESDA not = 1 then clear carry bit
RLF EEDATA, F ; rotate carry bit (=EESDA) into EEDATA;
BCF EEINTF,EESCL ; Lower EESCL
NOP
BSF EEINTF,EESDA ; reset EESDA
DECFSZ COUNTER, F ; Decrement counter
GOTO READ_BYTE_RBC; Read next bit if not finished reading byte
BSF EEINTF,EESCL
NOP
BCF EEINTF,EESCL
;*************** Generate a STOP bit and RETURN *********************
;**********************************************************************
STOP_BIT
BCF EEINTF,EESDA ; SDA=0 to prepare for transition to '1'
BSF EEINTF,EESCL ; EESCL = 1 to prepare for STOP bit
#ifdef FOURMHZ
call delay4 ; wait 4 cycles Tsu:sto (4.7 us)
#endif
#ifdef TENMHZ
call delay10
#endif
#ifdef TWENTYMHZ
call delay20
#endif
BSF EEINTF,EESDA ; Stop bit, SDA to '1' while SCL high
BCF STATUS,RP0
BTFSS PC_OFFSET,EE_OK ; Check for error
RETLW NO ; if error, send back NO
RETLW OK ; if no error, send back OK
#ifdef TWENTYMHZ
delay20 goto delay18
delay18 goto delay16
delay16 goto delay14
delay14 goto delay12
delay12 goto delay10
delay10 goto delay8
delay8 goto delay6
delay6 goto delay4
delay4 return
#endif
#ifdef TENMHZ
; delay function. Wait a number of cycles.
delay10 goto delay8
delay8 goto delay6
delay6 goto delay4
delay4 return
#endif
#ifdef FOURMHZ
; delay function. Wait a number of cycles.
delay4 return
#endif
;**********************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -