PDA

View Full Version : I2C Slave with a PIC



ralfmayr
- 21st November 2008, 10:58
Hi there,
i tried, as mentined in an other thread before, that i try to implement
the i2c-slave code from Robert Soubie in a PIC18F2431, but there are still
problems in that code: Writing to the slave wokrs fine, but reading from
the slave results in errors and sometimes senseless values on the master side.
Did someone here use this code from Robert and could help on this problem?
Regards,
Ralf

mister_e
- 21st November 2008, 17:32
How about posting what you've done so far, this way we could check with your code?

ralfmayr
- 21st November 2008, 18:29
Hi mister_e,
good idea:

What i have done is using th original code from Robert Soubie posted on his website.
I made some more remarks and checked all gaainst the MCHIP AppNote AN734, i hope i made no mistake.

1. I initialize the i2c:

'Initialize I2C slave mode
SCLDIR = 1 ' SCL must be made an input before enabling interrupts
SDADIR = 1 ' SDA must be made an input before enabling interrupts
SSPCON = $36 ' configure for i2c 7Bit
gosub read_12c_address ' get address from B0 - B3
SSPSTAT = 0 ' setting some regs...
SSPIE = 1
SSPIF = 0
' PIE1 = 1 ' ?????????
PEIE = 1
GIE = 1
RxBufferIndex = 0
TxBufferIndex = 0

2. In the main program i include the int-handler:

On Interrupt goto int_hand
.... main program parts ....

'***************** INTERRUPT HANDLER ************
Disable
int_hand:
include "i2c_slave_int2.inc"
'INTCON.1 = 0
Resume
Enable
'*********************************************

3. The "i2c_slave_int.inc" you can see here (derived from Robert Soubie)

'*********************************************
'* Name : i2c_slave_int.inc *
'* Author : Ralf Mayr *
'* : Derived from Robert Soubie *
'* Notes : Original Code by Robert Soubie *
'*********************************************


goto i2c_interrupt_handler

'*********************************************
'write TXBuffer to I2C
'*********************************************
write_i2c:

while STAT_BF = 1
i = 0
wend ' loop while buffer is full
wcol = 0 ' clear collision flag
SSPBUF = TxBuffer(TxBufferIndex)
while wcol = 1
wcol = 0
SSPBUF = TxBuffer(TxBufferIndex)
wend
CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
if TxBufferIndex = 2 then ' all bytes have been transmitted
TxBufferIndex = 0
int_ready = 1 ' so reset index
endif
goto i2c_interrupt_handler_exit

'***********************************************
'Interrupt handling starts here
'***********************************************
i2c_interrupt_handler:

'mask Status Register first
i2c_Save_Status = SSPSTAT & i2c_slave_mask

'***********************************************
' Write operation, last byte was an address, buffer is full
' BF = 1
' UA = NA
' RW = 0 (we get data)
' S = 1 (Start bit was set)
' P = NA
' DA = 0 (last byte was address)
'***********************************************
State_1:
if i2c_Save_Status <> %00001001 then State_2
i2cTempVAr = SSPBUF ' empty buffer
if sspov then ' overflow occured?
sspov = 0
i2cTempVAr = SSPBUF ' empty buffer
endif
for RxBufferIndex = 0 to 2 ' empty receive buffer
RxBuffer(RxbufferIndex) = 0
next RxBufferIndex
RxBufferIndex = 0 ' reset buffer index
goto i2c_interrupt_handler_exit

'***********************************************
' Write operation, last byte was data, buffer is full
' BF = 1
' UA = NA
' RW = 0 (we get data)
' S = 1 (Start bit was set)
' P = NA
' DA = 1 (last byte was data)
'***********************************************
State_2:
if i2c_Save_Status <> %00101001 then State_3
RxBuffer(RxBufferIndex) = SSPBUF ' read received byte and empty buffer
if sspov then ' overflow occured?
sspov = 0
i2cTempVAr = SSPBUF ' empty buffer
endif
RxBufferIndex = RxBufferIndex + 1 ' point at next empty slot in buffer
goto i2c_interrupt_handler_exit

'***********************************************
' Read operation, last byte was an address, buffer is empty
' BF = unknown ?
' UA = NA
' RW = 1 (we send data)
' S = 1
' P = NA
' DA = 0 (last byte was address)
'***********************************************
State_3:
if i2c_Save_Status <> %00001100 then State_4
goto write_i2c ' write next byte

'***********************************************
' Read operation, last byte was data, buffer is empty
' BF = 0
' UA = NA
' RW = 1 (we send data)
' S = 1 (Start bit was set)
' P = NA
' DA = 1 (last byte send was data)
'***********************************************
State_4:
if i2c_Save_Status <> %00101100 then State_5
goto write_i2c ' write next byte

'***********************************************
' Unknown operation
' BF = 0
' UA = NA
' RW = 0
' S = 1 (Start bit was set)
' P = NA
' DA = 1 (last byte send was data)
'***********************************************
State_5:
if i2c_Save_Status <> %00101000 then State_i2CErr
CKP = 1
goto i2c_interrupt_handler_exit


'***********************************************
' We should never get here... BUT WE GET HERE *g*
'***********************************************
State_i2cErr:
i2c_error = 1

'***********************************************
' Did we receive one command byte and two parameter bytes?
' if so, RxBufferIndex equals 3
'***********************************************
i2c_interrupt_handler_exit:

if rxbufferindex = 3 then
CKP = 0 ' Disable clock
include "Proc_i2c.inc"
rxBufferIndex = 0 ' Reset Rx Buffer index for next time we get a command
TxBufferindex = 0 ' Reset Tx Buffer index for sending two result bytes
int_ready = 1
CKP = 1 ' Enable clock
endif

if sspov then ' overflow occured?
i2cTempVAr = SSPBUF ' empty buffer
sspov = 0
endif

SSPIF = 0 ' reset interrupt flag that took us here

4. The include file "Proc_I2C.inc resolves the vars and their values:

i2c_OutL = RxBuffer(0)
i2c_OutH = $ff
if RxBuffer(0) = "M" then
i2c_OutL = var1
i2c_OutH = "M"
else
if RxBuffer(0) = "I" then
i2c_OutL = var2
i2c_OutH = "I"
else
if RxBuffer(0) = "G" then
i2c_OutL = var3
i2c_OutH = "G"
else
i2c_OutH = $FF 'error unknown command
endif
endif
endif

TxBuffer(0) = i2c_outh
TxBuffer(1) = i2c_outL

5. The masster accesses the slave with I2CWrite and I2CRead, but sometimes the master gets errors and also the slave sets the i2c_eror bit (a variable from me in State_i2cErr).
- The master always sends three bytes (the "id" and the two databytes).
- The master always reads two bytes (two values form the "id "in the write operation before)
Writing to the slave seems to work fine, but reading from the slave results in errors and sometimes senseless values on the master side.

DanPBP
- 16th February 2009, 22:12
Hey Ralf,

Where you able to solve the problem? I'm still working on the slave code, but I'm not able to read from the slave. I'm getting frustrated with this.

Thanks!

Daniel.

ralfmayr
- 17th February 2009, 07:03
Hi Daniel,
no, i stopped working on it, it is not possible to get it work... :-(

I solved my problem using serin/serout.
But it would be great when we exchange our know-how, so we can solve this together!

-,Ralf

aratti
- 18th February 2009, 16:14
Here a working snipet for I2C slave communication.

I2C write command will be received into variable array RxBuffer and RxBuffer[0] is used for the case select routine.
I2C read command will return content of variable array TxBuffer. So before reading you have to update the array in someways. The easy way is to write a command that will update the array TxBuffer with the values you need than you read them.

Array lenght can be changed, changing the constants "RxBufferLEN" & "TxBufferLEN"

Slave address can be changed changing the constant "I2Caddress".

A 20 MHz quartz is recommended.

Add your code in the correct place and you should not have problem.



'************************************************* ***************
'* Name : I2C_Slave *
'* Author : *
'* Notice : *
'* : All Rights Reserved *
'* Date : 03/01/2008 *
'* Version : 1.0 *
'* Notes : I2C Slave with Pic16F876 *
'* : *
'************************************************* ***************

'-- set device & tris port config here (don't use SCL & SDA pins other than I2C) ----

DEFINE I2C_HOLD 1

'---------------- Alias pins -----------------------------------

SCLDIR var TRISC.3
SDADIR var TRISC.4

'--------------- 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 SSPCON.4 ' SSP (I2C) SCK Release Control
SSPEN VAR SSPCON.5 ' SSP (I2C) Enable
SSPOV VAR SSPCON.6 ' SSP (I2C) Receive Overflow Indicator
WCOL VAR SSPCON.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


'------------------- Rx Buffer defintion --------------------
RxBufferLEN con 3
RxBuffer var byte[Rxbufferlen]
RxBufferIndex var byte

'------------------ Tx Buffer defintion ----------------------
TxBufferLEN con 3
TxBuffer var byte[txbufferlen]
TxBufferIndex var byte


'------------------ Define constants ------------------------
I2Caddress CON $2 ' Make address = 2




' --------- Define here your variables and alias ------------------




' -------------- Initialize I2C slave mode ------------------------

SCLDIR = 1 ' SCL must be an input before enabling interrupts
SDADIR = 1
SSPADD = I2Caddress ' Set our address
SSPCON2.7 = 0 ' General call address disabled
SSPCON = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0

'----------------- Initialization Done! -----------------------------



GoTo main


'------------------------- I2C subroutine --------------------------------

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data from us
IF BF = 0 Then i2cexit ' Nothing in buffer so exit
IF D_A = 1 Then i2cwr ' Data for us (not address)
IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from the buffer
readcnt = 0 ' Mark as first read
GoTo i2cexit

i2cwr: ' I2C write data to us
datain = SSPBUF ' Put buffer data into array
Rxbuffer[Rxbufferindex]=datain
rxbufferindex =rxbufferindex+1

if rxbufferindex=RxBufferLEN then ' end of buffer transfer
WrData=1
rxbufferindex=0
endif

GoTo i2cexit

i2crd: ' I2C read data from us
IF D_A = 0 Then
TxBufferIndex = 0
EndIF

while STAT_BF : wend ' loop while buffer is full
wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]
while wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
wend
CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
if TxBufferIndex = TxBufferlen then ' all bytes have been tx
TxBufferIndex = 0 ' reset index
endif

i2cexit:
Return

' -------------------------end I2C subroutine --------------------------------

Main:
IF SSPIF = 1 Then
GoSub i2cslave
EndIF

SSPOV = 0
WCOL = 0

Select case RBuffer[0]
'--------------------- Place here your case select code
end select

WrData=0
GoTo Main


' ----------------- This space for your program code

End

Enjoy

Al.

DanPBP
- 18th February 2009, 18:43
Hello Al,

Thank you very much for answering my message. You can't imagine how much I appreciate this working snipet for I2C slave communication.

I will test it right away and let you know how everything went.

Thanks a lot one more time!

Daniel.

DanPBP
- 18th February 2009, 20:14
Hello Al,

It works perfectly!!! I'm so happy now!!! Thank you very very much!!

I'll post my code as soon as it's done, so everybody else can use it.

Daniel.

aratti
- 18th February 2009, 21:33
Glad to hear that!

Al.

DanPBP
- 19th February 2009, 05:46
A short video: http://www.youtube.com/watch?v=15Ben1KFOWQ

The master on the left, a 16F877 is telling the first 16F88 (on the top) to turn on/off the LED, and then reads 24 from it. After that, the master tells the second 16F88 (on the bottom) to turn on/off the LED, and then reads 12 from it.

It's in slow motion because I was testing everything, but I discovered that the I2C protocol is very fast!

Al, thanks a lot one more time!

Daniel.

rcbandwidth
- 19th February 2009, 12:43
Hi Daniel: I would like to try out your code,could you post a link if not too much trouble.

Thanks RC

DanPBP
- 19th February 2009, 16:30
Sure! Let me know if you need anything else

Master 16f877 - External 4Mhz Crystal:

'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
' Master
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------

DEFINE OSC 4

'OPTION_REG.7 = 0

ADCON1 = 7

'------------------------------------------------------------------------------

DEFINE I2C_SLOW 1

TRISB.5 = 1
TRISB.7 = 1

'------------------------------------------------------------------------------

scl VAR PORTB.5 ' i2c clock input
sda VAR PORTB.7 ' i2c data input

'------------------------------------------------------------------------------

I2Caddress VAR BYTE

valor VAR BYTE

'------------------------------------------------------------------------------

' Set LCD Data port
DEFINE LCD_DREG PORTD
' Set starting Data BIT (0 OR 4) IF 4-BIT bus
DEFINE LCD_DBIT 4
' Set LCD Register Select port
DEFINE LCD_RSREG PORTC
' Set LCD Register Select BIT
DEFINE LCD_RSBIT 6
' Set LCD Enable port
DEFINE LCD_EREG PORTC
' Set LCD Enable BIT
DEFINE LCD_EBIT 7
' Set LCD bus size (4 OR 8 bits)
DEFINE LCD_BITS 4
' Set number of lines ON LCD
DEFINE LCD_LINES 2
' Set command delay time in us
DEFINE LCD_COMMANDUS 2000
' Set Data delay time in us
DEFINE LCD_DATAUS 50

