;12i2c-V1.asm
;WWW.MWFTR.COM/book.html
;Charles Kim
;i2C Master Mode Program 
;for use in 24LC16B Serial EEPROM access by 16F877
;in I2C Master Mode Operation of SSP Module of 16F877 (with Fosc = 20 MHz )
;
; by Charles J. Kim
; April 2003
;
; All right reserved. 2003
;
; I2C Master Mode Operation for EEPROM (slave) Access
; EEPROM SPEC: 1.Microchip 24LC16B  (16K Bits or 2KBytes)
;	       2.8 Internal blocks (256 Bytes for each block)
; I2C ADDRESS SPEC:
;	       1. I2C Spec code for EEPROM SELECTION: 1010 must be the highest 4 Bits in
;                                                  the Higher Address 
;              2. 3-bit Block Code (from 000 to 111) follows the EEPROM SELECTION bits
;	       3. R/W bit follows to Complete the 8-bit Higher Address
;              4. Actual EEPROM address is selected by the 8-bit Lower Address
;                                     
;                                                                             
; ===================================================================                                                                   

	list      p=16f877      


INTCON	EQU	0x0B
STATUS	EQU	0x03
ZERO	EQU	0x00		;for STATUS
PIR2	EQU	0x0D		;I2C Modules
BCLIF	EQU	0x03		;Bus Interrupt Flag
PIR1	EQU	0x0C
 SSPIF	EQU	0x03		;SSP Interrupt Flag (must be cleared for next I2C)
PIE1	EQU	0x8C
PIE2	EQU	0x8D
BCLIE	EQU	0x03		;bUS iNTERRUPT Enable
SSPCON	EQU	0x14
 WCOL	EQU	0x07		;Write Collision detect
 SSPSOV	EQU	0x06		;Receive Overflow Indicator
 SSPEN	EQU	0x05		;SSP Enable bit
SSPCON2	EQU	0x91
ACKSTAT EQU	0x06
 RCEN	EQU	0x03
 PEN	EQU	0x02
 RSEN	EQU	0x01
 SEN	EQU	0x00	
 ACKEN	EQU	0x04
 ACKDT	EQU	0x05
SSPSTAT	EQU	0x94
 SMP	EQU	0x07		;SMP=1 for 100KHz, SMP=0 for 400KHz  (Choose 100KHZ)
SSPADD	EQU	0x93
 baud100	EQU	0x31	;100KHZ standard speed
 baud400	EQU	0x0B	;400KHz fast speed  ($0B)
TRISC	EQU	0x87
SSPBUF	EQU	0x13
;
TXSTA	EQU	0x98		;TX status and control
RCSTA	EQU	0x18		;RX status and control
SPBRG	EQU	0x99		;Baud Rate assignment
TXREG	EQU	0x19		;USART TX Register
RXREG	EQU	0x1A		;USART RX Register
PIR1	EQU	0x0C		;USART RX/TX buffer status (empty or full)
RCIF	EQU	0x05		;PIR1<5>: RX Buffer 1-Full  0-Empty
TXIF	EQU	0x04		;PIR1<4>: TX Buffer 1-empty 0-full
TXMODE	EQU	0x20		;TXSTA=00100000 : 8-bit, Async
RXMODE	EQU	0x90		;RCSTA=10010000 : 8-bit, enable port, enable RX
BAUD	EQU	0x0F		;0x0F (19200), 0x1F (9600)

;
;
;
	CBLOCK	0x20
		temp
		Kount20us
		Kount120us   ;Delay count (number of instr cycles for delay)
		Kount100us
		Kount1ms
		Kount10ms
		Kount1s
		Kount10s
		Kount1m
	ENDC	
;=========================================================		
;The Next 5 lines must be here
;because of bootloader arrangement
;Bootloader first execute the first 4 addresses
;then jump to the address what the execution directs 
;=========================================================

        org 	0x0000      	;line 1
	GOTO	START		;line 2 ($0000)
	NOP			;line 3 ($0001)
	NOP			;line 4 ($0002)
	NOP			;line 5 ($0003)

;======================================================
				;start of the program from $0004

START
	call	ASYNC_MODE	;for RS232
	
		
;i2c operation
;PORTC setup  - SDA and SCL both as inputs
	movlw	0x18			;0001 1000
	banksel	TRISC
	movwf	TRISC			;RC4 (SDA) and RC3(SCL) as inputs
