PIC32MK1024MCM064 would not send I2C ACK in master mode

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
169
Hi,



I am doing a project involving mentioned PIC and ESP32S3 communication via I2C, when PIC is master, ESP is slave. I have already accomplished the master write scenario, it works with no problems. But now I want to do a master read, and the problem is that the PIC would not send any ACK/NACK bit after the slave has sent a data byte. My friend is writing the code for ESP32, he has written a test code putting exact data byte into the I2C buffer that we see in the logic analyzer, also the ESP32 sends correct ACK bit after it's address, so it is very unlikely there are any problems with the ESP32. However, the testing code is designed to test PIC32 reading one data byte from the ESP32, so the PIC should send a NACK after the last data bit has been sent from the ESP. As you can see in the logic analyzer, there is no NACK bit, also the ninth SCL pulse is missing, even though I have set the corresponding ACKDT and ACKEN bits of the I2CCON register. Wiring is correct, 5.1k pull-ups, 100 kbits (but the problem has to be in the software...) Any observations?

main.c
main.c:
#include <xc.h> 
    #include "configurations_bits.h"                                                                 
           
    #include <stddef.h>                                                                               
    #include <stdint.h>                    
    #include <stdbool.h>                    
    #include <stdlib.h> 
    #include "stdio.h" 
    #include <sys/attribs.h> 
 
    #include "delay.h"                                                                               
    #include "inter_integrated_circuit_protocol.h" 
    #include "liquid_crystal_display.h" 
    #include "pulse_width_modulation.h" 
    #include "timer.h" 
    #include "state_change_interrupts.h" 
    #include "analog_to_digital_conversion.h" 
 
 
    #define ESP32_WRITE (0B01100000)
    #define ESP32_READ  (0B01100001)   
    uint8_t test = 0;
 
    int main(void) {
 
    Inter_Integrated_Circuit_Setup ();                                                       
    Inter_Integrated_Circuit_Enable ();   
 
    delay_ms(400);
         
   Inter_Integrated_Circuit_Start();
   Inter_Integrated_Circuit_Write(ESP32_READ);   
   test = Inter_Integrated_Circuit_Read(1);
   Inter_Integrated_Circuit_Stop();
 
    while (true){  
   }
              
      return (EXIT_FAILURE);  
  }
inter_integrated_circuit_protocol.c:
 #include <xc.h>                                                                                 
 
 
  #include <stdint.h>
 
 
  #include "inter_integrated_circuit_protocol.h" 
  
  #include "delay.h"
 
 
    void Inter_Integrated_Circuit_Setup (void){
 
        I2C1BRG = 0xEC;                                                                       
        I2C1CON = 0x00000000;                                                                     
        I2C1CONbits.SDAHT = 0b0;                                                                    
        I2C1CONbits.SIDL = 0b0;                                                                    
        I2C1CONbits.SCLREL = 0b1;                                                                  
        I2C1CONbits.STREN = 0b0;                                                                   
        I2C1CONbits.DISSLW = 0b1;                                                                  
        I2C1CONbits.SMEN = 0b0;                                                                     
    }
 
 
    void Inter_Integrated_Circuit_Enable (void){
        
        I2C1CONbits.ON = 0b1;                                                                      
    }
    
     void Inter_Integrated_Circuit_Disable (void){
        
        I2C1CONbits.ON = 0b0;                                                                      
    }
     
   void Inter_Integrated_Circuit_State (void){
       
        while( (I2C1CON & 0x0000001F) || (I2C1STAT & 0x00000004) );                                
   }
   
    void Inter_Integrated_Circuit_Start (void){
        
        Inter_Integrated_Circuit_State ();                                                         
        I2C1CONbits.SEN = 0b1;                                                                     
    }
 
 
    void Inter_Integrated_Circuit_Stop (void){
        
        Inter_Integrated_Circuit_State ();                                                        
        I2C1CONbits.PEN = 0b1;                                                                     
    }
    
    void Inter_Integrated_Circuit_Repeated_Start (void){
        
        Inter_Integrated_Circuit_State ();
        I2C1CONbits.RSEN = 0b1;                                                                    
    }
 
 
    void Inter_Integrated_Circuit_Write (uint8_t I2C_data){
        
        Inter_Integrated_Circuit_State ();                                                         
        while(I2C1STATbits.TBF != 0);                                                                                                                        
        I2C1TRN = I2C_data;
        while(I2C1STATbits.TRSTAT != 0);  
        if(I2C1STATbits.ACKSTAT == 0){    
        }
        else delay_ms(1000);
    }
 
 
    uint8_t Inter_Integrated_Circuit_Read (uint8_t LastRead){
        
        uint32_t I2C_raw_data = 0;
        uint8_t I2C_data = 0;
        
        Inter_Integrated_Circuit_State ();                                                         
        I2C1CONbits.RCEN = 0b1;                                                                     
        while(I2C1STATbits.RBF != 0); 
        
        I2C_raw_data = I2C1RCV;        
        I2C_data = (I2C_raw_data & 0x000000FF);
 
 
        if(LastRead){
                I2C1CONbits.ACKDT = 0b1;   //Set NACK for acknowledge sequence
                I2C1CONbits.ACKEN = 0b1;   //Initiate acknowledge sequence
            }
            else { 
                I2C1CONbits.ACKDT = 0b0;  //Set ACK for acknowledge sequence
                I2C1CONbits.ACKEN = 0b1;  //Initiate acknowledge sequence
            }
        
        return (I2C_data);
     }
