最近调VL53L0X花了不少时间,特总结下
https://www.st.com/content/st_com/en/search.html#q=vl53l-t=products-page=1
VL53L0X测距2m
VL53L1X测距4m 支持将校准数据保存到芯片内部
我们使用是是VL53L0X作为接近传感器 项目中存在两个VL53L0X对射的情况
接st官方的塑料帽或者不接,VL53L0X的测距都是挺准的 官方的塑料帽的发射和接收进行了物理隔离,所以不会有干扰问题。
但接上我们的玻璃盖板后,60cm的距离测出的是40cm,而且每块玻璃盖板都有差异。官方有该现象的解释(VL53L0X calibration free dirty environment cover glass solution)https://www.st.com/content/st_com/en/search.html#q=vl53l-t=videos-page=1,可以通过校准来修正。
校准分为长距(xtalk 60cm)和短距(offect 10cm)校准
假设没校准前,测量60cm的距离,读出来的距离是40cm,校准后,读数也是60cm,相等于对数据进行了修正。
应用层的校准demo如下
#include
#include
#include
#include
#include
#include
#include
#include "vl53l0x_def.h"
#define MODE_RANGE 0
#define MODE_XTAKCALIB 1
#define MODE_OFFCALIB 2
#define MODE_HELP 3
#define MODE_PARAMETER 6
#define MODE_OFFCALIB_SET 7
#define MODE_XTAKCALIB_SET 8
//******************************** IOCTL definitions
#define VL53L0X_IOCTL_INIT _IO('p', 0x01)
#define VL53L0X_IOCTL_XTALKCALB _IOW('p', 0x02, unsigned int)
#define VL53L0X_IOCTL_OFFCALB _IOW('p', 0x03, unsigned int)
#define VL53L0X_IOCTL_STOP _IO('p', 0x05)
#define VL53L0X_IOCTL_SETXTALK _IOW('p', 0x06, unsigned int)
#define VL53L0X_IOCTL_SETOFFSET _IOW('p', 0x07, int8_t)
#define VL53L0X_IOCTL_GETDATAS _IOR('p', 0x0b, VL53L0X_RangingMeasurementData_t)
#define VL53L0X_IOCTL_PARAMETER _IOWR('p', 0x0d, struct stmvl53l0x_parameter)
//modify the following macro accoring to testing set up
#define OFFSET_TARGET 100
#define XTALK_TARGET 600
#define NUM_SAMPLES 20//20
typedef enum {
OFFSET_PAR = 0,
XTALKRATE_PAR = 1,
XTALKENABLE_PAR = 2,
GPIOFUNC_PAR = 3,
LOWTHRESH_PAR = 4,
HIGHTHRESH_PAR = 5,
DEVICEMODE_PAR = 6,
INTERMEASUREMENT_PAR = 7,
REFERENCESPADS_PAR = 8,
REFCALIBRATION_PAR = 9,
} parameter_name_e;
/*
* IOCTL parameter structs
*/
struct stmvl53l0x_parameter {
uint32_t is_read; //1: Get 0: Set
parameter_name_e name;
int32_t value;
int32_t value2;
int32_t status;
};
static void help(void)
{
fprintf(stderr,
"Usage: vl53l0x_test [-c] [-h]\n"
" -h for usage\n"
" -c for crosstalk calibration\n"
" -x set crosstalk calibration\n"
" -o for offset calibration\n"
" -s set offset calibration\n"
" default for ranging\n"
);
exit(1);
}
//xtalk Calibrate place black target at 600mm from glass===
//XtalkInt:280
//xtalk Calibrate place black target at 100mm from glass===
//get offset 9000 micrometer===
//get VhvSettings is 27 ===
//get PhaseCas is 1 ===
//get SpadCount is 9 ===
//get IsApertureSpads is 1 =
int main(int argc, char *argv[])
{
int fd;
unsigned long data;
VL53L0X_RangingMeasurementData_t range_datas;
struct stmvl53l0x_parameter parameter;
int flags = 0;
int mode = MODE_RANGE;
unsigned int targetDistance=0;
int i = 0;
/* handle (optional) flags first */
while (1+flags < argc && argv[1+flags][0] == '-') {
switch (argv[1+flags][1]) {
case 'c': mode= MODE_XTAKCALIB; break;
case 'x': mode= MODE_XTAKCALIB_SET; break;
case 'o': mode = MODE_OFFCALIB; break;
case 's': mode = MODE_OFFCALIB_SET; break;
case 'h': mode= MODE_HELP; break;
default:
fprintf(stderr, "Error: Unsupported option "
"\"%s\"!\n", argv[1+flags]);
help();
exit(1);
}
flags++;
}
if (mode == MODE_HELP)
{
help();
exit(0);
}
fd = open("/dev/stmvl53l0y_ranging",O_RDWR | O_SYNC);
if (fd <= 0)
{
fprintf(stderr,"Error open stmvl53l0x_ranging device: %s\n", strerror(errno));
return -1;
}
if (ioctl(fd, VL53L0X_IOCTL_STOP , NULL) < 0) { //make sure it's not started
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_STOP : %s\n", strerror(errno));
close(fd);
return -1;
}
if (mode == MODE_XTAKCALIB)//校准并获取校准因子
{
unsigned int XtalkInt = 0;
uint8_t XtalkEnable = 0;
fprintf(stderr, "xtalk Calibrate place black target at %dmm from glass===\n",XTALK_TARGET);
targetDistance = XTALK_TARGET;
if (ioctl(fd, VL53L0X_IOCTL_XTALKCALB , &targetDistance) < 0) {//开始校准
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_XTALKCALB : %s\n", strerror(errno));
close(fd);
return -1;
}
parameter.is_read = 1;
parameter.name = XTALKRATE_PAR;
if (ioctl(fd, VL53L0X_IOCTL_PARAMETER , ¶meter) < 0) {
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
XtalkInt = (unsigned int)parameter.value;//xtalk(得到的校准因子 需要保存)
fprintf(stderr,"XtalkInt:%d\n",XtalkInt);
close(fd);
return 0;
}else if(mode == MODE_XTAKCALIB_SET){//每次开机设置XTAK校准因子到机器
fprintf(stderr, "start MODE_XTAKCALIB_SET\n");
parameter.is_read = 0;
parameter.name = XTALKENABLE_PAR;
parameter.value=1;
if (ioctl(fd, VL53L0X_IOCTL_PARAMETER , ¶meter) < 0) { //使能校准
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
parameter.name = XTALKRATE_PAR;
parameter.value=432; //上面的XtalkInt 就是校准因子 假设是432
if (ioctl(fd, VL53L0X_IOCTL_PARAMETER , ¶meter) < 0) {//写校准因子到机器
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
fprintf(stderr, "stop MODE_XTAKCALIB_SET\n");
close(fd);
return 0;
}else if (mode == MODE_OFFCALIB){
int offset=0;
uint32_t SpadCount=0;
uint8_t IsApertureSpads=0;
uint8_t VhvSettings=0,PhaseCal=0;
fprintf(stderr, "offset Calibrate place black target at %dmm from glass===\n",OFFSET_TARGET);
targetDistance = OFFSET_TARGET;
if (ioctl(fd, VL53L0X_IOCTL_OFFCALB , &targetDistance) < 0) {
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_OFFCALB : %s\n", strerror(errno));
close(fd);
return -1;
}
parameter.is_read = 1;
parameter.name = OFFSET_PAR;
if (ioctl(fd, VL53L0X_IOCTL_PARAMETER, ¶meter) < 0) {
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
offset = (int)parameter.value; //获取offset校准因子
fprintf(stderr, "get offset %d micrometer===\n",offset);
close(fd);
return -1;
}else if (mode == MODE_OFFCALIB_SET){
int offset=0;
uint32_t SpadCount=0;
uint8_t IsApertureSpads=0;
uint8_t VhvSettings=0,PhaseCal=0;
fprintf(stderr, "start MODE_OFFCALIB_SET\n");
parameter.is_read = 0;
parameter.name = OFFSET_PAR;
parameter.value=400; //设置offset校准因子,假设是400
if (ioctl(fd, VL53L0X_IOCTL_PARAMETER, ¶meter) < 0) {
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
if (ioctl(fd, VL53L0X_IOCTL_SETOFFSET , ¶meter) < 0) {//写校准因子到机器
fprintf(stderr, "Error: Could not perform VL53L0X_IOCTL_PARAMETER : %s\n", strerror(errno));
close(fd);
return -1;
}
fprintf(stderr, "stop MODE_OFFCALIB_SET\n");
close(fd);
return -1;
}
else{
}
return 0;
}
驱动层的校准可参考
static ssize_t stmvl53l0x_store_enable_ps_sensor(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct stmvl53l0x_data *data = dev_get_drvdata(dev);
int ret;
FixPoint1616_t TempFix1616;
VL53L0Y_DEV vl53l0x_dev = data;
unsigned long val = simple_strtoul(buf, NULL, 10);
if ((val != 0) && (val != 1) && (val != 2) && (val != 3)) {
vl53l0x_errmsg("store unvalid value=%ld\n", val);
return count;
}
mutex_lock(&data->work_mutex);
vl53l0x_dbgmsg("Enter, enable_ps_sensor flag:%d\n",
data->enable_ps_sensor);
vl53l0x_dbgmsg("enable ps senosr ( %ld)\n", val);
if (val == 1) {
/* turn on tof sensor */
if (data->enable_ps_sensor == 0) {
/* to start */
stmvl53l0x_start(data, 3, NORMAL_MODE);
} else {
vl53l0x_errmsg("Already enabled. Skip !");
}
}else if(val == 2){
vl53l0x_dbgmsg("Enter XTALKCALIB_MODE!\n");
if (data->enable_ps_sensor == 1) {
data->enable_ps_sensor = 0;
stmvl53l0x_stop(data);
}
data->xtalkCalDistance=600;
stmvl53l0x_start(data, 3, XTALKCALIB_MODE);
papi_func_tbl->GetXTalkCompensationRateMegaCps(vl53l0x_dev, &TempFix1616);
vl53l0x_dbgmsg("Enter XTALKCALIB_MODE TempFix1616=%d\n",TempFix1616);
xtalk_calib = TempFix1616;
ret=stmvl53l0x_write_xtalk_calibration_file();
if(ret<0){
vl53l0x_errmsg("stmvl53l0x_write_xtalk_calibration_file error !");
count=-1;
}
}
else if(val == 3){
vl53l0x_dbgmsg("Enter OFFSETCALIB_MODE!\n");
if (data->enable_ps_sensor == 1) {
data->enable_ps_sensor = 0;
stmvl53l0x_stop(data);
}
data->offsetCalDistance=100;
stmvl53l0x_start(data, 3, OFFSETCALIB_MODE);
papi_func_tbl->GetOffsetCalibrationDataMicroMeter(vl53l0x_dev, &TempFix1616);
vl53l0x_dbgmsg("Enter XTALKCALIB_MODE TempFix1616=%d\n",TempFix1616);
offset_calib=TempFix1616;
ret=stmvl53l0x_write_offset_calibration_file();
if(ret<0){
vl53l0x_errmsg("stmvl53l0x_write_offset_calibration_file error !");
count=-1;
}
}
else {
/* turn off tof sensor */
if (data->enable_ps_sensor == 1) {
data->enable_ps_sensor = 0;
/* to stop */
stmvl53l0x_stop(data);
}
}
vl53l0x_dbgmsg("End\n");
mutex_unlock(&data->work_mutex);
return count;
}
保存和读取校准文件,注意文件的权限,否则会出现文件创建,读写失败
stmvl53l0x_read_calibration_file
stmvl53l0x_write_offset_calibration_file
stmvl53l0x_write_xtalk_calibration_file
f->f_op->read会变成NULL,直接调用有空指针,需修正为
f->f_op->read ---> vfs_read
f->f_op->write---> vfs_write
默认有个工作队列(非中断模式)循环获取数据data->delay_ms(修改成int型),因为我们的产品存在两个vl53l0x对射的情况,需要加大该值来降低vl53l0x工作频率(减少单位时间内的发光量)。