;
;       Program:          FL51XINC.ASM  V1.10
;       Revision Date:   
;                         09-09-97      Adapted to 12C51x parts
;			  01-Apr-1999	Added emulation hooks
;
;                         01-July-2004  Modified to suit LinTrain
;
; nolist
; comment this line out for use on real part
;#define EMULATED    
;
; PIC12C51X EEPROM communication code.  This code should be included in
; with the application.  These routines provide the following functionality:
; write a byte to a specified address.
; read a byte from a specified address.
; read a byte from the next address.
;
; Emulation Requires:
;     MPLAB-ICE
;     PCM16XA0 processor module
;     DVA12XP80 Device Adapter.
; Define EMULATED at the top of this file  (#define EMULATED)
;     This will set the I2C_PORT, SDA and SCL lines to communicate over 
;     Port A, pins 0 and 1.  It also assembles in the necessary TRIS 
;     instructions to allow reading from the SDA line.
;
; To convert the code for the actual part, simply comment out the #define EMULATED
; line and reassemble. 
;
;
; INTRODUCTION:
; The Microchip 12CE51x family of microntrollers are multichip modules 
; which contain a PIC12C508 microcontroller and a 24LC00 EEPROM.
; This application note is intended to provide users with highly compressed
; assembly code for communication between the EEPROM and the Microcontroller,
; which will leave the user a maximum amount of code space for the core 
; application.
;
;***************************************************************************
;***************************  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
;
; 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 ee_state register if the
;       EEPROM acknowledged OK, else that bit will be cleared.  This bit 
;       can be checked instead of refering to the value returned in W
;***************************************************************************
;
; OPERATION:
; For detailed operating information and other important information about 
; this code, see AN571.  This code was derived from AN571, with changes
; as appropriate for the specific hardware in the PIC12C51x parts.
;********************************************************************** 
;
;
;***************************************************************************
;***************************  Variable Listing  ****************************
;***************************************************************************
ok          equ     01h
no          equ     00h

#ifdef	EMULATED
i2c_port    equ     5		; Port A control register, used for I2C
scl         equ     01h         ; EEPROM Clock, SCL (I/O bit 7)
sda         equ     00h         ; EEPROM Data,  SDA (I/O bit 6)
#else
i2c_port    equ     GPIO        ; Port B control register, used for I2C
scl         equ     07h         ; EEPROM Clock, SCL (I/O bit 7)
sda         equ     06h         ; EEPROM Data,  SDA (I/O bit 6)
#endif

ee_ok       equ     07h         ; Bit 7 in ee_state used as OK flag for EE

; All variables declared in the RAMDEF.INC file


;***************************************************************************
;***************************  EEPROM Subroutines  **************************
;***************************************************************************
; Communication for EEPROM based on I2C protocol, with Acknowledge.
;
; WRITE_BYTE: Byte write routine
;       Inputs:  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 ee_state register if the
;       EEPROM acknowledged OK, else that bit will be cleared.  This bit 
;       can be checked instead of refering to the value returned in W
;***************************************************************************
;********************** Set up EEPROM control bytes ************************
;***************************************************************************
read_current:
 movlw   b'10000100'        ; PC offset for read current addr.  EE_OK bit7='1'
 movwf   ee_state           ; Load PC offset
 goto    init_read_control

write_byte:
 movlw   b'10000000'        ; PC offset for write byte.  EE_OK: bit7 = '1'
 goto    init_write_control

read_random:
 movlw   b'10000011'        ; PC offset for read random.  EE_OK: bit7 = '1'

init_write_control:
 movwf   ee_state           ; Load PC offset register, value preset in W
 movlw   b'10100000'        ; Control byte with write bit, bit 0 = '0'
 
start_bit:
 bcf     i2c_port,sda       ; Start bit, SDA and SCL preset to '1'

;
;******* Set up output data (control, address, or data) and ee_bitcnt ********
;***************************************************************************
prep_transfer_byte:
 movwf   eebyte             ; Byte to transfer to EEPROM already in W
 movlw   .8                 ; ee_bitcnt to transfer 8 bits
 movwf   ee_bitcnt
#ifdef	EMULATED
 movlw   0x00		    ; make sure both are outputs
 tris    i2c_port
#endif

;
;************  Clock out data (control, address, or data) byte  ************
;***************************************************************************
output_byte:
 bcf     i2c_port,scl       ; Set clock low during data set-up
 rlf     eebyte,f           ; Rotate left, high order bit into carry bit
 bcf     i2c_port,sda       ; Set data low, if rotated carry bit is
 skpnc                      ;   a '1', then:
 bsf     i2c_port,sda       ; reset data pin to a one, otherwise leave low
 nop
 bsf     i2c_port,scl       ; clock data into EEPROM
 decfsz  ee_bitcnt,f        ; Repeat until entire byte is sent
 goto    output_byte
 nop
;
;**************************  Acknowledge Check *****************************
;***************************************************************************
 bcf     i2c_port,scl       ; Set SCL low, 0.5us < ack valid < 3us
 nop
 bsf     i2c_port,sda
#ifdef	EMULATED
 movlw	 (0x01 << sda)	    ; make SDA an input
 tris	 i2c_port
#endif
 goto    $+1                ; May be necessary for SCL Tlow  at low voltage,
 bsf     i2c_port,scl       ; Raise SCL, EEPROM acknowledge still valid
 btfsc   i2c_port,sda       ; Check SDA for acknowledge (low)
 bcf     ee_state,ee_ok     ; If SDA not low (no ack), set error flag
 bcf     i2c_port,scl       ; Lower SCL, EEPROM release bus
 btfss   ee_state,ee_ok     ; If no error continue, else stop bit
 goto    stop_bit
#ifdef	EMULATED
 movlw   0x00		    ; SDA back to an output
 tris    i2c_port
#endif

;
;*****  Set up program ee_bitcnt offset, based on EEPROM operating mode  *****
;***************************************************************************
 movf    ee_state,w
 andlw   b'00001111'
 addwf   PCL,f 
 goto    init_address       ;PC offset=0, write control done, send address
 goto    init_write_data    ;PC offset=1, write address done, send data
 goto    stop_bit           ;PC offset=2, write done, send stop bit
 goto    init_address       ;PC offset=3, write control done, send address
 goto    init_read_control  ;PC offset=4, send read control
 goto    read_byte_setup    ;PC offset=5, set ee_bitcnt and read byte
 goto    stop_bit           ;PC offset=6, random read done, send stop

;
;**********  Initalize EEPROM data (address, data, or control) bytes  ******
;***************************************************************************
init_address:
 incf    ee_state,f         ; Increment PC offset to 2 (write) or to 4 (read)
 movf    eeaddr,w           ; Put EEPROM address in W, ready to send to EEPROM
 goto    prep_transfer_byte


init_write_data:
 incf    ee_state,f         ; Increment PC offset to go to STOP_BIT next
 movf    eedata,w           ; Put EEPROM data in W, ready to send to EEPROM
 goto    prep_transfer_byte

init_read_control:
 bsf     i2c_port,scl       ; Raise SCL
 nop
 bsf     i2c_port,sda       ; raise SDA
 incf    ee_state,f         ; Increment PC offset to go to read_byte_setup next
 movlw   b'10100001'        ; Set up read control byte, ready to send to EEPROM
 goto    start_bit          ; bit 0 = '1' for read operation

;
;**************************  Read EEPROM data  *****************************
;***************************************************************************
read_byte_setup:
 bsf     i2c_port,sda
 nop
 bsf     i2c_port,scl       ; set data bit to 1 so we're not pulling bus down.
 movlw   .8                 ; Set ee_bitcnt so 8 bits will be read into EEDATA
 movwf   ee_bitcnt
#ifdef	EMULATED
 movlw   (0x01 << sda)
 tris    i2c_port
#endif

read_byte:
 bsf     i2c_port,scl       ; Raise SCL, SDA valid.  SDA still input from ack
 setc                       ; Assume bit to be read = 1
 btfss   i2c_port,sda       ; Check if SDA = 1
 clrc                       ; if SDA not = 1 then clear carry bit
 rlf     eedata,f           ; rotate carry bit (=SDA) into EEDATA
 bcf     i2c_port,scl       ; Lower SCL
 bsf     i2c_port,sda       ; reset SDA
 decfsz  ee_bitcnt,f        ; Loop until bit count expires
 goto    read_byte          ; Read next bit if not finished reading byte

 bsf     i2c_port,scl
 nop
 bcf     i2c_port,scl
;******************  Generate a STOP bit and RETURN  ***********************
;***************************************************************************
stop_bit:
#ifdef	EMULATED
 movlw	0x00	; set SDA as output
 tris	i2c_port
#endif
 bcf     i2c_port,sda       ; SDA=0, on TRIS, to prepare for transition to '1' 
 bsf     i2c_port,scl       ; SCL = 1 to prepare for STOP bit
 goto    $+1                ; 4 NOPs neccessary for I2C spec Tsu:sto = 4.7us                  
 goto    $+1
 bsf     i2c_port,sda       ; Stop bit, SDA transition to '1' while SCL high
 
 btfss   ee_state,ee_ok     ; Check for error
 retlw   no                 ; if error, send back NO 
 retlw   ok                 ; if no error, send back OK
;
;Note: SDA and SCL still being driven by master, both set to outputs.
;****************************************************************************
 list
;************************  End EEPROM Subroutines  **************************


syntax highlighted by Code2HTML, v. 0.9.1