PDA

View Full Version : I2C Master Slave issues.



cpayne
- 28th March 2008, 01:58
Hi. I have been struggling getting an I2C master slave routine going between two different PICs. I can write from the master with no issues, but when ever I try to read it resets the master and it keeps resetting as soon as it hits an I2C cmd. It seems to go through once, but then starts to reset and never goes through the routine again as if something is not being released or the like from the slave... I have attached the master and slave code below. Any help would be greatly appreciated. Thanks, Charlie

' PicBasic Pro Master PIC18F2220 to read and write to I2C slave

@ __CONFIG _CONFIG1H, _INTIO2_OSC_1H
@ __CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_128_2H
@ __CONFIG _CONFIG3H, _MCLRE_OFF_3H
@ __CONFIG _CONFIG4L, _LVP_OFF_4L

OSCCON = $70
DEFINE OSC 8

DEFINE I2C_HOLD 1
SCL VAR PORTC.3 ' Clock pin
SDA VAR PORTC.4 ' Data pin

' Alias pins (0=Output, 1=Input)
L1 VAR LATB.7 :TRISB.7 = 0
L2 VAR LATB.6 :TRISB.6 = 0
L3 VAR LATB.5 :TRISB.5 = 0
L4 VAR LATB.4 :TRISB.4 = 0

'Define RAM
i var byte
a VAR BYTE[8] ' Holds 8 characters read from slave
j var byte

'blink L1-L4 to signal its alive or has been reset
for i = 1 to 12
high L1: pause 25: low L1
high L2: Pause 25: Low l2
high l3: pause 25: low l3
high l4: pause 25: low l4
next i
j= 125

loop:
toggle L1
j= j+1
I2CWrite SDA,SCL,$02,[j], bogus ' Write value to slave
if j>130 then j=125
Pause 250
'I2CRead SDA,SCL,$02,[STR a\8], bogus ' Read string from slave
GoTo loop ' Do it forever

bogus:
high l2: Pause 1000: low l2
GoTo loop


' PicBasic Pro I2C slave program - PIC16F819

@ device INTRC_OSC_NOCLKOUT
'@ DEVICE XT_OSC ' System Clock Options
@ DEVICE WDT_ON ' Watchdog Timer
@ DEVICE PWRT_ON ' Power-On Timer
@ DEVICE BOD_ON ' Brown-Out Detect
@ DEVICE MCLR_OFF ' Master Clear Options
@ DEVICE LVP_OFF ' Low-Voltage Programming
@ DEVICE CPD_OFF ' Data Memory Code Protect
@ DEVICE PROTECT_OFF ' Program Code Protection

OSCCON = $70 'sets the oscillator speed
DEFINE OSC 8 'Oscillator speed in MHz: 3(3.58) 4 8 10 12 16 20 24 25 32 33 40

' Alias pins (0=Output, 1=Input)
scl VAR PORTB.3: TRISB.3 = 1 ' I2C clock input
sda VAR PORTB.4: TRISB.4 = 1 ' I2C data input
L1 var PORTA.2 :TRISA.2 = 0
L2 VAR PORTA.4 :TRISA.4 = 0
L3 VAR PORTB.0 :TRISB.0 = 0
L4 VAR PORTB.3 :TRISB.3 = 0 ' Green LED
L5 VAR PORTB.6 :TRISB.6 = 0 ' Red LED

' Define used register flags
SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
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

' Define constants
I2Caddress CON 2 ' Make our address 2

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

' Initialize I2C slave mode
SSPADD = I2Caddress ' Set our address
'SSPCON2 = 0 ' General call address disabled
SSPCON = $36 ' Set to I2C slave with 7-bit address
readcnt = 0 ' Zero counter

high L1: high L2: high L3: high L4 : High L5
pause 200
low L1: low L2: low L3: low L4 : Low L5
dataout[0] = 0
dataout[1] = 1
dataout[2] = 2
dataout[3] = 3
dataout[4] = 4
dataout[5] = 5
dataout[6] = 6
dataout[7] = 7
dataout[8] = 8

