ADICUP3029/AD5940 I2C issues

Dear Analog (),


I am using the AD5940_BIOZ-2Wire example with the evaluation board for AD5940 (+ADICUP3029). I am attempting to write I2C commands to an external GPIO expander, but the code gets stuck. 

Initially I set up an I2C handle inside AD5940Main.c as seen below:

  /* Setup I2C */
  /* device handle */
  ADI_I2C_HANDLE i2cDevice;
  
  //Dont use pinmux since i2c pins are already defined in adi_i2c_v1.c
  //adi_initpinmux();
  
  /* check set speed API */
  if(ADI_I2C_SUCCESS == adi_i2c_Open(0, ADI_I2C_MASTER, &devMem, ADI_I2C_MEMORY_SIZE, &i2cDevice)){
    printf("I2C Open");
  }
  if(ADI_I2C_SUCCESS == adi_i2c_SetBitRate(i2cDevice, 400)){
    printf("I2C BitRate Set");  // top-end
  }
  if(ADI_I2C_SUCCESS == adi_i2c_SetHardwareAddress(i2cDevice, 0x20)){
    printf("I2C Addr Set");
  }

This code completes, but when I just after attempt to write on I2C using: adi_i2c_Write(i2cDevice, (unsigned char*)&Regs->Output, 1); this function gets stuck in I2C_BlockingIO() in adi_i2c_v1.c on this line:

/* Pend for the semaphore */
PEND_EVENT(DATA,ADI_I2C_ERR_RTOS);

I don't know where this semaphore would've been defined earlier in the code since the only place I can find where it otherwise could've been defined is in adi_i2c_Open(), also in adi_i2c_v1.c:

#if(ADI_CFG_ENABLE_RTOS_SUPPORT == 1)
/* create a semaphore for buffer management */
CREATE_SEM(drv->pData,ADI_I2C_ERR_RTOS);
#endif

I don't have ADI_CFG_ENABLE_RTOS_SUPPORT defined in the IAR Linker. I've tried simply out-commenting the PEND_EVENT, after which the code runs, but no data is seen on SDA or SCL.

Any suggestions on solving the issue much appreciated!

Thanks,

Andreas