'------------------------------------------------------------------------------

Pause 500 ' Wait for LCD to startup
LCDOut $fe, 1
LCDOut $fe, "Daniel"
Pause 500 ' Wait for LCD to startup

valor = 0

'------------------------------------------------------------------------------

main:
I2Caddress = $3

LCDOut $fe, 1
LCDOut "Writting 1 - LED ON..."
Pause 500

I2CWrite SDA, SCL, I2Caddress, [6], bogus ' Write offset to slave
Pause 500

LCDOut $fe, 1
LCDOut "Writting 1 - LED OFF..."
Pause 500

I2CWrite SDA, SCL, I2Caddress, [8], bogus ' Write to slave
Pause 500

LCDOut $fe, 1
LCDOut "Reading 1..."
Pause 500

I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
LCDOut $fe, 1
LCDOut "Value: ", DEC valor
Pause 500

'--------

I2Caddress = $5

LCDOut $fe, 1
LCDOut "Writting 2 - LED ON..."
Pause 500

I2CWrite SDA, SCL, I2Caddress, [4], bogus ' Write to slave
Pause 500

LCDOut $fe, 1
LCDOut "Writting 2 - LED OFF..."
Pause 500

I2CWrite SDA, SCL, I2Caddress, [10], bogus ' Write to slave
Pause 500

LCDOut $fe, 1
LCDOut "Reading 2..."
Pause 500

I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
LCDOut $fe, 1
LCDOut "Value: ", DEC valor
Pause 500

'--------

GoTo main ' Do it forever

'-------------------------------------------------------------------------------

bogus:
LCDOut $fe,1, "Time out" ' I2C command timed out
Pause 500

GoTo main

'-------------------------------------------------------------------------------

End

'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------

DanPBP
- 19th February 2009, 16:31
1' Slave - 16F88 - Internal 8Mhz

'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' Slave
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------

@ device pic16f88, intrc_osc_noclkout ' system clock options
@ device pic16f88, wdt_on ' watchdog timer
@ device pic16f88, pwrt_on ' power-on timer
@ device pic16f88, mclr_off ' master clear options (internal)
@ device pic16f88, protect_off ' code protect off

'--- set device ---------------------------------------------------------------

DEFINE OSC 8
OSCCON = %01110001 '8mhz

'------------------------------------------------------------------------------

CMCON = 7
ADCON1 = 0 ' Disable A/D converter
ADCON0 = 0
ANSEL = 0 ' all analog pins to digital

'--- tris port config here (don't use SCL & SDA pins other than I2C) ----------

DEFINE I2C_HOLD 1

TRISB.1 = 1
TRISB.4 = 1

'--- Alias pins ---------------------------------------------------------------

SDADIR VAR TRISB.1
SCLDIR VAR TRISB.4

'--- Define here your variables and alias -------------------------------------

LEDT VAR PORTA.2
LEDL VAR PORTB.7
LEDM VAR PORTB.6
LEDR VAR PORTB.5
readcnt VAR BYTE
datain VAR BYTE
WrData VAR BYTE

'--- 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 SSPCON.4 ' SSP (I2C) SCK Release Control
SSPEN VAR SSPCON.5 ' SSP (I2C) Enable
SSPOV VAR SSPCON.6 ' SSP (I2C) Receive Overflow Indicator
WCOL VAR SSPCON.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

'--- Rx Buffer defintion ------------------------------------------------------

RxBufferLEN CON 1
RxBuffer VAR BYTE[Rxbufferlen]
RxBufferIndex VAR BYTE

'--- Tx Buffer defintion ------------------------------------------------------

TxBufferLEN CON 1
TxBuffer VAR BYTE[txbufferlen]
TxBufferIndex VAR BYTE

'--- Define constants ---------------------------------------------------------

I2Caddress CON $3 ' Make address = 3

'--- Initialize I2C slave mode ------------------------------------------------

SCLDIR = 1 ' SCL must be an input before enabling interrupts
SDADIR = 1
SSPADD = I2Caddress ' Set our address
SSPCON = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0

'--- Initialization Done! -----------------------------------------------------

GoTo main

'--- I2C subroutine ----------------------------------------------------------

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data from us
IF BF = 0 Then i2cexit ' Nothing in buffer so exit
IF D_A = 1 Then i2cwr ' Data for us (not address)
IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from the buffer
readcnt = 0 ' Mark as first read
GoTo i2cexit

i2cwr: ' I2C write data to us
datain = SSPBUF ' Put buffer data into array
Rxbuffer[Rxbufferindex]=datain
Rxbufferindex=rxbufferindex+1

IF rxbufferindex=RxBufferLEN Then ' end of buffer transfer
WrData=1
rxbufferindex=0
EndIF

GoTo i2cexit

i2crd: ' I2C read data from us
IF D_A = 0 Then
TxBufferIndex = 0
EndIF

While STAT_BF : Wend ' loop while buffer is full

wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]

While wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
Wend

CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
IF TxBufferIndex = TxBufferlen Then ' all bytes have been tx
TxBufferIndex = 0 ' reset index
EndIF

i2cexit:
Return

'--- End I2C subroutine ------------------------------------------------------

Main:
txbuffer = 12

IF SSPIF = 1 Then
GoSub i2cslave
EndIF

SSPOV = 0
WCOL = 0

Select Case RxBuffer[0]
Case 6
High LEDT
Case 8
Low LEDT
End Select

WrData=0
GoTo Main


'------------------------------------------------------------------------------

End

'------------------------------------------------------------------------------
'------------------------------------------------------------------------------

DanPBP
- 19th February 2009, 16:32
2' Slave - 16F88 - Internal 8Mhz

'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' Slave
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------

@ device pic16f88, intrc_osc_noclkout ' system clock options
@ device pic16f88, wdt_on ' watchdog timer
@ device pic16f88, pwrt_on ' power-on timer
@ device pic16f88, mclr_off ' master clear options (internal)
@ device pic16f88, protect_off ' code protect off

'--- set device ---------------------------------------------------------------

DEFINE OSC 8
OSCCON = %01110001 '8mhz

'------------------------------------------------------------------------------

CMCON = 7
ADCON1 = 0 ' Disable A/D converter
ADCON0 = 0
ANSEL = 0 ' all analog pins to digital

'--- tris port config here (don't use SCL & SDA pins other than I2C) ----------

DEFINE I2C_HOLD 1

TRISB.1 = 1
TRISB.4 = 1

'--- Alias pins ---------------------------------------------------------------

SDADIR VAR TRISB.1
SCLDIR VAR TRISB.4

'--- Define here your variables and alias -------------------------------------

LEDT VAR PORTA.2
LEDL VAR PORTB.7
LEDM VAR PORTB.6
LEDR VAR PORTB.5
readcnt VAR BYTE
datain VAR BYTE
WrData VAR BYTE

'--- 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 SSPCON.4 ' SSP (I2C) SCK Release Control
SSPEN VAR SSPCON.5 ' SSP (I2C) Enable
SSPOV VAR SSPCON.6 ' SSP (I2C) Receive Overflow Indicator
WCOL VAR SSPCON.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

'--- Rx Buffer defintion ------------------------------------------------------

RxBufferLEN CON 1
RxBuffer VAR BYTE[Rxbufferlen]
RxBufferIndex VAR BYTE

'--- Tx Buffer defintion ------------------------------------------------------

TxBufferLEN CON 1
TxBuffer VAR BYTE[txbufferlen]
TxBufferIndex VAR BYTE

'--- Define constants ---------------------------------------------------------

I2Caddress CON $5 ' Make address = 5

'--- Initialize I2C slave mode ------------------------------------------------

SCLDIR = 1 ' SCL must be an input before enabling interrupts
SDADIR = 1
SSPADD = I2Caddress ' Set our address
SSPCON = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0

'--- Initialization Done! -----------------------------------------------------

GoTo main

'--- I2C subroutine ----------------------------------------------------------

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data from us
IF BF = 0 Then i2cexit ' Nothing in buffer so exit
IF D_A = 1 Then i2cwr ' Data for us (not address)
IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from the buffer
readcnt = 0 ' Mark as first read
GoTo i2cexit

i2cwr: ' I2C write data to us
datain = SSPBUF ' Put buffer data into array
Rxbuffer[Rxbufferindex]=datain
Rxbufferindex=rxbufferindex+1

IF rxbufferindex=RxBufferLEN Then ' end of buffer transfer
WrData=1
rxbufferindex=0
EndIF

GoTo i2cexit

i2crd: ' I2C read data from us
IF D_A = 0 Then
TxBufferIndex = 0
EndIF

While STAT_BF : Wend ' loop while buffer is full

wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]

While wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
Wend

CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
IF TxBufferIndex = TxBufferlen Then ' all bytes have been tx
TxBufferIndex = 0 ' reset index
EndIF

i2cexit:
Return

'--- End I2C subroutine ------------------------------------------------------

Main:
txbuffer = 24

IF SSPIF = 1 Then
GoSub i2cslave
EndIF

SSPOV = 0
WCOL = 0

Select Case RxBuffer[0]
Case 4
High LEDT
Case 10
Low LEDT
End Select

WrData=0
GoTo Main


'------------------------------------------------------------------------------

End

'------------------------------------------------------------------------------
'------------------------------------------------------------------------------

rcbandwidth
- 19th February 2009, 18:02
Daniel: Thanks to you and others in this great forum for sharing....It is so much easier to understand when you can see code that is working and is well commented.

Thanks again to all the generous people on this forum!

RC

BigWumpus
- 23rd February 2009, 21:18
I think, this part could be written better:



wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]

While wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
Wend


this is much shorter:



Repeat
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
Until !wcol


and works, too.

DanPBP
- 26th February 2009, 05:25
Thanks for the update! This should be a hot topic now!

Btw, how many times I have seen this topic on the forum without any answers because: "my code that works is proprietary and I cannot share it" or worst: "RTFM!". If the only thing you have to say is that, keep it to yourself! :mad: Or, the other one I hate: "did you try the example from melabs site?" The one that everyones says it doesn't work? Yes!!!

Thanks a lot to Al, the I2C slave routine is working great and he was kind enough to share it with all of us. You're great Sr.!

Archangel
- 7th March 2009, 05:35
Thanks for the update! This should be a hot topic now!

Btw, how many times I have seen this topic on the forum without any answers because: "my code that works is proprietary and I cannot share it" Translation: My code does not work, but I won't admit it. At least 50% of the time :)<br> Anyway thanks Daniel and AL, Ralf , mister_e, Big Wumpus Something new to play with.

aratti
- 7th March 2009, 10:59
Translation: My code does not work, but I won't admit it. At least 50% of the time

Interesting assumption Joe.

Al.

Archangel
- 7th March 2009, 22:40
Interesting assumption Joe.

Al.I mean it in context, sure there some very good professional coders in here, and when they say it I am sure it is true, now for a majority here, who likely are hobbyist, maybe some of which wish to make the jump with their own stellar idea, 50 / 50 is likely a generous assumption. And so what? EGO has value only to the person who owns it.<br>EDIT: Ego has value to anyone who wants to exert control over someone who has it too.

ralfmayr
- 8th March 2009, 10:45
Hi there,
i will test the code from Aretti soon on a PIC18F2431. I want to process four ad-converters and several digital io's. The slave address of the PIC is automatically detected when pluging it on a bus-system (16 addresses).
When it is finished and working (hope so), i will post it here.
FYI: I2C Apps from MCHIP seems not to work, a friend of me in an other company also tried this code to implement, no chance.
All others syaing "i can't post my working code here" must be sayed, that these people are wrong here, a forum lives from shared ideas, code snippets.... they should keep out here and keep their code for them selves.

Regards,
Ralf

Charles Linquis
- 8th March 2009, 17:31
I will be interested in your port to the 18F series, and it sounds like you will be willing to post your code. I have an upcoming project that will need it. I tried to get Soubie's code working about a year ago with no luck.

ralfmayr
- 9th March 2009, 17:00
Charles,
same problem, i also never got his code working.
I come back next week, hope with a working code.
Regards,
Ralf

rswain
- 11th March 2009, 00:32
Guys,

I've been working this problem for nearly a year now. I've tried Soubie's code with no luck, and the code from this thread isn't working either. I'm using the 18F4620 as the master talking to an 18F2620, the little brother of the 4620 as the slave. I've RTFM to the point I can recite it, and the Errata sheets, and I've tried countless logical, and even illogical options to trying to get it working.

The best I can get so far is the master sends the address byte and the slave accepts it when it matches its own address and ignores addresses not matching its own. When the address matches, the slave sets the Buffer Full and the Interrupt Flags, but for some reason won't send an ACK back to the master (at least that's what this thread's code [and my own polls of the master's ACK flag] are saying). I've made sure to clear all flags as the datasheet clearly directs, and still no luck. If the slave would ACK, the master would send the next byte and so on.

I'm operating both chips on their internal crystal (8 MHz) and using the 4x PLL (= 32MHz). Before you go there, I'm already successfully talking to an I2C real-time clock chip with no problems and I'm saving data to an external I2C EEPROM regularly with no problems. The I2C network is working with the PICs at 32 MHz. I'm on the right pins; I'm using the pull external pull-up resistors...