mainloop: ' Main program loop
toggle l1
IF SSPIF Then ' Check for I2C interrupt flag
GoSub i2cslave
EndIF
GoTo mainloop ' Do it all forever

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data
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 data into array
toggle l2
if datain = 127 then
high l5
else
low l5
endif
GoTo i2cexit

i2crd: ' I2C read data from us
toggle l3
IF D_A = 0 Then
readcnt = 0 ' Mark as first read
EndIF
SSPBUF = dataout[readcnt] ' Get data from array
CKP = 1 ' Release SCL line
readcnt = readcnt + 1 ' Move along read count
GoTo i2cexit ' That's it

i2cexit:
Return

mister_e
- 28th March 2008, 02:07
I must be blind... but nowhere i see you have disabled the multiplexed analog modules for your LEDs... didn't go too far in your code though.

And proof that's probably a copy/paste code example prior to check the datasheet ;) SDA=PORTB.1, SCL=PORTB.4. Lucky your are... TRISB=255 at power-up ;)

So.. is there any Register Bit that change between the F877 and the 819... hummm.. let's see

cpayne
- 28th March 2008, 02:27
I am only using a couple channels of the analog. I have removed all the analog and many other things from the program just trying to get a simple communication going, but to no avail...

I just changed the master loop to the following to see if once it crashes I can write anymore and I can not. The master writes until it does a read, then the master resets, and the master will not write anymore, it just keeps resetting. So I think the slave is being put in some state and holds there that causes the reset...

loop:
toggle L1
j= j+1
I2CWrite SDA,SCL,$02,[j], bogus ' Write value to slave
if j>130 then
j=125
I2CRead SDA,SCL,$02,[STR a\8], bogus ' Read string from slave
endif
Pause 250
GoTo loop ' Do it forever

JD123
- 28th March 2008, 20:01
I have yet to get involved with the hardware issues of I2C ports on PICs, so this may be no help, but who's doing the clocking during the read from the slave to the master? Is it setup in the slave I2C to allow for the master to provide the clock? Again, I'm not able to know for sure what you've done with the hardware since I'm not familiar with those registers.

In my bit-banging I2C (current project) I've locked down the I2C transfers by failing to deal with the acknowledges correctly and ill timed stop commands after a "last byte" read. I don't know how PBP deals with this read issue, but a correct stop command comes after a reading device does not send an acknowledge (after reading the last byte), else the sending I2C drives the SDA line with the next bit of data. This can mess up the master’s ability to issue the stop command.

Also, I noted in the PBP manual this:

I2CREAD and I2CWRITE can be used to read and write data to a serial EEPROM with a 2-wire I2C interface such as the Microchip 24LC01B and similar devices. This allows data to be stored in external non-volatile memory so that it can be maintained even after the power is turned off. These commands operate in the I2C master mode and may also be used to talk to other devices with an I2C interface like temperature sensors and A/D converters.

cpayne
- 28th March 2008, 21:55
I have it working kinda... It is real sensitive to having the slave ready and waiting, which is not possible because I ultimately need the slave to do more involved tasks.

I have attached the code below with comments of my remaining issues. ANy further help would be great!

' PicBasic Pro Master PIC18F2220 to read and write to I2C slave

@ __CONFIG _CONFIG1H, _INTIO2_OSC_1H
@ __CONFIG _CONFIG2H, _WDT_ON_2H & _WDTPS_128_2H
@ __CONFIG _CONFIG3H, _MCLRE_OFF_3H
@ __CONFIG _CONFIG4L, _LVP_OFF_4L

OSCCON = $70
DEFINE OSC 8

' Set up serial UART
define HSER_RCSTA 90H ' Set Receive Status and control register
DEFINE HSER_TXSTA 24H ' set transmit status and control register
'DEFINE HSER_BAUD 4800 ' 2403=2400Kbps, 9615=9600Kbps,19231=19.2Kbps
DEFINE HSER_BAUD 9615 ' 2403=2400Kbps, 9615=9600Kbps,19231=19.2Kbps
define HSER_CLROERR 1 ' reset on serial buffer overflow
'RCIF VAR PIR1.5 ' UART receive interrupt flag