Edited problem title to reflect updated problem description
[edited by: andyy at 10:07 PM (GMT 0) on 6 Dec 2018]
Parents
  • I have made a bit of progress, but still having issues making it work correctly. ()

    Since the example code for the AD5940/ADICUP3029 combo does not use an adi_pinmux.c file, and for some reason instead sets up the peripherals (UART/SPI/GPIO) by setting individual bits in relevant registers explicitly in AD5940_MCUResourceInit(), and UrtCfg(), I only just realized that the same thing has to be done to enable I2C on the I2C pins. Setting this up in a pinmux file, does enable the I2C which is now active!

    #include <ADuCM3029.h>
    
    #define I2C0_SCL0_PORTP0_MUX  ((uint16_t) ((uint16_t) 1<<8))
    #define I2C0_SDA0_PORTP0_MUX  ((uint16_t) ((uint16_t) 1<<10))
    
    int32_t adi_initpinmux(void);
    
    int32_t adi_initpinmux(void) {
        /* Port Control MUX registers */
        *pREG_GPIO0_CFG = I2C0_SCL0_PORTP0_MUX | I2C0_SDA0_PORTP0_MUX;
    
        return 0;
    }

    However, this breaks the SPI and UART communication, and halts the code again! I suspect since the config data (pADI_GPIO0) for these are overwritten when I define I2C in the pinmux file. Is there an easier way to make I2C behave without writing abstract init code as that which initializes SPI/UART (in AD5940_MCUResourceInit() in ADICUP3029Port.c and UrtCfg() in main.c)?

    Thanks!

    Andreas

  • 0
    •  Analog Employees 
    on Dec 12, 2018 7:58 AM over 1 year ago in reply to andyy

    Hello,

    Could you try:

    *pREG_GPIO0_CFG |= I2C0_SCL0_PORTP0_MUX | I2C0_SDA0_PORTP0_MUX;

    This should set the mux for the I2C pins without affecting the other pins.

    Also, what versions of DFP and BSP are you using? I will have a look at the problem with the semaphore too.

    Regards,

    Andrei.

  • Hi Andrei,

    I'm using v0.1.6 of the Evaluation Software for the AD5940 eval kit

    As mentioned adding the pinmux.c file configuring I2C with the line you've pasted above (but without the "|=") fixes the I2C communication for a single cycle, after which the code gets stuck in ADICUP3029Port.c:

    uint32_t AD5940_GetMCUIntFlag(void)
    {
       return ucInterrupted;
    }

    I assume, due to the interrupt service routine, also in ADICUP3029Port.c, never being called, and therefore never setting ucInterrupted to 1:

    /* MCU related external line interrupt service routine */
    void Ext_Int0_Handler()
    {
       pADI_XINT0->CLR = BITM_XINT_CLR_IRQ0;
       ucInterrupted = 1;
      /* This example just set the flag and deal with interrupt in AD5940Main function. It's your choice to choose how to process interrupt. */
    }

    So it seems like adding:

     

      *pREG_GPIO0_CFG = I2C0_SCL0_PORTP0_MUX | I2C0_SDA0_PORTP0_MUX;

    Activates I2C but breaks the external line interrupt which ADICUP3029 uses for SPI communication with the AD5940. All I need is to be able to run the "AD5940_BIOZ-2Wire" example from the Evaluation Software above with I2C communication to a different peripheral simultaneously.

    If I write the line as you propose, adding the "or" (|=):

     

    *pREG_GPIO0_CFG |= I2C0_SCL0_PORTP0_MUX | I2C0_SDA0_PORTP0_MUX;

    The code breaks in startup.c, on: 

    WEAK_FUNCTION( HardFault_Handler        )

    Giving me the following fault in the "Fault exception viewer":

    HardFault exception.

    The processor has escalated a configurable-priority exception to HardFault.

       An imprecise data access error has occurred.

    Exception occured at: 0x66b2

    See the call stack for more information.

    Best,

    Andreas

  • 0
    •  Analog Employees 
    on Dec 13, 2018 8:33 AM over 1 year ago in reply to andyy

    Hello Andreas,

    If you do not "or" the I2C bits in the pin mux you are basically resetting the other pin muxes on port 0 (like the SPI ones) back to 0 (most likely the GPIO setting) and that is why the SPI and UART stop working. By adding the "or" you only set the muxes for the I2C ones leaving the rest untouched.

    Commenting out the semaphore function should not be necessary. I believe that is for a resource manager running in the background and could be the cause of the hard-fault when two devices are trying to access the same memory resource at the same time (like I2C and SPI).

    These problems can be caused by not having enough memory for the I2C driver. What is "devMem" in the code? It should be a memory array the size of ADI_I2V_MEMORY_SIZE like so:

    /* Device memory required for operate Master I2C device */
    uint8_t devMem[ADI_I2C_MEMORY_SIZE];

    Most drivers use these kinds of memory arrays and some of them must be 32 bit aligned. The I2C and SPI don't have to be, but the UART one does have to be 32 bit aligned.

    Regards,

    Andrei.

Reply
  • 0
    •  Analog Employees 
    on Dec 13, 2018 8:33 AM over 1 year ago in reply to andyy

    Hello Andreas,

    If you do not "or" the I2C bits in the pin mux you are basically resetting the other pin muxes on port 0 (like the SPI ones) back to 0 (most likely the GPIO setting) and that is why the SPI and UART stop working. By adding the "or" you only set the muxes for the I2C ones leaving the rest untouched.

    Commenting out the semaphore function should not be necessary. I believe that is for a resource manager running in the background and could be the cause of the hard-fault when two devices are trying to access the same memory resource at the same time (like I2C and SPI).

    These problems can be caused by not having enough memory for the I2C driver. What is "devMem" in the code? It should be a memory array the size of ADI_I2V_MEMORY_SIZE like so:

    /* Device memory required for operate Master I2C device */
    uint8_t devMem[ADI_I2C_MEMORY_SIZE];

    Most drivers use these kinds of memory arrays and some of them must be 32 bit aligned. The I2C and SPI don't have to be, but the UART one does have to be 32 bit aligned.

    Regards,

    Andrei.