I've had this exact hardware working in the past when using MikroElektronika Basic, but I'm much happier in PBP. This one problem is holding up the entire project. Trying to get two 18F's to ping-pong data back and forth is a killer.

If someone has even a bit more success than what I've had, please post it and maybe together we can chip away at this challenge.

DanPBP
- 11th March 2009, 00:49
and the code from this thread isn't working either.

Well, the code provided by Al (aratti) and the one I posted works great both ways!

I don't know what to tell you without looking at your code, but I modified the one provided by Al just a little and worked right away!

Can you post your code? Maybe we can see something that you're not seeing.

Charles Linquis
- 11th March 2009, 00:52
I'll be able to devote some time to a group project in about two weeks. I will also have another issue - I'm forced to work with 10-bit addresses.

rswain
- 11th March 2009, 01:35
Here's my slave code that's not sending back the ACK. I appologize for the "dead code" and commented out parts in advance. Maybe you'll see something I'm missing...

Robert
----------------


'Oscillator Initialization
DEFINE OSC 32 ' Osc. in MHz
OSCCON = %01110000
OSCTUNE.6 = 1
ADCON0.0 = 0 ' Turns off A-to-D module
ADCON1 = $0F ' Makes 100% port A digital
TRISA = %00000000
CMCON = 7 ' Comparators Off

DEFINE I2C_HOLD 1
'DEFINE I2C_SLOW 1

' Define used register flags
SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
SSPIE VAR PIE1.3 ' SSP Int Enable
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
'STRETCH VAR SSPCON2.0 ' SSP (I2C) Clock stretch

A var byte ' my counter variable for testing
'---------------- Alias pins -----------------------------------

TRISC.3 = 1 ' input
TRISC.4 = 1 ' input

'--------------- Define used register flags -------------------

'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

'------------------- Rx Buffer definition --------------------
RxBufferLEN con 3
RxBuffer var byte[Rxbufferlen]
RxBufferIndex var byte

'------------------ Tx Buffer definition ----------------------
TxBufferLEN con 3
TxBuffer var byte[txbufferlen]
TxBufferIndex var byte

'------------------ Define constants ------------------------
'I2Caddress CON $2 ' Make address = 2
I2Caddress CON $06 ' Make address = 2

' Define constants
U8 var byte
U17 var byte
InByte var byte
OutByte var byte
readcnt VAR BYTE
'U8 = $06
U17 = $08

'STRETCH = 1
WRData var byte
' Initialize I2C slave mode
'SSPCON2 = 0 ' General call address disabled

'SSPADD = My_I2C_address ' Set our address
SSPADD = I2Caddress ' Set our address
SSPCON2.7 = 0 ' General call address disabled
SSPCON1 = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0
bf = 0
SSPOV = 0

'Port C harware UART settings
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 20h
DEFINE HSER_BAUD 9600
DEFINE HSER_CLROERR 1
'----------------- Initialization Done! -----------------------------
rxbuffer[0] = 0
rxbuffer[1] = 0
rxbuffer[2] = 0

GoTo main


