How to transfer large amounts of data over I2C

Hello,

I am using the vl53l5cx TOF sensor and implementing the ULD using zephyr. My setup is a bl654 board using zephyr on sdk 1.9.1. However I am running into an issue, since the sensor is RAM based in the init() it's required to upload ~82kB of firmware over the I2C connection into the sensor. Writing and reading single bytes are not a problem for me, but looping the I2C function continue to give me NACK on the I2C line and I cannot even transfer the internal register address to read or write from. Any ideas on how I can implement this. Here is an example of my multi-write function:

uint8_t WrMulti(
	VL53L5CX_Platform *p_platform,
	uint16_t RegisterAddress,
	uint8_t *p_values,
	uint32_t size)
{
	uint8_t status = 255;
	int offset = 0;
	uint8_t writeBuf[MAX_TX_BUFFER];

	while(size > 0){
		writeBuf[0] = RegisterAddress>>8;
    	writeBuf[1] = RegisterAddress & 0xff;
		memcpy(&(writeBuf[2]), &(p_values[offset]), MAX_TX_BUFFER - 2);
		if (size >= 256){
			status = i2c_write(p_platform->i2c_dev, writeBuf, MAX_TX_BUFFER, p_platform->address);
			if (status)
			{
				printk("%d\n", size);
			}
			offset += 256;
			size -= 256;
		} else{
			status = i2c_write(p_platform->i2c_dev, writeBuf, size, p_platform->address);
			if (status)
			{
				printk("%d\n", size);
			}
		}
	}

	return status;
}

Thanks,

Jacob

Parents
  • Don't be tempted to break the transfer into smaller blocks, because the ST sensor will not run properly if you do.  The main trick is that the DMA cannot handle transfers from FLASH, you must first copy the transfer to RAM.  The next trick is that this will result in a stack size that is far too large, so you need to make the buffer static:

    uint8_t WrMulti(
    VL53L5CX_Platform *p_platform,
    uint16_t RegisterAdress,
    uint8_t *p_values,
    uint32_t size)
    {

    uint8_t status=VL53L5CX_STATUS_OK;
    static uint8_t localBuf[0x8002];

    localBuf[0] = RegisterAdress/256;
    localBuf[1] = RegisterAdress;
    memcpy(&localBuf[2], p_values, size);

    /* Need to be implemented by customer. This function returns 0 if OK */
    status |= i2c_write(p_platform->i2c, localBuf, size+2, p_platform->address);


    return status;
    }

    Lastly, the examples from ST often require too much stack space.  This will frustrate you because the error will not occur when the offending function is started, it will fail when you attempt to use a variable inside the function, or called from that function.  You need to make their local variables static, so in example1(), make these static:

        static VL53L5CX_Configuration   Dev ;           /* Sensor configuration */
        static VL53L5CX_ResultsData     Results;        /* Results data from VL53L5CX */

    I also have this running with Nordic's SDK.  That requires some coding outside of Nordic's API because the function only has an 8-bit size, but the driver can handle 16 bits.
    uint8_t WrMulti(
    VL53L5CX_Platform *p_platform,
    uint16_t RegisterAdress,
    uint8_t *p_values,
    uint32_t size)
    {
    int32_t status_int;
    uint8_t status = VL53L5CX_STATUS_OK;
    uint8_t localBuf[size + 2];

    /* Need to be implemented by customer. This function returns 0 if OK */

    /*Copy from FLASH into RAM, as required by EasyDMA, and create address in first two bytes*/
    memcpy(&localBuf[2], p_values, size);
    localBuf[0] = RegisterAdress / 256;
    localBuf[1] = RegisterAdress;

    /*Wait for any current operations*/
    while (nrf_drv_twi_is_busy(p_platform->i2c));

    /*Create TWIM descriptor to get larger than 8 bit size, must be constant*/
    nrfx_twim_xfer_desc_t const twim_xfer_desc =
    {
    .type = (nrfx_twim_xfer_type_t)NRF_DRV_TWI_XFER_TX,
    .address = p_platform->address,
    .primary_length = size + 2,
    .p_primary_buf = localBuf,
    };
    status = nrfx_twim_xfer(&p_platform->i2c->u.twim, &twim_xfer_desc, 0);

    while (nrf_drv_twi_is_busy(p_platform->i2c));

    return status;
    }
    I was frustrated for days by this, so I hope I've been able to help somebody else in trouble.
Reply
  • Don't be tempted to break the transfer into smaller blocks, because the ST sensor will not run properly if you do.  The main trick is that the DMA cannot handle transfers from FLASH, you must first copy the transfer to RAM.  The next trick is that this will result in a stack size that is far too large, so you need to make the buffer static:

    uint8_t WrMulti(
    VL53L5CX_Platform *p_platform,
    uint16_t RegisterAdress,
    uint8_t *p_values,
    uint32_t size)
    {

    uint8_t status=VL53L5CX_STATUS_OK;
    static uint8_t localBuf[0x8002];

    localBuf[0] = RegisterAdress/256;
    localBuf[1] = RegisterAdress;
    memcpy(&localBuf[2], p_values, size);

    /* Need to be implemented by customer. This function returns 0 if OK */
    status |= i2c_write(p_platform->i2c, localBuf, size+2, p_platform->address);


    return status;
    }

    Lastly, the examples from ST often require too much stack space.  This will frustrate you because the error will not occur when the offending function is started, it will fail when you attempt to use a variable inside the function, or called from that function.  You need to make their local variables static, so in example1(), make these static:

        static VL53L5CX_Configuration   Dev ;           /* Sensor configuration */
        static VL53L5CX_ResultsData     Results;        /* Results data from VL53L5CX */

    I also have this running with Nordic's SDK.  That requires some coding outside of Nordic's API because the function only has an 8-bit size, but the driver can handle 16 bits.
    uint8_t WrMulti(
    VL53L5CX_Platform *p_platform,
    uint16_t RegisterAdress,
    uint8_t *p_values,
    uint32_t size)
    {
    int32_t status_int;
    uint8_t status = VL53L5CX_STATUS_OK;
    uint8_t localBuf[size + 2];

    /* Need to be implemented by customer. This function returns 0 if OK */

    /*Copy from FLASH into RAM, as required by EasyDMA, and create address in first two bytes*/
    memcpy(&localBuf[2], p_values, size);
    localBuf[0] = RegisterAdress / 256;
    localBuf[1] = RegisterAdress;

    /*Wait for any current operations*/
    while (nrf_drv_twi_is_busy(p_platform->i2c));

    /*Create TWIM descriptor to get larger than 8 bit size, must be constant*/
    nrfx_twim_xfer_desc_t const twim_xfer_desc =
    {
    .type = (nrfx_twim_xfer_type_t)NRF_DRV_TWI_XFER_TX,
    .address = p_platform->address,
    .primary_length = size + 2,
    .p_primary_buf = localBuf,
    };
    status = nrfx_twim_xfer(&p_platform->i2c->u.twim, &twim_xfer_desc, 0);

    while (nrf_drv_twi_is_busy(p_platform->i2c));

    return status;
    }
    I was frustrated for days by this, so I hope I've been able to help somebody else in trouble.
Children
No Data
Related