Files
SyncHome/trunk/MPLABXProjects/00_Lib/Ressources/i2C.c
paolo.iocco e51d00d234
2023-03-09 10:05:56 +00:00

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);
}