I2C_Slave: ' I2C slave subroutine
hserout ["SSPIF = ",#SSPIF," BF = ",#bf,13,10]
hserout ["R_W = ",#R_w," D_A = ",#D_A,13,10]
hserout ["SSPBUF = ",#SSPBUF,13,10]
SSPIF = 0 ' Clear interrupt flag
SSPov = 0 ' Clear interrupt flag
bf = 0 ' clear buffer full
IF R_W = 1 Then
hserout ["R_W = 1, Read data from U8",13,10]
goto I2C_Read ' Read data from us
endif
IF SSPBUF = I2Caddress Then
hserout ["It's to me! ",#SSPBUF," = ",#I2Caddress,13,10]
endif
IF D_A = 1 Then
hserout ["Data (not address)",13,10]
goto I2C_Write ' Data for us (not address)
endif
GoTo I2C_Exit

I2C_Write: ' I2C write data to us
InByte = SSPBUF ' Put buffer data into array
Rxbuffer[Rxbufferindex] = InByte
rxbufferindex = rxbufferindex + 1

hserout ["SSPBUF Write = ",#SSPBUF,13,10]
GoTo i2c_exit

I2C_Read: ' I2C read data from us
hserout ["SSPBUF Read = ",#SSPBUF,13,10]
IF D_A = 0 Then
TxBufferIndex = 0
EndIF

while BF : wend ' loop while buffer is full
wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]
while wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
wend
hserout ["SSPBUF Outbyte = ",#SSPBUF," should be outbyte 16 or 31",13,10]
CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
if TxBufferIndex = TxBufferlen then ' all bytes have been tx
TxBufferIndex = 0 ' reset index
endif

I2C_Exit:
Return

main:
pause 300
hserout [13,10,"Start of program U8",13,10]
hserout ["RxBuffer = "]
for a = 0 to 2
hserout [dec rxbuffer[a]," "]
next a
hserout [13,10]
loop:
' IF SSPIF = 1 Then
IF BF = 1 Then
hserout [13,10,"Went into I2C_Slave routine",13,10]
GoSub i2c_slave
hserout ["RxBuffer = "]
for a = 0 to 2
hserout [dec rxbuffer[a]," "]
next a
hserout [13,10]
hserout ["SSPCON1 = ",hex sspcon1," ",bin sspcon1,13,10]
hserout ["SSPCON2 = ",hex sspcon2," ",bin sspcon2,13,10]
hserout ["SSPSTAT = ",hex sspstat," ",bin sspstat,13,10]
hserout ["SSPBUF = ",#sspbuf,13,10]

if rxbufferindex = RxBufferLEN then ' end of buffer transfer
WrData = 1
rxbufferindex = 0
rxbuffer[0] = 0
rxbuffer[1] = 0
rxbuffer[2] = 0
hserout ["RxBuffer Cleared = "]
for a = 0 to 2
hserout [dec rxbuffer[a]," "]
next a
hserout [13,10]
endif
endif

SSPOV = 0
WCOL = 0
IF RxBuffer[0] = 15 Then
hserout ["Got a 15",13,10]
hserout [" Readcount is: ",#readcnt,13,10]
hserout ["Have a 16 to send if asked",13,10]
TxBuffer[0] = 16
EndIF

IF RxBuffer[0] = 30 Then
hserout ["Got a 30",13,10]
hserout [" Readcount is: ",#readcnt,13,10]
hserout ["Have a 31 to send if asked",13,10]
TxBuffer[0] = 31
EndIF
WrData=0
pause 50
GoTo loop

End

DanPBP
- 11th March 2009, 01:45
One thing I learn these days is that you cannot mess with the I2C timing.
Looking at your code, I guess those hserouts are killing the protocol.
I'll take a closer look at everything else tomorrow.

DanPBP
- 11th March 2009, 02:26
Try this code, I cleaned it up a lot. Slave address is 2.

1) From the master, try to read 12 from the slave

2) Try to send one byte (zero) to the slave and do something.

I don't have MPASM so, it could be some small errors, check them.


'Oscillator Initialization
DEFINE OSC 32 ' Osc. in MHz
OSCCON = %01110000
OSCTUNE.6 = 1
ADCON0.0 = 0 ' Turns off A-to-D module
ADCON1 = $0F ' Makes 100% port A digital
TRISA = %00000000
CMCON = 7 ' Comparators Off

DEFINE I2C_HOLD 1

' Define used register flags
SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
SSPIE VAR PIE1.3 ' SSP Int Enable
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
'STRETCH VAR SSPCON2.0 ' SSP (I2C) Clock stretch

'---------------- Alias pins -----------------------------------

TRISC.3 = 1 ' input
TRISC.4 = 1 ' input

'--------------- Define used register flags -------------------

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

'------------------- Rx Buffer definition --------------------
RxBufferLEN CON 1
RxBuffer VAR BYTE[Rxbufferlen]
RxBufferIndex VAR BYTE

'------------------ Tx Buffer definition ----------------------
TxBufferLEN CON 1
TxBuffer VAR BYTE[txbufferlen]
TxBufferIndex VAR BYTE

'------------------ Define constants ------------------------

I2Caddress CON $2 ' Make address = 2

' Define constants


datain VAR BYTE
readcnt VAR BYTE
WRData VAR BYTE


'SSPADD = My_I2C_address ' Set our address
SSPADD = I2Caddress ' Set our address
SSPCON2.7 = 0 ' General call address disabled
SSPCON1 = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0
bf = 0
SSPOV = 0

'Port C harware UART settings
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 20h
DEFINE HSER_BAUD 9600
DEFINE HSER_CLROERR 1

'----------------- Initialization Done! -----------------------------
GoTo main

'-------------------------------------------------------------------------------

I2C_Slave: ' I2C slave subroutine
sspif = 0 ' clear interrupt flag
IF r_w = 1 Then I2C_Read ' read data from us
IF bf = 0 Then I2C_Exit ' nothing in buffer so exit
IF d_a = 1 Then I2C_Write ' data for us (not address)
IF sspbuf != i2caddress Then I2C_Exit ' clear the address from the buffer
readcnt = 0 ' mark as first read

GoTo I2C_Exit

'-------------------------------------------------------------------------------

I2C_Write: ' I2C write data to us
datain = sspbuf ' put buffer data into array
rxbuffer[rxbufferindex] = datain
rxbufferindex=rxbufferindex + 1

IF rxbufferindex = rxbufferlen Then ' end of buffer transfer
wrdata = 1
rxbufferindex = 0
EndIF

GoTo i2c_exit

'-------------------------------------------------------------------------------
I2C_Read: ' I2C read data from us

IF d_a = 0 Then
txbufferindex = 0
EndIF

While stat_bf : Wend ' koop while buffer is full

wcol = 0 ' clear collision flag
sspbuf = txbuffer[txbufferindex]

While wcol
wcol = 0
sspbuf = txbuffer[txbufferindex]
Wend

ckp = 1 ' release clock, allowing read by master
txbufferindex = txbufferindex + 1 ' increment index

IF txbufferindex = txbufferlen Then ' all bytes have been tx
txbufferindex = 0 ' reset index
EndIF

GoTo i2c_exit


'-------------------------------------------------------------------------------

I2C_Exit:
sspov = 0
wcol = 0
wrdata = 0
Return

'-------------------------------------------------------------------------------

'-------------------------------------------------------------------------------
main:
txbuffer[0] = 12 ' test value

rxbuffer[0] = 0

IF sspif = 1 Then
GoSub i2c_slave
EndIF

Select Case rxbuffer[0]
Case 0
' do something here, turn on LED for example.
End Select

GoTo main

End

BigWumpus
- 11th March 2009, 18:10
You have to set the CKP-bit even after a WRITE-operation.
Try to disable the HSEROUT-lines.
Maybe this command will alter the TRISC-register, which will kill the I2C-engine.
Sometime, you should clear the OV-bit...

DaveC3
- 12th March 2009, 04:08
I have been trying to get a 18F2620 to run as a slave. i ran across this in the ERRATA for this device.


43. Module: MSSP (I2C Slave)
The MSSP module operating in I2C, 7-Bit Slave
mode (SSPM3:SSPM0 = 0110) may not send a
NACK bit (/ACK) in response to receiving the slave
address loaded in SSPADD<7:1>. Addresses are
in one of these ranges:
• 0x00 to 0x07
• 0x78 to 0x7F
These addresses were reserved by Philips® Semiconductors
in “The I2C Specification”, Version 2.1,
released January 2000. Section 10.1 “Definition of
bits in the first byte” defines the purposes of these
addresses.
This specification can be found at:
http://www.semiconductors.philips.com/i2c
Work around
This version of the silicon does not respond to
slave addresses in the ranges previously listed.
Use either of these work arounds:
• Change the 7-bit slave address in SSPADD to
an address in the range of 0x08 to 0x77.
• Use Revision B silicon. This version of silicon
removes this issue’s addressing restrictions.


I have gotten sporadic results for the master reading the slave in the following code.

Darrell's Instant Interrupt



' 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

''----------------- Initialization Done! -----------------------------

@ INT_ENABLE SSP_INT ; Master Synchronous Serial Port Interrupt Flag bit. The transmission/reception is complete
'txbuffer = 0


The Interrupt handler



I2C_Int:
If R_W = 0 then
if D_A = 0 then
address = SSPBUF
else

Still working on master to slave write
' if bf = 1 then
' if rxcnt <= rxbufferlen then
' datain[rxcnt] = SSPBUF
' rxcnt = rxcnt + 1
' if rxcnt = rxbufferlen then rxcnt = 0
' endif


EndIF
endif
else
dataout = SSPBUF 'dummy read
if txcnt > 0 then
SSPBUF = txbuffer[txoutptr]
txoutptr = txoutptr + 1
if txoutptr = txbufferlen then txoutptr = 0
txcnt = txcnt - 1
else
'no byte to send
endif
endif
CKP = 1
@ INT_RETURN


and the main loop. I want to send three bytes to the master. In this case "three sevens".



Main:
SSPOV = 0
WCOL = 0

if txcnt < txbufferlen - 1 then

TxBuffer[txinptr] = 7
txinptr = txinptr + 1
if txinptr = txbufferlen then txinptr = 0

txcnt = txcnt + 1

endif

WrData=0



goto main
end


I did the same slave program in "C" and it works well.

The Master is reading and writing to a RTC module and a bank of 4 24lc256 EEProms and a I2C Port expander with out any problems.

It is getting close

Take care

Dave

rswain
- 12th March 2009, 04:20
Although the 16F guys have been having good luck with low addresses, it makes sense that the errata sheet would point out the old Phillips rule about low addresses. I haven't tried an address above $08 before... gotta go give that a shot too. I'll report back with any results, good or bad.

Rswain

rswain
- 14th March 2009, 02:47
Today I tried changing the Master and Slave addresses to higher numbers. I tried $26, $A6, and $02 just for good measure. They all responded with the same results; the slave sees the address, matches to its own, sets the interrupt flag, sets the Buffer Full flag, loads the address byte into the SSPBUF and I can read it from there. Although I reported before that the Slave wasn't sending an ACK, I think I was wrong. When I poll the ACKSTAT in the Master, it comes back with a "0" which I thought was NO ACK, but in fact a 0 is reporting a good ACK.

I've also switched to 18F4685 on both Master and Slave just eliminate the possiblilty of bad chips. Same results.

I took Daniel's suggestion to try to read from the Slave instead of sending to it. The slave responds by loading the first digit in its SSPBUF but I'm not seeing it show up at the Master.

I notice that the Slave's D_A (SSPSTAT.5 ' SSP Data not Address) is always showing "1" for DATA and never have I seen a "0" for ADDRESS. Is that OK? How would that bit get changed?

Rswain

DanPBP
- 14th March 2009, 03:45
I'll try to buy a 18F2620 this week and run a few test :) Hope I can get it here, I live in Argentina, it's not always easy to find those "big brothers" ...

EDIT: I found a 18F452 laying around on the desk, will this serve the purpose???

rswain
- 14th March 2009, 04:25
Daniel - I'd be happy if any 18F would work. I'm starting to think something's wrong with PBP and 18F.

Thanks for your continued attempts to beat this problem into submission!

I'm going to switch to 16F877A (the old standby to see if I can get them to talk - once I get that, then we'll come back here and try again!

Robert

DanPBP
- 14th March 2009, 07:25
I played all night with a 16F88 as a master and a 16F877 as a slave (the other way around I was playing these days)...

I can send commands to the slave (16F877) and this is working fine, but something is wrong because I cannot read from the slave.

I mean, I can read from the slave, but I'm not getting the value I wanted. For example, I'm supposed to read 12, but I'm reading 1.

The code is the same I'm using for the 16F88 as a slave, so, that code is working fine. This is really strange.

PS: today is my birthday, and it's almots 7am and I need some sleep... :p I'll continue later in the afternoon...

BigWumpus
- 14th March 2009, 13:17
I has gone through this hell too.

I bought a cheap LA (Po.Scope) in order to debug my code. I was looking for errors in PBP too, but at least the error was im my code (and maybe in PBP)...

Please, post your code again.
I use the 16F872 and 16F876 and 18F252 as I2C-slaves. They work all !!!

Look at this page: http://www.astrosurf.com/soubie/pic_as_an_i2c_slave.htm

DanPBP
- 14th March 2009, 16:43
I saw that page and talked to Robert, but, to be honest, I don't understand how to use his code... :confused:

BigWumpus
- 14th March 2009, 20:54
Again ... post your code !

BigWumpus
- 14th March 2009, 20:59
I saw that page and talked to Robert, but, to be honest, I don't understand how to use his code... :confused:



What ?

It's PBP !

DaveC3
- 15th March 2009, 02:49
Here is the code that is working for me. I am sending three bytes to the slave. In this example 10, 20 and 30. The slave adds 1 and sends back 11, 21 and 31.

Here is a snipit of the master portion.


I2Caddress = $0A
I2CWrite i2cdat,i2cclk,I2Caddress,[10,20,30],NoGo
pauseus 5
i2cread i2cdat,i2cclk,i2caddress,[tp_x, tp_y, tp_dp],NoGo2
pauseus 5

USBBuffer[0] = tp_x
USBBuffer[1] = tp_y
USBBuffer[2] = tp_dp
USBBuffer[3] = 0
gosub dousbout ' I am using USB as a debugger
pause 100
goto mainmenu
NoGo:
USBBuffer[0] = 1
USBBuffer[1] = 2
USBBuffer[2] = 3
USBBuffer[3] = 0
gosub dousbout
goto mainmenu

NoGo2:
USBBuffer[0] = 4
USBBuffer[1] = 5
USBBuffer[2] = 6
USBBuffer[3] = 0
gosub dousbout
goto mainmenu



Here is the full slave. Pic18F2620 @ 20Mhz



asm
__CONFIG _CONFIG1H, _OSC_HS_1H
__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 ADC_BITS 10
'DEFINE ADC_CLOCK 3
'DEFINE ADC_SAMPLEUS 50

Define I2C_SLOW 1
DEFINE I2C_HOLD 1

'OSCCON = %01110001 '8mhz
'OSCTUNE.6 = 1 ' PLL selected 32mhz clock

' PicBasic Pro I2C slave program
' Alias pins
scl VAR PORTC.3 ' I2C clock input
sda VAR PORTC.4 ' I2C data input

'ADCON2.7=1
ADCON1 = $F '$B 'an1-an3 analog remainder digital
CMCON = 7 'PortA Digital
'
'---------------- Alias pins -----------------------------------

'SCLDIR var TRISC.3
'SDADIR var TRISC.4

'--------------- 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 3
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 constants ------------------------
rxinptr var byte
rxoutptr var byte
txinptr var byte
txoutptr var byte
rxinptr = 0
rxoutptr = 0
txinptr = 0
txoutptr = 0

I2Caddress CON $0A ' Make address = 2

' Allocate RAM
result VAR BYTE ' ADC result
DataIn VAR BYTE[8] ' Data in
DataOut VAR BYTE[8] ' Data out array
readcnt VAR BYTE ' I2C read count

readcnt = 0 ' Zero counter


'************************************************* ***************
' Variables used in the program.
'
'************************************************* ***************

transdata var byte
transbit7 var transdata.7
Tdata var word
txdata var word
tydata var word
txcomp var word
tycomp var word
tx var word
ty var word
tvalue var word
txcomp=0
tycomp=0
tx=0
ty=0
tvalue=0
PD var bit 'pen down flag
address var byte

' -------------- Initialize I2C slave mode ------------------------

'SCLDIR = 1 ' SCL must be an input before enabling interrupts
'SDADIR = 1
SSPADD = I2Caddress ' Set our address
SSPCON1 = $36 ' Set to I2C slave with 7-bit address
SSPCON2.0 = 1
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0


'LED1 var PORTC.1

' 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

''----------------- Initialization Done! -----------------------------

@ INT_ENABLE SSP_INT ; Master Synchronous Serial Port Interrupt Flag bit. The transmission/reception is complete
txbuffer = 0
portc.1 = 1'high led1
goto Main
'************************************************* ****************************


'------------------------- I2C subroutine --------------------------------
I2C_Int:

If R_W = 0 then 'Write from master
if D_A = 0 then ' It's address from master
address = SSPBUF ' do nothing
rxcnt = 1
rxinptr = 0
rxoutptr = 0
txinptr = 0
txoutptr = 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

dataout = SSPBUF 'dummy read
if bf = 0 then ' if buffer is empty
if txcnt > 0 then ' Is there a byte to transfer
SSPBUF = txbuffer[txoutptr] 'get the byte abd 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 I2C subroutine --------------------------------

'--------------------[ main program loop]---------------------------------------
Main:
'SSPOV = 0
'WCOL = 0
'INTCON.7 = 0 'trrn off interrupt
if txcnt < txbufferlen then

TxBuffer[txinptr] = rxbuffer[txinptr] | %00000001 ' add "1" to the data in
txinptr = txinptr + 1
if txinptr = txbufferlen then txinptr = 0

txcnt = txcnt + 1
'INTCON.7 = 1 'turn on interrupt
endif
if rxcnt != 0 then
datain = rxbuffer[rxoutptr]
rxoutptr = rxoutptr + 1
if rxoutptr = rxbufferlen then
rxoutptr = 0
endif
rxcnt = rxcnt - 1


endif


WrData=0



goto main
end


Disregard all the extra stuff, I am using this slave a Touch Screen controller.

Thanks

Dave

rswain
- 15th March 2009, 04:18
Group,

I'm back using the 18F4685, one in Master and one in Slave. I'm now able to read from the Slave, 3 digits (and their even the right ones!). I set up a fast loop to read the digits on the Master side and do nothing but send the data in a fast loop on the Slave side. (Will post both sides soon).

The problem is still the same on sending data from the Master to the Slave. The address gets there, matches, sets the SSPIF and BF, but also I get the SSPOV (overflow) bit set. Even on the first attempt to read the buffer, the OV is already set. There must be some "slow it down" handshaking not happening just right. Has anyone had luck sending multiple bytes to the Slave?

rswain

rswain
- 15th March 2009, 05:52
The attachments are the latest code (starting to work on the 18F4685). Still not able to send Master to Slave - Slave gets OVERFLOW flag.
Rswain

BigWumpus
- 15th March 2009, 13:20
You don't empty the SSPBUF after receiving the adress and the first data-byte gives you an buffer-overrun.....

rswain
- 16th March 2009, 15:57
BigWumpus,

You're right. I wasn't getting the bytes out of the Buffer fast enough. I tried a few things to empty the buffer faster and that helped. Now I don't have an Overflow problem, but I've managed to screw up the result that goes back to the Master when it asks for data from the Slave. I've got to go back double check the emptying and filling of the buffer.

I'm going to go back a step and make sure I've got the Master pulling data from the Slave perfectly. I'll take it up to 20 bytes or so to make sure. Once that's done, then I'll work again on sending data from the Master to the Slave. When I quit yesterday, I had it receiving two bytes (three if you count the address byte).

We're getting very close. I'll post working code as I accomplish these tasks.

Rswain

BigWumpus
- 16th March 2009, 16:52
-------------------------deleted-------------------------

BigWumpus
- 16th March 2009, 17:05
Try this:

I2C_Slave: ' I2C slave subroutine
IF R_W Then

I2C_Read: ' I2C read data from us ' THIS SECTION IS WORKING!
IF !d_a Then txbufferindex = 0
Repeat
Repeat
Until !bf
wcol = 0:sspbuf = txbuffer[txbufferindex]
Until !wcol
txbufferindex = txbufferindex + 1 ' increment index
IF txbufferindex = txbufferlen Then txbufferindex = 0 ' all bytes have been tx reset index

Else

IF !D_A Then
InByte=SSPBUF 'here is your error ! You don't empty the buffer!!!!!!!!!!!!!!!!!!!!!!
else

I2C_Write: ' I2C write data to us ' THIS SECTION NOT WORKING
If BF Then
InByte = SSPBUF ' Put buffer data into array
if rxbufferindex<rxbufferlen then Rxbuffer[Rxbufferindex] = InByte:rxbufferindex = rxbufferindex + 1
ENDIF
SSPov = 0 ' Clear overflow flag

endif
endif

ckp = 1 ' release clock, allowing read by master (makes no difference (yet))
SSPIF=0



The CODE-code is messy

DaveC3
- 20th March 2009, 16:13
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



'* 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

rswain
- 23rd March 2009, 19:49
Guys,

This weekend I put together software nuggets from both DaveC3 and BigWumpus. In doing so, I could see the influence of most of you that have been working on this thread over the last few weeks. I went through and trimmed out the dead code, and moved a few things around to work for my uses. I'll post both the master and slave(s) in the next day or two so others can learn from our efforts! Thanks to everyone for taking on this tough problem.

Thanks,
rswain

Pedro Pinto
- 10th April 2009, 18:02
Hello Danielhr77

I look in this moment for a slave I2C solution for my project

If i understand right if i need to receive or tramsmit more than one byte, it need to increase this value, like:
'--- Rx Buffer defintion ----------------------------------------------------
RxBufferLEN CON 4 'for receive up to 4 bytes
RxBuffer VAR BYTE[Rxbufferlen]
RxBufferIndex VAR BYTE
'
'--- Tx Buffer defintion ---------------------------------------------------
TxBufferLEN CON 4 'for send up to 4 bytes
TxBuffer VAR BYTE[txbufferlen]
TxBufferIndex VAR BYTE


'Master send 4 bytes
I2CWrite SDA, SCL, I2Caddress, [4,10,56,21], bogus ' Write offset to slave
Pause 500


On the Slave side, how can i read and show the received 4 bytes?
Is this correct? Sorry for asking but i have not very experience.
I want sent back the received 4 bytes

Main:
txbuffer = 24 ???????
IF SSPIF = 1 Then
GoSub i2cslave
EndIF
SSPOV = 0
WCOL = 0
'
Select Case RxBuffer[0]
Case 4
High LEDT
Case 10
Low LEDT
Case 56
do anything
Case 21
do anything
End Select
'
WrData=0
GoTo Main
End

Thank You
Best regards
Pedro

aratti
- 10th April 2009, 18:50
Pedro , to read back the bytes sent just tranfer the content from RxBuffer int TxBuffer in the slave.


Select Case RxBuffer[0]
TxBuffer[0]=RxBuffer[0]
TxBuffer[1]=RxBuffer[1]
TxBuffer[2]=RxBuffer[2]
TxBuffer[3]=RxBuffer[3]
Case 4
High LEDT
Case 10
Low LEDT
Case 56
do anything
Case 21
do anything
End Select

From master just use this code:


I2CRead SDA,SCL,I2Caddress,[str YourBuffer\4], bogus

You will find the four bytes into the array YourBuffer

Al.

Pedro Pinto
- 10th April 2009, 19:44
Hello Al

Thank you

Best regards
Pedro

Pedro Pinto
- 12th April 2009, 21:01
Hi Al

If the master send 4 bytes to the slave and the slave send the received bytes back to the master, must in that moment the master not change to slave mode to receive this 4 bytes?
If yes, i can't see that in yours and danielhr77 code

Regards
Pedro

aratti
- 12th April 2009, 22:52
Pedro, I am not sure to understand your question correctly, but I try to answer

Master remain master. Slave will not return your 4 bytes without a proper request. When you want the 4 bytes back to the master then you make the request with:

I2CRead SDA,SCL,I2Caddress,[str YourBuffer\4], bogus

and you will find the four bytes into the array YourBuffer that you have properly declared in your MASTER.

Al.

Pedro Pinto
- 13th April 2009, 10:35
Good morning Al

Thank You, now i understand it

Best regards
Pedro

DanPBP
- 21st April 2009, 16:34
I've been wondering... And it's just out of curiosity...

We are sending and receiving bytes, right?

Is it possible to send and receive words?

Thanks!

Daniel.

rswain
- 23rd April 2009, 18:39
I've been wondering the same thing - I need to move words and was just willing to break them up into two bytes, but maybe it's worth tracking it down to get a final answer.

DanPBP
- 26th October 2009, 01:05
Guys,

This weekend I put together software nuggets from both DaveC3 and BigWumpus. In doing so, I could see the influence of most of you that have been working on this thread over the last few weeks. I went through and trimmed out the dead code, and moved a few things around to work for my uses. I'll post both the master and slave(s) in the next day or two so others can learn from our efforts! Thanks to everyone for taking on this tough problem.


Hey rswain!

Any news about this?

Thanks!

Daniel.

ralfmayr
- 1st February 2010, 14:58
Hi all,
wanted to come back earlier with my experiences, but had too much work.
But :
I use Al's i2cslave in an 18F6722 I in an interrupthandler, in Al's code i made a little correction:

while STAT_BF = 1
i = SSP1BUF ' Dummy Read inserted => clears BF Flag
wend ' loop while buffer is full

This seems to be neccessary for this PIC's, the original code from Al results in
staying in this loop for ever.

I read back eight variables and write two variables with no problems.
The master and the slave have both the " DEFINE I2C_HOLD 1" at start up.

Regards,
Ralf

ralfmayr
- 16th February 2010, 16:34
Hi again,
did somebody test Al's code with a 18F14K22?
Regards,
Ralf

ralfmayr
- 25th March 2010, 11:29
There is an other problem with the i2cslave:

1. When the i2caddress is specifiead as constant (i2caddress CON $0F) it works perfect.

2. When bulding the address via a variable, it doesnt work:
...
i2caddress VAR byte
...
i2caddress = $0F

I need this because i build it from the status of four bits of Port A

Any idea?

Regards,
Ralf

aratti
- 25th March 2010, 13:14
I need this because i build it from the status of four bits of Port A

Any idea?


Ralf, you can try this:






Select Case PortA

Case 1
i2caddress CON $01
Case 2
i2caddress CON $02
case 3
i2caddress CON $03
.
.
.
.
Case x
i2caddress CON $x
End Select


It should work.

Edited:

No! it doesn't work sorry.

Al.

Dave
- 26th March 2010, 10:42
ralfmayr, Here is what I have been using for years. It uses the base address for the first of 3 PCF8574's. I then calculate the address depending on the one I need to gather or send data to.

UNIT0 CON %01000000 'U5's CONTROL BYTE (PCF8574) I/O EXPANDER

'**************** I2C I/O ROUTINE (PCF8574A) I/O EXPANDER ************
READ_WRITE VAR BIT 'READ DATA or WRITE DATA FLAG
EXPAND_UNIT VAR BYTE 'CONTROL BYTE FOR I2C
DEVICE VAR BYTE 'SELECTED I/O EXPANDER DEVICE
OUTPUTS VAR BYTE[3] 'RELAY OUTPUT BYTES
SWITCHES VAR BYTE[3] 'SWITCH INPUTS BYTES

'************************************************* *******************
R_WI2CS:'READ DIGITAL INPUTS FROM or WRITE DIGITAL OUTPUTS TO PCF8574'S
'************************************************* *******************
EXPAND_UNIT = UNIT0 'COPY BASE ADDRESS OF PCF8574'S
EXPAND_UNIT = (EXPAND_UNIT | (DEVICE << 1)) 'SELECT WHICH DEVICE
IF READ_WRITE = 0 THEN 'READ INPUT STATUS FROM PCF8574'S
I2CREAD SDA,SCL,EXPAND_UNIT,[SWITCHES(DEVICE)]
ELSE 'SET OUTPUT STATES TO PCF8574'S
I2CWRITE SDA,SCL,EXPAND_UNIT,[OUTPUTS(DEVICE)]
ENDIF
RETURN


Then to use it first set the device index.
Next set the read_write flag.
Then finally call the routine.....

Dave Purola,
N8NTA

aratti
- 26th March 2010, 18:19
Ralf, you can try the case select using the proper addresses (not my example). You must correct your code as per the line in black bold and comment out the line in red bold. The code compile so it should work. Longing to hear from you on the final test.

Al.




' Initialize I2C slave mode

Select case PortA

Case 1
SSPADD = 2
case 2
SSPADD = 3
case 3
SSPADD = 4

end select

SCLDIR = 1 ' SCL must be made an input before enabling interrupts
SDADIR = 1
'SSPADD = I2Caddress ' Set our address
SSPCON2.7 = 0 ' General call address disabled
SSPCON = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0

'Initialization Done!


GoTo main

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data from us
IF BF = 0 Then i2cexit ' Nothing in buffer so exit
IF D_A = 1 Then i2cwr ' Data for us (not address)
IF SSPBUF != SSPADD Then i2cexit ' Clear the address from the buffer
readcnt = 0 ' Mark as first read
GoTo i2cexit

ralfmayr
- 30th March 2010, 13:40
Hi Al,
my mistake, it works now.
I made a mistake in calculating the address from four bits of a port :-(

This works:

main_loop:
...
i2caddress = (addr0 * 16) + (addr1 * 8) + (addr2 * 4) + (addr3 * 2)
SSP1ADD = I2Caddress
...
goto main_loop

...in the moment i try to implement the slave routine into a 18LF14K22...
It does not work in the moment, it is the same like on a18F6722, there it
works great.

Regards,
Ralf

aratti
- 30th March 2010, 15:40
i2caddress = (addr0 * 16) + (addr1 * 8) + (addr2 * 4) + (addr3 * 2)
SSP1ADD = I2Caddress

Very good Ralf !

Al.

ralfmayr
- 30th March 2010, 16:52
Hi Al,
success!
The slave routine runs in a 18LF14K22.
I read back via i2c 20 values (a/d values and digital i/o) and i can set
several parameters with two bytes, 1st is array index, 2nd is value.
The handler runs via interrupt, in the moment i am cleaning a little bit,
but i am very happy with it.
Regards,
Ralf

ralfmayr
- 31st March 2010, 15:14
Some Remarks:

1. It is necessary to set the "DEFINE I2C_HOLD 1" in the master to get a
better behaviour of the timing. I checkrd it with the logic analyzer and it is
exactly like in the original "Phillips-Spec"

2. In the section
...
while STAT_BF = 1
i = SSPBUF ' Dummy Read inserted => clears BF Flag
wend
...
it is necessary to inlude the dummy read to get it work on
a 18F6722 and a 18LF14K22 (refer to MCHIP App Note 734, page 17)

3. Address range ist tested from 00h to FFh, all work well.

4. New address setting works (in the main loop) by only setting SSPADD
register with new value without reseting the complete handler.

Regards,
Ralf

aratti
- 31st March 2010, 15:44
ralfmayr wrote on 17th Feb. 2009


Hi Daniel,
no, i stopped working on it, it is not possible to get it work... :-(

I solved my problem using serin/serout.
But it would be great when we exchange our know-how, so we can solve this together!

-,Ralf


I quoted your last year post, just to congratulate with you for the remarkable knowledge you have gained in a year. This will teach new comer that persevering on a subject always pay.

Thank you for those additional I2C remarks that could be useful.

Al.

ralfmayr
- 31st March 2010, 17:59
Yep,
last year i made my first experiments with i2cslave,
but had no time to work on it for 8 months now as
i was very busy with other things.
Hope it will help others to come faster to their target.
-,Ralf

ralfmayr
- 3rd April 2010, 17:00
Hello Al,
another question:
What are the three definitions:
1. DEFINE I2C_SLOW 1
2. DEFINE I2C_HOLD 1
3. DEFINE I2C_SCLOUT 1
do?
1. => Used with LO > 8MHz and 100kHz devices
2. => Time Delay during acknowledge?
3. => Open Collector / Bipolar Clock on SCL? Dont Understand this!

So: I use PCF8575C and MAX3611 (both 400kHz devices) but both only work
correct when DEFINE I2C_HOLD 1 and DEFINE I2C_SCLOUT 1 is done.

Can you help?

Regards,
Ralf

Archangel
- 3rd April 2010, 19:36
Hello Al,
another question:
What are the three definitions:
1. DEFINE I2C_SLOW 1
2. DEFINE I2C_HOLD 1
3. DEFINE I2C_SCLOUT 1
do?
1. => Used with LO > 8MHz and 100kHz devices
2. => Time Delay during acknowledge?
3. => Open Collector / Bipolar Clock on SCL? Dont Understand this!

So: I use PCF8575C and MAX3611 (both 400kHz devices) but both only work
correct when DEFINE I2C_HOLD 1 and DEFINE I2C_SCLOUT 1 is done.

Can you help?

Regards,
Ralf
Hi Ralf,
I can answer part of #3:
Open collector means the PIC has 2 states LOW where it takes the pin to ground and an open state where the pin is high impedance, so to get a high state it must have a pull up resistor to supply the power. I do not know what bipolar clock means, have not read up on this yet.

aratti
- 4th April 2010, 00:27
1. => Used with LO > 8MHz and 100kHz devices

Yes.


2. => Time Delay during acknowledge?

Not Exactly, data transfer can be paused by the receiving device by holding the clock line low, so the receiving device can control the data flow. To enable this function you need the DEFINE I2C_HOLD 1 declared.


3. => Open Collector / Bipolar Clock on SCL? Dont Understand this!

Joe already answer to this point with a clear explanation. Let me add that bipolar is a device capable to pullup the lines by itself without the need of external pullup resistors


So: I use PCF8575C and MAX3611 (both 400kHz devices) but both only work
correct when DEFINE I2C_HOLD 1 and DEFINE I2C_SCLOUT 1 is done.


It is usefull to use DEFINE I2C_HOLD 1 as default since it will keep synchronized different devices connected on the I2C bus all with different internal clock.

I don't know much about PCF8575C and MAX3611 but very likely they are bipolar devices.

Al.

ralfmayr
- 4th April 2010, 11:54
Thank you both for the answers.
Now i understand.
So it seems always to be best to work with declaration 2 & 3 with
400 khz devices. This is what i found out yesterday.
Have nice eastern!
Regards,
Ralf

Kamikaze47
- 15th August 2010, 22:55
Hi all. I need to synchronise an array from one PIC to another (one way only), so I have been working to make a simplified version of the code seen in this thread with just the bare essentials to send the array from slave to master.

It mostly works, but there is one little bug. I am sending the array over and over again - the test array is 6 bytes and I have assigned test values of 1 2 3 4 5 and 6.

On the 1st i2cread the master receives 123456. On the 2nd i2cread it gets 234561, and the third 345612. It seems that every time I do an i2cread, the following byte goes missing. The first time I get 123456, the next 1 goes into oblivion and therefore the next packet is 234561.

I know I can get around this by sending a dummy byte after the array, knowing that it will be lost, but I'd like to know why this is happening if anyone has any ideas?

Master PIC (18F2620, 40Mhz HSPLL):

DEFINE OSC 40

DEFINE I2C_HOLD 1

DEFINE LCD_DREG PORTB
DEFINE LCD_DBIT 0
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 4
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 5
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
DEFINE LCD_COMMANDUS 2000
DEFINE LCD_DATAUS 50
PAUSE 100
LCDOUT $FE,1,$FE,2

ADCON1=%00001111
CMCON=%00000111

scl VAR PORTC.3
sda VAR PORTC.4

i2c_address VAR BYTE

i2c_data VAR BYTE[6]

i2c_address=$0A


main:
I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]
PAUSE 1000
GOTO main


Slave PIC (18F2620, 40Mhz HSPLL):

DEFINE OSC 40

INCLUDE "DT_INTS-18.bas"
INCLUDE "ReEnterPBP-18.bas"

ADCON1=%00001111
CMCON=%00000111

i2c_buffer_full VAR SSPSTAT.0
i2c_read VAR SSPSTAT.2
i2c_release_scl VAR SSPCON1.4

i2c_data VAR BYTE[6]
i2c_data_index VAR BYTE

dummy VAR BYTE

SSPADD=$0A ' I2C Address: $0A
SSPCON1=$36
SSPCON2.0=1

i2c_data[0]=1
i2c_data[1]=2
i2c_data[2]=3
i2c_data[3]=4
i2c_data[4]=5
i2c_data[5]=6

i2c_data_index=0

ASM
INT_LIST macro
INT_Handler SSP_INT, _i2c_int_handler, PBP, yes
endm
INT_CREATE
INT_ENABLE SSP_INT
endasm


main:
' main program goes here
goto main


i2c_int_handler:
IF i2c_read then
dummy=SSPBUF
IF i2c_buffer_full=0 THEN
SSPBUF=i2c_data[i2c_data_index]
i2c_data_index=i2c_data_index+1
IF i2c_data_index=6 THEN i2c_data_index=0
ENDIF
ENDIF
i2c_release_scl=1
@ INT_RETURN

Darrel Taylor
- 16th August 2010, 04:53
Hey Kamikaze,

I didn't see a problem from just looking at the code, so I ran it in the simulator.
And I don't see a problem there either.

On the master I get " 1 2 3 4 5 6".
It does not change.

I only added a toggle in the Master's main loop to verify it was looping.
And a toggle in the Slave's handler, to verify the transfers.

Is there something you stripped out before posting that could have caused it?

4704

Kamikaze47
- 16th August 2010, 13:41
Thanks for looking DT. I'm currently running the above code "as is" on 2 breadboards side by side.

Still losing a byte every time I do an i2cread.

I tried reading one byte at a time instead of six, and I get: 1, 3, 5, 1, 3, 5. So no matter how many bytes I receive at once, it always skips the byte immediately proceeding those bytes.

*edit* My circuit is identical to yours there DT except for 10mhz crystals + caps, and I only have an LCD on the master PIC.

In case its relevant, these are my configs:


__CONFIG _CONFIG1H, _OSC_HSPLL_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
__CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_512_2H
__CONFIG _CONFIG3H, _CCP2MX_PORTC_3H & _PBADEN_OFF_3H & _LPT1OSC_OFF_3H & _MCLRE_ON_3H
__CONFIG _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _XINST_OFF_4L



Hey Kamikaze,

I didn't see a problem from just looking at the code, so I ran it in the simulator.
And I don't see a problem there either.

On the master I get " 1 2 3 4 5 6".
It does not change.

I only added a toggle in the Master's main loop to verify it was looping.
And a toggle in the Slave's handler, to verify the transfers.

Is there something you stripped out before posting that could have caused it?

4704

Bruce
- 16th August 2010, 23:23
Have you looked at the errata sheet for the silicon rev you have? This series has a lot of issues with the MSSP module & I2C.

Kamikaze47
- 17th August 2010, 17:07
I'll have a look at that errata sheet as you suggest.

Incidentally, DT: What program do you use for PIC simulation? It looks like a handy tool to have.

Darrel Taylor
- 17th August 2010, 19:00
Incidentally, DT: What program do you use for PIC simulation? It looks like a handy tool to have.

It's Proteus VSM (http://www.labcenter.com), and it's extremely handy.

My breadboards are all covered in a layer of dust.
They don't get used much anymore.

Kamikaze47
- 17th August 2010, 19:08
It's Proteus VSM (http://www.labcenter.com), and it's extremely handy.

My breadboards are all covered in a layer of dust.
They don't get used much anymore.

Unfortunately, as I feared, the price is pretty steep.

Darrel Taylor
- 17th August 2010, 20:25
That was my initial reaction too.
But I've since found that it is EXTREMELY inexpensive, if you consider the value vs. cost.

You can't even buy a decent Digital O-Scope for that much.
But Proteus gives you Multiple 4ch-DSO's, 32ch-logic analyzers, voltmeters, ammeters, virtual terminals, signal generators, pattern generators, power supplies, I2C debuggers, SPI debuggers, frequency counters ...

There's a whole boat load of PIC's to choose from, plus all the other chips and discrete parts.
You can simulate multiple PIC's in the same circuit simultaneously.

And with PBP 2.60a, it integrates right into the MPLAB IDE for step by step debugging.

$479 gives you all that and either the 16F's or 18F's.
Can't get much cheaper than that.

I should be getting a commission, I sound like a commercial. :)

P.S. You can download the Demo Version and run your PBP programs on the sample circuits. You just can't modify the circuit.
&nbsp; &nbsp; Take a test drive.
<br>
<br>

aberco
- 30th August 2010, 01:06
Would it be possible to easily change one of these example routines to use the MSSP module for I2C Master?

In my program I change the clock speed from 12MHz to 250kHz, and it takes about 50ms to execute an I2CWRITE command at the slower speed. I measured the I2C clock to be about 800µs which is 1,2kHz... that's not much. I'm guessing that the PBP command includes it's own set of hardcoded delay... I'd like to save power, and if running I2C at 100kHz, I could get the PIC back to sleep 100 time faster.

I'm understanding quite well changes to make in registers to configure the hardware part, but I must say that when it get to assembler I don't understand too much of it right now...

tacbanon
- 24th April 2012, 09:35
Hi, I'm interested to try the master-slave(I2C) worked by Daniel and other members, sorry if it is not obvious for me in the code given, but can someone share the schematic used in the example?
I'm using Pic16877(master) and Pic16F88(salve).
Thanks,
tacbanon

tacbanon
- 25th April 2012, 03:10
From the code provided, here is how I interpret the pin connection between pics.
Master(Pic16F877A) Slave(Pic16F88)
(SCL)RB5 <---> RB4
(SDA)RB7 <---> RB1
but with this connection, LCD displays "Time out", can anyone help me what I'm missing?

aratti
- 25th April 2012, 05:36
Did you pullup SCL and SDA with a 3.3K resistor?

Cheers

Al.

tacbanon
- 25th April 2012, 07:06
Hi aratti,
Thanks for replying, I added the resistors but I have the same output("Time out" on the lcd). Any idea what causing the issue?

Thanks in advance,
tacbanon

6422

aratti
- 25th April 2012, 07:53
You should post the schematic of your setup along with the code you are using. Without more info it is difficult to give you proper answers.

Al.

tacbanon
- 25th April 2012, 09:59
Hi again,
Here is the code I'm using..
Master pic using Pic16F877A


'-------------------------------------------------------------------------------
' Master
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------


DEFINE OSC 4


'OPTION_REG.7 = 0


ADCON1 = 7


'------------------------------------------------------------------------------


DEFINE I2C_SLOW 1


TRISB.5 = 1
TRISB.7 = 1


'------------------------------------------------------------------------------


scl VAR PORTB.5 ' i2c clock input
sda VAR PORTB.7 ' i2c data input


'------------------------------------------------------------------------------


I2Caddress VAR BYTE


valor VAR BYTE


'------------------------------------------------------------------------------


' Set LCD Data port
DEFINE LCD_DREG PORTD
' Set starting Data BIT (0 OR 4) IF 4-BIT bus
DEFINE LCD_DBIT 4
' Set LCD Register Select port
DEFINE LCD_RSREG PORTC
' Set LCD Register Select BIT
DEFINE LCD_RSBIT 6
' Set LCD Enable port
DEFINE LCD_EREG PORTC
' Set LCD Enable BIT
DEFINE LCD_EBIT 7
' Set LCD bus size (4 OR 8 bits)
DEFINE LCD_BITS 4
' Set number of lines ON LCD
DEFINE LCD_LINES 2
' Set command delay time in us
DEFINE LCD_COMMANDUS 2000
' Set Data delay time in us
DEFINE LCD_DATAUS 50


'------------------------------------------------------------------------------


Pause 500 ' Wait for LCD to startup
LCDOut $fe, 1
LCDOut $fe, "Daniel"
Pause 500 ' Wait for LCD to startup


valor = 0


'------------------------------------------------------------------------------


main:
I2Caddress = $3


LCDOut $fe, 1
LCDOut "Write 1 - LED ON"
Pause 500


I2CWrite SDA, SCL, I2Caddress, [6], bogus ' Write offset to slave
Pause 500


LCDOut $fe, 1
LCDOut "Write 1 - LED OFF"
Pause 500


I2CWrite SDA, SCL, I2Caddress, [8], bogus ' Write to slave
Pause 500


LCDOut $fe, 1
LCDOut "Reading 1..."
Pause 500


I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
LCDOut $fe, 1
LCDOut "Value1: ", DEC valor
Pause 500


'--------


I2Caddress = $5


LCDOut $fe, 1
LCDOut "Write 2 - LED ON"
Pause 500


I2CWrite SDA, SCL, I2Caddress, [4], bogus ' Write to slave
Pause 500


LCDOut $fe, 1
LCDOut "Write 2 - LED OFF"
Pause 500


I2CWrite SDA, SCL, I2Caddress, [10], bogus ' Write to slave
Pause 500


LCDOut $fe, 1
LCDOut "Reading 2..."
Pause 500


I2CRead SDA, SCL, I2Caddress, [valor], bogus ' Read from slave
LCDOut $fe, 1
LCDOut "Value2: ", DEC valor
Pause 500


'--------


GoTo main ' Do it forever


'-------------------------------------------------------------------------------


bogus:
LCDOut $fe,1, "Time out" ' I2C command timed out
Pause 500


GoTo main


'-------------------------------------------------------------------------------


End

Here is the code for the Slave Pic16F88


DEFINE OSC 8
OSCCON = 110001 '8mhz


'------------------------------------------------------------------------------


CMCON = 7
ADCON1 = 0 ' Disable A/D converter
ADCON0 = 0
ANSEL = 0 ' all analog pins to digital


'--- tris port config here (don't use SCL & SDA pins other than I2C) ----------


DEFINE I2C_HOLD 1


TRISB.1 = 1
TRISB.4 = 1

'--- Alias pins ---------------------------------------------------------------


SDADIR VAR TRISB.1
SCLDIR VAR TRISB.4


'--- Define here your variables and alias -------------------------------------


LEDT VAR PORTA.2
LEDL VAR PORTB.7
LEDM VAR PORTB.6
LEDR VAR PORTB.5
readcnt VAR BYTE
datain VAR BYTE
WrData VAR BYTE


'--- 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 SSPCON.4 ' SSP (I2C) SCK Release Control
SSPEN VAR SSPCON.5 ' SSP (I2C) Enable
SSPOV VAR SSPCON.6 ' SSP (I2C) Receive Overflow Indicator
WCOL VAR SSPCON.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


'--- Rx Buffer defintion ------------------------------------------------------


RxBufferLEN CON 1
RxBuffer VAR BYTE[Rxbufferlen]
RxBufferIndex VAR BYTE


'--- Tx Buffer defintion ------------------------------------------------------


TxBufferLEN CON 1
TxBuffer VAR BYTE[txbufferlen]
TxBufferIndex VAR BYTE


'--- Define constants ---------------------------------------------------------


I2Caddress CON $3 ' Make address = 3


'--- Initialize I2C slave mode ------------------------------------------------


SCLDIR = 1 ' SCL must be an input before enabling interrupts
SDADIR = 1
SSPADD = I2Caddress ' Set our address
SSPCON = $36 ' Set to I2C slave with 7-bit address
SSPSTAT = 0
SSPIE = 1
SSPIF = 0
RxBufferIndex = 0
TxBufferIndex = 0


'--- Initialization Done! -----------------------------------------------------


GoTo main


'--- I2C subroutine ----------------------------------------------------------


i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data from us
IF BF = 0 Then i2cexit ' Nothing in buffer so exit
IF D_A = 1 Then i2cwr ' Data for us (not address)
IF SSPBUF != I2Caddress Then i2cexit ' Clear the address from the buffer
readcnt = 0 ' Mark as first read
GoTo i2cexit


i2cwr: ' I2C write data to us
datain = SSPBUF ' Put buffer data into array
Rxbuffer[Rxbufferindex]=datain
Rxbufferindex=rxbufferindex+1


IF rxbufferindex=RxBufferLEN Then ' end of buffer transfer
WrData=1
rxbufferindex=0
EndIF


GoTo i2cexit


i2crd: ' I2C read data from us
IF D_A = 0 Then
TxBufferIndex = 0
EndIF


While STAT_BF : Wend ' loop while buffer is full


wcol = 0 ' clear collision flag
SSPBUF = TxBuffer[TxBufferIndex]


While wcol
wcol = 0
SSPBUF = TxBuffer[TxBufferIndex]
Wend


CKP = 1 ' release clock, allowing read by master
TxBufferIndex = TxBufferIndex + 1 ' increment index
IF TxBufferIndex = TxBufferlen Then ' all bytes have been tx
TxBufferIndex = 0 ' reset index
EndIF

i2cexit:
Return


'--- End I2C subroutine ------------------------------------------------------


Main:

pause 100
txbuffer = 12


IF SSPIF = 1 Then
GoSub i2cslave
EndIF


SSPOV = 0
WCOL = 0


Select Case RxBuffer[0]
Case 6
High LEDT
Case 8
Low LEDT
End Select


WrData=0
GoTo Main


'------------------------------------------------------------------------------


End

The codes I'm using is from Daniel(copy and paste).
BTW I'm using a simulator...

regards,
tacbanon

aratti
- 25th April 2012, 15:18
Very likely is a simulator problem!

Al.

tacbanon
- 25th April 2012, 16:15
Okay, I will try it in the real hardware(need first to buy Pic16F88), and post here the result. Can anybody in the forum who has Proteus to try to test if similar output I'm having?

Thanks,
tacbanon

tacbanon
- 26th April 2012, 12:47
Hello,
While waiting for my Pic16F88(probable by next week) I tried to play with this code below with success in the simulation...
master(Pic18F2620)


DEFINE OSC 40


DEFINE I2C_HOLD 1


DEFINE LCD_DREG PORTB
DEFINE LCD_DBIT 0
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 4
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 5
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
DEFINE LCD_COMMANDUS 2000
DEFINE LCD_DATAUS 50
PAUSE 100
LCDOUT $FE,1,$FE,2


ADCON1=001111
CMCON=000111


scl VAR PORTC.3
sda VAR PORTC.4


i2c_address VAR BYTE


i2c_data VAR BYTE[6]


main:


i2c_address=$02
I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]


GOTO main


Slave(Pic18F2620)


DEFINE OSC 40


INCLUDE "DT_INTS-18.bas"
INCLUDE "ReEnterPBP-18.bas"


ADCON1=001111
CMCON=000111


i2c_buffer_full VAR SSPSTAT.0
i2c_read VAR SSPSTAT.2
i2c_release_scl VAR SSPCON1.4


i2c_data VAR BYTE[6]
i2c_data_index VAR BYTE


dummy VAR BYTE


;----------------------------------
i var byte
i=1


SSPADD=$02 ' I2C Address: $0A
SSPCON1=$36
SSPCON2.0=1


i2c_data[0]=6
i2c_data[1]=5
i2c_data[2]=4
i2c_data[3]=3
i2c_data[4]=2
i2c_data[5]=1




i2c_data_index=0


ASM
INT_LIST macro
INT_Handler SSP_INT, _i2c_int_handler, PBP, yes
endm
INT_CREATE
INT_ENABLE SSP_INT
endasm




main:


goto main




i2c_int_handler:
IF i2c_read then
dummy=SSPBUF
IF i2c_buffer_full=0 THEN
SSPBUF=i2c_data[i2c_data_index]
i2c_data_index=i2c_data_index+1
IF i2c_data_index=6 THEN i2c_data_index=0
ENDIF
ENDIF
i2c_release_scl=1
@ INT_RETURN


As I understood, in the master pic the code below tries to communicate to the Slave having the address $02


i2c_address=$02
I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]

and display data from Slave to the Master pic LCD. Then I tried to modify the setup and add another Slave#2 with address $0B like the this code below..


main:


i2c_address=$02
I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]


PAUSE 1000
LCDOUT 254,1
pause 500
i2c_address=$0B


I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]


PAUSE 1000


GOTO main

What I get is "6 5 4 3 2 1" and "0 0 0 0 0 0 " should be "1 2 3 4 5 6" for Slave#2
Slave#2


DEFINE OSC 40


INCLUDE "DT_INTS-18.bas"
INCLUDE "ReEnterPBP-18.bas"


ADCON1=001111
CMCON=000111


i2c_buffer_full VAR SSPSTAT.0
i2c_read VAR SSPSTAT.2
i2c_release_scl VAR SSPCON1.4


i2c_data VAR BYTE[6]
i2c_data_index VAR BYTE


dummy VAR BYTE


;----------------------------------
i var byte
i=1


SSPADD=$0B ' I2C Address: $0B
SSPCON1=$36
SSPCON2.0=1


i2c_data[0]=1
i2c_data[1]=2
i2c_data[2]=3
i2c_data[3]=4
i2c_data[4]=5
i2c_data[5]=6




i2c_data_index=0


ASM
INT_LIST macro
INT_Handler SSP_INT, _i2c_int_handler, PBP, yes
endm
INT_CREATE
INT_ENABLE SSP_INT
endasm




main:


goto main




i2c_int_handler:
IF i2c_read then
dummy=SSPBUF
IF i2c_buffer_full=0 THEN
SSPBUF=i2c_data[i2c_data_index]
i2c_data_index=i2c_data_index+1
IF i2c_data_index=6 THEN i2c_data_index=0
ENDIF
ENDIF
i2c_release_scl=1
@ INT_RETURN

I was only hoping that it would work that way(I guess not :)), how can I add another Slave and get data from it? I appreciate any help and the time for sharing.

