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!
Bookmarks