2020-10-13 01:57 AM
Hi,
I am facing some issues related with VL6180x offset calibration (I am not using cover glass, and I have tested many sensors on breakout boards from Adafruit and Pololu).
First, with default SYSRANGE__PART_TO_PART_RANGE_OFFSET value, range measurement is 150:350mm instead of 0:100mm. (With target at 160mm I get 10mm from sensor, etc). So first 0-150 mm I get 0 as range measure.
If applying calibration procedure : https://www.st.com/resource/en/application_note/dm00122600-vl6180x-basic-ranging-application-note-stmicroelectronics.pdf I get:
Any idea of what could be happening?
Kind Regards,
Rafa
2020-10-15 07:48 AM
Sorry this took so long...
One response I got was:
"The maximum measurement for a babybear is 255mm unless you turn on a multiplier. So, if he is doing an offset test, you need to take into account what multiplier you used for the calibration. This value then has to be compensated in the final answer.
"My guess is that the calibration was done with a multiplier of 1 and then they are running it with a different multiplier when running. "
But I'm not so sure. If you get 0 at 50mm that is due to crosstalk, not offset. The worst offset i have ever seen is 20mm. 50 is out of the question.
But you say you don't have a coverglass - so in theory crosstalk is not possible.
The only thing left is that the light is bouncing off your structure somehow and immediately returning. Could that be possible?
If you embed the sensor into your structure and don't have a wide enough apperature, it's possible for light to hit he sides and bounce back. (It happend to another custoemer.)
Solutions to this problem would be to polish the walls of the structure so eliminate an reflections, painting the structure black, or simply making the apperature wider.
2020-10-19 10:55 PM
Hi @John E KVAM
I have tried crosstalk calibration and keeps not working.
We are not embedding the sensor in any structure, as we are using the breakout boards of Adafruit and Pololu, where sensor is on the top.
However, we have realised that we get "Raw Ranging Algo Overflow" in RESULT__RANGE_STATUS register. That could be related?
Kind Regards,
Rafa
2020-12-17 11:28 PM
Hi @John E KVAM
I have been testing the same vl6180x breakout board with a Raspberry Pi and adafruit_vl6180x.py software and with it that's working (distance measurements are right).
However, I am adapted the adafruit library for using in the STM32 and keeps not working. I have replicated too the I2C timming characteristics from Raspberry on the STM32.
This is the algorithm that is working on RPI but not on the STM32:
I have compared data frames sent/received on RPI and STM32 and both are identical, excepting the 0x062 response, where on RPI is the right distance, and on STM32 it's not.
Please find the I2C timming data on the attached .zip
What I am missing? II don't know what else to do.
Kind Regards,
2020-12-18 08:05 AM
RCres.1 -
It is always better to ask a new question in a new thread. Then others can find it.
When communicating with the VL6180 there are two things you have to solve. The first is the I2C traffic itself.
Hardware set up...
When you move to the STM32, the easy way to set things up is to use the STM32CubeMX code from ST. It's free - just download it from the ST site. This graphical tool will allow you to configure your MCU into the exact, valid configuration - and it writes all the initialization code for you. It even goes so far as to tell you "your code goes here".
And the best part - if you want to change something it re-writes the initialization code - leaving your code intact.
So changing an I2C bus speed after the project is almost completed is trivial. Heck you can even change the MCU itself and it leaves your code intact.
By doing this I can guarantee your I2C configuration will be valid.
Software set up...
Now download the ST API
STSW-IMG011 VL6180V1 Application Programming Interface API
You will get a bunch of functions that control the chip.
This should make it easy for you.
There is almost a one-to-one correspondence with the AdaFruit library.
You can use this or the Adafruit - that is up to you.
But either way, look at main.c in the example code.
You will see the I2C_Read, and I2C_write functions that are valid for an ST chip.
if you use the example code, you are practically done. Just run it.
if you use the AdaFruit code, extract the following from the ST code and insert it into the ada fruit.
/**
* VL6180x CubeMX F401 multiple device i2c implementation
*/
#define i2c_bus (&hi2c1)
#define def_i2c_time_out 100
int VL6180x_I2CWrite(VL6180xDev_t dev, uint8_t *buff, uint8_t len) {
int status;
status = HAL_I2C_Master_Transmit(i2c_bus, dev->I2cAddr, buff, len, def_i2c_time_out);
if (status) {
XNUCLEO6180XA1_I2C1_Init(&hi2c1);
}
return status? -1 : 0;
}
int VL6180x_I2CRead(VL6180xDev_t dev, uint8_t *buff, uint8_t len) {
int status;
status = HAL_I2C_Master_Receive(i2c_bus, dev->I2cAddr, buff, len, def_i2c_time_out);
if (status) {
XNUCLEO6180XA1_I2C1_Init(&hi2c1);
}
return status? -1 : 0;
}
/**
* platform and application specific for XNUCLEO6180XA1 Expansion Board
*/
void XNUCLEO6180XA1_WaitMilliSec(int n) {
WaitMilliSec(n);
}
So now you have a valid hardware set up.
And you have valid software to read and write the I2C.
That should do it.
Digging into the I2C timing is really hard. Then you have to figure out what to change in the I2C initialization code, and it's a real pain.
The STM32CubeMX really is one of the best things ST has ever done.
Use it once and you will be an ST customer for life.
(And I don't work for the MCU group. I used to dread changing MCUs - and I did it a lot. This app makes that job completely obsolete.)
2020-12-22 04:24 AM
Hi @John E KVAM , thanks a lot for the information.
However, it keeps answering with wrong distances (with vl6180_simple_ranging.c and using I2C1 interface). I am using a STM32F769-Discovery board.
As you said, I have used ST32Cube for adjusting I2C timmings and GPIO config. I also have followed the API Integration guide:
My GPIO config:
// Enable I2C interface clock
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// Configuring I2C Hardware
I2C_PORT->MODER |= 0x02<<I2C_SDA*2 | 0x02<<I2C_SCL*2; //Alternate function
I2C_PORT->OTYPER |= 0x01<<I2C_SDA | 0x01<<I2C_SCL; //Open Drain
I2C_PORT->PUPDR |= (0x01<<I2C_SDA*2) | (0x01<<I2C_SCL*2); //pull-up
I2C_PORT->OSPEEDR |= 0x11<<I2C_SDA*2 | 0x11<<I2C_SCL*2; // Very High Speed
I2C_PORT->AFR[1] |= 0x44<<GPIO_AFRH_AFRH0_Pos; // AF4 for SDA and SCL (pins 8 & 9)
My I2C Config:
void i2c_init(I2C_TypeDef* interface,unsigned char slave){
// Disable I2C (PE bit)
interface->CR1 &= ~I2C_CR1_PE;
// Disable Analog noise filter
interface->CR1 |= I2C_CR1_ANFOFF;
// Disable Digital noise filter
interface->CR1 &= ~I2C_CR1_DNF;
// Set timmings //100kHz //62,5kHz
interface->TIMINGR = 0x20404768;//0x10B0AAFE;
interface->CR1 |= I2C_CR1_NOSTRETCH;
// Enable I2C (PE bit)
interface->CR1 |= I2C_CR1_PE;
// 7-Bit Addressing mode
interface->CR2 &= ~I2C_CR2_ADD10;
// Set slave address
interface->CR2 |= slave<<1;
}
I2C routines (I have tested and they work well):
/**
****************************************************************************
* FUNCTION: i2c_write
*
* DESCRIPTION: Writes data and reads ACK/NAK
*
* ARGUMENTS:
* interface I2C Interface used
* len Number of bytes sent (<=255)
* data data to send
***************************************************************************/
void i2c_write(I2C_TypeDef* interface,unsigned char len, unsigned char* data){
// Clear interrupts
interface->ICR = 0x00FF;
// Write mode
interface->CR2 &= ~I2C_CR2_RD_WRN;
// Number of bytes to be written
interface->CR2 |= len<<I2C_CR2_NBYTES_Pos;
// Send START condition
i2c_start(interface);
for(unsigned char i = 0; i<len;i++){
//while(!(interface->ISR & I2C_ISR_TXIS));
interface->TXDR = *data;
data++;
while(!(interface->ISR & I2C_ISR_TXE));
}
// Send STOP condition
i2c_stop(interface);
// Transfer complete - Disable interrupts
interface->CR1 &= ~I2C_CR1_TXIE & ~I2C_CR1_TCIE & ~I2C_CR1_NACKIE;
}
/**
****************************************************************************
* FUNCTION: i2c_read
*
* DESCRIPTION: Reads Byte and sends NACK
*
* ARGUMENTS:
* interface I2C Interface used
* return Byte received
***************************************************************************/
char i2c_read(I2C_TypeDef* interface){
char ret = -1 ;
// Clear interrupts
interface->ICR = 0x00FF;
// Read mode
interface->CR2 |= I2C_CR2_RD_WRN;
// Number of bytes to be read
interface->CR2 |= 1<<I2C_CR2_NBYTES_Pos;
// Send START condition
i2c_start(interface);
// Get data
while(!(interface->ISR & I2C_ISR_RXNE));
ret = interface->RXDR;
// Send Stop condition
i2c_stop(interface);
// Disable interrupts
interface->CR1 &= ~I2C_CR1_RXIE & ~I2C_CR1_TCIE;
return ret;
}
/**
****************************************************************************
* FUNCTION: i2c_start
*
* DESCRIPTION: Sends start command to I2C Bus
*
* ARGUMENTS:
* interface I2C Interface used
***************************************************************************/
void i2c_start(I2C_TypeDef* interface){
interface->CR2 |= I2C_CR2_START;
while(interface->CR2 & I2C_CR2_START);
}
/**
****************************************************************************
* FUNCTION: i2c_stop
*
* DESCRIPTION: Sends stop command to I2C Bus
*
* ARGUMENTS:
* interface - I2C Interface used
***************************************************************************/
void i2c_stop(I2C_TypeDef* interface){
interface->CR2 |= I2C_CR2_STOP;
while(interface->CR2 & I2C_CR2_STOP);
}
And the vl6180_i2c.c modifications:
/**
* @brief Write data buffer to VL6180 device via i2c
* @param dev The device to write to
* @param buff The data buffer
* @param len The length of the transaction in byte
* @return 0 on success
* @ingroup cci_i2c
*/
int VL6180_I2CWrite(VL6180Dev_t dev, uint8_t *buff, uint8_t len){
i2c_write(I2C,len,buff);
return 0;
}
/**
*
* @brief Read data buffer from VL6180 device via i2c
* @param dev The device to read from
* @param buff The data buffer to fill
* @param len The length of the transaction in byte
* @return 0 on success
* @ingroup cci_i2c
*/
int VL6180_I2CRead(VL6180Dev_t dev, uint8_t *buff, uint8_t len){
for(unsigned char i=0;i<len;i++){
*buff = i2c_read(I2C);
buff++;
}
return 0;
}
Please find attached the API files I have added to my project to support it, and the Sample_SimpleRanging.c (Sample_SimpleRanging is executing every 1s).
As I said, all works as expected, excepting that for a target to 100mm, I get a distance of 39 mm, for a target to 180 mm, I get 59 mm, etc. And this value is checked with the one on the SDA line, through oscilloscope.
Kind Regards,
Rafa