Thanks in advance,
tacbanon
6430

Darrel Taylor
- 26th April 2012, 16:24
In your previous code with the F88, 3 is not a valid I2C address.
In the current code, $B is not a valid I2C address.

They should be a multiple of 2.
Bit 0 is reserved for the R/W bit.

tacbanon
- 27th April 2012, 11:48
In your previous code with the F88, 3 is not a valid I2C address.
In the current code, $B is not a valid I2C address.

They should be a multiple of 2.
Bit 0 is reserved for the R/W bit.
Hi Darrel,
Thanks for replying, I did some testing on 1Master and 1Slave with these address($02,$04,$06,$08,$10,$12,$14) and works fine. But when I trying to add Slave #2 I get "0 0 0 0 0 0". It only reads on the Slave #1 correct (displays "1 2 3 4 5 6").
This is how I code to read the 2nd Slave.


DEFINE OSC 40


DEFINE I2C_HOLD 1


DEFINE LCD_DREG PORTB
DEFINE LCD_DBIT 0
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 4
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 5
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
DEFINE LCD_COMMANDUS 2000
DEFINE LCD_DATAUS 50
PAUSE 100
LCDOUT $FE,1,$FE,2


ADCON1=001111
CMCON=000111


scl VAR PORTC.3
sda VAR PORTC.4