;MSSP Module Setup
	movlw	baud100			;100KHz speed 
	banksel	SSPADD
	movwf	SSPADD			;write baud rate

	movlw	0x80			;1000 0000
	banksel	SSPSTAT
	movwf	SSPSTAT		;100KHZ (no slew rate control) 
				;selection with I2C mode

	movlw	0x28			;0010 1000 (SSPEN=1, SSPM3:0= 1000 ) master mode
	banksel	SSPCON
	movwf	SSPCON

;===========================================================================
;WRITING for a byte data
;Sequence 
;1. Start event <S>
;2. Send Control Byte for EPEROM with Write Op <C>
;    CONTROL BYTE STRUCTURE OF 24LC16B
;    1   0   1   0   A2   A1   A0  R/W
;    The first 4-bit code is establisihed for serial EEPROM by the I2C Bus Specification
;    Next 3 bits are for Block Select  (26LC16B has 8 blocks, with each block having 256 Bytes)
;    R/W = 1 for reading 
;    R/W = 0 for writing
;3. Wait for ACK from EEPROM (Use SSPIF of PIR1)<K>
;4. Send the address in a block of EEPROM<A>
;5. Wait for ACK from EEPROM<K>
;6  Send a byte of data<D>
;7. Wait for ACK from EEPROM <K>
;8. STop EVENT<P>
;9. Give 24LC16 a few ms to write the data
;====================================================================
; SEQUENCE IN WRITING
; PIC (master Side)	>S>> >C>>    >A>>      >D>>       >P>>
; 24LC16 (slave side)            <<K<     <<K<       <<K<
;====================================================================
;
;Flag Clear
	banksel	PIR1
	bcf	PIR1, SSPIF		;clear the SSP flag

;>S>>
	call	i2cStart
;>C>>	
	movlw	0xA0			
	call	i2cSend		;block code write to 24LC16
;>A>>
	movlw	0x00			;address info
	call	i2cSend
;>D>>
	movlw	'm'			;1 byte data
	call	i2cSend
;>P>> 	
	call	i2cSTOP

	call	delay10ms
;	call	delay10ms

;	
;================================================================
;Sequence of Data (random) Read operation

; >S>>  >C>>    >A>>      >RS>>   >C>>      <<D<   >P>>
;           <<K<     <<K<            <<K<
;1. Start Event >S>>
;2. Send Control Byte to Slave (with RW=0 i.e., write) >C>>
;3. Check for ACK from slave  <<K<
;4. send actual EEPROM address in a block  >A>>
;5. Wait for ACK  <<K<
;6. Repeated Start Event   >RS>>
;7.Send Control Byte to EEPROM again (with RW=1 i.e. now we read) >C>>
;8. wait for ACK  <<K<
;9. Read a byte of data <<D<
;10. STop event  >P>>

;=================================================================
;disable i2c
;	banksel	SSPCON
;	movlw	0x08
;	banksel	SSPCON
;	movwf	SSPCON
;Flag Clear
;	banksel	PIR1
;	bcf	PIR1, SSPIF		;clear the SSP flag
;enable i2C
;	banksel	SSPCON
;	movlw	0x28
;	movwf	SSPCON
;	movlw	0x18
;	banksel	TRISC
;	movwf	TRISC
	
;>S>>
	call	I2CStart
;>C>>
	movlw	0xA0
	call	i2cSend
;>A>>	
	movlw	0x00
	call	i2cSend

;>RS>>	REPEATED START
	call	i2cRstart
	call	delay100us					
;>C>> 
; COntrol Byte = 10100001=0xA1 for READ
	movlw	0xA1
	banksel	SSPBUF
	movwf	SSPBUF
	call	delay100us
;<<D<
	call	I2CREAD			

;	banksel	SSPBUF
;	movf	SSPBUF,0		;EEPROM data --> W
;	banksel	temp
;	movwf	temp			;W --> TEMP

; Send NACK to 24LC16
	call	i2cNack

;>P>>
	call	i2cSTOP

	banksel	SSPBUF
	movf	SSPBUF,0		;EEPROM data --> W	
	call	DATATX		
	movlw	'.'
	call	DATATX
	movlw	H'0d'		;CR
	call	DATATX
	movlw	H'0a'		;LF
	call	DATATX

FINI	nop
	goto	FINI			;idling


;sybroutine I2CSTART =====
I2Cstart
	banksel	SSPCON2
	bsf	SSPCON2, SEN		;START	
	banksel	PIR1
Swait	btfss	PIR1, SSPIF
	goto	Swait
	bcf	PIR1, SSPIF
	return
;--------------------------------------------------
;subroutine i2cblock for write

i2cSend	
	banksel	SSPBUF
	movwf	SSPBUF
	banksel	PIR1
cwait	btfss	PIR1, SSPIF
	goto	cwait
	bcf	PIR1, SSPIF
	return
