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. It should be "plug and play" in virtually any 18F chip.
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


Menu

Re: 16F690 MCLR as Input
many thanks Henrik to clarify this post.
jackberg1 - 27th October 2025, 20:42that make more sense to me now with further test, when the pin RA3
has nothing connected to it, the input is floating thus
when adding a pulldown it's...