221 lines
7.2 KiB
C
221 lines
7.2 KiB
C
|
|
#include <pic.h>
|
|
// demo of Mike's optimised IIC code for eeprom access under hitech C
|
|
// (C) Mike Harrison 2001 email mike -at- whitewing.co.uk
|
|
// Note that this doesn't seem to work when local optimisation is enabled, as it
|
|
// appears to 'optimise' the asm code by simply ignoring it! Global optimisation seems to be OK.
|
|
// this is intended for inclusion within C source, not as a seperate 'extern' module
|
|
// It could be converted to the latter form easily, and this would avoid the above optimisation
|
|
// problem. (If anyone does this please let me have a copy!)
|
|
// see notes on original assembler version for more details.
|
|
|
|
// this version works under hitech C V7.86, and was tested on a PIC16C74
|
|
// if anyone who knows C better can suggest any improvements to the C side of this please let me know!
|
|
|
|
|
|
//asm macros
|
|
#define c 0
|
|
#define z 2
|
|
#define skpnc btfsc _STATUS,c
|
|
#define skpc btfss _STATUS,c
|
|
#define sec bsf _STATUS,c
|
|
#define clc bcf _STATUS,c
|
|
#define skpz btfss _STATUS,z
|
|
#define skpnz btfsc _STATUS,z
|
|
#define tris dw 0x60+
|
|
#define rp0 5
|
|
// define indf - missing from hitech
|
|
static volatile unsigned char INDF @ 0x00;
|
|
|
|
// eeprom port bits (PORT A)
|
|
#define sclbit 2
|
|
#define sdabit 3
|
|
#define ee_scl _PORTA,sclbit
|
|
#define ee_sda _PORTA,sdabit
|
|
#define hitris 0x1a // PORT A TRIS for SDA high
|
|
#define lotris hitris-(1<<sdabit)
|
|
|
|
#define iiport _PORTA
|
|
#define iiadr 0x0a0
|
|
|
|
|
|
void do_iic(char cnt,char eeadr,char fsrval)
|
|
|
|
// flags
|
|
#define rden 2
|
|
#define addr 1
|
|
#define read 0
|
|
|
|
{
|
|
char flags;
|
|
char temp;
|
|
// the following assignments seem to stop the compiler optimising
|
|
// out the temp vars because it thinks they are unused.
|
|
// This is required when global optimisation is enabled.
|
|
// if anyone knows a better way (i.e. one that doesn't waste ROM)
|
|
// of telling the compiler that these vars are used please let me know!
|
|
// one alternative would be to use global temp vars for temp & flags
|
|
|
|
flags=flags;
|
|
eeadr=eeadr;
|
|
cnt=cnt;
|
|
temp=temp;
|
|
FSR=fsrval; // pointer to read/write data
|
|
#asm
|
|
do_iic ; read/write byte(s) to I2C EEPROM
|
|
; W & FSR to be setup as follows :
|
|
; read : cnt=EF - nbytes FSR = RAM address-1
|
|
; write : cnt=E0 - (nbytes<<4) FSR = RAM address-3
|
|
; eeadr holds eeprom address (preserved on exit)
|
|
; on exit, FSR points to the byte after the last one read/written
|
|
; nbytes can be up to 14, but eeprom write cache may limit this
|
|
|
|
|
|
retry_iic
|
|
clrf _do_iic$flags ; initialise flags and bit count
|
|
phaseloop
|
|
movlw hitris
|
|
tris iiport ; tris iiportensure SDA high
|
|
bsf ee_scl ; SCL high
|
|
bcf ee_sda ; ensure SDA o/p reg low
|
|
movlw lotris
|
|
goto $+1 ; ensure Tsu:sta - can be omitted in fast mode
|
|
tris iiport ; sda low - start condition
|
|
|
|
|
|
movlw iiadr ; IIC control byte (write)
|
|
btfsc _do_iic$flags,rden
|
|
movlw iiadr+1 ; .. or read control byte if read pending
|
|
movwf _do_iic$temp ; IIC control byte
|
|
bcf ee_scl ; scl low - code above ensures Thd:sta
|
|
|
|
|
|
byteloop ;
|
|
; start of byte read/write section
|
|
movlw lotris
|
|
btfss _do_iic$flags,read ; set SDA high (tri-state) if reading
|
|
btfsc _do_iic$temp,7 ; set SDA low only if writing and data bit = 0
|
|
movlw hitris ; (sda o/p register bit will be low)
|
|
tris iiport
|
|
goto $+1 ; wait set-up time
|
|
bsf ee_scl ; clock high (may set SDA o/p reg bit high)
|
|
clc ; used later - done here for timing
|
|
movlw 0xff^(1<<sclbit)^(1<<sdabit) ; mask to clear SDA and SCL, " "
|
|
btfsc ee_sda ; test SDA input for read
|
|
sec
|
|
andwf iiport ; SCL low, SDA o/p reg bit low
|
|
rlf _do_iic$temp ; shift read data in or write data out
|
|
movlw 0x20
|
|
addwf _do_iic$flags ; increment bitcount in b5-7
|
|
skpc
|
|
goto byteloop ; do 8 bits
|
|
|
|
movlw 0xf0
|
|
xorwf _do_iic$cnt,w ; =f0 (last byte of read), result used later
|
|
|
|
movlw lotris ; ack low if reading to send ack to eeprom
|
|
skpz ; no ack on read of last byte
|
|
btfss _do_iic$flags,read
|
|
movlw hitris ; ack high for write to test ack from eeprom, or on last byte read
|
|
|
|
tris iiport
|
|
|
|
bsf ee_scl ; clock high to get or send ack bit
|
|
goto $+1 ; wait ack pull-up time
|
|
movlw 0xff^(1<<sclbit)^(1<<sdabit) ; SDA/SCL low mask, done here to add delay
|
|
skpz ; last byte of read - skip retry
|
|
btfss ee_sda ; read ack bit state
|
|
goto no_retry_iic ; no retry if ack low, or on last byte of read
|
|
goto retry_iic ; retry if ack high (will be forced low on reads, except last byte)
|
|
no_retry_iic
|
|
|
|
andwf iiport ; set scl and sda o/p register bit low
|
|
;..................... end of byte read/write section
|
|
movf _do_iic$temp,w
|
|
btfsc _do_iic$flags,read
|
|
movwf indf ; store data if reading
|
|
movf indf,w ; get write data
|
|
incf fsr ; increment RAM pointer
|
|
|
|
btfss _do_iic$flags,addr
|
|
movf _do_iic$eeadr,w ; load eeprom address if not disabled
|
|
movwf _do_iic$temp ; byte to send next loop - address or data
|
|
bsf _do_iic$flags,addr ; disable address flag
|
|
btfsc _do_iic$flags,rden ; read mode pending?
|
|
bsf _do_iic$flags,read ; set read mode
|
|
|
|
movlw 0x10
|
|
addwf _do_iic$cnt ; increment byte counter in B4..7
|
|
skpnz
|
|
goto done ; both nibbles zero - all done
|
|
skpc ; c set if b7-4 now clear - write phase done
|
|
goto byteloop
|
|
bsf _do_iic$flags,rden ; set 'read pending' flag
|
|
swapf _do_iic$cnt ; load byte counter with read byte count
|
|
goto phaseloop ; do second phase of command
|
|
|
|
done
|
|
; do stop condition
|
|
movlw lotris ; (SDA o/p bit will be low)
|
|
tris iiport ; set SDA low
|
|
bsf ee_scl ; scl high
|
|
goto $+1 ; ensure Tsu:sto
|
|
goto $+1 ; both these can be omitted for fast mode
|
|
movlw hitris
|
|
tris iiport ; sda high
|
|
|
|
|
|
#endasm
|
|
|
|
}
|
|
|
|
|
|
//eeprom read/write routines. If only called once it would be more efficient to use macros instead
|
|
// note upper limit of 14 bytes that can be written/read at a time
|
|
// and for writes, #bytes may be limited by eeprom cache size
|
|
|
|
void write_ee(char eeaddress,char nbytes,char source)
|
|
// call with write_ee(eeadr,#bytes,(char)&varname);
|
|
{
|
|
do_iic(0xe0-(nbytes<<4),eeaddress,source-1);
|
|
}
|
|
|
|
|
|
void read_ee(char eeaddress,char nbytes,char dest)
|
|
// call with read_ee(eeadr,#bytes,(char)&varname);
|
|
{
|
|
do_iic(0xef-nbytes,eeaddress,dest-3);
|
|
}
|
|
|
|
|
|
|
|
main()
|
|
{
|
|
|
|
long test1=0x33221100;
|
|
int test2=0x5544;
|
|
char test3=0x66;
|
|
char buffer[8];
|
|
|
|
TRISA=hitris;
|
|
ADCON1=7; // disable adc
|
|
OPTION=0b10110000;
|
|
|
|
|
|
write_ee(0x10,4,(char)&test1); // write long test1 to ee address 0x10
|
|
// data will be written LSB first, i.e. 00,11,22,33 in addresses 10..13
|
|
|
|
write_ee(0x14,2,(char)&test2); // write int test2 to ee address 0x14
|
|
write_ee(0x16,1,(char)&test3); // write char test3 to ee address 0x16
|
|
|
|
read_ee(0x10,4,(char)&test1); // read long test1 from ee address 0x10
|
|
read_ee(0x14,2,(char)&test2); // read int test2 from ee address 0x14
|
|
read_ee(0x16,1,(char)&test3); // read char test3 from ee address 0x16
|
|
|
|
|
|
read_ee(0x10,8,(char)&buffer[0]); // read 8 bytes from ee addresses 0x10..17 to buffer[0..7]
|
|
|
|
do
|
|
asm("clrwdt");
|
|
while(1);
|
|
} |