' Define Analog parameters
ADCON0 = %00010001 '
ADCON1 = %00001100 ' Anolog AN0-AN4, all else digital
ADCON2 = %10000111 ' right justify result
CMCON = %00000111 ' Comparators OFF to allow inputs on pins CMCON = 7
INTCON = %00000000 ' all interrupts OFF
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 3 ' Set clock source (3=rc)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS

DEFINE I2C_HOLD 1
DEFINe I2C_SLOW 1 'access a standard speed device at above 8MHz
SCL VAR PORTC.3 ' Clock pin
SDA VAR PORTC.4 ' Data pin

' Alias pins (0=Output, 1=Input)
L1 VAR LATB.7 :TRISB.7 = 0
L2 VAR LATB.6 :TRISB.6 = 0
L3 VAR LATB.5 :TRISB.5 = 0
L4 VAR LATB.4 :TRISB.4 = 0

'Define RAM
i var byte
a VAR BYTE[4] ' Holds 8 characters read from slave
j var byte

'blink L1-L4 to signal its alive or has been reset
for i = 1 to 12
high L1: pause 25: low L1
high L2: Pause 25: Low l2
high l3: pause 25: low l3
high l4: pause 25: low l4
next i
j= 125

loop:
toggle L1
j= j+1
I2CWrite SDA,SCL,$02,[j], bogus ' Write value to slave
if j>130 then
j=125
pause 1
I2CRead SDA,SCL,$02,[STR a\4], bogus ' Read string from slave
endif
hserout ["DATA 0:",dec a[0]," 1:",dec a[1]," 2:",dec a[2]," 3:",dec a[3]," 4:", dec a[4] ," ",13]
'a[0-2] are the slave analogs, it seems to be reporting back the right numbers :-)
'a[3] should be readcnt from master, its just toggles between 2 and 3??
'a[4] should be datain from the slave. j is toggling LED in slave, so the number is
' being sent, its just not getting returned, it only send back an 8 :-(
Pause 250
GoTo loop

bogus:
high l2: Pause 1000: low l2
GoTo loop


' PicBasic Pro I2C slave program - PIC16F819

@ device INTRC_OSC_NOCLKOUT
@ DEVICE WDT_ON ' Watchdog Timer
@ DEVICE PWRT_ON ' Power-On Timer
@ DEVICE BOD_ON ' Brown-Out Detect
@ DEVICE MCLR_OFF ' Master Clear Options
@ DEVICE LVP_OFF ' Low-Voltage Programming
@ DEVICE CPD_OFF ' Data Memory Code Protect
@ DEVICE PROTECT_OFF ' Program Code Protection

OSCCON = $70 'sets the oscillator speed
DEFINE OSC 8 'Oscillator speed in MHz: 3(3.58) 4 8 10 12 16 20 24 25 32 33 40

' Define ADCIN parameters
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 3 ' Set clock source (3=rc)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS
TRISA = %11111111 ' Set PORTA to all input
ADCON1 = %10000100 ' Set A2 A4 digital, A0 A1 A3 Analog, rt justify

AN0 var word ' average Positioning Potentiometer Voltage
AN1 var word ' Trim Pot 1
AN3 VAR WORD ' Trim Pot 2
AN0i var word ' Positioning Potentionmeter

' Alias pins (0=Output, 1=Input)
scl VAR PORTB.3: TRISB.3 = 1 ' I2C clock input
sda VAR PORTB.4: TRISB.4 = 1 ' I2C data input
L1 var PORTA.2 :TRISA.2 = 0
L2 VAR PORTA.4 :TRISA.4 = 0
L3 VAR PORTB.0 :TRISB.0 = 0
L4 VAR PORTB.3 :TRISB.3 = 0 ' Green LED
L5 VAR PORTB.6 :TRISB.6 = 0 ' Red LED

