Help writing I2C-1 Slave Event interrupts

General discussion on mikroC PRO for PIC32.
Post Reply
Author
Message
chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Help writing I2C-1 Slave Event interrupts

#1 Post by chris11jed » 12 Jan 2023 07:08

Hello all,

I have some code written (not complete code, just some code), and would just like a second set of eyes to advise if what direction I am headed in is okay, or needs course-correcting please :D

MCU: PIC32MX695F512L

Oscillators:
  • {Primary} 20MHz resonator
  • {Secondary} 32.768kHz resonator
Clock Speed: 100MHz

Power: +3V3

Board (PCB): Custom

General notes:
  • The PIC32MX695F512L MCU is the 'Slave' on the I2C bus
  • I2C-1 is being used
  • 7-bit addressing
  • Mikro I2C Library will be used on the 'Master' MCU
  • I2C speed is 100khz (Set up on 'Master' using I2C-Library)
  • I wish the 'Master' MCU to send the 'Slave' MCU (5) five bytes of data, so that the 'Slave' can then read them, and do stuff with those bytes
Code thus far:

Setting up the I2C module on the 'Slave':

Code: Select all

// ----------------------------------------------------------------------------- Set up I2C as Slave
void Init_I2C_Slave_Mode () {                                                   // Set up as a Slave
  // ---------------------------------------------------------------------------
  I2C1ADD = PIC32_Slave_Address;                                                // const-unsigned-short = 0xD0
  // ---------------------------------------------------------------------------
  I2C1MSK = 0;                                                                  // Must get the address right
  // ---------------------------------------------------------------------------
  STREN_I2C1CON_bit = 1;                                                        // Enable clock stretching
  DISSLW_I2C1CON_bit = 1;                                                       // Disable slew rate control (For 100kHz and 400kHz)
  // ---------------------------------------------------------------------------
  I2C1IS0_bit = 1;                                                              // Interrupt Sub-priority is 3
  I2C1IS1_bit = 1;                                                              //
  // ---------------------------------------------------------------------------
  I2C1IP0_bit = 1;                                                              // Interrupt Priority is 7
  I2C1IP1_bit = 1;                                                              //
  I2C1IP2_bit = 1;                                                              //
  // ---------------------------------------------------------------------------
  I2C1SIE_bit = 1;                                                              // Enable I2C-1 Slave Interrupts
  I2C1SIF_bit = 0;                                                              // Clear Slave Interrupt Flag
  // ---------------------------------------------------------------------------
  ON__I2C1CON_bit = 1;                                                          // Enable the I2C-1 module
  // ---------------------------------------------------------------------------
}
Setting up the interrupt section:
(I used the inbuilt 'Interrupt Assistant' for this)

Code: Select all

// ----------------------------------------------------------------------------- Interrupts Service Routine Area
void Interrupt_I2C1_Slave() iv IVT_I2C_1 ilevel 7 ics ICS_SRS {                 // Slave Event on I2C-1
// Code to service the interrupt goes here
}
Questions:
  1. For the I2C Slave module set-up code, do I also need to configure INTCON register too? Or by using the 'Interrupt Assistant' tool, has that been handled already? OR, is the only worthwhile thing in INTCON the SSO and MVEC bits (single or multi vectoring), which is configured in the 'Edit Project' window already?
  2. While using the 'Interrupt Assistant' tool, I chose I2C_1 "iv IVT_I2C_1", but there was an option of I2C_1A "iv IVT_I2C_1A" as well. What is the difference between these two? Is one for 'Master Events' and the other for 'Slave Events'? Which one should I have used for 'Slave Events'?
  3. The PDF's have a LOT of information. Almost too much, while also ironically, not enough. Makes my head hurt :roll: :lol: However, thus far, is this enough framework code to get the MCU into 'Slave' mode and launching interrupts when the 'Master' sends bytes? (Yes, I know I have to put code into the ISR, to sort out the bytes coming in, as well as to handle the clock-stretching too)
This is where I am at, and would love to know that what code I do have (here) would enable the MCU to function as a slave, throw up interrupts when the address and following data bytes come in.

Thank you for any tips and tricks, or, okay-ing what I've put forth here.
8)

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Help writing I2C-1 Slave Event interrupts

#2 Post by chris11jed » 13 Apr 2023 03:00

[SOLVED] - Well, partially. I got the two PIC's I am working on to talk.

PIC's:
  1. PIC18F45K22 (I2C Master)
  2. PIC32MX695F512L (I2C Slave)
Power:
  1. PIC18; +5V
  2. PIC32; +3V3
Extras: - Level Shifter (Adafruit 4-channel I2C-safe Bi-directional Logic Level Converter - BSS138)

