Here are some bit-banged I^2C routines that should be easy to understand. They are fairly optimized, with near-minimum instruction count allowable for 4MHz operation.Tested/bulletproof at 4MHz. Bank switching is provided. Routines exit with Bank 0 selected.
;------------------------------------------------------------------------ ; ; I^2C master send/receive routines for 24Cxx EEPROM memory chips ; by Andrew D. Vassallo ; ; email: snurple@hotmail.com ; ; Timing set for 4MHz clock, 4.7K pullup resistors to SDA and SCL lines. ; Checks provided for failed ACK during EEPROM write access. ; Optimized for speed and clarity of function. ; Full comments provided. ; Bank switching correct. ; ;------------------------------------------------------------------------ ;------ NOTE: Locate all variables in Bank 1!! CBLOCK GenCount ; generic counter/temp register (use with caution) Mem_Loc ; EEPROM memory address to be accessed Data_Buf ; byte read from EEPROM is stored in this register OutputByte ; used for holding byte to be output to EEPROM flags ; flag bit register ENDC ;------ Define port pins: RB2=SDA=data, RB1=SCL=clock ;------ Pins are connected via 4.7K pullup resistors for passive control ;------ Any port pins can be used. #define SDA TRISB, 2 #define SCL TRISB, 1 ;------ The memory address is predefined as Mem_Loc coming in to this routine to read from EPROM. ;------ Note that using the 24Cxx requires an address for bits <1:3> for control bytes. For a one- ;------ memory circuit used here, just use address 000. This routine uses "random addressing." ;------ This code has been optimized for speed at 4MHz, assuming Temperature is between -40 and +85 deg. C. ; ; Call with: EEPROM address in Mem_Loc ; Returns with: byte in Data_Buf ReadEPROM bcf STATUS, RP0 movf PORTB, 0 ; for EEPROM operation, andlw 0xF9 ; load zero into RB1 and RB2 movwf PORTB ; for passive control of bus bsf STATUS, RP0 ; select Bank 1 for TRISB access (passive SCL/SDA control) bsf SDA ; let SDA line get pulled high bsf SCL ; let SCL line get pulled high bcf SDA ; START - data line low movlw 0xA0 ; send "dummy" write (10100000) to set address call Byte_Out btfsc flags, 0 goto Error_Routine ; NOTE: MUST USE "RETURN" FROM THERE movf Mem_Loc, 0 call Byte_Out btfsc flags, 0 goto Error_Routine bcf SCL ; pull clock line low in preparation for 2nd START bit nop bsf SDA ; pull data line high - data transition during clock low bsf SCL ; pull clock line high to begin generating START bcf SDA ; 2nd START - data line low movlw 0xA1 ; request data read from EPROM (read=10100001) call Byte_Out btfsc flags, 0 goto Error_Routine ;------ Note that Byte_Out leaves with SDA line freed to allow slave to send data in to master. call Byte_In movf Data_Buf, 0 ; put result into W register for returning to CALL bcf SCL ; extra cycle for SDA line to be freed from EPROM nop bcf SDA ; ensure SDA line low before generating STOP bsf SCL ; pull clock high for STOP bsf SDA ; STOP - data line high bcf STATUS, RP0 ; leave with Bank 0 active as default return ;------ Save each byte as it's written (not page write mode). ;------ We can speed this up to ~1.5ms for fast page write capable EEPROMs, but in case we want to ;------ use another slower memory chip, the default is 10ms delay per write. ; ; Call with: EEPROM address in Mem_Loc, byte to be sent in Data_Buf ; Returns with: nothing returned WriteEPROM bcf STATUS, RP0 movf PORTB, 0 ; for EEPROM operation, andlw 0xF9 ; load zero into RB1 and RB2 movwf PORTB ; for passive control of bus bsf STATUS, RP0 ; select Bank 1 for TRISB access (passive SCL/SDA control) bsf SDA ; ensure SDA line is high bsf SCL ; pull clock high bcf SDA ; START - data line low movlw 0xA0 ; send write (10100000) to set address call Byte_Out btfsc flags, 0 goto Error_Routine ; NOTE: MUST USE "RETURN" FROM THERE movf Mem_Loc, 0 call Byte_Out btfsc flags, 0 goto Error_Routine movf Data_Buf, 0 ; move data to be sent to W call Byte_Out btfsc flags, 0 goto Error_Routine bcf SCL ; extra cycle for SDA line to be freed from EPROM nop bcf SDA ; ensure SDA line low before generating STOP bsf SCL ; pull clock high for STOP bsf SDA ; STOP - data line high call Delay10ms ; 10ms delay max. required for EPROM write cycle bcf STATUS, RP0 ; leave with Bank 0 active by default return ;------ This routine reads one byte of data from the EPROM into Data_Buf Byte_In clrf Data_Buf movlw 0x08 ; 8 bits to receive movwf GenCount ControlIn rlf Data_Buf, 1 ; shift bits into buffer bcf SCL ; pull clock line low nop bsf SCL ; pull clock high to read bit bcf STATUS, RP0 ; select Bank 0 to read PORTB bits directly! btfss SDA ; test bit from EPROM (if bit=clear, skip because Data_Buf is clear) goto $+3 bsf STATUS, RP0 ; select Bank 1 to access variables bsf Data_Buf, 0 ; read bit into 0 first, then eventually shift to 7 bsf STATUS, RP0 ; select Bank 1 to access variables decfsz GenCount, 1 goto ControlIn return ;------ This routine sends out the byte in the W register and then waits for ACK from EPROM (256us timeout period) Byte_Out movwf OutputByte movlw 0x08 ; 8 bits to send movwf GenCount rrf OutputByte, 1 ; shift right in preparation for next loop ControlOut rlf OutputByte, 1 ; shift bits out of buffer bcf SCL ; pull clock line low nop btfsc OutputByte, 7 ; send current "bit 7" goto BitHigh bcf SDA goto ClockOut BitHigh bsf SDA ClockOut bsf SCL ; pull clock high after sending bit decfsz GenCount, 1 goto ControlOut bcf SCL ; pull clock low for ACK change bsf SDA ; free up SDA line for slave to generate ACK nop nop nop ; wait for slave to pull down ACK bsf SCL ; pull clock high for ACK read clrf GenCount ; reuse this register as a timeout counter (to 256us) to test for ACK WaitForACK bsf STATUS, RP0 ; select Bank1 for GenCount access incf GenCount, 1 ; increase timeout counter each time ACK is not received btfsc STATUS, Z goto No_ACK_Rec bcf STATUS, RP0 ; select Bank0 to test SDA PORTB input directly! btfsc SDA ; test pin. If clear, EEPROM is pulling SDA low for ACK goto WaitForACK ; ...otherwise, continue to wait bsf STATUS, RP0 ; select Bank1 as default during these routines bcf flags, 0 ; clear flag bit (ACK received) return ;------ No ACK received from slave (must use "return" from here) ;; Typically, set a flag bit to indicate failed write and check for it upon return. No_ACK_Rec bsf flags, 0 ; set flag bit return ; returns to Byte_Out routine (Bank 1 selected) ;------ No ACK received from slave. This is the error handler. Error_Routine ; Output error message, etc. here bcf STATUS, RP0 ; select Bank0 as default before returning home return ; returns to INITIAL calling routine Delay10ms movlw 0x0A movwf GenCount Delay_Start nop movlw 0x07 ; 249 cycles * 4us per cycle + 5us = 1.000ms Delay addlw 0x01 btfss STATUS, Z goto Delay decfsz GenCount, 1 goto Delay_Start return
PICList post "IIC Routines - giving, not needing"
Questions:
I am currently exploring the capabilities of i2c. We are using the picdem.net demo board from Microchip and what I want to do is write to and read to the eeprom (24LC256) on the demo board using a pic18c452 microchip. I'm using assembler sample code I got from the Microchip site, but I can't seem to get the i2c up and running. The demo board has a OscFreq of 19.66 MHz, so maybe I got the i2c clock rate wrong?Or maybe I don't set the configuration bits correctly. I don't think there is anything wrong with the code. I would much appreciate any help you can give me. With this email I will include the sample assembler code I'm using. Thank you in advance Andre Steenkamp
Hello, All I can find on the Internet are assembler sources that allow the PIC to be an I2C MASTER. That's exactly what I need. But when you want your PIC master to communicate with a PC, you need I2C slave software for the PC. And that's where the problem is. There is no such thing as as PC I2C SLAVE. Well... Not that I can find one. What I need is a simple piece of software for the PIC and PC so the PIC can send a few bytes of data to the PC every 100ms. This doesn't HAVE to be I2C... Thanks to anyone who can point me in the right direction. Kees
Interested:
Comments: