SX Microcontroller EEPROM Memory Method

24C32

by Virgil Stamps converted and optimized for the SX by James Newton

; ee.asm
;	LIST P=16C74A, F=INHX8M
;        __CONFIG _BODEN_OFF & _CP_OFF & _WDT_OFF & _XT_OSC
;
;        include <p16c74a.inc>
;*****************************************************************************
; This application written by Virgil Stamps,  6-12-99
; I had an application that needed configuration files for various modes of operation.
; A 24C32 I2C EEPROM provides 4Kx8 non-volatile storage. A PIC 16C74 is the processor.
; "Creating electronic products for fun and profit." This code is given
; to the public domain. If you need help, email author at stamps@brokersys.com
;*****************************************************************************
PortCTris	equ	%10010110
SDAOnMask	equ	%11111101
SDAOffMask	equ	%00000010

	RESET	START

	
INIT	setb	STATUS.RP0	; bank 1
	mov	M, #$0F
	mov	!RC, #PortCTris
; rc7= RX rc6= TX rc5=SDO rc4=SDI rc3=SCLK rc1=na rc0=na
	clrb	STATUS.RP0	; bank 0
	clr	event
	ret

;*****************************************************************************
; Program start. Simple poll loop. A good place for breakpoint and testing.
;*****************************************************************************

START	call	INIT	; Initialize Processor Registers
	setb	event.ee_busy	; read block
	mov	W, #0	; which block
	mov	BLK, W
POLL	nop
	snb	event.ee_busy	; ee busy?
	call	EE_SERVICE	; service eeprom if needed
	jmp	POLL

; Driver for Microchip 24C32A Serial EEPROM (32K-bit).
; Generic program listing for those seeking information on programming this type EEPROM.
; Approximately 30 ms to read and 80 ms to write a block of 80 bytes.
; The EEPROM is organized as virtual memory accessed by a block number
; and temporarily stored in RAM at BLOCK (0x0A0 - 0x0EF)
; A 32K-bit device has a maximum of 51 blocks. 4,096 bytes / 80 = 51.2 blocks
; A virtual memory location in your application could be defined as BLK (0-50)
; and a offset (0-79).


EE		equ	RC	; EEPROM port
SDA		equ	1	; EE data
SCL		equ	0	; EE clk
block		equ	$0A0
blk_size	equ	$050	; 80 decimal

; VARIABLES defined  in low RAM   Arbitrary as to where you want to put them
event		equ	$03D	; event register where:
ee_busy		equ	2	; service requested  1=busy, 0=idle
ee_write	equ	1	; 1=write, 0=read
ee_blk_op	equ	0	; 1=read or write block operation, 0=idle
ADDR0		equ	$03E	; address high byte
ADDR1		equ	$03F	; address low byte
BLK		equ	$040	; block number (0-50)
BLKC		equ	$041	; block counter
blkptr		equ	$042	; block pointer
o_data		equ	$043	; output data
i_data		equ	$044	; input data
ee_cntr4	equ	$045	; temp counter
ee_cntr3	equ	$046	; temp counter
ee_cntr2	equ	$047	; temp counter
ee_cntr1	equ	$048	; temp counter
X_DELAY		equ	$04B	; delay counter MS
DELAY		equ	$04C	; delay counter LS


;*****************************************************************************
; This application burst mode writes 10 groups of 8-bytes each.
; The read function block reads 80-bytes in a loop.
; To write the 80 bytes at block, set BLK= block number in EEPROM, set event,ee_busy,
; set event,ee_write, call EE_SERVICE.
; To read 80 bytes to block from EEPROM, set BLK= block number in EEPROM,
; set event,ee_busy, clear event,ee_write, call EE_SERVICE.
; on return:  event,ee_busy is zero, event,ee_block is zero.
;*****************************************************************************

EE_SERVICE
	sb	event.ee_busy	; busy?
	ret	; no
	call	EEPRMSVC	; service eeprom
	sb	event.ee_busy
	jmp	EE_SERVICE
	mov	W, #10
	call	X_DELAY500
	jmp	EE_SERVICE	; if yes

EEPRMSVC
	snb	event.ee_write	; write to ee?
	jmp	write_block	; if yes
	jmp	read_block	; no, read a block

write_block
	snb	event.ee_blk_op	; writing a block now?
	jmp	write_busy	; if yes
	setb	event.ee_blk_op	; no, signal writing a block
	call	set_addr
	mov	W, #block
	mov	blkptr, W	; addr of block buffer
	mov	W, #10
	mov	ee_cntr4, W	; write 10 groups of 8 bytes each
write_busy
	call	low_scl
	call	low_sda
	mov	W, #blk_size/10
	mov	ee_cntr2, W
	mov	W, blkptr
	mov	FSR, W
	call	w_block
	decsz	ee_cntr4	; done all groups of 8?
	jmp	poll_ack	; if no
	clrb	event.ee_blk_op	; eeprom block operation done
	clrb	event.ee_write	; eeprom not writing
	clrb	event.ee_busy	; eeprom not busy
	jmp	delay50ms	; return after delay

poll_ack
	mov	W, #100
	mov	ee_cntr3, W
ack_loop
	decsz	ee_cntr3
	jmp	ack_tst
	jmp	err
ack_tst
	call	strt
	mov	W, #$0a0
	call	out_byte
	call	wait
	snb	RC.SDA	; did chip acknowledge?
	jmp	clkit	; if no
	jmp	pulse	; yes, so exit
