; i2c_eeprom.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 communication with
; the eeprom 24AA512 through the i2c bus

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

	udata
 ; eeprom control byte
i2c_ee_control	res	1
 ; eeprom LOW and HIGH address
i2c_ee_addr_L	res	1
i2c_ee_addr_H	res	1

;; usually stores the last read byte from the eeprom after
;; the appropriate routine is called.
i2c_ee_data	res	1
;; temporary counter for the number of bytes to read when using the
;; sequential read routine.
i2c_ee_nbytes	res	1

;--------------------------------------------------------------------
; external functions
	extern	i2c_send
	extern	i2c_receive
	extern	i2c_debug

	extern	delay10
	extern	delay100
;; 	extern	delayms

	extern	uart_receive
	extern	uart_send


;--------------------------------------------------------------------
; external symbols
	extern	i2c_status
	extern	i2c_ioe_matrix

	code
;--------------------------------------------------------------------
;	set eeprom address to 1010 000 0
;	1010 - control code
;	 000 - chip select :	 we have only one on the controller
;                                board with address 000.
;	   0 - R/W
;	see page 8 of 24AA512
i2c_eeprom_setup:
	movlw	B'10100000'
	banksel	i2c_ee_control
	movwf	i2c_ee_control
	;; clear data
	banksel i2c_ee_data
	clrf	i2c_ee_data
	return

;--------------------------------------------------------------------
; set i2c eeprom address for next read/write operation
; see page 13, figure 8-2 of the 24AA512 manual
i2c_eeprom_set_address:
i2c_eeprom_set_address_start:
	; wait for clock line to be free
 	i2c_SCL_WAIT
	; send start
	i2c_START
; 	; we want to send ACK
; 	i2c_SET_SEND_ACK
	; set control bit to WRITE to select address
	banksel	i2c_ee_control
	bcf	i2c_ee_control,0
	; send control byte:
	movf	i2c_ee_control,W
	call	i2c_send
	; check ACK and if error, jump to START
	i2c_CHECK_ACK	i2c_eeprom_set_address_start
	; send address high byte
	banksel	i2c_ee_addr_H
	movf	i2c_ee_addr_H,W
	call	i2c_send
	; check ACK and if error, jump to START
	i2c_CHECK_ACK	i2c_eeprom_set_address_start
	; send address low byte
	banksel	i2c_ee_addr_L
	movf	i2c_ee_addr_L,W
	call	i2c_send
	; check ACK and if error, jump to START
	i2c_CHECK_ACK	i2c_eeprom_set_address_start
	return

;--------------------------------------------------------------------
; read a byte from the eeprom connected to the i2c bus. the address
; must be previously set by the 'set_address' routine.
; see pages 12,13 of the 24AA512 manual
; the data is returned in 'W'
i2c_eeprom_random_read:
	; wait for clock line to be free
 	i2c_SCL_WAIT
	; send start
	i2c_START
	; set control bit to READ
	banksel	i2c_ee_control
	bsf	i2c_ee_control,0
	; send control byte:
	movf	i2c_ee_control,W
	call	i2c_send
	; check ACK and if error, jump to beginning
	i2c_CHECK_ACK	i2c_eeprom_random_read
	; we don't want to send ACK
	i2c_SET_SEND_NACK
	; read byte and put it in 'i2c_ee_data'
	; to save it
	call	i2c_receive
	banksel	i2c_ee_data
	movwf	i2c_ee_data
	; check ACK and if error, jump to beginning
	i2c_CHECK_ACK	i2c_eeprom_random_read
	; all done, send STOP
	i2c_STOP
	; put data back to 'W'
	banksel i2c_ee_data
	movf	i2c_ee_data,W
	return


;--------------------------------------------------------------------
; read N bytes from the eeprom connected to the i2c bus. the address
; must be previously set by the 'set_address' routine.
; see pages 12,13 of the 24AA512 manual, section 8.3 and figure 8-3
;
; the number of bytes to read must be specified by the user using the
; 'W' register
i2c_eeprom_sequential_read:
	banksel	i2c_ee_nbytes
	movwf	i2c_ee_nbytes
	;; initialize the indirect addressing register
	movlw	i2c_ioe_matrix
	movwf	FSR
i2c_eeprom_sequential_read_cont:

	; wait for clock line to be free
 	i2c_SCL_WAIT
	; send start
	i2c_START
	; we want to send ACK
	i2c_SET_SEND_ACK
	; set control bit to READ
	banksel	i2c_ee_control
	bsf	i2c_ee_control,0
	; send control byte:
	movf	i2c_ee_control,W
	call	i2c_send
	; check ACK and if error, jump to beginning
	i2c_CHECK_ACK	i2c_eeprom_sequential_read_cont

