.def	Ax	=R16
.def	Bx	=R17
.def	Cx	=R18



;**************************************************************************
;
; Hugo Vos, Rotterdam. 16-1-98
;
; E-mail		: hugo@hvos.myweb.nl
;
; I2C assembler code including the next function's :
;
;	I2Cstop		= send I2C stop command
;	initI2Cbus	= init I2C bus
;	I2Cstart	= send I2C start command
;	I2Csend		= send byte on the I2C bus
;	I2Cread		= read byte from I2C bus
;	master_ack	= send master acknowledge to I2C device
;	master_noack	= send No master acknowledge to I2C device
;	Acknowledge	= read acknowledge bit fron I2C slave
;	I2C_regread	= Read data from I2C register
;	I2C_regstore	= Write's data into I2C register
;	Devicecheck	= Check if I2C device(ses) exist on the bus
;	no_I2Cresponce	= displays a messa on the display and stops the programm.
;
;	Used port pins	= PortD, 5 ( I2C data line out)
;			= PortD, 4 ( I2C clock line )
;			= PortD, 6 ( I2C data line in )
;
;***************************************************************************
;
; Initialize I2C stop function.
;
;	Used registers : None
;
;***************************************************************************
	
I2Cstop:
	sbi	PortD, 5			; Set SDL to low level
	rcall	delayfunc	

	cbi	PortD, 4			; Set SCL to high level
	rcall	delayfunc

	cbi	PortD, 5			; Set SDL to high level

	nop
	nop

	ret
;***************************************************************************
;
; Initialize I2C-bus function.
;
;	Used registers : None
;
;***************************************************************************

initI2Cbus:

	cbi	PortD, 4			; Set SCL line in high level
	cbi	PortD, 5			; Set SDL line in high level

	nop
	nop
	
	ret

;***************************************************************************
;
; Initialize I2C start function.
;
;	Used registers : None
;
;***************************************************************************

I2Cstart:

	cbi	PortD, 4			; Set SCL line in high level
	cbi	PortD, 5			; Set SDL line in high level
	rcall	delayfunc
	sbi	PortD, 5			; Set SDL line in low level
	rcall	delayfunc

	nop
	nop
	
	ret

;***************************************************************************
;
; perform an I2C send function.
;
;	Used registers : Ax = Data to send on the I2C bus
;			 Bx = temp variabele 
;			 Cx = temp variabele
;
;***************************************************************************

I2Csend:

	push	Bx
	push	Cx

	sbi	PortD, 4			; Set SCL line in low level
	ldi	Bx, $80

testbit:
	mov	Cx, Bx				; Load Counter with new bit vallue
	and	Cx, Ax				; logical AND function to check bit to send	
	cpi	Cx, $00
	brne	Highbit 			; skip next instuction is bit is set

	sbi	PortD, 5			; set SDL line to low level
	rcall	delayfunc
	cbi	PortD, 4			; create clock puls on the SCL line
	rcall	delayfunc
	sbi	PortD, 4
	rcall	delayfunc

end_byte:	
	lsr	Bx				; rol Tempvar 1 bit right
	cpi	Bx, $00				; check if vallue = $ff ( if all 8 bits
	brne	testbit				; are send. If so jump to end of routine
	rjmp	end_send			; if not, jump to testbit to send next bit 

Highbit:
	cbi	PortD, 5			; set SDL line in high level
	rcall	delayfunc
	cbi	PortD, 4			; create clock puls on the SCL line
	rcall	delayfunc
	sbi	PortD,4
	rcall	delayfunc
	rjmp	end_byte

end_send:

	rcall	delayfunc
	sbi	PortD, 4			; set SCL line in low level
	cbi	PortD, 5			; release SDL line allow acknowledge from slave

	pop	Cx
	pop	Bx

	rcall	Acknowledge
		
	ret

;***************************************************************************
;
; perform a I2C Read function.
;
;	Used registers : Ax = Temp register
;			 Bx = Temp register
;			 Cx = Temp register
;	Return : Ax = Read data
;
;***************************************************************************

I2Cread:

	Push	Bx
	Push	Cx

	sbi	PortD, 4			; Set SCL line to low level
	ldi	Bx, $08
	ldi	Ax, $00				; Set read_data to $00
	nop
	nop
	nop

beginread:
	cbi	PortD, 4			; set SCL line to high level ( begin off clock )
	nop					; wait for stable clock signal
	nop
	nop
	in	Cx, PinD			; Read vallue of SDL line
	sbrs	Cx, 6				; skip next instuvtion if high level.
	rjmp	Bitlow

	sbr	Ax, 1				; Set bit in Read_data register
	rjmp	testRbyte

Bitlow:
	cbr	Ax, 1				; reset bit in Read_data register

testRbyte:

	sbi	PortD, 4			; Set SCL line to low level ( end off clock )
	dec	Bx 
	cpi	Bx, 0
	breq	endRead				; check for 8 bits received
	clc					; clear carry bit before rol left function
	rol	Ax
	rjmp	beginread

endread:		

	nop
	pop	Cx
	pop	Bx

	ret

;***************************************************************************
;
; perform a master Acknowledge function.
;
;	Used registers : none
;
;***************************************************************************

master_ack:

	sbi	PortD, 5			; set SDL line to low level
	rcall	delayfunc
	cbi	PortD, 4			; porform a SCL clock signal
	rcall	delayfunc
	sbi	PortD, 4
	rcall	delayfunc
	cbi	PortD, 5			; releace SDL line 

	nop					; wait for stable SDL line
	nop
	
	ret

;***************************************************************************
;
; perform a master No_Acknowledge function.
;
;	Used registers : none
;
;***************************************************************************

master_noack:

	cbi	PortD, 5			; release SDL line 
	rcall	delayfunc
	cbi	PortD, 4			; porform a SCL clock signal
	rcall	delayfunc
	sbi	PortD, 4

	nop					; wait for stable SCL line
	nop
	
	ret

;***************************************************************************
;
; perform a read Acknowledge function.
;
;	Used registers : Ax = Acknowledge register
;			 Bx = temp register, only vallid in this function
;
;	Return : Ax ( Bit 1 is set when acknowledge )
;
;***************************************************************************

Acknowledge:

	push	Bx

	ldi	Ax,$00				; clear acknowledgement register
	cbi	PortD, 4			; set SCL line in high level
	rcall	delayfunc			; wait for stable SCL line

	in	Bx, PinD			; load PortD pin levels
	andi	Bx, 0b01000000
	cpi	Bx, 0b01000000
	breq	resetSCL			; Goto resetSCL is SDL is high (No ack.)
	
	ldi	Ax, $01				; PortD, 6 line is low when acknow. )

