Can I user NRF TWI Manager to write varying lengths from a buffer?

Hello,

I am attempting to use the NRF twi manager based on the "twi master using nrf twi manager" example. I noticed that the example only reads and wrties a single byte and hence the transfer sizes can be defined as constants. However, I am trying to use an I2C uart module, since NRF52832 only has one UART, to output some debug information.

Hence the writes are of variable length and I am struggling to get the code to compile as it requires the transfer[x].length to be a constant. So my question is if I can use the manager to schedule these writes.

I have attempted to use the standard twi master which results in watchdog resets as the "while (m_xfer_done == false)" results in a blocking call even when using an event handler. I understand that I could implement a queuing mechanism however, I am trying to avoid that effort if the twi manager does this already.

Ankit

  • Hi,

    exsurgo_ankit said:

    One of the problems is indeed that the transfer size should be known at compile time. I want them to be variable.

    The size parameter should be initialized to a constant, are you getting a "initializer element is not constant" error? Exactly what error are you getting?

    Also, note that the log module also supports RTT as backend, which use the SWD interface to output logs instead of the UART interface.

    exsurgo_ankit said:
    Regarding watchdog resets, I am feeding the watchdog in a timer. This is our third development project using nRF52. So we are using a stable base code that has been working in the field for about 2 years now and does not lead to watchdog resets. However, the current project uses TWI while the previous ones didn't. I added various log messages throughout to isolate the issue which seem to be the wait for TWI transfer to complete. I would prefer to queue the bytes and let the system clear them when possible. Is there something already available in NRF libraries that would achieve this for TWI?

    What kind of timer are you using? Are you using a HW Timer + PPI or an app timer and feeding the watchdog in the app timers timeout handler?

    I have attempted to use the standard twi master which results in watchdog resets as the "while (m_xfer_done == false)" results in a blocking call even when using an event handler.

    Can you share the main loop when you're trying to use the standard twi master? The while (m_xfer_done == flase) loop shouldn't block any other task of executing. 

    regards

    Jared

  • Hello Jared,

    1. You are right. I am getting this error "initializer element is not constant"

    2. RTT logger is a workaround but I need a usb uart interface to the device to allow applications on Windows to connect to it.

    3. I am using an app timer and feeding the watchdog in the app timer's timeout handler. Also note that calls to TWI also happen in this timer and the TWI priority is higher than app timer's priority. If I set it the same then the app does watchdog resets from the start.

    4. Here is the code snippet of read reg and write to a fifo of the i2c-uart module.

    static uint8_t readReg(uint8_t reg, void* pBuf, size_t size)
    {
      if(pBuf == NULL)
      {
        DBG("pBuf ERROR!! : null pointer");
        return 0;
      }
      
      uint8_t * _pBuf = (uint8_t *)pBuf;
      
      //set the address to read
      _addr = updateAddr(_addr, _subSerialChannel, OBJECT_REGISTER);
      _addr &= 0xFE;
        m_xfer_done = false;
        nrf_drv_twi_tx(&m_twi, _addr, &reg, 1, false);
        while (m_xfer_done == false); //wait for transfer to complete
        
    
        //read the data
        m_xfer_done = false;
        nrf_drv_twi_rx(&m_twi, _addr, _pBuf, size);
        while (m_xfer_done == false); //wait for transfer to complete
        
      
      return size;  
    }
    
    static void writeFIFO(void *pBuf, size_t size)
    {
      if(pBuf == NULL)
      {
          DBG("pBuf ERROR!! : null pointer");
          return;
      }
      _addr = updateAddr(_addr, _subSerialChannel, OBJECT_FIFO);
      uint8_t *_pBuf = (uint8_t *)pBuf;
      size_t left = size;
      while(left)
      {
          size = (left > IIC_BUFFER_SIZE) ? IIC_BUFFER_SIZE: left;
          
          #if TWI_DRV == 1
          m_xfer_done = false;
          nrf_drv_twi_tx(&m_twi, _addr, _pBuf, size, false); 
          while (m_xfer_done == false); //wait for transfer to complete
          
          left -= size;
          _pBuf = _pBuf + size;
          if(left > 0)
          {
             nrf_delay_us(100);
          }
          else
          {
              break; //done transmitting, no need to wait
          }
      }
    }

    Regards

    Ankit

  • Hello mrono,

    A part of the problem is that I am not sure how to vary the size due to a compile time error expecting the intiialiser element for transfer to be a constant.

    uninit/reinit may be an option but I have to get the twi manager to work first with varying transfer length.

  • Here is a snippet:

    nrf_twi_mngr_transfer_t vl53l0x_tfers[2];
    
    int32_t VL53L0X_read_multi(uint8_t address,  uint8_t index, uint8_t  *pdata, int32_t count)
    {
        static uint8_t idx;
        idx = index;
        vl53l0x_tfers[0].operation = NRF_TWI_MNGR_WRITE_OP(address);
        vl53l0x_tfers[0].p_data = &idx;
        vl53l0x_tfers[0].length = 1;
        vl53l0x_tfers[0].flags = NRF_TWI_MNGR_NO_STOP;
    
        vl53l0x_tfers[1].operation = NRF_TWI_MNGR_READ_OP(address);
        vl53l0x_tfers[1].p_data = pdata;
        vl53l0x_tfers[1].length = count;
        vl53l0x_tfers[1].flags = 0;
    
        ret_code_t status = nrf_twi_mngr_perform_timeout(&m_nrf_twi_mngr, NULL, vl53l0x_tfers, 2, NULL, 100);
        
        if(status == NRF_SUCCESS)
            return 0;
        
        return 1;
        
    }
    

    nrf_twi_mngr_perform is the blocking version. The _timeout is my addition to exit if the transfer never finishes.

  • Hi,
    See  reply, that seems like a good suggestion for you.


    exsurgo_ankit said:
    3. I am using an app timer and feeding the watchdog in the app timer's timeout handler. Also note that calls to TWI also happen in this timer and the TWI priority is higher than app timer's priority. If I set it the same then the app does watchdog resets from the start.

    Seems like the TWI is blocking the feeding of the watchdog somehow. Preferably time spent in interrupt context should be as short as possible. Could you try to move the TWI call to main context by replacing the calls to the TWI module in the callback handler by setting a flag that is checked in the main loop. That way you would also avoid the app timer callback handler being blocked by the TWI.

    Something like this:

    void app_timer_handler()
    
    {
        feed_watchdog();
        flag = 1;
    
    }
    
    int flag = 0;
    
    int main(void)
    {
        twi_init()
        watchdog_init()
        while(true)
        {
            if(flag)
            {
                nrf_drv_twi_tx();
                flag = 0;
            }
        }
    
    }

    regards

    Jared

Related