; i2c.asm
;
; Copyright (C) 2006,2007 Nuno Sucena Almeida
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
;

; simple support for I2C interface in master mode operation
; see page 95 of 16F88 manual.

	include	<proc.inc>
	include <i2c.inc>


	udata
i2c_register		res	1
i2c_counter		res	1
i2c_debug_counter	res	1
;	i2c_status:
;	bit 0	-	i2c bus ACK read status (read by user,sent by slaves)
;	bit 1	-	i2c bus ACK set status  (set by user,sent by master )
i2c_status		res	1


;--------------------------------------------------------------------
; external functions

	extern	delay10
	extern	uart_send
	extern	uart_show_hex

	code
;--------------------------------------------------------------------
; setup I2C communication
; see page 88ff,92 of the manual
i2c_setup:
	banksel	i2c_debug_counter
	clrf	i2c_debug_counter
	; 5	- SSPEN (enable SPI/I2C mode)
	; 4	- CKP   (enable Clock)
	; <3:0>	- 1011 I2C firmware controlled master mode (slave idle)
	movlw	B'00111011'
	banksel	SSPCON
	movwf	SSPCON
	;; free the clock and data i2c lines
	scl_HIGH
	sda_HIGH
	return

;--------------------------------------------------------------------
; reads the i2c port data pin (SDA) and returns its value in 'W'
i2c_read_sda:
	banksel	PORTB
	btfsc	SDA_IO
	retlw	1
	retlw	0
;--------------------------------------------------------------------
; reads the i2c port clock pin (SCL) and returns its value in 'W'
i2c_read_scl:
	banksel	PORTB
	btfsc	SCL_IO
	retlw	1
	retlw	0

;--------------------------------------------------------------------
; send byte through the i2c bus, MSB first
; the user must provide the byte to send in 'W'
; see sequence for example on page 7 of 24AA512 datasheet
; or Philips I2C specification page 9,10
; it sets the i2c ACK status flag if necessary
i2c_send:
	; put 'W' into a temp variable
	banksel	i2c_register
 	movwf	i2c_register
; 	; send debug to uart
;  	movlw	A's'
;  	call	uart_send
;  	movlw	A'['
;  	call	uart_send
	; we have 8 bits:
	movlw	D'8'
	banksel	i2c_counter
	movwf	i2c_counter
i2c_send_loop:
	; set clock LOW
	scl_LOW
	; rotate contents of register to the left and check the carry bit.
	banksel	i2c_register
	rlf	i2c_register,F
	; if carry bit = 1, the bit was 1
	btfss	STATUS,C
	goto	i2c_send_LOW
i2c_send_HIGH:
	sda_HIGH
	goto	i2c_send_loop_end
i2c_send_LOW:
	sda_LOW
i2c_send_loop_end:
	; set clock HIGH so that the I2C slave loads the bit
	scl_HIGH
	banksel	i2c_counter
	decfsz	i2c_counter,F
	goto	i2c_send_loop
	;; read acknowledge bit from slave
	i2c_READ_ACK
	; wait for clock line to be free
 	i2c_SCL_WAIT
; 	;; send debug to uart
;  	movlw	A']'
;  	call	uart_send
	return

;--------------------------------------------------------------------
; receive byte from the i2c bus, MSB first
; and put the result in 'W'
i2c_receive:
	; clear receive register
	banksel	i2c_register
	clrf i2c_register
; 	; send debug to uart
;  	movlw	A'r'
;  	call	uart_send
;  	movlw	A'['
;  	call	uart_send
	; we have 8 bits:
	movlw	D'8'
	banksel	i2c_counter
	movwf	i2c_counter
	;; set data line to high impedance mode:
	sda_HIGH
i2c_receive_loop:
	; set clock LOW
	scl_LOW
	; set clock HIGH
	scl_HIGH
	; read bit,the value will be in the carry STATUS bit
	; which will pass to the first bit of a left-rotated
	; register
	i2c_READ
	banksel	i2c_register
	; rotate left
	rlf	i2c_register,F
	banksel	i2c_counter
	decfsz	i2c_counter,F
	goto	i2c_receive_loop
	; send ACK to the slave
	i2c_WRITE_ACK
; 	;; send debug to uart
;  	movlw	A']'
;  	call	uart_send
	; put the result in 'W'
	banksel	i2c_register
	movf	i2c_register,W
	return

;--------------------------------------------------------------------
; send debug info to uart
i2c_debug:
	;; show counter to more easily keep track of bits
	banksel	i2c_debug_counter
	movf	i2c_debug_counter,W
	call	uart_show_hex
	banksel	i2c_debug_counter
	incf	i2c_debug_counter,F
	;; Data
	movlw	A'A'
	call	uart_send
	movlw	A'0'
	banksel	PORTB
	btfsc	SDA_IO
	movlw	A'1'
	call	uart_send
; 	movlw	A' '
; 	call	uart_send
	;; Clock
	movlw	A'C'
	call	uart_send
	movlw	A'0'
	banksel	PORTB
	btfsc	SCL_IO
	movlw	A'1'
	call	uart_send
	movlw	A' '
	call	uart_send
	return

	global	i2c_setup
	global	i2c_read_sda
	global	i2c_read_scl
	global	i2c_send
	global	i2c_receive
	global	i2c_debug

	global	i2c_status

	end