;*****************************************************************************
;
;       8051- Programm fuer ATMEL Flash- Mikrocontroller AT89S53
;	
;	Teilprogramm, wird durch INCLUDE eingebunden
;
;       Autor: Andreas Hoeger
;       Datum: 16.09.2000		
;
; 	03_06_01: EEPROM Doppeltablage eingefuehrt
;       10_06_01: Bugfix Doppeltablage: auch urspruengliche Page updaten!
; 	12_06_01: read_random_internal eingefuegt
;
;*****************************************************************************

        ;*********************************************************************
        ; Unterprogramme fuer I2C- Bus
        ;*********************************************************************


	; All EEPROM data is kept twice to be sure not to loose data while 
	; power is switched off during byte_write.
	
	; read_random checks BIT06 to determine the valid page.

write_byte:

	; I2C-Bus Byte Write function for EEPROM (adr & data).
	; Called with address in I2CADR_H:I2CADR_L. Data in I2C_DATA.
	; Device ID in I2C_ID.
	; Main program with double-page functionality, uses
	; write_byte_internal" to actually write to the bits

	; check which page contains the valid data
	; and choose the other page for writing now
	
	jnb	BIT06, bit6notset1
	mov	I2C_ADR_H,#00h		; page 1 is valid
					; set page 0
	jmp	validcheckend1
bit6notset1:
	mov	I2C_ADR_H,#01h		; page 0 is valid
					; set page 1
validcheckend1:

	call	write_byte_internal

	push	I2C_ADR_L	; save for updating the original
				; page at the end !
	push	I2C_DATA	; save for updating the original
				; page at the end !
					
	; set the page valid that was updated 
	; in EEPROM (EE_VALID_PAGE) and RAM (BIT06)

	mov	A,I2C_ADR_H
	rrc	A			; put bit into carry
	jc	page_one_in_data
	mov	I2C_DATA,#00h
	clr	BIT06
	jmp	pagevalidset

page_one_in_data:
	mov	I2C_DATA,#01h		; write page 1 valid
	setb	BIT06

pagevalidset:
	mov	I2C_ADR_L,#EE_VALID_PAGE
	mov	I2C_ADR_H,#00H		; valid info is always in 
					; page 0
	; I2C_ID  is set already
	
	call	write_byte_internal	; write "valid byte info"
					; to EEPROM
	; update the byte in the originally valid page now:
	; I2C_DATA contains the updated page, take the other one:

	mov	A,I2C_DATA
	rrc	A
	cpl	C
	rlc	A		; now LSB is inverted

	mov	I2C_ADR_H,A	; select page
	pop	I2C_DATA	; restore the data that was to store
				; in the EEPROM
	pop	I2C_ADR_L	; restore the address for the byte

	call	write_byte_internal	; update the originally valid
					; page in the EEPROM 

	; now the data is updated in both data pages 
	
	ret

write_byte_internal:

	; I2C-Bus Byte Write function for EEPROM (adr & data).
	; Called with address in I2CADR_H:I2CADR_L. Data in I2C_DATA.
	; Device ID in I2C_ID.
	; Does not wait for write cycle to complete.
	; Destroys A.

	; includes 10 ms waiting time

	call	start

	mov	a,I2C_ADR_H	; 
	rl	a		; programmable address to bits 3:1
	orl	a,I2C_ID	; add fixed address
	clr	acc.0		; specify write operation
	call	shout		; send device address

	mov	a,I2C_ADR_L	; send low byte of address
	call	shout		;

	mov	a,I2C_DATA	; get data
	call	shout		; send data

	call	stop
	call	zehn_ms

	ret

read_random:

	; call "read_random" if data from valid page is wished
	; call "read_random_internal" if data from given page is needed
	
	; I2C Random Read function.
	; Called with programmable address as "write byte". 
	; Returns data in I2C_DATA.

	; checks which page contains the valid data
	
	jnb	BIT06, bit6notset
	mov	I2C_ADR_H,#01h		; page 1 is valid
	jmp	validcheckend
bit6notset:
	mov	I2C_ADR_H,#00h		; page 0 is valid
validcheckend:

read_random_internal:

	mov	a,I2C_ADR_H

	; Send dummy write command to set internal address.

	call	start

	rl	a		; programmable address to bits 3:1
	orl	a,I2C_ID	; add fixed address
	clr	acc.0		; specify write operation
	call	shout		; send device address

	mov	a,I2C_ADR_L	; send low byte of address
	call	shout		;

	call	start

	mov	a,I2C_ADR_H
	rl	a		; programmable address to bits 3:1
	orl	a,I2C_ID	; add fixed address
	setb	acc.0		; specify read operation
	call	shout		; send device address

	call	shin		; receive data byte
				; data is returned in A
	mov	I2C_DATA,A
	call	NAK		; do not acknowledge byte
	call	stop
	ret