i2c_eeprom_sequential_read_loop:
	; read byte
	call	i2c_receive
	; check ACK and if error, jump to beginning
	i2c_CHECK_ACK	i2c_eeprom_sequential_read

	;put byte in 'i2c_ioe_matrix' using indirect addressing.
	banksel	i2c_ioe_matrix
	movwf	INDF
	incf	FSR,F
	;; send to uart for debugging
	call	uart_send

	;; check the byte counter to see if we are done
	banksel	i2c_ee_nbytes
	decfsz	i2c_ee_nbytes,F
	goto	i2c_eeprom_sequential_read_loop

	;; all done, send STOP
	i2c_STOP

	return

;--------------------------------------------------------------------
; write a byte to the eeprom connected to the i2c bus. the address
; must be previously set by the 'set_address' and the byte data in the
; 'W' register.
; see pages 9,10 from 24AA512 manual
i2c_eeprom_random_write:
	; send data byte:
; 	banksel	i2c_ee_data
; 	movf	i2c_ee_data,W
	call	i2c_send
	; check ACK and if error, jump to beginning
	i2c_CHECK_ACK	i2c_eeprom_random_write
	; all done, send STOP
	i2c_STOP
	;; wait longer
; 	call	delayms
; #warning see section 7.0 for the fact that ACK is not returned while device
; 	is busy
	return


;--------------------------------------------------------------------
; this routine gets 3 values from the uart, which correspond to the
; i2c eeprom HI and LO address and the byte to write.
i2c_eeprom_write_uart:
	;; get and set i2c eeprom HI address
	call	uart_receive
	banksel	i2c_ee_addr_H
	movwf	i2c_ee_addr_H
	;; get and set i2c eeprom LO address
	call	uart_receive
	banksel	i2c_ee_addr_L
	movwf	i2c_ee_addr_L
	;; set i2c eeprom address:
	call i2c_eeprom_set_address
	;; read uart and send byte to i2c eeprom
	call	uart_receive
	call	i2c_eeprom_random_write
	return

;--------------------------------------------------------------------
; this routine gets 2 values from the uart, which correspond to the
; i2c eeprom HI and LO address and sends through the uart the read
; data byte
i2c_eeprom_read_uart:
	;; get and set i2c eeprom HI address
	call	uart_receive
	banksel	i2c_ee_addr_H
	movwf	i2c_ee_addr_H
	;; get and set i2c eeprom LO address
	call	uart_receive
	banksel	i2c_ee_addr_L
	movwf	i2c_ee_addr_L
	;; set i2c eeprom address:
	call i2c_eeprom_set_address
	;; read byte from i2c eeprom and send it through the uart
 	call	i2c_eeprom_random_read
 	call	uart_send
	return


;--------------------------------------------------------------------
; this routine gets 2 values from the uart, which correspond to the
; i2c eeprom HI and LO initial address and then calls the sequential
; read
i2c_eeprom_read_sequential_uart:
	;; get and set i2c eeprom HI address
	call	uart_receive
	banksel	i2c_ee_addr_H
	movwf	i2c_ee_addr_H
	;; get and set i2c eeprom LO address
	call	uart_receive
	banksel	i2c_ee_addr_L
	movwf	i2c_ee_addr_L
	;; set i2c eeprom address:
	call i2c_eeprom_set_address
	;; read 'MATRIX_SIZE' bytes from i2c eeprom
	movlw	MATRIX_SIZE
 	call	i2c_eeprom_sequential_read
	return

;--------------------------------------------------------------------
; this routine will start an infinite loop, where it sends a byte
; to the i2c eeprom and reads it back
;
i2c_eeprom_test:
	;; set i2c eeprom HI address
	movlw	B'00000001'
	banksel	i2c_ee_addr_H
	movwf	i2c_ee_addr_H
	;; set i2c eeprom LO address
	movlw	B'00000001'
	banksel	i2c_ee_addr_L
	movwf	i2c_ee_addr_L
	call	i2c_eeprom_set_address
	;; data we want to write to the i2c eeprom
; 	movlw	B'00100101'
	movlw	A'&'
	call	i2c_eeprom_random_write

i2c_eeprom_test_loop:
	movlw	'T'
	call	uart_send
	;; trigger:
  	banksel	PORTA
  	bsf	PORTA,0
	call	delay100
  	banksel	PORTA
  	bcf	PORTA,0
	call	delay100

	;; increment address
; 	banksel	i2c_ee_addr_L
; 	incf	i2c_ee_addr_L,F
; 	;; set i2c eeprom address:
; 	call i2c_eeprom_set_address
; 	;; data we want to write to the i2c eeprom
; 	movlw	B'01110111'
; 	call	i2c_eeprom_random_write
	;; set i2c eeprom address:
	call	i2c_eeprom_set_address
	movlw	A':'
	call	uart_send
	;; read byte from i2c eeprom and send it through the uart
 	call	i2c_eeprom_random_read
	call	uart_send
	goto	i2c_eeprom_test_loop
	return

	global	i2c_eeprom_setup
	global	i2c_eeprom_set_address
	global	i2c_eeprom_random_read
	global	i2c_eeprom_random_write

	global	i2c_eeprom_read_uart
	global	i2c_eeprom_write_uart

	global	i2c_eeprom_read_sequential_uart
	global	i2c_eeprom_test
	end
