Unfortunately, as I feared, the price is pretty steep.
Printable View
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.
Take a test drive.
<br>
<br>
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...
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
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?
Did you pullup SCL and SDA with a 3.3K resistor?
Cheers
Al.
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
Attachment 6422
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.
Hi again,
Here is the code I'm using..
Master pic using Pic16F877A
Here is the code for the Slave Pic16F88Code:'-------------------------------------------------------------------------------
' 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
The codes I'm using is from Daniel(copy and paste).Code: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
BTW I'm using a simulator...
regards,
tacbanon
Very likely is a simulator problem!
Al.
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
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)
Slave(Pic18F2620)Code: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
As I understood, in the master pic the code below tries to communicate to the Slave having the address $02Code: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
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..Code: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]
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#2Code: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
Slave#2
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.Code: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
Thanks in advance,
tacbanon
Attachment 6430
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.
BTW the code for Slave1 and Slave2 are identical (Slave#1 is address $10 and Slave#2 is address $14)Code: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
What I'm doing wrong in the code? I appreciate very much your help.
Thanks,
Tacbanon
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?
Yes it is able to read.Quote:
If you comment out the first I2CREAD, is it able to read from the second slave?
Nope, displays "0 0 0 0 0 0" for first Slave, 2nd Slave shows "6 5 4 3 2 1"Quote:
If you change the order so that it reads the second slave first, is it then unable to read the first slave?
It sounds like a buffer overflow.
... bad comment deleted ... duh!!
If you post or email your .DSN, I'll take a closer look.
Thanks Darrel, oh sorry to ask this, but how do I send a winrar file?
Thanks mister_e :)
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
Thanks for the info Robert...
and here is the zip file.
Regards,
tacbanon
Oops here are the codes I'm using for the master, Slave1 and Slave 2.
Thanks for the files tacbanon.
Well, this code for the slaves will allow it to read from both devices.
But it's still not right. And is not a complete slave.Code: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
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.
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
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.
Code:'****************************************************************
'* 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!
Hi Charles, thanks for sharing..I will try it out..
tacbanon
@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.
I hope it will be alright if you share the master side code?Code:I2CMajorAddress CON $3
I2CDeviceAddress = $8
'I2CAddress = (I2CMajorAddress <<4) | I2CDeviceAddress
I2CAddress =$14
Thanks,
tacbanon
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.
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.
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
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.
@ 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
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
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
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.:)
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.
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
WIKI I2C
CAT5 CAPACITANCEQuote:
The maximum number of nodes is limited by the address space, and also by the total bus capacitance of 400 pF, which restricts practical communication distances to a few meters.
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
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.