This may help. Here is a fairly full-featured interrupt-driven I2C slave. The master part is easy.
This slave relies on the work of many others, but I have added some bug fixes and extra features. For example, you can write
0xAA,0X55,0x00,**your new address here**,0x00
and you can change the address of the slave.
Like my other stuff, it ONLY works on 18F chips.
Code:
ASM
ifndef __18F2321
error "18F2321 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,$38 ; Default I2C address if not determined in HW
DEFINE OSC 4
Define USE_LFSR 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
MasterClock VAR WORD BANKA SYSTEM
'------------------- Buffer defintion --------------------
RxBufferLEN con 10
RxBuffer var BYTE[Rxbufferlen + 1]
TxBufferLEN CON 10
TXBuffer VAR BYTE[TxBufferLEN + 1]
GeneralCall VAR BIT
RXBufferOverFlow VAR BIT
TxBufferUnderFlow VAR BIT
HWAddress VAR BIT
RXBufferPointer VAR Byte
TXBufferPointer VAR Byte
address VAR BYTE
x VAR BYTE
dummy var byte
I2CAddress VAR BYTE
WCOLCounter VAR BYTE
I2CDeviceAddress VAR BYTE
Holdoff VAR WORD ; retry timer
HWAddress = 0 ; Get address from EEPROM, not pins
TestArray VAR BYTE [200]
IF !HWAddress THEN
;----------- Get our Address from EEPROM --------------
READ 0,I2CAddress
;-----------------------------------------------------
ELSE
;----------------OR Get our address from PORT B0..2-----------
I2CDeviceAddress = PORTB & %00000111
I2CMajorAddress CON $3 ; upper 4 bits
I2CAddress = (I2CMajorAddress <<4) | I2CDeviceAddress <<1 ; bit 0 is R/W
;---------------------------------------------------------------
Endif
SSPADD = I2Caddress ' Move our address into the proper reg
SSPCON2.0 = 1 ; Allow clock stretching
SSPCON2.1 = 0 ; Set to 1 to enable masking of address 1 ; 0 is R/W bit
SSPCON2.2 = 0 ; Set to 1 to enable masking of address bit 2
SSPCON2.3 = 0 ; Set to 1 to enable masking of address bit 3
SSPCON2.4 = 0 ; Set to 1 to enable masking of address bit 4
SSPCON2.5 = 0 ; Set to 1 to enable masking of address bit 5
SSPCON2.6 = 0 ; Not used in slave
SSPCON2.7 = 1 ; General Call enabled
SSPSTAT = 0
SSPEN = 1
SSPIE = 1
SSPIF = 0
SSPCON1 = $36 ' Set to I2C slave with 7-bit address
;---------------------------------------------------------------
TRISA = %11111111
TRISB = %11111111
TRISC = %11111111
' -------------- Turn on Ints ------------------------
INTCON.7 = 1 ; global ints
INTCON.6 = 1 ; peripheral ints
IPR1.3 = 1 ; high priority
;------------------ Clear parameters --------------
RXBufferPointer = 0
TxBufferPointer = 0
GeneralCall = 0
RxBufferOverFlow = 0
TXBufferUnderFlow = 0
WCOLCounter = 0
;----------------Timer Setup-----------------------------------
T0CON = %10000011 ; /16 prescaler
;-------- -------------------------------------------
WDTCON = 1 ; Turn on the WDT
;--------------------------------------------------------------
INCLUDE "DT_INTS-18.bas" ' Thanks Darrel!
INCLUDE "ReEnterPBP-18.bas" '
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler SSP_INT, _I2C_Int, PBP, yes
INT_Handler TMR0_INT, MainTimer, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
endasm
''----------------- Initialization Done! -----------------------------
Goto OverInt ; jump over ISR, don't execute it.
;----------------------- Timer INt ------------------------------------
ASM
MainTimer
movlw 0x00 ; 10 mSec
movwf TMR0H
movlw 0x63
movwf TMR0L
infsnz MasterClock
incf MasterClock + 1
; This is a good place to pick up pin changes
INT_ENABLE TMR0_INT
INT_RETURN
ENDASM
;---------------------------------------------------------------------
'------------------------- I2C subroutine --------------------------------
I2C_Int:
; We don't get here uless someone has matched our address register.
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 Address from buffer to clear it.
IF Address = 0 THEN
GeneralCall = 1 ; Set flag if we got a general call address
ELSE
GeneralCall = 0
ENDIF
else
IF WCOL OR SSPOV THEN I2CExit
IF RXBufferPointer < RxBufferLen - 1 THEN
RXBuffer [RxBufferPointer] = SSPBuf ;
RxBufferPointer = RXBufferPointer + 1
ELSE
Dummy = SSPBuf ; Grab the data but don't overwrite old
RXBufferOverFlow = 1
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!
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 >= 7 Then goto I2CExit ; 7
RANDOM Holdoff
Pauseus Holdoff >> 3 ; wait 0-8 msec
Goto SendI2C ; Retry
ELSE
WColCounter = 0
ENDIF
TXBufferPointer = TXBufferPointer + 1
If TXBufferPointer = TXBufferLen THEN
TXBufferUnderflow = 1
TXBufferPointer = TXBufferLen - 1 ; Repeat last if req made again, but set flag now
ENDIF
ENDIF
I2CEXIT:
SSPOV = 0
BF = 0
sspif = 0
CKP = 1
@ INT_ENABLE SSP_INT ; probably don't need this, but I like to be certain it gets restarted
@ INT_RETURN
' -------------------------end I2C subroutine --------------------------------
'--------------------[End ISRs]---------------------------------------
OverInt:
@ INT_ENABLE SSP_INT
@ INT_ENABLE TMR0_INT
'--------------------[ main program loop]---------------------------------------
Main:
IF RXBuffer[0] = $AA THEN
IF RXBuffer[1] = $55 and RXBuffer[2] = 0 And RxBuffer[4] = 0 THEN
Dummy = RXBuffer[3]
WRITE 0,Dummy ; Write a new address
Pause 10
@ RESET ; and pick up the new address
ENDIF
ENDIF
;------------------- Load the input buffer to the output buffer for test -----------
For X = 0 to TXBufferLen - 1
TxBuffer[X] = RXBuffer[X]
next x
;----------------------------------------------------------------------
goto main
end
ASM
ORG 0x0FF0 ; Keep track of pgm size in case we want to use it on a smaller chip
NOP
ENDASM
Bookmarks