'**************************************************************************** '* I2C SUBROUTINES * '**************************************************************************** 'Author: Jordan Haralampopoulos 'E-mail: jordanis@ix.netcom.com ' 'Version: 0.1 (more to come) 'Devices supported: 'Philips PCF8583 Real time clock with 256 bytes RAM 'Philips PCF8574 I/O 'Microchip 24C65 8Kbytes EEPROM ' 'Note_1 : 10K pull-up resistors used on the I2C data & clock lines ' 'Note_2 : PCF8574 has quasi-bidirectional I/O ports. If you want ' to configure a port bit as input you load it with 1. ' To configure a port bit as output you load it with 0. ' E.G. ' To drive an LED you connect the anode through a resistor to ' +5V and the cathode to the PCF8574 pin. Drive the pin low ' to turn the LED on. To test for switch closure you put a ' pull-up resistor from the input pin to +5 and connect the switch ' between the input pin and ground. ' ' 'Note_3 : You must consult the Data Sheets of any I2C component ' that you want to use. You have to follow the sequences ' for read & write religiously. Speed is not critical. ' 'Note_4 : There is a bit variable named i2ack. After you call the ' ackrx you can test this bit to see if you received an ' acknowledge and act accordingly. I don't test it because ' so far I didn't have any nacks coming back. This may change. '******************************************************************* i2addr var word '16 bit mem address i2addr1 var i2addr.highbyte '8 MSBs of mem address i2addr0 var i2addr.lowbyte '8 LSBs of mem address temp1 var byte 'temporary byte variable i2data var byte 'byte for I2C I/O i2io var byte 'byte of I/O port 8574 i2iop0 var i2io.bit0 'port 0 of 8574 i2iop1 var i2io.bit1 'port 1 of 8574 i2iop2 var i2io.bit2 'port 2 of 8574 i2iop3 var i2io.bit3 'port 3 of 8574 i2iop4 var i2io.bit4 'port 4 of 8574 i2iop5 var i2io.bit5 'port 5 of 8574 i2iop6 var i2io.bit6 'port 6 of 8574 i2iop7 var i2io.bit7 'port 7 of 8574 i2ack var bit 'I2C acknowledge bit i2dt con 6 'I2C data pin i2clk con 7 'I2C clock pin i2memWR con %10100010 'I2C mem write control byte i2memRD con %10100011 'I2C mem read control byte i2ioWR con %01000000 'I2C I/O write control byte i2ioRD con %01000001 'I2C I/O read control byte rtcWR con %10100000 'I2C real time clock write rtcRD con %10100001 'I2C real time clock read memtop con 8191 'I2C EEPROM memory top pause 20 'give the system time to settle 'Some stupid tests to demo the routines 'Remove the remark to run each test 'goto test1 'goto test2 'goto test3 'goto test4 ' Test1 tests the PCF8574. Port 0 has a buzzer & port 2 an LED. ' Port 4 has a button. When you press the button down the LED and ' the buzzer turn on. When depress the button they turn off. test1: i2io=%11111111 'init he PCF8574 gosub wrio chkio1: gosub rdio 'check for button if i2iop4=%1 then chkio1 i2iop0=%0 'if press LED & buzzer on i2iop2=%0 gosub wrio i2iop4=%1 'reset the button port gosub wrio chkio2: gosub rdio 'if pressed continue if i2iop4=%0 then chkio2 i2iop0=%1 'if not turn off LED & buzzer i2iop2=%1 gosub wrio i2iop4=%1 'reset the button port goto chkio1 'goto check for button stop ' Test2 writes a byte to the RTC, clears the temp1, and then reads ' back the byte to verify. Addresses 0-6 contain the clock registers ' so don't write there (you can read them). The rest of the RTC ' addresses up to 255 are OK to write & read. More code for the ' clock on the next version test2: i2addr0=45 'pick an address from 7-255 temp1=201 'put a value up to 255 gosub wrrtc 'go write temp1=0 'clear temp1 i2addr0=45 'go to the same address gosub rdrtc 'read & verify the value debug dec ? temp1,cr stop ' Test3 writes a value to the 24C65 EEPROM and then reads back ' to verify test3: i2addr=125 'pick an address 0-8191 temp1=133 'pick a value 0-255 gosub wrmem 'go write debug bin ? i2ack,cr 'got ack=%0 (aknowledged) if i2ack=%1 then test3 'if not do it again temp1=0 'clear temp1 i2addr=125 'go to the same address gosub rdmem 'read and verify debug dec ? temp1,cr stop ' Test4 read & displays sequentialy all the values from the starting ' address that you supply to the end of memory (memtop) test4: i2addr=0 'start from the beginning 'max value is 8190 gosub rdmemsq 'do the sequential read stop ' This is the dosomething routines. In this case it displays ' the memory values trough debug. It could be a serial output ' to something else. dosomething: pause 100 'give some time to see the value debug dec ? temp1,cr return '**************************** '* I2C Routines begin here * '**************************** '********************************************** '* (wrmem) write a byte to I2C memory routine * '********************************************** 'Before you call wrmem 'Load i2addr with memory address word 'Load temp1 with variable byte to store '********************************************** wrmem: gosub i2start 'send start contition i2data=i2memWR 'send wr command gosub i2tx gosub ackrx i2data=i2addr1 'send high address gosub i2tx gosub ackrx i2data=i2addr0 'send low address gosub i2tx gosub ackrx i2data=temp1 'send data byte gosub i2tx gosub ackrx gosub i2stop 'send stop condition return '*********************************************** '* (rdmem) read a byte from I2C memory routine * '*********************************************** 'Before you call rdmem 'Load i2addr with memory address word to retrieve from 'Returns the value from address in temp1 '****************************************************** rdmem: gosub i2start 'send start condition i2data=i2memWR 'send wr command gosub i2tx gosub ackrx i2data=i2addr1 'send high address gosub i2tx gosub ackrx i2data=i2addr0 'send low address gosub i2tx gosub ackrx gosub i2start 'again start condition i2data=i2memRD 'send rd command now gosub i2tx gosub ackrx gosub i2rx 'get byte temp1=i2data 'put it in temp1 gosub i2stop 'send stop condition return '************************************* '* (rdmemsq) Sequential I2C mem read * '************************************* 'Before you call rdmemsq: 'Load i2addr with starting memory address word 'Gets constant memtop (top of memory) 'Returns value from memory in temp1 in the subroutine dosemething 'Example: 'This is used to upload data stored in the EEPROM to a PC 'through the serial port. Much faster than the single byte read '***************************************************************** rdmemsq: gosub i2start 'send start condition i2data=i2memWR 'send wr command gosub i2tx gosub ackrx i2data=i2addr1 'send high address gosub i2tx gosub ackrx i2data=i2addr0 'send low address gosub i2tx gosub ackrx gosub i2start 'send start condition again i2data=i2memRD gosub i2tx gosub ackrx for i2addr=i2addr to memtop-1 'loop to memtop-1 shiftin i2dt,i2clk,0,[i2data] 'get data 'shifin instead of gosub 'to i2rx speeds up things shiftout i2dt,i2clk,1,[%0\1] 'send ack temp1=i2data 'put data in temp1 gosub dosomething 'do something with the data next shiftin i2dt,i2clk,0,[i2data] 'get the last byte gosub i2stop 'send stop condition temp1=i2data 'put last byte in temp gosub dosomething 'do something with the data return '******************************* '* (wrrtc) Write a byte to RTC * '******************************* 'Before you call wrrtc 'Load i2addr0 with memory address byte 'Load temp1 with byte to store '************************************** wrrtc: gosub i2start 'send start condition i2data=rtcWR 'send wr command gosub i2tx gosub ackrx i2data=i2addr0 'send address byte gosub i2tx gosub ackrx i2data=temp1 'send data gosub i2tx gosub ackrx gosub i2stop 'send stop condition return '******************************** '* (rdrtc) Read a byte from RTC * '******************************** 'Before you call rdrtc 'Load i2addr0 with address byte 'Returns the value from address in temp1 '*************************************** rdrtc: gosub i2start 'send start condition i2data=rtcWR 'send wr command gosub i2tx gosub ackrx i2data=i2addr0 'send address byte gosub i2tx gosub ackrx gosub i2start 'send start condition again i2data=rtcRD 'send rd command gosub i2tx gosub ackrx gosub i2rx temp1=i2data 'get byte in temp1 gosub i2stop 'send stop condition return '***************************** '* (wrio) I2C I/O 8574 write * '***************************** 'Before you call wrio 'load i2io or i2iop0-i2iop7 '***************************** wrio: gosub i2start 'send start condition i2data=i2ioWR 'send wr command gosub i2tx gosub ackrx i2data=i2io 'send byte gosub i2tx gosub ackrx gosub i2stop 'send stop condition return '**************************** '* (rdio) I2C I/O 8574 read * '**************************** 'Returns i2io (I/O status byte) rdio: gosub i2start 'send start condition i2data=i2ioRD 'send rd command gosub i2tx gosub ackrx gosub i2rx i2io=i2data 'get data gosub i2stop 'send stop condition return '********************************* '* (i2start) I2C start contition * '********************************* i2start: high i2dt 'I2C data line high high i2clk 'I2C clock line high low i2dt 'I2C data line low return '******************************* '* (i2stop) I2C stop contition * '******************************* i2stop: low i2dt 'I2C data line low high i2clk 'I2C clock line high high i2dt 'I2C data line high return '******************************************** '* (i2tx & i2rx) I2C byte tensmit & receive * '******************************************** i2tx: shiftout i2dt,i2clk,1,[i2data] 'shift out byte to I2C return i2rx: shiftin i2dt,i2clk,0,[i2data] 'shift in byte from I2C return '****************************************************** '* (acktx & ackrx) I2C acknowledge transmit & receive * '****************************************************** acktx: shiftout i2dt,i2clk,1,[%0\1] 'send acknowledge to I2C return ackrx: shiftin i2dt,i2clk,0,[i2ack\1] 'receive acknowledge from I2C return