i2c_address VAR BYTE
i2c_address2 VAR BYTE


i2c_data VAR BYTE[6]
i2c_data2 VAR BYTE[6]




main:


i2c_address=$10
pause 200
I2CREAD sda,scl,i2c_address,[STR i2c_data\6]
LCDOUT $FE,$80,"1> ",DEC i2c_data[0]
LCDOUT " ", DEC i2c_data[1]
LCDOUT " ", DEC i2c_data[2]
LCDOUT " ", DEC i2c_data[3]
LCDOUT " ", DEC i2c_data[4]
LCDOUT " ", DEC i2c_data[5]


PAUSE 500
LCDOUT $FE,1
'*********************************************
i2c_address2=$14
pause 200
I2CREAD sda,scl,i2c_address2,[STR i2c_data2\6]
LCDOUT $FE,$80,"2> ",DEC i2c_data2[0]
LCDOUT " ", DEC i2c_data2[1]
LCDOUT " ", DEC i2c_data2[2]
LCDOUT " ", DEC i2c_data2[3]
LCDOUT " ", DEC i2c_data2[4]
LCDOUT " ", DEC i2c_data2[5]


PAUSE 500
LCDOUT $FE,1
GOTO main

BTW the code for Slave1 and Slave2 are identical (Slave#1 is address $10 and Slave#2 is address $14)
What I'm doing wrong in the code? I appreciate very much your help.

Thanks,
Tacbanon

Darrel Taylor
- 27th April 2012, 16:35
If you comment out the first I2CREAD, is it able to read from the second slave?

If you change the order so that it reads the second slave first, is it then unable to read the first slave?

tacbanon
- 27th April 2012, 16:52
If you comment out the first I2CREAD, is it able to read from the second slave?

Yes it is able to read.


If you change the order so that it reads the second slave first, is it then unable to read the first slave?

Nope, displays "0 0 0 0 0 0" for first Slave, 2nd Slave shows "6 5 4 3 2 1"

Darrel Taylor
- 27th April 2012, 18:17
It sounds like a buffer overflow.

... bad comment deleted ... duh!!

If you post or email your .DSN, I'll take a closer look.

tacbanon
- 27th April 2012, 23:34
Thanks Darrel, oh sorry to ask this, but how do I send a winrar file?

mister_e
- 28th April 2012, 00:11
by e-mail
http://melabs.com/contact.htm?source=sidebar

tacbanon
- 28th April 2012, 01:01
Thanks mister_e :)

