cancel
Showing results for 
Search instead for 
Did you mean: 

VL53L5CX driver vl53l5cx_init() fails

Peter111
Associate II

The Ultra Light Driver (STSW-IMG023) init function vl53l5cx_init() fails in this section :

/* Get offset NVM data and store them into the offset buffer */
 
	status |= WrMulti(&(p_dev->platform), 0x2fd8,
 
		(uint8_t*)VL53L5CX_GET_NVM_CMD, sizeof(VL53L5CX_GET_NVM_CMD));
 
	status |= _vl53l5cx_poll_for_answer(p_dev, 4, 0,
 
		VL53L5CX_UI_CMD_STATUS, 0xff, 2);

The sensor does not answer with '2' as expected by _vl53l5cx_poll_for_answer. (The senosr answers '0')

This results in a failed initialization.

How can this be fixed?

50 REPLIES 50
AA.16
Associate III

i wanted to be sure so i started to read the registers where the firmware should be written to.

here is what i get as output:

 

 

/* Check if firmware correctly downloaded and compare with firmware */
	/* Read the first chunk of chip data */
	uint8_t chipFirstChunk[16];
	status |= RdMulti(&(p_dev->platform), 0, chipFirstChunk, 16);
	APP_LOG(TS_ON, 3, "[PLAT] Read the first chunk of chip data. Status: %d\n", status);

	/* Compare the first chunk of firmware with the first chunk of chip data */
	bool firstChunkMatch = true;
	for (int i = 0; i < 16; i++) {
		if (VL53L8CX_FIRMWARE[i] != chipFirstChunk[i]) {
			firstChunkMatch = false;
			break;
		}
	}

	/* Print chipFirstChunk */
	APP_LOG(TS_ON, 3, "[PLAT] Chip First Chunk:\n");
	for (int i = 0; i < 16; i++) {
		APP_LOG(TS_OFF, 3, "%02X, ", chipFirstChunk[i]);
	}
	APP_LOG(TS_OFF, 3, "\n");
	/* Print VL53L8CX_FIRMWARE */
	APP_LOG(TS_ON, 3, "[PLAT] Firmware:\n");
	for (int i = 0; i < 16; i++) {
		APP_LOG(TS_OFF, 3, "%02X, ", VL53L8CX_FIRMWARE[i]);
	}
	APP_LOG(TS_OFF, 3, "\n");
	if (firstChunkMatch) {
		APP_LOG(TS_ON, 3, "[PLAT] First chunk of firmware matches with chip data.\n");
	} else {
		APP_LOG(TS_ON, 3, "[PLAT] First chunk of firmware does not match with chip data.\n");
	}

 

 

here is the output:

 

 

17:37:44.211 -> 26s958:[PLAT] Check if FW correctly downloaded status 0; temp 0
17:37:44.211 -> 26s960:[PLAT] Read the first chunk of chip data. Status: 0
17:37:44.211 -> 26s960:[PLAT] Chip First Chunk:
17:37:44.211 -> F0, 0C, 62, 01, 29, 00, 00, 00, 01, 04, 01, 01, 01, 00, 01, 40, 
17:37:44.211 -> 26s961:[PLAT] Firmware:
17:37:44.211 -> E0, 00, 03, 08, E0, 00, 0A, C8, E0, 00, 05, 08, E0, 60, 37, C8, 
17:37:44.211 -> 26s962:[PLAT] First chunk of firmware does not match with chip data.

 

 

 

is this correct?

 

 

here is my two functions that read write multiple bytes:

 

 