Issues:
  1. On the PIC32 side; What was the difference between the interrupt-assistant generated term "iv IVT_I2C_1" and "iv IVT_I2C_1A"
  2. On the PIC32 side; Which of those two terms to use, as part of the ISR
  3. On the PIC32 side; Getting the I2C Slave stuff to work, and via a 'Slave Interrupt'
  4. On the PIC18 side; Would the Level-Shifter work, converting the +5V I2C clock and data, into usable +3V3 I2C clock and data for the PIC32 to see and react to.
Basic circuit:
NOTE: Basic timing, caps, and any associated componets to make either PIC18 or PIC32 have been ommitted in this image. In real life, I used a 16MHz crystal + caps for the PIC18 and a 20MHz crystal-resonator + caps for the PIC32 (as well as all decoupling caps for both too).
Image

Issues answered:
  1. The difference between "iv IVT_I2C_1" and "iv IVT_I2C_1A" is unanswered.
  2. When testing the PIC32 code, "iv IVT_I2C_1" worked, and "iv IVT_I2C_1A" didn't do anything.
  3. See code examples below for what worked.
  4. The Level-Shifter does work. Tested with buttons on the PIC18 lighting LEDs on the PIC32, and vise-versa. As well, obviously, with I2C at 100kHz
I2C Code:
- The PIC18 Master uses the mikroBASIC I2C library, to send five bytes to the I2C-slave

Code: Select all

const PIC32_Slave_Address as byte = 0xA0                                       ' Slave Address on PIC32 is 0xD0; 0xD0 << 1 = 0xA0
...and...

Code: Select all

I2C_Init(100000)
...and...

Code: Select all

I2C1_Start()
I2C1_Wr(PIC32_Slave_Address)
I2C1_Wr(Data_Byte_1)
I2C1_Wr(Data_Byte_2)
I2C1_Wr(Data_Byte_3)
I2C1_Wr(Data_Byte_4)
I2C1_Wr(Data_Byte_5)
I2C1_Stop()
- I2C-1 set-up code on PIC32:

Code: Select all

const unsigned short PIC32_Slave_Address = 0xD0;
...and...

Code: Select all

  // --------------------------------------------------------------------------- I2C Slave
  SCL_1_PIC18_Direction = 0;                                                    // Config SCL and SDA as outputs (probably doesn't matter once module is ON)
  SDA_1_PIC18_Direction = 0;
  // ---------------------------------------------------------------------------
  SCL_1_PIC18 = 0;                                                              // Clear I2C pins
  SDA_1_PIC18 = 0;
  // ---------------------------------------------------------------------------
  ODCA14_bit = 1;                                                               // Configure I2C pins as Open-Drain (probably doesn't matter once module is ON)
  ODCA15_bit = 1;
  // ---------------------------------------------------------------------------
  SIDL_I2C1CON_bit = 0;                                                         // Continue module ops when in idle
  // ---------------------------------------------------------------------------
  SCLREL_I2C1CON_bit = 1;                                                       // Release SCL
  // ---------------------------------------------------------------------------
  STRICT_I2C1CON_bit = 0;                                                       // Strict I2C reserved addresses rule is not enabled
  // ---------------------------------------------------------------------------
  A10M_I2C1CON_bit = 0;                                                         // 7-bit Address used in I2C1ADD
  // ---------------------------------------------------------------------------
  DISSLW_I2C1CON_bit = 0;                                                       // Slew rate enabled for High Speed Mode (400kHz Default value. Am using 100kHz)
  // ---------------------------------------------------------------------------
  SMEN_I2C1CON_bit = 0;                                                         // Disable SMBus specific inputs
  // ---------------------------------------------------------------------------
  GCEN_I2C1CON_bit = 0;                                                         // General call addresses disabled
  // ---------------------------------------------------------------------------
  STREN_I2C1CON_bit = 1;                                                        // Enable clock stretching
  // ---------------------------------------------------------------------------
  ACKDT_I2C1CON_bit = 0;                                                        // ACK is sent (probably not required here?)
  // ---------------------------------------------------------------------------
  ACKEN_I2C1CON_bit = 0;                                                        // Acknowledge sequence is idle (Master mode: not required here)
  // ---------------------------------------------------------------------------
  RCEN_I2C1CON_bit = 0;                                                         // Receive sequence not in progress (Master mode: not required here)
  // ---------------------------------------------------------------------------
  PEN_I2C1CON_bit = 0;                                                          // Stop condition is idle (Master mode: not required here)
  // ---------------------------------------------------------------------------
  RSEN_I2C1CON_bit = 0;                                                         // Restart condition is idle (Master mode: not required here)
  // ---------------------------------------------------------------------------
  SEN_I2C1CON_bit = 0;                                                          // Start condition is idle (Master mode: not required here)
  // ---------------------------------------------------------------------------
  I2C1ADD = PIC32_Slave_Address;                                                // Const-unsigned-short = 0xD0 is this MCUs I2C Slave Address
  // ---------------------------------------------------------------------------
  I2C1MSK = 0;                                                                  // Master must get the I2C Slave Address right
  // ---------------------------------------------------------------------------
  I2C1IS0_bit = 1;                                                              // Interrupt Sub-priority is 3
  I2C1IS1_bit = 1;                                                              //
  // ---------------------------------------------------------------------------
  I2C1IP0_bit = 1;                                                              // Interrupt Priority is 7
  I2C1IP1_bit = 1;                                                              //
  I2C1IP2_bit = 1;                                                              //
  // ---------------------------------------------------------------------------
  I2C1MIF_bit = 0;                                                              // Clear 'Master Interrupt' flag (IFS0.B31)
  I2C1SIF_bit = 0;                                                              // Clear 'Slave Interrupt' Flag (IFS0.B30)
  I2C1BIF_bit = 0;                                                              // Clear 'Bus Collision Fault' Flag (IFS0.B29)
  I2C1SIE_bit = 1;                                                              // Enable I2C-1 Slave Interrupts
  // ---------------------------------------------------------------------------
  EnableInterrupts();                                                           // General interrupt enable code
  // ---------------------------------------------------------------------------
  ON__I2C1CON_bit = 1;                                                          // Enable the I2C-1 module
  // ---------------------------------------------------------------------------
