A short video:
The master on the left, a 16F877 is telling the first 16F88 (on the top) to turn on/off the LED, and then reads 24 from it. After that, the master tells the second 16F88 (on the bottom) to turn on/off the LED, and then reads 12 from it.
It's in slow motion because I was testing everything, but I discovered that the I2C protocol is very fast!
Al, thanks a lot one more time!
Daniel.
Master 16f877 - External 4Mhz Crystal:
'------------------------------------------------------------------------------- '------------------------------------------------------------------------------- ' 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 "Writting 1 - LED ON..." Pause 500 I2CWrite SDA, SCL, I2Caddress, [6], bogus ' Write offset to slave Pause 500 LCDOut $fe, 1 LCDOut "Writting 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 "Value: ", DEC valor Pause 500 '-------- I2Caddress = $5 LCDOut $fe, 1 LCDOut "Writting 2 - LED ON..." Pause 500 I2CWrite SDA, SCL, I2Caddress, [4], bogus ' Write to slave Pause 500 LCDOut $fe, 1 LCDOut "Writting 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 "Value: ", DEC valor Pause 500 '-------- GoTo main ' Do it forever '------------------------------------------------------------------------------- bogus: LCDOut $fe,1, "Time out" ' I2C command timed out Pause 500 GoTo main '------------------------------------------------------------------------------- End '------------------------------------------------------------------------------- '-------------------------------------------------------------------------------
'------------------------------------------------------------------------------ '------------------------------------------------------------------------------ ' Slave '------------------------------------------------------------------------------ '------------------------------------------------------------------------------ @ device pic16f88, intrc_osc_noclkout ' system clock options @ device pic16f88, wdt_on ' watchdog timer @ device pic16f88, pwrt_on ' power-on timer @ device pic16f88, mclr_off ' master clear options (internal) @ device pic16f88, protect_off ' code protect off '--- set device --------------------------------------------------------------- DEFINE OSC 8 OSCCON = %01110001 '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: 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 '------------------------------------------------------------------------------ '------------------------------------------------------------------------------
'------------------------------------------------------------------------------ '------------------------------------------------------------------------------ ' Slave '------------------------------------------------------------------------------ '------------------------------------------------------------------------------ @ device pic16f88, intrc_osc_noclkout ' system clock options @ device pic16f88, wdt_on ' watchdog timer @ device pic16f88, pwrt_on ' power-on timer @ device pic16f88, mclr_off ' master clear options (internal) @ device pic16f88, protect_off ' code protect off '--- set device --------------------------------------------------------------- DEFINE OSC 8 OSCCON = %01110001 '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 $5 ' Make address = 5 '--- 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: txbuffer = 24 IF SSPIF = 1 Then GoSub i2cslave EndIF SSPOV = 0 WCOL = 0 Select Case RxBuffer[0] Case 4 High LEDT Case 10 Low LEDT End Select WrData=0 GoTo Main '------------------------------------------------------------------------------ End '------------------------------------------------------------------------------ '------------------------------------------------------------------------------
Re: PBP3 Optimization & Peripheral Control Techniques on PIC16F/18F Devices
Yes, all the time and most times in conjuction with DT-Ints for PBP context save/restore. For periodic/cyclic interrupts using TMR2 (or one of the same type) is nice because you set it up once and...
HenrikOlsson - 22nd May 2025, 15:27