Demon
- 28th April 2012, 04:18
Or you can stuff it in a ZIP file and post it here. The forum accepts ZIPs as attachments.

Some RAR programs can also save as ZIP format.

Robert

tacbanon
- 28th April 2012, 04:59
Thanks for the info Robert...
and here is the zip file.

Regards,
tacbanon

tacbanon
- 28th April 2012, 08:13
Oops here are the codes I'm using for the master, Slave1 and Slave 2.

Darrel Taylor
- 28th April 2012, 17:12
Thanks for the files tacbanon.

Well, this code for the slaves will allow it to read from both devices.

main:
IF SSPCON1.6 THEN SSPCON1.6 = 0
goto main

i2c_int_handler:
IF i2c_read then
IF i2c_buffer_full=0 THEN
SSPBUF=i2c_data[i2c_data_index]
i2c_data_index=i2c_data_index+1
ELSE
dummy = SSPBUF
ENDIF
ELSE
i2c_data_index=0
ENDIF
i2c_release_scl=1
@ INT_RETURN

But it's still not right. And is not a complete slave.

According to AN734 there are 5 states to maintain. 6 including the ERROR state.
Your original code only handles 1 state, and this modification only does 2 states.

I think by adding the other 4 states, it will get rid of the buffer overflows, but I'm not absolutely sure.
I'm going to convert AN734 state machine to PBP and see what happens.
May take me awhile.

tacbanon
- 29th April 2012, 02:24
Thank you for the code, and thanks in advance DT for your valuable time...looking forward for the end result(I wish and pray that things goes well). I've been reading about I2C on net, trying to understand and the applications that might be done(particularly on Master-(multi-Slave/Multi-master)) are very interesting...

Regards,
tacbanon

Charles Linquis
- 29th April 2012, 04:04
Although my slave example may not be as robust as AN734, I have systems running that have 14 slaves running. All 14 slaves are polled on 2 second intervals. The master uses bit-banging (I2CWrite). I have never had trouble with the code below. I have tested it with -
18F8723, 18F2221, 18F2321, 18F2523 and had excellent results.



'************************************************* ***************
'* Name : I2C slave *
'* Author : Charles Linquist *
'* Notice : Copyright (c) 2010 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 10/20/2010 *
'* Version : 1.0 *
'* Notes : Transmits 100 bytes in ~14 ms (Master SEND) *
'* : Recives 100 bytes in ~12 ms (Master RECEIVE) *
'* : Could possibly go faster. Master used for the *
'* : test used bit-banging (I2CREAD/I2CWRITE *
'* : settings on that end were *
'* : DEFINE I2C_SLOW 1 ,DEFINE I2C_HOLD 1 *
'************************************************* ***************
ASM
ifndef __18F2221
error "18F2221 not selected"
endif
ENDASM

ASM
movlw 0x62 ; %0110 0010 = 4 Mhz, internal osc block
movwf OSCCON
movlw 0x80 ; %1000 0000 = PLL disabled
movwf OSCTUNE
ENDASM


DATA @0,0 ; programming flag

DEFINE OSC 4
DEFINE NO_CLRWDT 1 ' Don't waste cycles clearing WDT
Define USE_LFSR 1
define USE_LOWPRIORITY 1
;---------------- Alias the bits ------------------------------------

SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
SSPIE VAR PIE1.3 ' Int Enable

SSPUA VAR SSPSTAT.1
SSPS VAR SSPSTAT.3
SSPP VAR SSPSTAT.4
SSPSMP VAR SSPSTAT.7

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
GENCALL VAR SSPCON2.7 ' General Call enabled

MainTimer var word bankA System
I2CTimer var word bankA System
I2CTimerGate var byte bankA System
I2CErr var byte bankA System
;-------------------------------------------------------------------------
;Alias pins
SCL VAR PORTC.3 ' I2C clock input
SDA VAR PORTC.4 ' I2C data input
LED VAR PORTB.5


'------------------- Rx Buffer defintion --------------------
RxBufferLEN con 10
RxBuffer var BYTE[Rxbufferlen + 1]
TxBufferLEN CON 10
TXBuffer VAR BYTE[TxBufferLEN + 2]
InBuff VAR BYTE

GeneralCall VAR BIT
RXBufferOverFlow VAR BIT
TxBufferUnderFlow VAR BIT
RXBufferPointer VAR Byte
TXBufferPointer VAR Byte
address VAR BYTE
x VAR BYTE
dummy var byte
I2CAddress VAR BYTE
WCOLCounter VAR BYTE
;-------------------------------------------------------------
TXBuffer[TxBufferLen + 1] = $FF ; Char to send if requestor asks for too much.

I2CDeviceAddress VAR BYTE

I2CMajorAddress CON $3
I2CDeviceAddress = $8

I2CAddress = (I2CMajorAddress <<4) | I2CDeviceAddress
;---------------------------------------------------------------
SSPADD = I2Caddress ' Set our address
SSPCON2.0 = 1 ; Stretch the clock
SSPSTAT = 0
SSPEN = 1
SSPIE = 1
SSPIF = 0
GenCall = 1 ; Turn general call on
SSPCON2.7 = 1 ' General call address ENABLED
SSPCON1 = $36 ' Set to I2C slave with 7-bit address