Logic_diagram.jpg
 

Papabravo

Joined Feb 24, 2006
21,159
IIRC the master is responsible for all of the SCL pulses. Also refresh my memory on the difference between an ACK and a NACK. My memory says that an ACK is where a device pulls SDA low, and a NACK is where it remains at a level established by the pullup. It has been over a decade since I last did this so I could be wrong.
 

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
169
IIRC the master is responsible for all of the SCL pulses. Also refresh my memory on the difference between an ACK and a NACK. My memory says that an ACK is where a device pulls SDA low, and a NACK is where it remains at a level established by the pullup. It has been over a decade since I last did this so I could be wrong.
Yes, you are right. So my main problem is that the acknowledgement sequence is not getting triggered. As I am reading only one byte, the PIC should generate ninth scl pulse, pull the sda low for a short period and then release the SDA (sending NACK to the slave, indicating the end of transaction). But why is the sequence not triggering?
 

nsaspook

Joined Aug 27, 2009
13,086
My pic model is not in the table. Moreover, according to the document, the I2C problems could only appear at 400 kbits + speed
I listed both the MCM and MCJ models of the PIC32MK. The MCF version of the PIC32MK is effectively obsolete.

There is a 7-bit address issue that's a known problem with some i2c chips.

Edit: correct obsolete part to MCF.
 
Last edited:

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
169
I listed both the MCM and MCJ models of the PIC32MK. The MCM version of the PIC32MK is effectively obsolete.

There is a 7-bit address issue that's a known problem with some i2c chips.
So the ESP sends and ACK after the address has been sent. In this case, esp would not respond or send the data byte if there was a problem with the address, the problem has to be with the PIC's i2c read function (Pic should generate extra scl clock and send the ACK/NACK bit), but after experimenting for a long time, I can not come up with a solution
 

geekoftheweek

Joined Oct 6, 2013
1,201
While I am not familiar with the PIC32 family itself it does look like your program is right from what I could find in the datasheet / reference manual.

I did notice here https://ww1.microchip.com/downloads/en/DeviceDoc/60001116G.pdf on page 28 it mentions in section 24.5.4 that the five least significant bits of I2CxCON must be zero before setting ACKEN. It does seem that it would naturally work out that way after a receive operation, but maybe might be worth checking just in case the datasheet is wrong and it takes an extra couple of cycles to settle.

I would suspect for some reason the ESP is holding the clock line low for some strange reason. Just because it works correct after the address byte doesn't mean it's going to work afterwards. Is there a way to connect to something else and check your I2C code with say an EEPROM or something to see if the PIC is right?
 

geekoftheweek

Joined Oct 6, 2013
1,201
Alright, I solved it, the problem was the extended delay before the ACK sequence
I've been bit there too once or twice...

It looks like there may be a way to alter the timeout on the ESP32. You may want to investigate that or consider using an interrupt on the PIC instead of polling. Sooner or later you will probably run into the problem again as your program grows.
 
Top