resetSCL:
	sbi	PortD, 4

	nop					; wait for stable SCL line
	nop
	pop	Bx
	
	ret

;***************************************************************************
;
; perform a I2C register Read function
;
;	Used registers : Ax = I2C drives adres
;			 Bx = I2C device register nummer to read
;			 Cx = temp register, only vallid in this function
;
;	Return 	       : Ax = Read data 
;
;***************************************************************************

I2C_regread:

	push	Cx

	mov	Cx, Ax			; save Ax contens into Cx

	rcall	I2Cstart
	rcall	I2Csend			; Select device with adres named

	sbrs	Ax, 0
	rjmp	set_I2Cstatreg

	mov	Ax, Bx			; Send register number to device
	rcall	I2Csend

	sbrs	Ax,0
	rjmp	set_I2Cstatreg

	rcall	I2Cstart

	mov	Ax, Cx			; REstore I2C address in Ax from Cx
	ori	Ax, $01			; set device to read 
	rcall	I2Csend

	sbrs	Ax,0
	rjmp	set_I2Cstatreg

	rcall	I2Cread			; Read data from selected device
	rcall	master_noack

	rcall	I2Cstop
	ldi	I2Cstatreg, $00
	rjmp	No_regread

set_I2Cstatreg:
	ldi	I2Cstatreg, $FF

No_regread:
	
	pop	Cx

	ret

;***************************************************************************
;
; perform a I2C register write function
;
;	Used registers : Ax = I2C drives adres
;			 Bx = I2C device register nummer write in
;			 Cx = Data to store in register
;
;***************************************************************************

I2C_regstore:

	rcall	I2Cstart		; Start I2C sequence

	rcall	I2Csend			; send device adres to bus

	sbrs	Ax, 0
	rcall	no_I2Cresponce	

	mov	Ax, Bx			; send registernummer to device
	rcall	I2Csend

	sbrs	Ax, 0
	rcall	no_I2Cresponce

	mov	Ax, Cx			; send data to device
	rcall	I2Csend

	sbrs	Ax, 0
	rcall	no_I2Cresponce

	rcall	I2Cstop			; stop I2C sequence

	ret

;**************************************************************************
;
; Display's an error on the display and the program stay's in an deadlock
;
;**************************************************************************

no_I2Cresponce:

	ldi	Ax, Dispclear			; Set display cursor function
	ldi	Bx, disp_CTRL
	rcall	disp_lcd

	ldi	Ax, low(errortxt1)		; write "I2C error" to te display
	ldi	Bx, high(errortxt1)
	ldi	Cx, $00				; starting at position 00
	rcall	STr2disp

	ldi	Ax, low(errortxt2)		; write "I2C error" to te display
	ldi	Bx, high(errortxt2)
	ldi	Cx, $40				; starting at first position 2e line
	rcall	Str2disp

no_I2Clus:
	sbi	PortD,0
	nop
	cbi	PortD,0
	rjmp	no_I2Clus

	ret

;***************************************************************************
;
; Check if I2C device(s) exist on the bus
;
;	Used registers : Ax = I2C device start address
;			 Bx = addresses to check with Ax as start address
;			
;	Return	       : Ax = Bit0 is set if device with address Ax exist
;			      Bit1 is set if device Ax+2 exist
;			      ......
;			      Bit7 is set if device Ax + 14 exist
; 
;***************************************************************************

devicecheck:
	
	push	Cx		; Save Cx to stack
	ldi	Cx, $00		; Set all device available bit's to zero
	push	Bx		; Calculate the highest I2C address
	dec	Bx		; to check and store the new address
	lsl	Bx		; in Ax
	add	Ax, Bx
	pop	Bx		; store device to check number back in Bx

addres_I2C:

	dec	Bx		; decrease loop counter with one
	cpi	Bx, $FF
	breq	end_check	; and check for end of loop
	
	rcall	I2Cstart	; send I2C addres on the bus
	push	Ax		; save I2C address on stack
	rcall	I2Csend		; and check for I2C slave responce
	rcall	I2Cstop		; end I2C sequence
	push	Bx		; save loop counter

bit_shift:

	dec	Bx		; rotate Ax left to shift it to the proper bit	
	cpi	Bx, $00		; corresponding with the check I2C device
	breq	end_bitshift
	lsl	Ax
	rjmp	bit_shift

end_bitshift:

	or	Cx, Ax		; Set proper Acknowleds bit in Cx
	pop	Bx		; restore loop counter and
	pop	Ax		; currend I2C address from stack
	subi	Ax, $02		; reduce I2C address with two to check the next I2C
	rjmp	addres_I2C	; device

end_check:

	mov	Ax, Cx		; load Ax with the available Sw module information
	pop	Cx		; restore Cx vallue

	ret