DigiKey support,
I was told by STMicroelectronics that I could get technical assistance on this site for their VL6180X laser sensor device.
I get good distance measurements using an Arduino sketch but can’t use that platform on my final product. Instead I’m using a pic18f46k22 microcontroller but the measured value of the distance at register 0X62 always reads zero.
The device is configured exactly as shown in the Arduino_VL6180X.cpp file and I can successfully read the VL6180X slave address at register 0x212. All voltages for the sensor are 3.3.
I have used a pic and I2C with other devices and I’m getting all the acknowledgements with the VL6180X.
My code for reading the measurement is:
int readDistance(void)
{
int distance = 0;
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x18); // VL6180X_REG_SYSRANGE_START
I2C_Master_Write(0x01); // start a distance measurement
\__delay_us(100); // wait for measurement to complete
I2C_Master_RepeatedStart();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x62); // register to read distance
I2C_Master_RepeatedStart();
I2C_Master_Write((0x29 << 1) | 1); // TOF050C I²C address read
distance = I2C_Read_Byte(1); // Read the measured distance
I2C_Master_Stop();
return (distance);
} // end unsigned int readDistance(void)
I must be doing something wrong.
Hello,
Welcome to the DigiKey TechForum. I’m not familiar with this type of setup.
Maybe one of the engineers that monitor the TechForum, can help with this.
Hello @jerdonaldson,
Since you have working Arduino code that you are trying to replicate, it would be a great help if you could share that with us as well. Am I correct in assuming that you are using the Adafruit VL6180 Library? Adafruit_VL6180X.cpp was the closest in name I could find to the “Arduino_VL6180X.cpp” file you mentioned.
Also, can you explain why you are only waiting 100us before attempting to read the range measurement? I may be missing something, but Section 2.7.1 of the VL6180 datasheet suggests it should take on the order of milliseconds for a single range measurement to complete, especially with averaging. I would suggest polling bit 2 of the VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO register as done in the Adafruit library instead of a simple delay.
uint8_t Adafruit_VL6180X::readRange(void) {
// wait for device to be ready for range measurement
while (!(read8(VL6180X_REG_RESULT_RANGE_STATUS) & 0x01))
;
// Start a range measurement
write8(VL6180X_REG_SYSRANGE_START, 0x01);
// Poll until bit 2 is set
while (!(read8(VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04))
;
// read range in mm
uint8_t range = read8(VL6180X_REG_RESULT_RANGE_VAL);
// clear interrupt
write8(VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07);
return range;
}
Matt_Mielke,
Thanks for the reply.
In the Arduino sketch I do use the Adafruit_VL6180X.cpp. But in my pic microcontroller program I don’t have a library.
I missed that spec in the data sheet about 3.2 ms between reads. The device I was working with suggested much less. I’ll update my code and try it.
In the long run I have to read nine of these sensors in sequence and then do some code. After that’s complete I will wait enough time to not re-read any sensor before about 30ms.
I’ll let you know the results. It may be a few days.
Jerry
Matt_Mielke,
Finally updated my code to wait 4ms between readings and got some scope shots.
START.jpg are the waveforms for the first three steps that initiate a measurement.
READ.jpg are the waveforms for the last four steps that read the distance.
I placed the time measurement lines on the 9th clock to show the acknowledgements. According to the data sheet this should be enough to get a reading but as you can see I just get zero.
Here’s the code:
int readDistance(void)
{
int distance = 0;
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x18); // VL6180X_REG_SYSRANGE_START
I2C_Master_Write(0x01); // start a distance measurement
I2C_Master_Stop();
__delay_ms(4); // wait for measurement to complete
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x62); // register to read distance
I2C_Master_RepeatedStart();
I2C_Master_Write((0x29 << 1) | 1); // TOF050C I²C address read
distance = I2C_Read_Byte(1); // Read the measured distance
I2C_Master_Stop();
return (distance);
} // end unsigned int readDistance(void)
Jerry Donaldson
(attachments)
Hello @jerdonaldson,
Thanks for the response and the screenshots. Looking again at the datasheet, I see that I missed something in my previous post. From Section 4:
Your code is using 8-bit indexing rather than 16-bit as specified. This can easily be corrected by padding the register addresses as shown.
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x00); // DON'T FORGET ME!
I2C_Master_Write(0x18); // VL6180X_REG_SYSRANGE_START
I2C_Master_Write(0x01); // start a distance measurement
I2C_Master_Stop();
I assume this is why you were able to read the slave address at register 0x0212. You had to use 16-bit indexing to communicate this higher address to the sensor.
I’d also like to point out that 4ms is likely not going to be a long enough delay to allow the ranging to complete. The datasheet shows that the pre-calibration time plus the readout averaging period alone will take 7.5ms (assuming you haven’t changed the default averaging period). On top of this, you have to allow for the range convergence time, which Table 11 indicates may take anywhere from 180us up to nearly 11ms! Figure 20 summarizes this nicely.
I’ll point out again that best practice in this scenario it to utilize the interrupt flags within the sensor to determine when the measurement is complete. This simplest way to do this is by polling the interrupt status register and waiting for the New Sample Ready bit to be set (see the Adafruit code I posted previously). You could also configure the sensor’s GPIO to generate an external interrupt on the PIC, but that requires significantly more effort to get working properly.
Matt_Mielke,
Good stuff.
I have worked with some sensors that had 8 bit register addressing. I’ve just got to read the data sheet more closely.
I added the extra byte for the register addresses and increased the wait time to 20ms. Now I get reasonable distance readings.
I’ll work on using the status register as you suggest.
int readDistance(void)
{
int distance = 0;
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x00); // msb of VL6180X_REG_SYSRANGE_START
I2C_Master_Write(0x18); // lsb of VL6180X_REG_SYSRANGE_START
I2C_Master_Write(0x01); // start a distance measurement
I2C_Master_Stop();
__delay_ms(20); // wait for measurement to complete
I2C_Master_Start();
I2C_Master_Write(0x29 << 1); // TOF050C I²C address write
I2C_Master_Write(0x00); // msb of register byte to read distance
I2C_Master_Write(0x62); // lsb of register byte to read distance
I2C_Master_RepeatedStart();
I2C_Master_Write((0x29 << 1) | 1); // TOF050C I²C address read
distance = I2C_Read_Byte(1); // Read the measured distance
I2C_Master_Stop();
return (distance);
} // end int readDistance(void)
Thanks,
Jerry
Matt_Mielke,
I have run into another problem with a VL6180X laser sensor.
Since you pointed out that the registers have 16 bid addresses I can successfully read distances.
For cross checking I do this with an Arduino and with a pic microcontroller. The problem is the Arduino can measure down to 2cm but the pic only 10cm. I have configured the registers in the pic as they are in the Arduino. The power and I2C voltages are the same. Each has a 1000ms delay between reads.
My project will require the pic and it has to measure distances down to 2cm.
I can’t find anything in the data sheet to explain this.
I have attached the I2C code for the pic and the Arduino sketch.
Hope you can help.
Jerry Donaldson
(attachments)
VL6180Sketch (854 Bytes)
picSnipit (827 Bytes)
Hey @jerdonaldson,
Sure, I’ll look though your code here soon. In the meantime, it might speed things up if you could provide a bit more detail about the behavior you’re experiencing.
How exactly does the data become inaccurate below these ranges? Does it stop changing, drop down to 0, or just become progressively inaccurate? Any chance you could provide some serial output which clearly demonstrates the issue?