' Define used register flags
DEFINe I2C_SLOW 1 'access a standard speed device at above 8MHz
SSPIF VAR PIR1.3 ' SSP (I2C) interrupt flag
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

' Define constants
I2Caddress CON 2 ' Make our address 2

' Allocate RAM
result VAR BYTE ' ADC result
datain VAR BYTE ' Data in
dataout VAR BYTE[4] ' Data out array
readcnt VAR BYTE ' I2C read count

' Initialize I2C slave mode
SSPADD = I2Caddress ' Set our address
'SSPCON2 = 0 ' General call address disabled
SSPCON = $36 ' Set to I2C slave with 7-bit address
readcnt = 0 ' Zero counter

low L1: low L2: low L3: low L4 : Low L5

GoTo mainloop ' Skip over subroutines

i2cslave: ' I2C slave subroutine
SSPIF = 0 ' Clear interrupt flag
IF R_W = 1 Then i2crd ' Read data
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 data into array
toggle l2
if datain = 127 then
high l5
else
low l5
endif
'this is toggling, so the master is sending the number is its toggling the led, so apears good
'I want to receive more than on byte, how do I do this?
GoTo i2cexit

i2crd: ' I2C read data from us
IF D_A = 0 Then
'toggle l3 'this causes master to continously reset, too much of a pause?
readcnt = 0 ' Mark as first read
EndIF
SSPBUF = dataout[readcnt] ' Get data from array
CKP = 1 ' Release SCL line
readcnt = readcnt + 1 ' Move along read count
GoTo i2cexit ' That's it

i2cexit:
Return

mainloop: ' Main program loop
' have to check12c every step of both PICs lock up. Need a way of doing this as an iterupt??
' I plan on having a lot of other code the slave needs to execute, It is not practical to
' be continously checking like this...
gosub checki2c
toggle l1
gosub checki2c
ADCIN 0, AN0 ' Read ADC channel 0
gosub checki2c
AN0 = an0/4
gosub checki2c
dataout[0] = AN0

gosub checki2c
ADCIN 1, AN1
gosub checki2c
AN1 = an1/4
gosub checki2c
dataout[1] = AN1

gosub checki2c
ADCIN 3, AN3
gosub checki2c
AN3 = an3/4
gosub checki2c
dataout[2] = AN3

gosub checki2c
dataout[3] = readcnt

gosub checki2c
dataout[4] = datain
'this value is not getting back to the master???/

GoTo mainloop ' Do it all forever

checki2c:

'Check for I2C interrupt flag
IF SSPIF Then
GoSub i2cslave
EndIF

return

JD123
- 29th March 2008, 14:03
If you are having issues with the slave not being ready, use the standard I2C busy protocol; have the slave hold the clock low while it's busy. Master will check clock line status before trying to send out data.

cpayne
- 29th March 2008, 16:44
Hi. The issue with holding the clock line low is I have multiple slaves, so I don't think the master will get a word in inchwise with all the slaves pulling the clock low...

JD123
- 29th March 2008, 17:08
Hi. The issue with holding the clock line low is I have multiple slaves, so I don't think the master will get a word in inchwise with all the slaves pulling the clock low...Yes, with muli slaves that could get nasty. I thought about that but had re-read your first line in the top post and it said 2 pics. I thought this was just a 2 pic i2c system.

cpayne
- 29th March 2008, 17:28
Yes, I am trying to get two working for now, then as soon as I get one good slave, I plan on adding...

Charles Linquis
- 29th March 2008, 19:33
Maybe I'm missing something, but it looks like you are polling for the Interrupt Staus Bit in MAIN, and when it is set, you go off and read the receive register. Because you are using a polled operation, not an interrupt-driven one, you can't catch data as soon as it arrives. If you use Darrel's Instant Interrupts and make your I2C routine an ISR, you should be able to receive data any time - even halfway through an A/D conversion.