;---------------------------------------------------------------
TRISA = %11111111
TRISB = %11011111
TRISC = %11111111

' -------------- Turn on Ints ------------------------
INTCON.7 = 1 ; global ints
INTCON.6 = 1 ; peripheral ints

;------------------ Clear parameters --------------
RXBufferPointer = 0
TxBufferPointer = 0
GeneralCall = 0
RxBufferOverFlow = 0
TXBufferUnderFlow = 0
WCOLCounter = 0
;---------------------------------------------------------------------

INCLUDE "DT_INTS-18.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ' Use PBP interrupts
include "ReEnterPBP-18LP.bas"
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, TimerInt, ASM, yes

endm
INT_CREATE ; Creates the interrupt processor

INT_LIST_L macro ; IntSource, Label, Type, ResetFlag?
INT_Handler SSP_INT, _I2C_Int, PBP, yes
endm
INT_CREATE_L ; Creates the LP interrupt processor


endasm
''----------------- Initialization Done! -----------------------------
Goto OverInt ; jump over ISR

asm
TimerInt

infsnz MainTimer
incf MainTimer + 1
btfsc I2CTimerGate,0 ; Don't count if low bit is clr
incf I2CTimer
btfss I2CTimer,2 ; Bit 2 is set , we have at least 4 counts, we must be hung
bra NoProb
bcf PIR1,3 ; Clear Int flag
bcf SSPCON1,5 ; Stop the
bsf SSPCON1,5
bsf I2CErr,0
clrf I2CTimer
NoProb
bcf INTCON,2

INT_RETURN
endasm
'------------------------- I2C subroutine --------------------------------
I2C_Int:
; We don't get here uless someone has matched our address register.
@ bsf I2CTimerGate,0 ; start the counter
If R_W = 0 then 'This is a WRITE by the master to us
IF BF = 0 Then goto i2CEXIT ; Nothing for us!
if D_A = 0 then 'We have an address
address = SSPBUF 'Need to READ to satisfy Microchip
IF Address = 0 THEN
GeneralCall = 1
ELSE
GeneralCall = 0
ENDIF

else
IF WCOL OR SSPOV THEN I2CExit

RXBuffer [RxBufferPointer] = SSPBuf ;
RxBufferPointer = RXBufferPointer + 1

IF RXBufferPointer > RxBufferLen THEN
RXBufferOverFlow = 1
RXBufferPointer = 0
ENDIF

ENDIF


else ' R_W = 1 Master wants to read FROM us
IF !D_A Then
TxBufferPointer = 0 ' Mark as first read
RXBufferPointer = 0
EndIF

IF CKP THEN I2CExit
dummy = SSPBUF ;NECESSARY!

If TXBufferPointer < TxbufferLen THEN
SENDI2C:
WCOL = 0
SSPBUF = TXBuffer[TXBufferPointer] ' Get data from array
IF WCOL THEN
WCOLCounter = WCOLCounter + 1 ; Don't hang the bus with infinite WRITES!
IF WColCounter = 0 Then goto I2CExit ; Rolled over
Goto SendI2C ; Rety
ELSE
WColCounter = 0
ENDIF
TXBufferPointer = TXBufferPointer +1
TXBufferPointer = TXBufferPointer MIN TXBufferLen + 1 ; Don't let it get larger than this
; Send FFs if requestor asks for too much data
ELSE
TXBufferPointer = 0
TXBufferUnderflow = 1
ENDIF
ENDIF

I2CEXIT:
Asm
bcf SSPCON1,6
bcf SSPSTAT,0
bcf PIR1,3
bsf SSPCON1,4

bcf I2CTimerGate,0 ; stop the counter
INT_ENABLE SSP_INT ; probably don't need this, but want to make certain it gets reenabled
INT_RETURN
endasm
' -------------------------end I2C subroutine --------------------------------
'--------------------[ main program loop]---------------------------------------
OverInt:
Asm
clrf MainTimer
clrf I2CTimer
bcf INTCON,2
bcf PIR1,3 ; Clear the I2C flag before we turn on the INT.
movlw 0x83
movf T0CON
INT_ENABLE SSP_INT
INT_ENABLE TMR0_INT
endasm



MAIN PROGRAM LOOP GOES HERE!

tacbanon
- 29th April 2012, 06:38
Hi Charles, thanks for sharing..I will try it out..


tacbanon

tacbanon
- 30th April 2012, 03:42
@Charles
Sorry I was not able to make it work on my setup..I'm not really sure what the following code is doing, so I tried to comment it and assign a value, but no effect.


I2CMajorAddress CON $3
I2CDeviceAddress = $8
'I2CAddress = (I2CMajorAddress <<4) | I2CDeviceAddress
I2CAddress =$14

I hope it will be alright if you share the master side code?

Thanks,
tacbanon

Darrel Taylor
- 30th April 2012, 17:45
At this point, I'm thinking Al (aratti) was right.

AN734 didn't work either.
Bytes are being put in the SSPBUF without an address match.
So the slaves that aren't being accessed are overflowing during reads from other devices.

The code I gave earlier works because it's resetting the overflow in the main loop, but that's just not right.

I'm trying to get some more info from Labcenter.

Charles Linquis
- 1st May 2012, 02:57
I am using a slight variant of the code I posted, and it is working fine. The only major change is that I'm sending a packet length byte, and I'm handling the entire receive packet inside the ISR. This is safe because I have my ( higher priority) timer interrupt watching over the whole process.

tacbanon
- 2nd May 2012, 03:42
Hi,
Just want to ask if anyone tried to extend say 50meters communication between master and slave using I2C bus extender P82B715PN or similar?

Regards,
tacbanon

Darrel Taylor
- 8th May 2012, 01:12
tacbannon,

The problem with Proteus and multiple I2C slaves has been fixed by Labcenter.
If you update Proteus to 7.10 SP1, you should have better luck.

tacbanon
- 10th May 2012, 07:13
@ DT, Thanks for the update..I will find time to play with it(I2C multiple slaves) this weekend(so many work, little time to play :) )

Kind regards,
tacbanon

ralfmayr
- 2nd June 2012, 18:12
Hi all,
for info to all: I have the code from Aretti here on a 18F25K22, works fine with one slave on one master. When i connect more slaves (up to 31) i get the effect that a NACK comes right behind the address, the SDA line is high during the 9th clock cycle... my bus is about 70cm long (long backplane) and i use a 3.3k on the i2c bus. It seems that this "NACK" effect is coming sporadic, does someone tried to do the same like me?
Regards,
Ralf

ralfmayr
- 3rd June 2012, 13:08
Hi again,
found the bug: the bus i am using is splitted in two 2 x 25cm long busses which is not the best solution. Sometimes because of layout reasons it must be done this way. In this case two 100 Ohm resistors close to the Master in series with SDA and SCL and pullup's 2 x 10kOhm at both ends of the splittet bus make the bus working fine. I checked the timing with a logic analyser and the quality of the signals with a scope, both ok, no glitches or other dirty effects. Hope this help others having same trouble. I must say best solution is always making the bus from one end to the other end in one direction, i tested this also with 80cm. Adding also two 100 Ohm resitors in series with SDA and SCL close to the master makes the signal quality better also.
Both versions run with 80kHz very fine now.
Regards,
Ralf

mimie64
- 10th February 2014, 00:25
Excuse me for my English. :D
How can I send data from a PIC 16F876 to four slaves
using the I2C bus protocol, with the same address?
Could you help me, a piece of code would be welcome!
mimie64.:)

EarlyBird2
- 10th February 2014, 08:54
I2C bus protocol requires unique addresses for each slave. Just code the address into each slave if you are building the slaves yourself of course.

Wirecut
- 27th March 2016, 12:19
Thanks to all of you for your greath wok!

... just to avoid to have trouble in future and have a clear view of a new project, I have in plan to use the slave routine on almost 12 PIC of 18F2550 and one Master with 18F4550.

The idea is that:
I shall have the Master far from the first slaves at about two to three meters and the second slave is far from the first slave around 50 cm, and all other slaves are distanced 50 cm between them.

At the end the total length shall be more than 10 meters. but less that 12 meters.

Also, I would use the RJ45 connectors to make interconnection between the PCBs and use the preassembled CAT5 Ethernet patch cable to carry the supply voltage from the Master to the slaves.

I have thinked to put on pin 1, 2 and 3 the VCC, on pin 4 the Clk, on pin 5 the Dat and on pin 6. 7 and 8 the GND?

The lenght of the cables can be a problem?

The idea to use the RJ45 with the CAT5 patch cable is good?

Leo

richard
- 27th March 2016, 12:52
WIKI I2C

The maximum number of nodes is limited by the address space, and also by the total bus capacitance (https://en.wikipedia.org/wiki/Capacitance) of 400 pF (https://en.wikipedia.org/wiki/Picofarad), which restricts practical communication distances to a few meters.

CAT5 CAPACITANCE
52pF / metre

cat5 balanced cable / i2c unbalanced signal.
cat5 characteristic impedence of 100 ohms is probably a big load for the i2c pins also.
its not a good match , and won't go far

tumbleweed
- 27th March 2016, 13:03
A regular CAT5 RJ45 has 4 twisted pairs, but they're not "in order"

1,2 orange,orange/wht
3,6 green,green/wht
4,5 blue,blue/wht
7,8 brown,brown/wht

I'd pair the SDA and SCL signals each with a GND (ie 1=GND, 2=SDA, and 7=SCL, 8=GND) and use the others for PWR/GND.

10 meters is a long way for a single-ended TTL level signal. They make I2C bus extenders for this sort of thing.

Wirecut
- 27th March 2016, 13:51
If I have correctly understand, the maximum length from the capacitance point of view is around 8 meters, but what shall be the maximum distance for the single ended TTL?

Leo

tumbleweed
- 27th March 2016, 14:24
I don't think there's any good single answer to that... it's dependent on so many different things it's hard to make a general statement.

Personally, I start getting nervous as soon as a signal leaves a board!

pedja089
- 27th March 2016, 14:49
SDA and SCL are not good combination in one pair. Because of cross talk.
Much better is to combine GND and SDA on ona pair, GND and SCL on second pair. But capacitance play big roll. Probably CLK must be driven, not with pull up. SDA should have strong pull up.
But do not connect multiple GND on both sides, as that will create long loop antenna, probably...
I thing, but maybe i'm wrong, best chanse to get it to work is:
1. Pair GND and signal on 2 pairs for SDA and SCL, connect that ground only on output port of module.
2. Use third pair for power
3. Ground unused pair master, as GND for signals.
Generally not good idea to run TTL signals out of boards.
If you design master and slave's why not to go with RS485, half duplex or duplex?

richard
- 28th March 2016, 01:05
Personally, I start getting nervous as soon as a signal leaves a board!
me too

spi can get that sort of distance but you need a cs signal for each slave

I wonder if you used a pair of those 3v-5v level shifters back to back if they could act like a repeater ?
could be a cheap solution

wireless may be an option
rfm69 modules and the like can get 300k bps over that range and a master slave setup is no too difficult

Charles Linquis
- 28th March 2016, 03:12
It will work fine - as long as the PICs are the only I2C devices on the bus. The I2C specification was written to deal with low-power devices that have no more than 2 mA drive. As a result, all the pull ups (in parallel) could be no smaller than 2.5K. Since a PIC pin will sink more than 20mA, you can use much smaller pull ups. If there are only two PICs on the bus, I regularly use a 1K pull up at each end, with 10 ohms in series (one at each end) to reduce ringing. Since your CAT-5 will be twisted pairs, use one wire of each pair as GND (and the other as SDA or SCL). You should have no trouble at distance of at least 50'. But if you do get errors, you can slow the I2C bus down to lower data rates.

Be aware, however that any other non-PIC devices on the bus will probably not be able to drive such small pullups properly.

Wirecut
- 28th March 2016, 14:41
Your tips are pushing me towards the RS485.
The only trouble and not've never used before.

I am somewhat concerned about the protocol to use.

You have to give me a hint where to look to select the protocol that I could use?

Leo

Ioannis
- 28th March 2016, 16:28
If you have used serial RS232, then the RS485 is just the same. Only drivers (chips) are different. And the signal is TTL level from your controller to the RS485 driver.

The RS485 is not a protocol, rather a low level electric specs of the medium. It does not care about how the data will be sent.

So you really have not many concerns on this. Almost Plug and play.

Ioannis

Wirecut
- 28th March 2016, 17:00
In reality the RS232 is designed to connect generally ONLY two devices toghether.

The RS485 have the capability to manage multiple slave and one master.

In my project I have one master and I connect the USART to the RS485 chip and then in daisy chain all the other RS485 chip to the USART's slave. But that means every slave have his proper address, manage receiving, CRC chek etc etc and to do in the best way is needed a protocol.

Googling I have found the SNAP protocol ( http://www.hth.com/snap/ ) that shall be good to be implemented in my solution and have the source code in PBP (old version).

If I do not found a protocol more simple than SNAP, I will study and implement it.

Leo

Address
- 19th August 2016, 06:47
I need Working I2C Slave code for PIC16LF1513 in C program with some instructions.

Scampy
- 21st August 2016, 17:44
I need Working I2C Slave code for PIC16LF1513 in C program with some instructions.



So why post on a BASIC forum - :tongue: