I2C action from Timer callback

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

I2C action from Timer callback

1,724 Views
embedded_eng_
Contributor III

Hi all,

I'm using LPC55S16-EVK, and having some trouble reading from I2C within a Ctimer callback.

 

I have CTIEMR2 calling my callback every X seconds which works well.

I have I2C read/write (non blocking) functions working well, based on i2c_interrupt_b2b_transfer_master demo.

 

The problem occurs when I'm trying to read from I2C within the CTIMER callback.

The I2C handle is not been called at all.

 

I guess that this has something to do with IRQ priority.

I figured that the MCU is busy executing the CTIMER callback, so if CTIEMR's IRQ has a higher/equal priority than the I2C's IRQ, the I2C handle will have to wait until CTIMER callback finishes, so I tried to change the I2C priority to a higher one.

I added a NVIC_SetPriority() call in I2C_MasterTransferCreateHandle function with a lower number (higher priority), but the problem still occurs.

 

void I2C_MasterTransferCreateHandle(I2C_Type *base,
                                    i2c_master_handle_t *handle,
                                    i2c_master_transfer_callback_t callback,
                                    void *userData)
{
    assert(handle != NULL);

    uint32_t instance;
    i2c_to_flexcomm_t handler;
    handler.i2c_master_handler = I2C_MasterTransferHandleIRQ;

    /* Clear out the handle. */
    (void)memset(handle, 0, sizeof(*handle));

    /* Look up instance number */
    instance = I2C_GetInstance(base);

    /* Save base and instance. */
    handle->completionCallback = callback;
    handle->userData           = userData;

    FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle);

    /* Clear internal IRQ enables and enable NVIC IRQ. */
    I2C_DisableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags);

    NVIC_SetPriority(s_i2cIRQ[instance], 1);
    (void)EnableIRQ(s_i2cIRQ[instance]);
}

 

Maybe someone can help with that?

Thanks

0 Kudos
5 Replies

1,701 Views
Omar_Anguiano
NXP TechSupport
NXP TechSupport

Hello
Hope you are well.
I don´t think that the priorities in the interruption are involved on this since when the callback is executed the IRQ was dispatched.
I suggest you to check the bus communication with an oscilloscope to check how the communication is done. The I2C callback is called when the communication is done.

Additionally, how are you integrating the I2C read function in the Ctimer callback?

I´m looking forward to your reply, if you have more questions do not hesitate to ask me.
Best regards,
Omar

0 Kudos

1,676 Views
embedded_eng_
Contributor III

Thanks for the answer, and sorry for the delay, I had to work on other stuff.

 

Here is the I2C read function:

 

 

uint8_t *readI2C(uint32_t address, uint32_t size, uint8_t slave)
{
  i2c_master_transfer_t masterXfer = {0};


  /*if size is bigger than buffer, return error*/
  if(size > I2C_DATA_LENGTH)
  {
    return NULL;
  }

  /*lock I2C*/
  if( lockI2C() < 0 )
  {
    //could not lock i2c - return null pointer
    return NULL;
  }

  /* subAddress = 0x01, data = i2cTransfers.receiveBfr - read from slave.
    start + slaveaddress(w) + subAddress + repeated start + slaveaddress(r) + rx data buffer + stop */
  masterXfer.slaveAddress   = slave;
  masterXfer.direction      = kI2C_Read;
  masterXfer.subaddress     = address;
  masterXfer.subaddressSize = 1;
  masterXfer.data           = i2cTransfers.receiveBfr;
  masterXfer.dataSize       = size;
  masterXfer.flags          = kI2C_TransferDefaultFlag;

  /*  Reset master completion flag to false. */
  setNotCompletedI2C();

  if ( I2C_MasterTransferNonBlocking(I2C_MASTER, &g_m_handle, &masterXfer) != kStatus_Success )
  {
      /*free lock and return*/
      freeI2C();
      return NULL;
  }

  /*  Wait for transfer completed. */
  while (!isCompletedI2C()) {};

  /*check for errors*/
  if( isErrorI2C() )
  {
    /*free lock, clean error flag and return*/
    freeI2C();
    resetErrorI2C();
    return NULL;
  }
  //change the completed flag
  setNotCompletedI2C();
  //remove the lock
  freeI2C();

  //return data pointer
  return i2cTransfers.receiveBfr;
}

 

 

 

Example of I2C read from main function:

 

 

    uint8_t *buf = readI2C(0x00, 5, 57);
    for(int i = 0 ; i < 5 ; i ++)
      usb_echo("%02x: %02x\r\n",i, buf[i]);

 

 

Output:

 

 

 0:  0
 1:  0
 2:  1
 3: 20
 4: 71

 

 

Signals:

Screenshot from 2021-10-14 09-40-17.png

Now doing the same from a ctimer callback:

 

 

void callback()
{
  usb_echo("READ START:\r\n");
  uint8_t *buf = readI2C(0x00, 5, 57);
  for(int i = 0 ; i < 5 ; i ++)
    usb_echo("%02x: %02x\r\n",i, buf[i]);

  usb_echo("READ END\r\n");
}

ctimer_callback_t ctimerCallbackTable[] = {
    NULL, callback, callback, NULL, NULL, NULL, NULL, NULL};