uint8_t WrMulti(VL53L8CX_Platform *p_platform, uint16_t RegisterAddress, uint8_t *p_values, uint32_t size) {
	// Send register address
	uint8_t regBuff[2] = { RegisterAddress >> 8, RegisterAddress & 0xFF };
	if (HAL_I2C_Master_Transmit(&hi2c2, addr, regBuff, 2, 5000) != HAL_OK) {
		APP_LOG(TS_ON, 3, "[PLAT] WrMulti  Send register address failed\n");
		return -1;
	}

	// Loop through the data in chunks of 255 bytes // #define chunk 1<<8
	for (uint32_t offset = 0; offset < size; offset += chunk) {
        uint8_t chunkSize = (size - offset > chunk) ? chunk : (size - offset);
        // Write data chunk
        if (HAL_I2C_Master_Transmit(&hi2c2, addr, p_values + offset, chunkSize, 5000) != HAL_OK) {
            APP_LOG(TS_ON, 3, "[PLAT] WrMulti  Write data chunk failed\n");
            return -1;
        }
    }

    // If the size is not a multiple of 255, we need to write the remaining bytes
    if (size % chunk != 0) {
        // Write remaining data
        if (HAL_I2C_Master_Transmit(&hi2c2, addr, p_values + size - (size % chunk), size % chunk, 5000) != HAL_OK) {
            APP_LOG(TS_ON, 3, "[PLAT] WrMulti  Write remaining data failed\n");
            return -1;
        }
    }
    return 0;
}

 

 

uint8_t RdMulti(VL53L8CX_Platform *p_platform, uint16_t RegisterAdress, uint8_t *p_values, uint32_t size) {
	uint8_t buff[2] = { RegisterAdress >> 8, RegisterAdress & 0xFF };

    if (size>chunk) {
    	APP_LOG(TS_ON, 3, "[PLAT]RdMulti  size>%d, %d \n",chunk, size);
	}

	//send which register we want to write to
	if (HAL_I2C_Master_Transmit(&hi2c2, addr, buff, 2, 5000) != HAL_OK) {
		APP_LOG(TS_ON, 3, "[PLAT] RdMulti send which register we want to write to failed \n");
		return -1;
	}

	//now read it
	if (HAL_I2C_Master_Receive(&hi2c2, addr, p_values, size, 5000) != HAL_OK) {
		APP_LOG(TS_ON, 3, "[PLAT] RdMulti now read it failed \n");
		return -2;
	}
	return 0;
}

 

i am at a loss what is causing this mismatch. can someone point it out to me?

AA.16
Associate III

ok i kept trying and now i get random new bytes when reading the first few registers after FW download. At least they are different now ...

 

i changed my write functions as follows:

 

uint8_t WrMulti(VL53L8CX_Platform *p_platform, uint16_t RegisterAddress, uint8_t *p_values, uint32_t size) {
	 return HAL_I2C_Mem_Write(&hi2c2, addr, RegisterAddress, I2C_MEMADD_SIZE_16BIT, p_values, size, 100);
}