clkit
	call	pulse
	jmp	ack_loop	; poll again

err
	nop	; EEPROM is bad  (add your own error recovery)
	jmp	err	; eeprom did not respond in 100 tries


w_block
	call	strt	; burst write 8 bytes
	mov	W, #$0a0
	call	out_byte
	call	ack
	mov	W, ADDR0	; ms addr
	call	out_byte
	call	ack
	mov	W, ADDR1	; ls addr
	call	out_byte
	call	ack
wrtblk
	mov	W, INDF	; byte to write
	call	out_byte
	call	ack
	inc	FSR
	inc	blkptr
	mov	W, #1
	add	ADDR1, W	; EEPROM addr low
	snb	C
	inc	ADDR0	; EEPROM addr high
	decsz	ee_cntr2
	jmp	wrtblk
	call	stop
	ret

read_block
	setb	event.ee_blk_op	; signal reading a block
	call	set_addr
	call	low_scl
	call	high_sda
	call	high_scl	; idle bus
	mov	W, #block
	mov	FSR, W	; addr of block buffer
	mov	W, #blk_size
	mov	ee_cntr2, W	; bytes to copy

	call	strt
	mov	W, #$0a0	; write preface
	call	out_byte
	call	ack
	mov	W, ADDR0	; ms address
	call	out_byte
	call	ack
	mov	W, ADDR1	; ls address
	call	out_byte
	call	ack

	call	strt
	mov	W, #$0a1
	call	out_byte	; read preface
more_seq
	call	ack
	call	in_byte	; fetch the byte
	mov	W, i_data
	mov	INDF, W	; copied to current block
	inc	FSR	; point to next address in block
	mov	W, #1
	add	ADDR1, W
	snb	C
	inc	ADDR0
	decsz	ee_cntr2	; read all bytes?
	jmp	more_seq
	call	stop
	clrb	event.ee_blk_op	; block read is done
	clrb	event.ee_busy	; ee not busy
	ret

out_byte
	mov	o_data, W	; output 8 bits to eeprom
	mov	W, #8
	mov	ee_cntr1, W
o_loop
	rl	o_data	; msb to carry
	call	low_sda
	snb	C	; is it a 1?
	call	high_sda
	call	pulse
	decsz	ee_cntr1	; done?
	jmp	o_loop	; no
	call	high_sda
	ret

in_byte
	mov	W, #8	; input 8 bits from eeprom
	mov	ee_cntr1, W
	call	high_sda
i_loop
	call	high_scl	; data valid
	clrb	C
	snb	RC.SDA
	setb	C
	rl	i_data
	call	low_scl
	call	wait
	decsz	ee_cntr1
	jmp	i_loop
	ret

ack
	call	high_sda	; acknowledge operation
	snb	RC.SDA	; bit low?
	jmp	force0	; if no
	jmp	pulse	; slave acknowledges
force0
	call	low_sda	; master acknowledges
	call	pulse
	jmp	high_sda	; return as input

strt
	call	high_sda
	call	high_scl
	call	low_sda	; strt condition
	jmp	low_scl

stop
	call	low_sda
	call	high_scl
	call	high_sda	; stop condition
	jmp	low_scl

low_sda
	call	wait
	setb	STATUS.RP0	; bank 1
	mov	M, #$0F
	mov	!RC, #PortCTris | SDAOnMask
; rc1=SDA rc0=SCL
	clrb	STATUS.RP0	; bank 0
	clrb	RC.SDA
	jmp	wait

high_sda	call	wait
	setb	STATUS.RP0	; bank 1
	mov	M, #$0F
	mov	!RC, #PortCTris & SDAOffMask
; rc1=SDA rc0=SCL
	clrb	STATUS.RP0	; bank 0
	setb	RC.SDA
	jmp	wait

low_scl
	clrb	EE.SCL	; clock line goes low
	ret
high_scl
	setb	EE.SCL	; clock line goes high
	ret

wait
	nop	; delay for the chip
	nop
	nop
	nop
	ret

pulse
	call	high_scl	; one clock pulse
	call	wait
	jmp	low_scl


; subroutine to compute block address: addr = n * 80

set_addr
	clr	ADDR0	; assume 1st block
	clr	ADDR1
	mov	W, BLK
	mov	BLKC, W
	mov	W, BLK
	sb	Z	; zero?
	jmp	next_addr2	; if n
	jmp	set_done	; if yes
next_addr2
	clrb	C	; set ADDR0 and ADDR1 modulo 80
	mov	W, #blk_size	; sizeof block
	add	ADDR1, W
	snb	C	; overflow?
	inc	ADDR0	; if yes
	decsz	BLKC
	jmp	next_addr2	; if no
set_done
	ret

; a delay routine

delay50ms
	mov	W, #100
X_DELAY500
	mov	X_DELAY, W	; +1		1 cycle
X_DELAY500_LOOP
	call	DELAY500	; step1		wait 500uSec
	decsz	X_DELAY	; step2		1 cycle
	jmp	X_DELAY500_LOOP	; step3		2 cycles
	ret	; +2            2 cycles

DELAY500
	mov	W, #165	; +1            1 cycle
	mov	DELAY, W	; +2		1 cycle
DELAY500_LOOP
	decsz	DELAY	; step 1	1 cycle
	jmp	DELAY500_LOOP	; step 2	2 cycles
	ret	; +3            2 cycles

	END				; End of program