read_ds_random_2byte:

	; I2C Random Read function for two bytes from ds1621.
	; Called with programmable address as "write byte". 
	; Returns first data byte in TEMP_H.
	; Returns second data byte in TEMP_L.

	mov	a,I2C_ADR_H

	; Send dummy write command to set internal address.

	call	start

	rl	a		; programmable address to bits 3:1
	orl	a,I2C_ID	; add fixed address
	clr	acc.0		; specify write operation
	call	shout		; send device address

	mov	a,I2C_ADR_L	; send low byte of address
	call	shout		;

	call	start

	mov	a,I2C_ADR_H
	rl	a		; programmable address to bits 3:1
	orl	a,I2C_ID	; add fixed address
	setb	acc.0		; specify read operation

	call	shout		; send device address
	call	shin		; receive first data byte
				; data is returned in A
	mov	TEMP_H,A
	call	ACK		; acknowledge first byte

	call	shin		; receive second data byte
				; data is returned in A
	mov	TEMP_L,A
	call	NAK		; do not acknowledge byte
	call	stop
	ret

start:
	; Send START, defined as high-to-low SDA with SCL high.
	; Return with SCL, SDA low.
	; Sets Error Code if bus is not available.

	setb	SDA
	setb	SCL

	; Verify bus available.

	jnb	SDA, x40	; jump if not high
	jnb	SCL, x40	; jump if not high
	nop			; enforce setup delay and cycle delay
	clr	SDA
	mov	R7,#6		; enforce hold delay

wait_start:	
	
	nop	
	djnz	R7,wait_start
	clr	SCL
	jmp	x41

x40:	mov	ERROR,#EW_IIC_NOT_READY	; I2C-Bus-Fehler eintragen
	call	errorcode_out

x41:	ret

stop:
	; Send STOP, defined as low-to-high SDA with SCL high.
	; SCL expected low on entry. Return with SCL, SDA high.

	clr	SDA
	nop			; enforce SCL low and data setup
	nop
	setb	SCL
	mov	R7,#6		; enforce setup delay
wait_stop:	
	nop	
	djnz	R7,wait_stop
	setb	SDA
	ret

shout:
	; Shift out a byte to the I2C-bus, most significant bit first.
	; SCL, SDA expected low on entry. Return with SCL low.
	; Called with data to send in A.
	; Sets Error Code to indicate failure by slave to acknowledge.
	; Destroys A.

	push	b
	mov	b, #8		; bit counter
x42:	nop
	rlc	a		; move bit into CY
	mov	SDA, c		; output bit
	nop			; enforce SCL low and data setup
	nop
	nop
	setb	SCL		; raise clock
	nop			; enforce SCL high
	nop			;
	nop			;
	nop			;
	nop			;
	nop			;
	clr	SCL		; drop clock
	djnz	b, x42		; next bit
	setb	SDA		; release SDA for ACK
	nop			; enforce SCL low and tAA
	nop			;
	setb	SCL		; raise ACK clock
	nop			; enforce SCL high
	nop			;
	nop			;
	nop			;
	mov	c, SDA		; get ACK bit
	nop
	nop
	clr	SCL		; drop ACK clock
	jnc	x42a		; Low=ACK from slave i.o.

	mov	ERROR,#EW_IIC_BYTESEND_ACK	; I2C-Bus-Fehler eintragen
	call	errorcode_out

x42a:	pop	b
	ret

shin:
	; Shift in a byte from the I2C-bus, most significant bit first.
	; SCL expected low on entry. Return with SCL low.
	; Returns received data byte in A.

	setb	SDA		; make SDA an input
	push	b
	mov	b, #8		; bit count
x43:
	nop			; enforce SCL low and data setup
	nop			;
	nop			;
	setb	SCL		; raise clock
	nop			; enforce SCL high
	nop
	nop			;
	nop
	mov	c, SDA		; input bit
	rlc	a		; move bit into byte
	nop
	nop
	clr	SCL		; drop clock
	djnz	b, x43		; next bit
	pop	b
	ret

ACK:
	; Clock out an acknowledge bit (low).
	; SCL expected low on entry. Return with SCL, SDA low.

	clr	SDA		; ACK bit
	nop			; enforce SCL low and data setup
	nop			;
	setb	SCL		; raise clock
	mov	R7,#6		; enforce SCL high
wait_ack:	
	nop	
	djnz	R7,wait_ack
	clr	SCL		; drop clock
	ret


NAK:
	; Clock out a negative acknowledge bit (high).
	; SCL expected low on entry. Return with SCL low, SDA high.

	setb	SDA		; NAK bit
	nop			; enforce SCL low and data setup
	nop			;
	setb	SCL		; raise clock
	mov	R7,#6		; enforce SCL high
wait_nak:	
	nop	
	djnz	R7,wait_nak
	clr	SCL		; drop clock
	ret

;*****************************************************************************
; ENDE DES QUELLTEXTES
;*****************************************************************************