- I2C-1 ISR:

Code: Select all

void Interrupt_I2C1_Slave() iv IVT_I2C_1 ilevel 7 ics ICS_SRS {                 // Slave Event on I2C-1
  // ---------------------------------------------------------------------------
  unsigned short I2C_Holder_File = 0;
  unsigned short I2C_Index = 0;
  // ---------------------------------------------------------------------------
  if (DA_I2C1STAT_bit == 0) {                                                   // Address was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow
    I2C_Index = 0;                                                              // Clear counter
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line

  } else if ((DA_I2C1STAT_bit == 1) && (I2C_Index == 4)) {                      // Data was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow and hold new data
    // -- Do whatever with data in 'I2C_Holder_File' --
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line
    I2C_Index = 5;                                                              // Update counter
    Update_LCD_Flag = 0xFF;                                                     // Tell LCD update code new data is ready

  } else if ((DA_I2C1STAT_bit == 1) && (I2C_Index == 3)) {                      // Data was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow and hold new data
    // -- Do whatever with data in 'I2C_Holder_File' --
    I2C_Index = 4;                                                              // Update counter
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line

  } else if ((DA_I2C1STAT_bit == 1) && (I2C_Index == 2)) {                      // Data was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow and hold new data
    // -- Do whatever with data in 'I2C_Holder_File' --
    I2C_Index = 3;                                                              // Update counter
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line

  } else if ((DA_I2C1STAT_bit == 1) && (I2C_Index == 1)) {                      // Data was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow and hold new data
    // -- Do whatever with data in 'I2C_Holder_File' --
    I2C_Index = 2;                                                              // Update counter
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line

  } else if ((DA_I2C1STAT_bit == 1) && (I2C_Index == 0)) {                      // Data was last received
    I2C1SIF_bit = 0;                                                            // Clear Slave Flag
    I2C1BIF_bit = 0;                                                            // Clear Error (buffer-overflow) Flag
    I2C_Holder_File = I2C1RCV;                                                  // Prevent Buffer overflow and hold new data
    // -- Do whatever with data in 'I2C_Holder_File' --
    I2C_Index = 1;                                                              // Update counter
    SCLREL_I2C1CON_bit = 1;                                                     // Set, to release clock line
  }
  // ---------------------------------------------------------------------------
}
Comments:
- Took a lot of hair-pulling, but this basic code works.
- Biggest wtf moment was working out the addressing; The master should left-shift the address by 1. Using the same address byte on both MCU's didn't freeze anything. But if I right-shifted the address on the master, or used some other address, the Master froze. Weird. But, it works when left-shifted. Which... doesn't come as a complete surprise, as I do this on Arduino's ...so, there ya go :wink: :lol:
- In the ISR code for the PIC32, I did use different variables to read I2C1RCV, but have just used I2C_Holder_File here (along with the comment under those lines stating 'do whatever' with this data).
- I haven't done any work with buffer overloads or anything like that, where if something happened, it'd throw an error. This 'solved' post-reply is just a "I got it working" type-o-deal.
- The level shifter was a safety precaution thing. I have had weird results with sticking +5V onto supposedly "5V Tolerant" pins on the PIC32MX695F512l before (namely, hooking up a keyboard powered via 5V and the MCU acting weird as a result), so just used this level shifter. As a bonus to using it, it can also be used for other things than just I2C too!
- This is just for 'Master writes to Slave'. That's all I needed. I haven't played around with 'Master reads from Slave', so don't know if the addressing is the same type of deal or not (probably is, idk).

Thanks
Chris 8)

Post Reply

Return to “mikroC PRO for PIC32 General”