;**** I 2 C . A S M ********************************************************
;*
;* Title		: I2C (Single) Master Implementation
;* Version		: 1.0 (BETA)
;* Last updated		: 04.01.2003
;* Target		: AT90Sxxxx (any AVR device)
;*
;* DESCRIPTION
;* 	Basic routines for communicating with I2C slave devices. This
;*	"single" master implementation is limited to one bus master on the
;*	I2C bus.
;*
;*	Some features :
;*	* All interrupts are free, and can be used for other activities.
;*	* Supports normal and fast mode.
;*	* Supports the entire AVR microcontroller family.
;*
;* NOTES
;*	The I2C routines can be called either from non-interrupt or
;*	interrupt routines, not both.
;*
;* STATISTICS
;*	Code Size	: 72 words
;*	Register Usage	: 2 High, 0 Low
;*	Interrupt Usage	: None
;*	Other Usage	: Uses two I/O pins
;*	XTAL Range	: N/A (delay's adjusted for 8 MHz)
;*
;***************************************************************************

.EQU I2C_FAST_MODE = 1	; 0 = 100 kHz, 1 = 400 kHz

.EQU SDA_PORT = PORTD
.EQU SDA_DDR  = DDRD
.EQU SDA_PIN  = PIND
.EQU SDA      =	4

.EQU SCL_PORT = PORTD
.EQU SCL_DDR  = DDRD
.EQU SCL      =	3


.EQU I2C_R = 1
.EQU I2C_W = 0

;**** Global Register Variables ****
.DEF i2c_data       = r26	; data to send / receive
.DEF i2c_delay_reg  = r27	; used in i2c_delay


;**** Macros to switch the I2C signals ****
.MACRO SCL_Lo			; force SCL to 0V
	sbi	SCL_DDR, SCL
.ENDMACRO

.MACRO SCL_Hi			; release SCL to 5V
	cbi	SCL_DDR, SCL
.ENDMACRO

.MACRO SDA_Lo			; force SDA to 0V
	sbi	SDA_DDR, SDA
.ENDMACRO

.MACRO SDA_Hi			; release SDA to 5V
	cbi	SDA_DDR, SDA
.ENDMACRO


;***************************************************************************
;*
;* FUNCTION
;*	i2c_delay
;*
;* DESCRIPTION
;*	half i2c clock period delay (normal: 5.0µs / fast: 1.25µs; @ 8 MHz)
;*
;* REGISTERS USED
;*	1 (i2c_delay_reg)
;*
;***************************************************************************
i2c_delay:
	ldi 	i2c_delay_reg, I2C_FAST_MODE
	cpi	i2c_delay_reg, 1
	brne	i2c_delay_5ys  
	ret
i2c_delay_5ys:
	ldi	i2c_delay_reg, 3 ;9
i2c_delay_5ys_2:
	dec	i2c_delay_reg
	brne	i2c_delay_5ys_2
	nop
	nop
	
	ret
	
;***************************************************************************
;*
;* FUNCTION
;*	i2c_init
;*
;* DESCRIPTION
;*	Initialization of the I2C bus interface.
;*
;* USAGE
;*	Call this function once to initialize the I2C bus. No parameters
;*	are required.
;*
;* NOTE
;*	The two Port Pins are used as Open-Collector Outputs, by set PORTx
;*	to fix 0x0 and switch via DDRx.
;*
;***************************************************************************
i2c_init:
	SCL_Hi
	SDA_Hi	
	cbi	SCL_PORT, SCL
	cbi	SDA_PORT, SDA
	
	ret
	
;***************************************************************************
;*
;* FUNCTION
;*	i2c_restart
;*
;* DESCRIPTION
;*	Generates start condition (i.e. after an aborted transfer ...)
;*
;* NOTE
;*	IMPORTANT! : This funtion must be directly followed by i2c_start.
;*
;***************************************************************************
i2c_restart:
	rcall	i2c_stop

;***************************************************************************
;*
;* FUNCTION
;*	i2c_start
;*
;* DESCRIPTION
;*	Generates start condition
;*
;***************************************************************************
i2c_start:
	rcall	i2c_stop	;test
	rcall	i2c_delay	;test
	SDA_Lo
	rcall	i2c_delay	

	ret
	
;***************************************************************************
;*
;* FUNCTION
;*	i2c_stop
;*
;* DESCRIPTION
;*	Generates stop condition
;*
;***************************************************************************
i2c_stop:
	SCL_Lo
	rcall	i2c_delay	
	SDA_Lo
	rcall	i2c_delay	

	SCL_Hi
	rcall	i2c_delay	
	SDA_Hi
	rcall	i2c_delay	

	sbis	SDA_PIN, SDA
	rjmp	i2c_stop	; if slave hang in Acknowledge
	
	ret
	
;***************************************************************************
;*
;* FUNCTION
;*	i2c_write
;*
;* DESCRIPTION
;*	Writes data (one byte) to the I2C bus.
;*
;* USAGE
;*	i2c_data - Contains data to be transmitted.
;*
;* RETURN
;*	T-flag - Clear if the slave respond with an Acknowledge.
;*
;* REGISTERS USED
;*	1 (i2c_delay_reg (by i2c_delay))
;*
;***************************************************************************
i2c_write:
	sec				; set carry flag
	rol	i2c_data		; shift in carry and out bit one
	rjmp	i2c_write_first
i2c_write2:
	lsl	i2c_data
i2c_write_first:
	breq	i2c_write_get_ack	; if i2c_data reg. empty => 8 bits written
	
	SCL_Lo
	brcc	i2c_write_low 
	rcall	i2c_delay	
	SDA_Hi
	rjmp	i2c_write_next_bit

i2c_write_low:	
	rcall	i2c_delay	
	SDA_Lo
i2c_write_next_bit:	
	rcall	i2c_delay	
	SCL_Hi
	rcall	i2c_delay	
	rjmp	i2c_write2

i2c_write_get_ack:	
	SCL_Lo
	rcall	i2c_delay	
	SDA_Hi
	rcall	i2c_delay	
	SCL_Hi
	rcall	i2c_delay	

	clt
	sbic	SDA_PIN,SDA	; get Acknowledge
	set
	SCL_Lo
	
	ret

;***************************************************************************
;*
;* FUNCTION
;*	i2c_read
;*
;* DESCRIPTION
;*	Reads data (one byte) from the I2C bus.
;*
;* USAGE
;*	T-flag - If set no acknowledge is given to the slave
;*		 indicating last read operation before a STOP.
;*		 If cleared acknowledge is given to the slave
;*		 indicating more data.
;*
;* RETURN
;*	i2cdata - Contains received data.
;*
;* REGISTERS USED
;*	1 (i2c_delay_reg (by i2c_delay))
;*
;***************************************************************************
i2c_read:
	ldi	i2c_data, 0x01	; if the one comes to carry while rotate => 8 bits are received
	SDA_Hi
i2c_read2:
	SCL_Lo			; after comes the databit / signal change
	rcall	i2c_delay	

	SCL_Hi
	rcall	i2c_delay	

	clc
	sbic	SDA_PIN,SDA
	sec	
	rol	i2c_data
	brcc	i2c_read2	; all bits received ?
	
	SCL_Lo
	brts	i2c_read3
	SDA_Lo
i2c_read3:
	rcall	i2c_delay	
	SCL_Hi
	rcall	i2c_delay	
	SCL_Lo

	clt			; clear T-flag => no errors ...
	
	ret