Here is a working 18F2455 I2C slave. It will receive or send any amount of bytes you choose. The slave will not work if USB is enable on the chip.
In this example the Master is sending 0 to 127 on byte(0), and fixed data on Bytes 1 and 2.
The Slave sends three bytes back, tests the received byte(0) and lights a couple of leds. When the master sends (Byte(0) = 4), the slave ends three different bytes back. I also have eight leds connected to the slave, four on PortB 4-7 and four on PortA 0-3. These leds display the value of the received Byte(0).
Slave code
Code:
'* Date : 3/17/2009 *
'* Version : 1.0 *
'* Notes : *
'* : 18F2550 Slave I2c *
'****************************************************************
asm __CONFIG _CONFIG1L, _PLLDIV_5_1L & _FCMEN_OFF_1H
__CONFIG _CONFIG1H, _FOSC_HS_1H
__CONFIG _CONFIG2L, _VREGEN_OFF_2L
__CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_512_2H
__CONFIG _CONFIG3H, _PBADEN_OFF_3H
__CONFIG _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
endasm
DEFINE OSC 20
Define I2C_SLOW 1 'At this clock speed this needed to be added to make it work.
DEFINE I2C_HOLD 1
ADCON1 = $F 'All digital
CMCON = 7 'PortA Digital
'--------------- Define used register flags -------------------
SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
SSPIE VAR PIE1.3
BF VAR SSPSTAT.0 ' SSP (I2C) Buffer Full
R_W VAR SSPSTAT.2 ' SSP (I2C) Read/Write
D_A VAR SSPSTAT.5 ' SSP (I2C) Data/Address
CKP VAR SSPCON1.4 ' SSP (I2C) SCK Release Control
SSPEN VAR SSPCON1.5 ' SSP (I2C) Enable
SSPOV VAR SSPCON1.6 ' SSP (I2C) Receive Overflow Indicator
WCOL VAR SSPCON1.7 ' SSP (I2C) Write Collision Detect
STAT_BF VAR SSPSTAT.0 ' SSP (I2C) Buffer Full
STAT_RW VAR SSPSTAT.2 ' SSP (I2C) Read/Write
STAT_DA VAR SSPSTAT.5 ' SSP (I2C) Data/Address
CKE VAR SSPSTAT.6 ' SSP (I2C) Data/Address
WrData var byte
j var byte
'------------------- Rx Buffer defintion --------------------
RxBufferLEN con 16
RxBuffer var byte[Rxbufferlen]
RxBufferIndex var byte
RxCnt var byte
rxcnt = 0
'------------------ Tx Buffer defintion ----------------------
TxBufferLEN con 3
TxBuffer var byte[txbufferlen]
TxBufferIndex var byte
TxCnt var byte
txcnt = 0
'------------------ Define vars ------------------------
rxinptr var byte
rxoutptr var byte
txinptr var byte
txoutptr var byte
rxinptr = 0
rxoutptr = 0
txinptr = 0
txoutptr = 0
I2Caddress CON $02 ' Make address = 2
result VAR BYTE ' ADC result
DummyRead var byte
DataIn VAR BYTE[8] ' Data in
DataOut VAR BYTE[8] ' Data out array
readcnt VAR BYTE ' I2C read count
address var byte
StatMask var byte
StatMask = %00101101
readcnt = 0 ' Zero counter
' PicBasic Pro I2C slave program
' Alias pins
Led1 var PORTC.0
LED2 Var PORTC.1
scl VAR PORTB.1 ' I2C clock input
sda VAR PORTB.0 ' I2C data input
TRISB = %00000011
TRISC = %00000000
TRISA = %00000000
LATB = $F0
'PortA= $0F
PORTA = $0F 'leds off on A port
PORTB = $F0 ' Leds off on B port
high LED1
high led2
' -------------- Initialize I2C slave mode ------------------------
SSPADD = I2Caddress ' Set our address
SSPCON1 = $36 ' Set to I2C slave with 7-bit address
SSPCON2 = %00000000 ' clear sspcon2
SSPCON2 = %00000001 'enable clock stretching
SSPSTAT = %00000000 ' clear sspstat
SSPIE = 1 'Enable MSSP interrupt enable bit
SSPIF = 0 'Clear MSSP interrupt flag
RxBufferIndex = 0
TxBufferIndex = 0
Temp var byte
RxIndex var byte
TxIndex var byte
Stat var byte
dataout[0] = 41 ' just some data to start with
dataout[1] = 122
dataout[2] = 103
dataout[3] = 4
' ' Interrupt definition
' ' ====================
' ' USB interrupt used to keep USB connection alive
INCLUDE "DT_INTS-18.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ' Include if using PBP interrupts
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler SSP_INT, _I2C_Int, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
endasm
@ INT_ENABLE SSP_INT ; Master Synchronous Serial Port Interrupt Flag bit. The transmission/reception is complete
'----------------------[ Skip over subs ]--------------------------------------
goto Programstart
I2C_Int:
If R_W = 0 then 'Write from master
if D_A = 0 then ' It's address from master
address = SSPBUF ' do nothing
rxinptr = 0 ' Clear counters
rxoutptr = 0
else
WCOL = 0
sspov = 0
if bf = 1 then 'There is data in buffer
if rxcnt < rxbufferlen then
rxbuffer[rxinptr] = SSPBUF
rxinptr = rxinptr + 1
if rxinptr = rxbufferlen then rxinptr = 0
endif
rxcnt = rxcnt + 1
EndIF
endif
else ' Read by master
if SSPSTAT = %00001101 then 'last byte was an address byte.
txinptr = 0 'read BF
txoutptr = 0
endif
dummyread = SSPBUF 'dummy read empty buffer
if bf = 0 then ' if buffer is empty
if txcnt > 0 then ' Is there a byte to transfer
SSPBUF = txbuffer[txoutptr] 'get the byte and send it
txoutptr = txoutptr + 1
if txoutptr = txbufferlen then txoutptr = 0
txcnt = txcnt - 1
else
'no byte to send
endif
endif
endif
sspif = 0
CKP = 1
@ INT_RETURN
'-------------------[ end of interrupt ]--------------------------------------
' ************************************************************
' * main program loop - *
' * *
' * .. *
' ************************************************************
ProgramStart:
if txcnt < txbufferlen then
TxBuffer[txinptr] = dataout[txinptr]
txinptr = txinptr + 1
if txinptr = txbufferlen then txinptr = 0
txcnt = txcnt + 1
endif
if rxcnt != 0 then
datain[rxoutptr] = rxbuffer[rxoutptr]
rxoutptr = rxoutptr + 1
if rxoutptr = rxbufferlen then
rxoutptr = 0
endif
rxcnt = rxcnt - 1
endif
Select case datain[0] 'Test data from master byte 0
case 0
low led1 'turn on LED1
case 1
high led1 'turn off led1
low led2 'turn on led2
case 2
low led1: low led2 'turn on both
case 3
high led1: high led2 'turn off both
case 4 'Change the data out to master
dataout[0] = 44
dataout[1] = 45
dataout[2] = 46
end select
'Eight leds for testing, 4 on Port b and four on Port a
portb = ~datain(0) <<4 'Lower nibble to RB4 to RB7
porta = ~(datain(0) & $F0)>>4 'upper nibble RA0 to RA3
goto ProgramStart
end
It seems the 18F chips are very sensitive to the correct timing. Also it is a good idea to read AN734B to understand the difference between I2C slave with 16F chips and 18F chips.
Hope this helps someone out
Dave
Bookmarks