/*init ctimer*/
void initCTIMER()
{
  ctimer_config_t config;
  ctimer_match_config_t matchConfig;

  /*init ctimer*/
  CTIMER_GetDefaultConfig(&config);
  CTIMER_Init(CONSUMPTION_ADC_CTIMER_BASE, &config);

  /* set timer configuration */
  matchConfig.enableCounterReset = true;
  matchConfig.enableCounterStop  = false;
  matchConfig.matchValue         = CONSUMPTION_ADC_CTIMER_CLK_FREQ * 5;//5 sec
  matchConfig.outControl         = kCTIMER_Output_Toggle;
  matchConfig.outPinInitState    = false;
  matchConfig.enableInterrupt    = true;

  /*register timer callback*/
  CTIMER_RegisterCallBack(CONSUMPTION_ADC_CTIMER_BASE, &ctimerCallbackTable[0], kCTIMER_MultipleCallback);
  CTIMER_SetupMatch(CONSUMPTION_ADC_CTIMER_BASE, CONSUMPTION_ADC_CTIMER_MAT0_OUT, &matchConfig);
  /*init timer*/
  CTIMER_StartTimer(CONSUMPTION_ADC_CTIMER_BASE);
}

 

 

Output (appears after 5 seconds):

 

 

READ START:

 

 

 

Signals:

Screenshot from 2021-10-14 09-48-07.png

Adding more debug prints:

 

 

uint8_t *readI2C(uint32_t address, uint32_t size, uint8_t slave)
{
  i2c_master_transfer_t masterXfer = {0};


  /*if size is bigger than buffer, return error*/
  if(size > I2C_DATA_LENGTH)
  {
    return NULL;
  }
  usb_echo("1\r\n");
  /*lock I2C*/
  if( lockI2C() < 0 )
  {
    //could not lock i2c - return null pointer
    return NULL;
  }

  usb_echo("2\r\n");

  /* subAddress = 0x01, data = i2cTransfers.receiveBfr - read from slave.
    start + slaveaddress(w) + subAddress + repeated start + slaveaddress(r) + rx data buffer + stop */
  masterXfer.slaveAddress   = slave;
  masterXfer.direction      = kI2C_Read;
  masterXfer.subaddress     = address;
  masterXfer.subaddressSize = 1;
  masterXfer.data           = i2cTransfers.receiveBfr;
  masterXfer.dataSize       = size;
  masterXfer.flags          = kI2C_TransferDefaultFlag;

  /*  Reset master completion flag to false. */
  setNotCompletedI2C();
  usb_echo("3\r\n");
  if ( I2C_MasterTransferNonBlocking(I2C_MASTER, &g_m_handle, &masterXfer) != kStatus_Success )
  {
      /*free lock and return*/
      usb_echo("4\r\n");
      freeI2C();
      return NULL;
  }
  usb_echo("5\r\n");

  /*  Wait for transfer completed. */
  while (!isCompletedI2C()) {};

  usb_echo("6\r\n");
  /*check for errors*/
  if( isErrorI2C() )
  {
    /*free lock, clean error flag and return*/
    freeI2C();
    resetErrorI2C();
    return NULL;
  }
  //change the completed flag
  setNotCompletedI2C();
  //remove the lock
  freeI2C();

  //return data pointer
  return i2cTransfers.receiveBfr;
}

 

 

Output:

 

 

READ START:
1
2
3
5

 

 

So code is stuck in loop

 

 

 while (!isCompletedI2C()) {};

 

 

Which is implemented:

 

 

bool isCompletedI2C()
{
  return i2cTransfers.completed;
}

 

This flag is changed to true from setCompletedI2C function 

 

 

void setCompletedI2C(I2C_Type *base, i2c_master_handle_t *handle, status_t status, void *userData)
{
  i2cTransfers.completed = true;
  /*indicates if an error occured*/
  i2cTransfers.transferError = status != kStatus_Success;
}

 

Which is registered as the I2C handle in initI2C function (initI2C is called from main)

 

void initI2C()
{
  i2c_master_config_t masterConfig;

  /*reset flexcomm*/
  RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);
  /*init i2c struct parameters*/
  initStructI2C();
  /*get i2c master config*/
  I2C_MasterGetDefaultConfig(&masterConfig);
  /*set baudrate*/
  masterConfig.baudRate_Bps = I2C_BAUDRATE;
  /*init i2c*/
  I2C_MasterInit(I2C_MASTER, &masterConfig, I2C_MASTER_CLOCK_FREQUENCY);
  /* Create the I2C handle for the non-blocking transfer */
  I2C_MasterTransferCreateHandle(I2C_MASTER, &g_m_handle, setCompletedI2C, NULL);
}

 

 

 

 

 

Maybe you know what's wrong?

 

Thanks

 

0 Kudos

1,639 Views
Omar_Anguiano
NXP TechSupport
NXP TechSupport

Thank you for the information and I apologize for my delayed reply.
I suggest you test the Blocking read function to receive the data, in this way we can assure that the issue might be caused by the interruptions.

Another suggestion is to use the callback to only set/clear flags, so the I2C functions stay outside the CTimer callback.

If you require to read every determined time, maybe you could consider adding RTOS to your application, in that way you can delay the reading task so it is executed every "x" time.

If you have more questions do not hesitate to ask me.
Best regards,
Omar

0 Kudos

1,546 Views
embedded_eng_
Contributor III

Hi,

Seems that the problem has nothing to do with the I2C protocol.

I tried the following:

* Implement a delay function using a timer.

* Call the delay function from a different timer callback function.

The result is that the delay function won't return, and the code is stuck forever.

So maybe it is related to IRQs?

0 Kudos

1,566 Views
embedded_eng_
Contributor III

Hi,

Thanks for your reply.

I can't use RTOS with this application.

Blocking I2C actions do work from the timer callback, the problem occurs only with non blocking I2C actions.

Any idea how can I fix it?

Thanks.

 

0 Kudos