uint8_t WrByte(VL53L8CX_Platform *p_platform, uint16_t RegisterAdress, uint8_t value) {
	uint8_t data_write[3];
	uint8_t status = 0;

	data_write[0] = (RegisterAdress >> 😎 & 0xFF;
	data_write[1] = RegisterAdress & 0xFF;
	data_write[2] = value & 0xFF;
	status = HAL_I2C_Master_Transmit(&hi2c2, addr, data_write, 3, 100);

	return status;
}

 

i am still trying to check if things got written correctly by reading the first few bytes after writing. is this even a correct approach? i was thinking about making a checksum but its obviously different from the original firmware and i wanted to see if its maybe shifted by a few bytes. but no its different each time i run the init code.

 

here is how i read the bytes:

	/* Check if firmware correctly downloaded and compare with firmware */
	/* Read the first chunk of chip data */
	uint8_t chipFirstChunk[32];
	status |= RdMulti(&(p_dev->platform), 0, chipFirstChunk, 32);
	APP_LOG(TS_ON, 3, "[PLAT] Read the first chunk of chip data. Status: %d\n", status);

	/* Compare the first chunk of firmware with the first chunk of chip data */
	bool firstChunkMatch = true;
	for (int i = 0; i < 32; i++) {
		if (VL53L8CX_FIRMWARE[i] != chipFirstChunk[i]) {
			firstChunkMatch = false;
			break;
		}
	}

	/* Print chipFirstChunk */
	APP_LOG(TS_ON, 3, "[PLAT] Chip First Chunk:\n");
	for (int i = 0; i < 32; i++) {
		APP_LOG(TS_OFF, 3, "%02X, ", chipFirstChunk[i]);
	}
	APP_LOG(TS_OFF, 3, "\n");
	/* Print VL53L8CX_FIRMWARE */
	APP_LOG(TS_ON, 3, "[PLAT] Firmware:\n");
	for (int i = 0; i < 32; i++) {
		APP_LOG(TS_OFF, 3, "%02X, ", VL53L8CX_FIRMWARE[i]);
	}
	APP_LOG(TS_OFF, 3, "\n");
	if (firstChunkMatch) {
		APP_LOG(TS_ON, 3, "[PLAT] First chunk of firmware matches with chip data.\n");
	} else {
		APP_LOG(TS_ON, 3, "[PLAT] First chunk of firmware does not match with chip data.\n");
	}

 

an here are two possible outputs. i show you two because its different each time.

 

16:25:06.758 -> 27s304:[PLAT] Chip First Chunk:
16:25:06.758 -> E5, 0D, 00, 08, 20, FE, 00, 20, 13, 27, 00, 08, 00, 00, 00, 00, 00, 00, 00, 00, 30, FE, 00, 20, 00, 28, 00, 40, 20, 00, 00, 00, 
16:25:06.758 -> 27s305:[PLAT] Firmware:
16:25:06.758 -> E0, 00, 03, 08, E0, 00, 0A, C8, E0, 00, 05, 08, E0, 60, 37, C8, E0, 00, 0A, 88, E0, 00, 0A, 88, E0, 00, 0A, 88, E0, 00, 0A, 88, 
16:25:06.758 -> 27s306:[PLAT] First chunk of firmware does not match with chip data.
16:25:06.758 -> 40s718:[ToF] init return value -1




16:25:29.902 -> 63s973:[PLAT] Chip First Chunk:
16:25:29.902 -> 54, 02, FF, FF, 00, 00, 00, FA, 93, 29, 00, 08, 36, 25, 00, 08, 00, 00, 00, 01, 00, 00, 00, FA, 00, 28, 00, 40, 8E, 29, 00, 08, 
16:25:29.902 -> 63s974:[PLAT] Firmware:
16:25:29.902 -> E0, 00, 03, 08, E0, 00, 0A, C8, E0, 00, 05, 08, E0, 60, 37, C8, E0, 00, 0A, 88, E0, 00, 0A, 88, E0, 00, 0A, 88, E0, 00, 0A, 88, 
16:25:29.902 -> 63s975:[PLAT] First chunk of firmware does not match with chip data.
16:25:43.335 -> 77s388:[ToF] init return value -1

 

 

I wrote some code to verify the I2C. can you try this bit of code?

I'm thinking byte swap or word swap.

/* --------------------------------------------------
 * --- This function can be used to test VL53L5CX I2C 
 * --------------------------------------------------
 */

uint8_t vl53l5cx_test_i2c(VL53L5CX_Configuration *p_dev){
	uint8_t status = VL53L5CX_STATUS_OK;
	printf("Starting VL53L5CX I2C test...\n");

/* To check the I2C RdByte/WrByte function :
 * Inside the function “vl53l5cx_is_alive()”, it will call I2C RdByte/WrByte to
 * read device and verion ID. which can help you to verify the I2C RdByte/WrByte
 * functions at same time.
 */
	uint8_t device_id, revision_id;
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x00);
	status |= RdByte(&(p_dev->platform), 0, &device_id);
	status |= RdByte(&(p_dev->platform), 1, &revision_id);
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x02);

	if(status)
	{
		printf("Error Rd/Wr byte: status %u\n", status);
		return status;
	}