Children
  • Hi Andrei,

    Thanks for the quick response! 

    It definitely makes sense to "or" them, also looking at the way SPI and UART is set up in ADUCUO3029.c and main.c respectively.

    I already have the device memory defined in AD5940Main.c, where I've chosen to initialize the I2C:

    /* device memory */
      uint8_t devMem[ADI_I2C_MEMORY_SIZE];

    After this I define the I2C pins by runnning the pinmux function that defines the I2C in the way we've discussed above (by "or-ing" with the registry): 

    /* Initialize I2C pins */
      adi_initpinmux();

    After this I setup the I2C device: 

    /* Setup I2C */
      /* device handle */
      ADI_I2C_HANDLE i2cDevice;
      
      /* check set speed API */
      if(ADI_I2C_SUCCESS == adi_i2c_Open(0, ADI_I2C_MASTER, &devMem, ADI_I2C_MEMORY_SIZE, &i2cDevice)){
        printf("I2C Open");
      }
      
      if(ADI_I2C_SUCCESS == adi_i2c_SetBitRate(i2cDevice, 100)){
        printf("I2C BitRate Set");  // top-end
      }
    
      if(ADI_I2C_SUCCESS == adi_i2c_SetHardwareAddress(i2cDevice, 0x20)){
        printf("I2C Addr Set");
      }
    
      if(ADI_I2C_SUCCESS == adi_i2c_Enable(i2cDevice,true)){
        printf("I2C Enabled");
      }

    And transmit the first bytes to my peripheral (a GPIO expander):

    // Initialize GPIO Expander
      TCA6408AInitDefault((TCA6408ARegs*)&TCA6408A_Reg_map);
      // Set all outputs low
      TCA6408A_Reg_map.Output.all = 0x00;
      TCA6408AWriteOutput(i2cDevice, (TCA6408ARegs*)&TCA6408A_Reg_map);
      
      //Initialize TCA6408A
      if (ADI_I2C_SUCCESS != TCA6408AInitI2CReg(i2cDevice, (TCA6408ARegs*) &TCA6408A_Reg_map)){
        printf("Initializing GPIO Expander");
      }

    This works great, with the GPIO expander visibly acknowledging the commands seen through my logic analyzer.

    The code then proceeds into this while loop, which waits for the external SPI pin interrupt before entering the if statement. 

      while(1)
      {
        //printf("Hello!");
        /* Check if interrupt flag which will be set when interrupt occured. */
        if(AD5940_GetMCUIntFlag())
        {
          //Wrap mux channel loop
          if (mux_channel >= S_NUM){
            mux_channel = 0x00;
          }
          
          TCA6408A_Reg_map.Output.all = act_sens[mux_channel];
          TCA6408AWriteOutput(i2cDevice, (TCA6408ARegs*)&TCA6408A_Reg_map);
          
          /* Main */
          AD5940_ClrMCUIntFlag(); /* Clear this flag */
          temp = APPBUFF_SIZE;
          AppBIOZISR(AppBuff, &temp); /* Deal with it and provide a buffer to store data we got */
          BIOZShowResult(AppBuff, temp, msg); /* Show the results to UART */
          /* Main End */
          
          //next channel
          mux_channel++;
            
          //add comma if we are not at last sensor
          if(mux_channel < S_NUM){
            strcat(msg,",");
          }
            
          printf(msg);
          //new line if we are at last sensor
          if(mux_channel >= (S_NUM)){
            printf("\n\r");
          }
        }
      }

    The code runs through this loop once, sending a byte of data on I2C as instructed on this line: 

    TCA6408AWriteOutput(i2cDevice, (TCA6408ARegs*)&TCA6408A_Reg_map);

    I do notice that nothing is printed on serial on this line printf(msg); which could mean something stopped working on UART.

    On the second run trough the loop, the codes runs up until this line: 

    TCA6408AWriteOutput(i2cDevice, (TCA6408ARegs*)&TCA6408A_Reg_map);

    A data-byte is transmitted to the GPIO expander again, but is showing NAK (not acknowledged) on my logic analyzer, and it immediately enter the HardFault described above.

    ________

    Out-commenting the semaphore line in adi_i2c_v1.c circumvents the HardFault: 

        /* Pend for the semaphore */
        PEND_EVENT(DATA, ADI_I2C_ERR_RTOS);

    But the I2C communication gets wonky. I imagine because it gets interrupted by the SPI/UART interrupts before it gets a chance to do anything, or simply completes before I2C comm is completed.

    Best,

    Andreas

  • 0
    •  Analog Employees 
    on Dec 14, 2018 8:43 AM over 1 year ago in reply to andyy

    Hello Andreas,

    Could you post here the code you have written so far? It would be easier to debug it that way.

    Of the top of my head the "adi_i2c_Open" function takes a void pointer as an argument for the memory and by writing

     adi_i2c_Open(0, ADI_I2C_MASTER, &devMem, ADI_I2C_MEMORY_SIZE, &i2cDevice)

    you are passing the reference to the pointer to the first element of the array devMem. That can have undefined behavior in this case, as it might work if the pointer is stored just before the actual contiguous memory it points to, but might bug out any time the pointer is corrupted by the driver wanting to write over it. So try writing

     adi_i2c_Open(0, ADI_I2C_MASTER, devMem, ADI_I2C_MEMORY_SIZE, &i2cDevice)

    Also make sure UART memory is 32 bit aligned.

    /* Memory for  UART driver */
    uint8_t uart_device_mem[ADI_UART_BIDIR_MEMORY_SIZE] __attribute__ ((aligned (4)));

    I will look through the code you have posted so far more and try to see if anything else stands out.

    Regards,

    Andrei.

  • Thanks Andrei. I tested adi_i2c_Open() without passing a reference to the pointer of the first array element. The code unfortunately still encounters the HardFault error. 

    The way SPI and UART are initialized in this eval kit is quite abstract compared to previous ADuCM versions I've looked at. uart_device_mem is not found anywhere in the files, but UART works fine, until I attempt implementing I2C. 

    Please find my modified version of the AD5940_BIOZ-2Wire example attached. My main edits can be found in the AD5940_Main() in the file AD5940Main.c, 

    Thanks again,

    Andreas

    AD5940 Firmware.zip

  • Hi Andrei,

    Just wanted to supply you with image dumps of the I2c communication up until I get the HardFault.

    First the GPIO expander is set up (works as expected): 

    Then upon first loop, it's output registry is updated, which is acknowledged: 

    On second loop through the while(1) loop in AD5940_Main() the HardFault happens. This is the last thing I see on I2C:

    Best,

    Andreas

  • 0
    •  Analog Employees 
    on Jan 7, 2019 8:59 AM over 1 year ago in reply to andyy

    Hello Andreas,

    I apologize for the delayed response.

    Unfortunately I don't have access to a ad5940 board here so I cannot directly debug the code, but I managed to look at it a little. The NACK on the last I2C transmission is definitely a clue to what's wrong. Are you able to step through the code? If you are can you point to the exact location at which the program fails and enters the Default Handler? Most often the code enters the Default handler when there is a memory violation like an out of bounds read/write of a buffer. I suspect the code fails somewhere between the lines

    TCA6408AWriteOutput(i2cDevice, (TCA6408ARegs*)&TCA6408A_Reg_map);

    and

    BIOZShowResult(AppBuff, temp, msg); /* Show the results to UART */

    And more precisely in the "adi_i2c_Write" function from the "TCA6408AWriteOutput" function in the "TCA6408A.c" file. We would need the error code returned by the "adi_i2c_Write".

    Also is the first value read displayed on the console by "printf(msg);"? Is the value an expected one?

    Regards,

    Andrei.