;==========================================================
;
; I2C library with AT24Cxx support
; 
; Modified version of the original Atmel AT24CXX.ASM library
;
; (C) HELIUM  
; http://helium.webz.cz/
;
; compiled with MetaLink 8051 Cross-Assembler, Version 1.2k
;==========================================================

$MOD51

SDA	EQU	P1.0	; serial Data	
SCL	EQU	P1.1	; serial Clock

I2C_t	EQU	5	; konstanta cekani pri SCL = Hi

;----------------------------------------------------------
; organizace dat  [Lo - Hi] 
	DSEG AT 02Ch
I2C_wt:	DS	1	; wait loop byte
I2C_dat:DS	1	; DATA byte
I2C_adr:DS	1	; device address, for AT24Cxx 0A0h

I2C_al	BIT	7	; address long (in the device)
			; I2C_al = 0, 1 byte addres, AT24C01/02/04/08/16 
			; I2C_al = 1, 2 byte addres, AT24C32/64/128/256/512	
			
	CSEG

;==========================================================
; Byte Write function.
; Called with programmable address in A, byte address in
; register pair DPH:DPL, data in register I2C_dat.
; Does not wait for write cycle to complete.
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
; Destroys A.
;
I2C_wr:	call	I2C_on		;
	jc	I2C_wr9		; abort if bus not available

	mov	A, I2C_adr	; device address
	clr	ACC.0		; specify write operation
	call	I2C_out		; send device address
	jc	I2C_wr8		; abort if no acknowledge

	jnb	I2C_al, I2C_wr1	; jump if device have 8bit address
	mov	A, DPH		; send high byte of address
	call	I2C_out		;
	jc	I2C_wr8		; abort if no acknowledge

I2C_wr1:mov	A, DPL		; send low byte of address
	call	I2C_out		;
	jc	I2C_wr8		; abort if no acknowledge

	mov	A, I2C_dat	; get data
	call	I2C_out		; send data
	jc	I2C_wr8		; abort if no acknowledge

	clr	C		; clear error flag
I2C_wr8:call	I2C_off		;
I2C_wr9:RET			;

;==========================================================
; Current Address Read function.
; Called with programmable address in A. Returns data in A.
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
;
I2C_rdc:call	I2C_on		;
	jc	I2C_rd5		; abort if bus not available

	mov	A, I2C_adr	; device address
	setb	ACC.0		; specify read operation
	call	I2C_out		; send device address
	jc	I2C_rd4		; abort if no acknowledge

	call	I2C_in		; receive data byte
	call	I2C_NAK		; do not acknowledge byte
	clr	C		; clear error flag
I2C_rd4:call	I2C_off		;
I2C_rd5:RET			;

;==========================================================
; Random Read function.
; Called with programmable address in A, byte address in
; register pair DPH:DPL. Returns data in A.
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
;
I2C_rd:	push	B		;
	mov	B, A		; save copy of programmable address

	; Send dummy write command to set internal address.
	call	I2C_on		;
	jc	I2C_rd0		; abort if bus not available

	mov	A, I2C_adr	; device address
	clr	ACC.0		; specify write operation
	call	I2C_out		; send device address
	jc	I2C_rd1		; abort if no acknowledge

	jnb	I2C_al, I2C_rd3	; jump if device have 8bit address
	mov	A, DPH		; send high byte of address
	call	I2C_out		;
	jc	I2C_rd1		; abort if no acknowledge

I2C_rd3:mov	A, DPL		; send low byte of address
	call	I2C_out		;
	jc	I2C_rd1		; abort if no acknowledge

; Call Current Address Read function.
	mov	A, B		; get programmable address
	call	I2C_rdc		;
	jmp	I2C_rd0		; exit
I2C_rd1:call	I2C_off		;
I2C_rd0:pop	B		;
	RET			;

;==========================================================
; Send START, defined as high-to-low SDA with SCL high.
; Return with SCL, SDA low.
; Returns CY set if bus is not available.
;
I2C_on:	setb	SDA		;
	setb	SCL		;
	nop			;
; Verify bus available.
	jnb	SDA, I2C_on0	; jump if not high
	jnb	SCL, I2C_on0	; jump if not high

	call	I2C_wait	; enforce setup delay and cycle delay
	nop			;
	clr	SDA
	call	I2C_wait	; enforce hold delay
	clr	SCL		;
	call	I2C_wait	;

	clr	C		; clear error flag
	jmp	I2C_on1		;
I2C_on0:setb	C		; set error flag
I2C_on1:RET			;

;==========================================================
; Send STOP, defined as low-to-high SDA with SCL high.
; SCL expected low on entry. Return with SCL, SDA high.
;
I2C_off:clr	SDA		;
	call	I2C_wait	; enforce SCL low and data setup
	setb	SCL		;
	call	I2C_wait	; enforce setup delay
	setb	SDA		;
	call	I2C_wait	;
	RET			;

;==========================================================
; Shift out a byte to the I2C device, most significant bit first.
; SCL, SDA expected low on entry. Return with SCL low.
; Called with data to send in A.
; Returns CY set to indicate failure by slave to acknowledge.
; Destroys A.
;
I2C_out:push	B
	mov	B, #8		; bit counter
	
I2C_ou0:rlc	A		; move bit into CY
	mov	SDA, C		; output bit
	nop			; enforce SCL low and data setup
	nop			;
	setb	SCL		; raise clock
	call	I2C_wait	; enforce SCL high
	clr	SCL		; drop clock
	djnz	B, I2C_ou0	; next bit

	setb	SDA		; release SDA for ACK
	nop			; enforce SCL low and tAA
	nop			;
	setb	SCL		; raise ACK clock
	call	I2C_wait	; enforce SCL high
	mov	C, SDA		; get ACK bit
	clr	SCL		; drop ACK clock

	pop	B		;
	RET			;

;==========================================================
; Shift in a byte from the I2C device, most significant bit first.
; SCL expected low on entry. Return with SCL low.
; Returns received data byte in A.
;
I2C_in:	setb	SDA		; make SDA an input
	push	B
	mov	B, #8		; bit count

I2C_in0:call	I2C_wait	; enforce SCL low and data setup
	setb	SCL		; raise clock
	call	I2C_wait	; enforce SCL high
	mov	C, SDA		; input bit
	rlc	A		; move bit into byte
	clr	SCL		; drop clock
	djnz	B, I2C_in0	; next bit

	pop	B		;
	RET			;

;==========================================================	
; Clock out an acknowledge bit (low).
; SCL expected low on entry. Return with SCL, SDA low.
;
I2C_ACK:clr	SDA		; ACK bit
	nop			; enforce SCL low and data setup
	nop			;
	setb	SCL		; raise clock
	call	I2C_wait	; enforce SCL high
	clr	SCL		; drop clock
	nop			;
	nop			;
	RET			;

;==========================================================	
; Clock out a negative acknowledge bit (high).
; SCL expected low on entry. Return with SCL low, SDA high.
;
I2C_NAK:setb	SDA		; NAK bit
	nop			; enforce SCL low and data setup
	nop			;
	setb	SCL		; raise clock
	call	I2C_wait	; enforce SCL high
	clr	SCL		; drop clock
	nop			;
	nop			;
	RET			;

;==========================================================	
; Delay cycle
;
I2C_wait:
IF XTAL=24
	mov	I2C_wt, #(2*I2C_t)	; n x 1us
ELSE
	mov	I2C_wt, #I2C_t	; n x 2us (2.17us)
ENDIF
	djnz	I2C_wt, $	;
	RET			;

;==========================================================		

;	END