/* To check the I2C RdMulti/WrMulti function:
 * Below is example codes which can help you vefify the I2C RdMulti/WrMulti
 * function.
 */

	uint8_t Data_write[4]={0x5A,0xA5,0xAA,0x55};
	uint8_t Data_read[4]={0,0,0,0};
	uint8_t Data_default[4]={0,0,0,0};

	status |= RdMulti(&(p_dev->platform), 0x100, Data_default, 4);
	if(status)
	{
		printf("Error RdMulti: status %u\n", status);
		return status;
	}

	printf("Read default value and save it at begging\n");
	printf("Data_default (0x%x)\n", Data_default[0]);
	printf("Data_default (0x%x)\n", Data_default[1]);
	printf("Data_default (0x%x)\n", Data_default[2]);
	printf("Data_default (0x%x)\n", Data_default[3]);

	status |= WrMulti(&(Dev.platform), 0x100, Data_write, 4);
	if(status)
	{
		printf("Error WrMulti: status %u\n", status);
		return status;
	}
	printf("Writing values 0x5A 0xA5 0xAA 0x55\n");

	status |= RdMulti(&(p_dev->platform), 0x100, Data_read, 4);
	if(status)
	{
		printf("Error RdMulti: status %u\n", status);
		return status;
	}

	printf("Reading:\n");
	printf("Data_read (0x%x)\n", Data_read[0]);
	printf("Data_read (0x%x)\n", Data_read[1]);
	printf("Data_read (0x%x)\n", Data_read[2]);
	printf("Data_read (0x%x)\n", Data_read[3]);


	status |= WrMulti(&(Dev.platform), 0x100, Data_default, 4);
	printf("Write back default value\n");
	if(status)
	{
		printf("Error WrMulti: status %u\n", status);
		return status;
	}

	status |= RdMulti(&(Dev.platform), 0x100, Data_default, 4);
	if(status)
	{
		printf("Error RdMulti: status %u\n", status);
		return status;
	}

	printf("Read value again to make sure default value was correct loaded\n");
	printf("Data_default (0x%x)\n", Data_default[0]);
	printf("Data_default (0x%x)\n", Data_default[1]);
	printf("Data_default (0x%x)\n", Data_default[2]);
	printf("Data_default (0x%x)\n", Data_default[3]);

	printf("I2C test done - everything works fine.\n");

	return status;
}

Our community relies on fruitful exchanges and good quality content. You can thank and reward helpful and positive contributions by marking them as 'Accept as Solution'. When marking a solution, make sure it answers your original question or issue that you raised.

ST Employees that act as moderators have the right to accept the solution, judging by their expertise. This helps other community members identify useful discussions and refrain from raising the same question. If you notice any false behavior or abuse of the action, do not hesitate to 'Report Inappropriate Content'

i did try your code. however i needed to change some things. printf has to be app_log in my codebase and some DEV.platform had to be changed into p_dev->platform. Also i needed to change the VL53L5CX_Configuration to VL53L8CX_Configuration, since i am using the 8CX and nto the 5CX. execpt from that i did use a copy paste of your test code. here is the output:

 

 

15:22:30.839 -> 23s753:[ToF] vl53l8cx_is_alive 1
15:22:30.839 -> 23s753:Starting VL53L5CX I2C test...
15:22:30.839 -> 23s757:Read default value and save it at begging
15:22:30.839 -> 23s757:Data_default (0x61)
15:22:30.839 -> 23s757:Data_default (0x94)
15:22:30.839 -> 23s758:Data_default (0xe2)
15:22:30.839 -> 23s758:Data_default (0x66)
15:22:30.839 -> 23s759:Writing values 0x5A 0xA5 0xAA 0x55
15:22:30.839 -> 23s760:Reading:
15:22:30.839 -> 23s760:Data_read (0x5a)
15:22:30.839 -> 23s760:Data_read (0xa5)
15:22:30.839 -> 23s760:Data_read (0xaa)
15:22:30.839 -> 23s760:Data_read (0x55)
15:22:30.839 -> 23s761:Write back default value
15:22:30.839 -> 23s762:Read value again to make sure default value was correct loaded
15:22:30.839 -> 23s762:Data_default (0x61)
15:22:30.839 -> 23s762:Data_default (0x94)
15:22:30.839 -> 23s762:Data_default (0xe2)
15:22:30.839 -> 23s762:Data_default (0x66)
15:22:30.839 -> 23s763:I2C test done - everything works fine.
15:22:30.839 -> 23s763:[ToF] vl53l5cx_test_i2c 0

 

 