;------------------------------------------------
;----------------
;sybroutine I2CRSTART (Repeated Start. Use in READING =====
i2cRstart		
	banksel	SSPCON2
	bsf	SSPCON2, RSEN		;REPEATED START	
	return
;--------------------------------------------------

;SUBROUTINE i2cREAD READ event =============
i2cREAD
	banksel	SSPCON2
	bsf	SSPCON2, RCEN
	banksel	PIR1				;read event completion check
Rwait	btfss	PIR1, SSPIF
	goto	Rwait
	bcf	PIR1, SSPIF
	return
;---------------------------------------------		
;SUBROUTINE I2CSTOP =============================================
I2CSTOP
	banksel	SSPCON2			
	bsf	SSPCON2, PEN	
	banksel	PIR1
Pwait	btfss	PIR1, SSPIF
	goto	Pwait
	bcf	PIR1, SSPIF
	return
;---------------------------------------
;subroutine
i2cNack
	banksel	SSPCON2
	bsf	SSPCON2, ACKDT
	bsf	SSPCON2, ACKEN
	banksel	PIR1
nwait	btfss	PIR1, SSPIF
	goto	Nwait
	bcf	PIR1, SSPIF
	return
;--------------------------------
;SUBROUTINE ACK from SLAVE ============================
ACK
	banksel	SSPCON2
ackwait btfsc	SSPCON2, ACKSTAT	;test for ACK 					;from slave (ACKSTAT=0 if rec.)
	goto	ackwait	
	return
;--------------------------------------------------
;subroutine ACKTX ========================
;SENDING ACK from EEPROM PC MONITOR
ACKTX
	banksel	PIR1	
	btfss	PIR1, TXIF
	goto	ACKTX		;if full, wait
	movlw	'A'
	banksel	TXREG
	movwf	TXREG
	return

;==subroutine DATATX
;W contains the data to be transferred to PC
DATATX
	banksel	PIR1	
	btfss	PIR1, TXIF
	goto	DATATX		;if full, wait
	banksel	TXREG
	movwf	TXREG
	return

;========================================		

;========================================
Async_mode
	banksel	SPBRG 
	movlw	baud
	movwf	SPBRG		;Baud Rate Setup
	movlw	TXMODE
	movwf	TXSTA
	banksel	RCSTA
	movlw	RXMODE
	movwf	RCSTA
	return
;=========================================
;==========================================================
;DELAY SUBROUTINES 


; 1 instruction cycle for 20MHz clock is 0.2 us
; Therefore 120 uS delay needs 600 instuction cycles
;  600 =199*3 +3 ---->Kount=199=0xC7
;  or  =198*3 +6 ---->Kount=198=0xC6
;  or  =197*3 +9 ---->Kount=197=0xC5

Delay20us
	banksel	Kount20us
	movlw	H'1F'		;D'31'
	movwf	Kount20us
R20us	decfsz	Kount20us
	goto	R20us
	return
;
;
Delay120us
	banksel	Kount120us
	movlw	H'C5'		;D'197'
	movwf	Kount120us
R120us	decfsz	Kount120us
	goto	R120us
	return
;
;100us delay needs 500 instruction cycles
;  500 =166*3 +2 ---->Kount=166=0xA6
;  or  =165*3 +5 ---->Kount=165=0xA5
;  or  =164*3 +8 ---->Kount=164=0xA4
Delay100us
	banksel	Kount100us
	movlw	H'A4'
	movwf	Kount100us
R100us	decfsz	Kount100us
	goto	R100us
	return

;
;10ms delay
; call 100 times of 100 us delay  (with some time discrepancy)
Delay10ms 
	banksel	Kount10ms
	movlw	H'64'	;100
	movwf	Kount10ms
R10ms	call	delay100us
	decfsz	Kount10ms
	goto	R10ms
	return
;
;
	
;1 sec delay
;call 100 times of 10ms delay
Delay1s
	banksel	Kount1s
	movlw	H'64'
	movwf	Kount1s
R1s	call	Delay10ms
	decfsz	Kount1s
	goto	R1s
	return
;
;
;10 s delay
;call 10 tiems of 1 s delay
Delay10s
	banksel	Kount10s
	movlw	H'0A'		;10
	movwf	Kount10s
R10s	call	Delay1s
	decfsz	Kount10s
	goto	R10s
	return
;
;1 min delay
;call	60 times of 1 sec delay
Delay1m
	banksel	Kount1m
	movlw	H'3C'	;60
	movwf	Kount1m
R1m	call	Delay1s
	decfsz	Kount1m
	goto	R1m
	return
;======================================================

	END