i am calling it like so:

 

 

	//first test if its possible to communicate at all via I2C
	uint8_t p_is_alive = 3; // 3 as default so we know if the alive function actually changed it to 0 or 1.
	vl53l8cx_is_alive(&pObj.Dev, &p_is_alive);
	APP_LOG(TS_ON, MDS_VLEVEL, "[ToF] vl53l8cx_is_alive %d\n", p_is_alive);

	//test i2c
	uint8_t rett;
	rett = vl53l5cx_test_i2c(&pObj.Dev);
	APP_LOG(TS_ON, MDS_VLEVEL, "[ToF] vl53l5cx_test_i2c %d\n", rett);

 

 

 

right after these lines i get the -1 from init:

 

 

int32_t ret = VL53L8CX_Init(&pObj);
	APP_LOG(TS_ON, MDS_VLEVEL, "[ToF] init return value %d\n", ret);


// the output:
15:22:30.839 -> 37s832:[ToF] init return value -1

 

AA.16
Associate III

in the official code it states:

 

	/* Download FW into VL53L5 */
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x09);
	status |= WrMulti(&(p_dev->platform),0,	(uint8_t*)&VL53L8CX_FIRMWARE[0],0x8000);
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x0a);
	status |= WrMulti(&(p_dev->platform),0,	(uint8_t*)&VL53L8CX_FIRMWARE[0x8000],0x8000);
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x0b);
	status |= WrMulti(&(p_dev->platform),0,	(uint8_t*)&VL53L8CX_FIRMWARE[0x10000],0x5000);
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x01);

	/* Check if FW correctly downloaded */
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x01);
	status |= WrByte(&(p_dev->platform), 0x06, 0x03);
	status |= _vl53l8cx_poll_for_answer(p_dev, 1, 0, 0x21, 0x10, 0x10);
	status |= WrByte(&(p_dev->platform), 0x7fff, 0x00);
	status |= RdByte(&(p_dev->platform), 0x7fff, &tmp);
	status |= WrByte(&(p_dev->platform), 0x0C, 0x01);

 

is this even correct?

first of all it doesnt even check for a checksum when testing if FW is downloaded correctly. well ok maybe the registers its reading  via poll for anser, are already doing something similiar?

 

but even before that it keeps downloading chunks of 0x8000 bytes of firmware onto the same address (0x0).

Also what are these single bytes its writing before a big chunk? People in this forum said they needed to cut the big write operations into smaller chunks. i had no luck with that the init function returns with -1 regardless of chunk size. But what are the small single register writes? maybe these are some sort of checksum that i would have to also send if i would cut the 0x8000 write operation into smaller chunks? Did anyone else in the forum do so?

 

Another small thing i noticed: the comment is for the VL53L5. i however have a VL53L8CX. i have seen this name mismatch alot in the forum and the codebase. i assume its fine and not a problem?

The VL53L5 and the L8 run the same code. The L8 has some other features (sync pin, brighter light, separate power and SPI) but they run the same basic code.

Those single byte writes change the page number. So you are putting the executable code on page 9, A and 0xB before putting the page number back to 1.

upon writing the last byte, the code should start. If the code pops up and gives you a response to your 'poll for answer', then you are all set.

AS you are not 'all set' I can only surmise that somehow the chunk-wise writes went bad on you.  Or that you missed something in your simplification. 


Our community relies on fruitful exchanges and good quality content. You can thank and reward helpful and positive contributions by marking them as 'Accept as Solution'. When marking a solution, make sure it answers your original question or issue that you raised.

ST Employees that act as moderators have the right to accept the solution, judging by their expertise. This helps other community members identify useful discussions and refrain from raising the same question. If you notice any false behavior or abuse of the action, do not hesitate to 'Report Inappropriate Content'

And how can i proceed now?

Deadlines are approaching and i cant see what i am doing wrong.

The alive test passes.

the i2c test passes

 

everything seems to work. but i still get a -1 on my init attempt.

 

I am running out of ideas and time. What could fix this issue?

I tried multiple approaches of the write multi and read multi. none has worked. They all passed the i2c test above. however every init try results in a -1 return value.

I'm also having errors in this area of the ST provided Linux driver, I mentioned earlier up the thread.

 

What could be causing these issues if there official driver support doesn't work?

 

I2c speeds?

 

I'm also stuck and have unfortunately had to move to a different sensor for the time being to manage development time.

 

Would be great to get it working though, as is the first and still better choice.

 

Thank you,

William

John E KVAM
ST Employee

I'm really sorry you are having such issues.

Here is what I would do...

Start with the EVAL kit and its software. get that running. 

Then place a print statement inside the I2C write function. 

Print out the I2C address, register address, length, first few bytes and the last few bytes.

then put that same print into your code. 

the output hast to be identical.

If you have shorter chunks, we still have to verify you have the exact same bytes. There is even an include in the code that tells you what the code should be. (But keep in mind the bytes might be reversed in your processor.

there is something going on here.  And almost every time, it's byte-swap, word-swap or i2C max length.

- john


Our community relies on fruitful exchanges and good quality content. You can thank and reward helpful and positive contributions by marking them as 'Accept as Solution'. When marking a solution, make sure it answers your original question or issue that you raised.

ST Employees that act as moderators have the right to accept the solution, judging by their expertise. This helps other community members identify useful discussions and refrain from raising the same question. If you notice any false behavior or abuse of the action, do not hesitate to 'Report Inappropriate Content'

i got it to work. for future generations here is how i could solve it.

my implementation in wrMulti looks like this and does work. meaning init returns a 0 now:

 

uint8_t WrMulti(VL53L8CX_Platform *p_platform, uint16_t RegisterAddress, uint8_t *p_values, uint32_t size) {
    uint8_t status = HAL_OK;  // Initialize status as HAL_OK
    uint32_t remaining_size = size;  // Calculate remaining size to write
    uint16_t current_address = RegisterAddress;  // Initialize current address
    // Loop until all data is written
    while (remaining_size > 0) {
        // Calculate the current chunk size to write
        uint32_t current_chunk_size = (remaining_size > chunk_size) ? chunk_size : remaining_size;

        // Perform write operation for current chunk
        status = HAL_I2C_Mem_Write(&hi2c2, addr, current_address, I2C_MEMADD_SIZE_16BIT, p_values, current_chunk_size, 100);

        // Check for error
        if (status != HAL_OK) {
            APP_LOG(TS_ON, 3, "[WrMulti] error. status %d, regADDR %d, size %d, remaining_size %d\n", status, current_address, size, remaining_size);
            return status;  // Return error status
        }

        // Update remaining size and pointer to move to the next chunk
        remaining_size -= current_chunk_size;
        current_address += current_chunk_size; // Increment register address for next chunk
        p_values += current_chunk_size;
        HAL_Delay(10);
    }

    return status;  // Return status (HAL_OK if all writes were successful)
}

notice how i used tha HAL command to write 16 bit addr and had to manually recalculate the current address before each loop. 

i have chuunk size defined as:

#define chunk_size (1<<8)

the same has to be done for RdMulti. as it tries to read 420 bytes at once at some point.

 

do not try to read the registers after downloading firmware, to check if they match with the firmware, you just downlaoded like i tried. it will make init fail. i used the default firmware download code.