前言
最近在倒腾毕业设计,需要用到TOF050C,但是现有的案例都是软IIC,并且还是基于STM32F103的,笔者用的STM32F767,没有GPIO->CRH寄存器。问题来了,如果我每次都要去看寄存器手册属实费时间,这不干脆直接用硬IIC?
于是乎,打开了TOF050C手册,硬啃!
这手册好在它有工作流程图,能提高开发人员的理解速度。
硬IIC开发代码
由于是使用IIC,用定时器实现微秒级延时,这就不多说了。
直接上库代码
vl6180x.c:
#include "vl6180x.h"
#define addr_write 0x52
#define addr_read 0x53
#define IDENTIFICATION__MODEL_ID 0x000
#define IDENTIFICATION__MODEL_REV_MAJOR 0x001
#define IDENTIFICATION__MODEL_REV_MINOR 0x002
#define IDENTIFICATION__MODULE_REV_MAJOR 0x003
#define IDENTIFICATION__MODULE_REV_MINOR 0x004
#define IDENTIFICATION__DATE_HI 0x006
#define IDENTIFICATION__DATE_LO 0x007
#define IDENTIFICATION__TIME 0x008
#define SYSTEM__MODE_GPIO0 0x010
#define SYSTEM__MODE_GPIO1 0x011
#define SYSTEM__HISTORY_CTRL 0x012
#define SYSTEM__INTERRUPT_CONFIG_GPIO 0x014
#define SYSTEM__INTERRUPT_CLEAR 0x015
#define SYSTEM__FRESH_OUT_OF_RESET 0x016
#define SYSTEM__GROUPED_PARAMETER_HOLD 0x017
#define SYSRANGE__START 0x018
#define SYSRANGE__THRESH_HIGH 0x019
#define SYSRANGE__THRESH_LOW 0x01A
#define SYSRANGE__INTERMEASUREMENT_PERIOD 0x01B
#define SYSRANGE__MAX_CONVERGENCE_TIME 0x01C
#define SYSRANGE__CROSSTALK_COMPENSATION_RATE 0x01E
#define SYSRANGE__CROSSTALK_VALID_HEIGHT 0x021
#define SYSRANGE__EARLY_CONVERGENCE_ESTIMATE 0x022
#define SYSRANGE__PART_TO_PART_RANGE_OFFSET 0x024
#define SYSRANGE__RANGE_IGNORE_VALID_HEIGHT 0x025
#define SYSRANGE__RANGE_IGNORE_THRESHOLD 0x026
#define SYSRANGE__MAX_AMBIENT_LEVEL_MULT 0x02C
#define SYSRANGE__RANGE_CHECK_ENABLES 0x02D
#define SYSRANGE__VHV_RECALIBRATE 0x02E
#define SYSRANGE__VHV_REPEAT_RATE 0x031
#define SYSALS__START 0x038
#define SYSALS__THRESH_HIGH 0x03A
#define SYSALS__THRESH_LOW 0x03C
#define SYSALS__INTERMEASUREMENT_PERIOD 0x03E
#define SYSALS__ANALOGUE_GAIN 0x03F
#define SYSALS__INTEGRATION_PERIOD 0x040
#define RESULT__RANGE_STATUS 0x04D
#define RESULT__ALS_STATUS 0x04E
#define RESULT__INTERRUPT_STATUS_GPIO 0x04F
#define RESULT__ALS_VAL 0x050
#define RESULT__HISTORY_BUFFER_0 0x052
#define RESULT__HISTORY_BUFFER_1 0x054
#define RESULT__HISTORY_BUFFER_2 0x056
#define RESULT__HISTORY_BUFFER_3 0x058
#define RESULT__HISTORY_BUFFER_4 0x05A
#define RESULT__HISTORY_BUFFER_5 0x05C
#define RESULT__HISTORY_BUFFER_6 0x05E
#define RESULT__HISTORY_BUFFER_7 0x060
#define RESULT__RANGE_VAL 0x062
#define RESULT__RANGE_RAW 0x064
#define RESULT__RANGE_RETURN_RATE 0x066
#define RESULT__RANGE_REFERENCE_RATE 0x068
#define RESULT__RANGE_RETURN_SIGNAL_COUNT 0x06C
#define RESULT__RANGE_REFERENCE_SIGNAL_COUNT 0x070
#define RESULT__RANGE_RETURN_AMB_COUNT 0x074
#define RESULT__RANGE_REFERENCE_AMB_COUNT 0x078
#define RESULT__RANGE_RETURN_CONV_TIME 0x07C
#define RESULT__RANGE_REFERENCE_CONV_TIME 0x080
#define RANGE_SCALER 0x096
#define READOUT__AVERAGING_SAMPLE_PERIOD 0x10A
#define FIRMWARE__BOOTUP 0x119
#define FIRMWARE__RESULT_SCALER 0x120
#define I2C_SLAVE__DEVICE_ADDRESS 0x212
#define INTERLEAVED_MODE__ENABLE 0x2A3
const uint16_t ScalerValues[] = {0, 253, 127, 84};
uint8_t ptp_offset;
uint8_t scaling;
uint16_t io_timeout;
void VL6180X_WR_CMD(uint16_t cmd, uint8_t data)
{
uint8_t data_write[3];
data_write[0]=(cmd>>8)&0xff;
data_write[1]=cmd&0xff;
data_write[2]=data&0xff;
HAL_I2C_Master_Transmit(&hi2c1, addr_write, data_write, 3, 0x100);
}
void VL6180X_WR_CMD2(uint16_t cmd, uint16_t data)
{
uint8_t data_write[4];
data_write[0]=(cmd>>8)&0xff;
data_write[1]=cmd&0xff;
data_write[2]=(data>>8)&0xff;
data_write[3]=data&0xff;
HAL_I2C_Master_Transmit(&hi2c1, addr_write, data_write, 4, 0x100);
}
uint8_t VL6180X_ReadByte(uint16_t reg)
{
uint8_t data_write[2];
uint8_t receive_data=0;
data_write[0]=(reg>>8)&0xff;
data_write[1]=reg&0xff;
HAL_I2C_Master_Transmit(&hi2c1, addr_write, data_write, 2, 0x100);
HAL_I2C_Master_Receive(&hi2c1, addr_read, &receive_data, 1, 0x100);
return receive_data;
}
uint8_t VL6180X_Init()
{
ptp_offset = 0;
scaling = 0;
io_timeout = 2;
ptp_offset = VL6180X_ReadByte(SYSRANGE__PART_TO_PART_RANGE_OFFSET);
uint8_t reset=VL6180X_ReadByte(0x016);//check wether reset over
if(reset==1)
{
VL6180X_WR_CMD(0X0207,0X01);
VL6180X_WR_CMD(0X0208,0X01);
VL6180X_WR_CMD(0X0096,0X00);
VL6180X_WR_CMD(0X0097,0XFD);
VL6180X_WR_CMD(0X00E3,0X00);
VL6180X_WR_CMD(0X00E4,0X04);
VL6180X_WR_CMD(0X00E5,0X02);
VL6180X_WR_CMD(0X00E6,0X01);
VL6180X_WR_CMD(0X00E7,0X03);
VL6180X_WR_CMD(0X00F5,0X02);
VL6180X_WR_CMD(0X00D9,0X05);
VL6180X_WR_CMD(0X00DB,0XCE);
VL6180X_WR_CMD(0X02DC,0X03);
VL6180X_WR_CMD(0X00DD,0XF8);
VL6180X_WR_CMD(0X009F,0X00);
VL6180X_WR_CMD(0X00A3,0X3C);
VL6180X_WR_CMD(0X00B7,0X00);
VL6180X_WR_CMD(0X00BB,0X3C);
VL6180X_WR_CMD(0X00B2,0X09);
VL6180X_WR_CMD(0X00CA,0X09);
VL6180X_WR_CMD(0X0198,0X01);
VL6180X_WR_CMD(0X01B0,0X17);
VL6180X_WR_CMD(0X01AD,0X00);
VL6180X_WR_CMD(0X00FF,0X05);
VL6180X_WR_CMD(0X0100,0X05);
VL6180X_WR_CMD(0X0199,0X05);
VL6180X_WR_CMD(0X01A6,0X1B);
VL6180X_WR_CMD(0X01AC,0X3E);
VL6180X_WR_CMD(0X01A7,0X1F);
VL6180X_WR_CMD(0X0030,0X00);
VL6180X_WR_CMD(0X0011,0X10);
VL6180X_WR_CMD(0X010A,0X30);
VL6180X_WR_CMD(0X003F,0X46);
VL6180X_WR_CMD(0X0031,0XFF);
VL6180X_WR_CMD(0X0040,0X63);
VL6180X_WR_CMD(0X002E,0X01);
VL6180X_WR_CMD(0X001B,0X09);
VL6180X_WR_CMD(0X003E,0X31);
VL6180X_WR_CMD(0X0014,0X24);
VL6180X_WR_CMD(0x016,0x00);
return 1;
}
return 0;
}
void VL6180X_SetScaling(uint8_t new_scaling)
{
uint8_t const DefaultCrosstalkValidHeight = 20; // default value of SYSRANGE__CROSSTALK_VALID_HEIGHT
// do nothing if scaling value is invalid
if (new_scaling < 1 || new_scaling > 3) { return; }
scaling = new_scaling;
VL6180X_WR_CMD2(RANGE_SCALER, ScalerValues[scaling]);
// apply scaling on part-to-part offset
VL6180X_WR_CMD(SYSRANGE__PART_TO_PART_RANGE_OFFSET, ptp_offset / scaling);
// apply scaling on CrossTalkValidHeight
VL6180X_WR_CMD(SYSRANGE__CROSSTALK_VALID_HEIGHT, DefaultCrosstalkValidHeight / scaling);
// This function does not apply scaling to RANGE_IGNORE_VALID_HEIGHT.
// enable early convergence estimate only at 1x scaling
uint8_t rce = VL6180X_ReadByte(SYSRANGE__RANGE_CHECK_ENABLES);
VL6180X_WR_CMD(SYSRANGE__RANGE_CHECK_ENABLES, (rce & 0xFE) | (scaling == 1));
}
void VL6180X_ConfigureDefault()
{
VL6180X_WR_CMD(READOUT__AVERAGING_SAMPLE_PERIOD,0x30);
VL6180X_WR_CMD(SYSALS__ANALOGUE_GAIN, 0x46);
VL6180X_WR_CMD(SYSRANGE__VHV_REPEAT_RATE, 0xFF);
VL6180X_WR_CMD2(SYSALS__INTEGRATION_PERIOD, 0x0063);
VL6180X_WR_CMD(SYSRANGE__VHV_RECALIBRATE, 0x01);
VL6180X_WR_CMD(SYSRANGE__INTERMEASUREMENT_PERIOD, 0x09);
VL6180X_WR_CMD(SYSALS__INTERMEASUREMENT_PERIOD, 0x31);
VL6180X_WR_CMD(SYSTEM__INTERRUPT_CONFIG_GPIO, 0x24);
VL6180X_WR_CMD(SYSRANGE__MAX_CONVERGENCE_TIME, 0x31);
VL6180X_WR_CMD(INTERLEAVED_MODE__ENABLE, 0);
VL6180X_SetScaling(1);
}
void VL6180X_SetTimeout(uint16_t timeout)
{
io_timeout = timeout;
}
uint8_t VL6180X_Start_Range()
{
VL6180X_WR_CMD(0x018,0x01);
return 0;
}
uint16_t timeoutcnt=0;
/*poll for new sample ready */
uint8_t VL6180X_Poll_Range()
{
uint8_t status;
uint8_t range_status;
status=VL6180X_ReadByte(0x04f);
range_status=status&0x07;
while(range_status!=0x04)
{
timeoutcnt++;
if(timeoutcnt > io_timeout)
{
break;
}
status=VL6180X_ReadByte(0x04f);
range_status=status&0x07;
delay_ms(1);
}
return 0;
}
/*read range result (mm)*/
uint8_t VL6180_Read_Range()
{
int range;
range=VL6180X_ReadByte(0x062);
return range;
}
/*clear interrupt*/
void VL6180X_Clear_Interrupt()
{
VL6180X_WR_CMD(0x015,0x07);
}
uint16_t VL6180X_ReadRangeSingleMillimeters()
{
/*Start Single measure mode*/
VL6180X_Start_Range();
/* Wait for measurement ready. */
VL6180X_Poll_Range();
delay_ms(100);
return (uint16_t)scaling * VL6180_Read_Range();
}
vl6180x.h:
#ifndef __VL6180X_H
#define __VL6180X_H
#include "main.h"
#include "i2c.h"
#include "tim.h"
uint8_t VL6180X_Start_Range(void);
uint8_t VL6180X_Poll_Range(void);
uint8_t VL6180_Read_Range(void);
uint16_t VL6180X_ReadRangeSingleMillimeters(void);
void VL6180X_Clear_Interrupt(void);
uint8_t VL6180X_Init(void);
void VL6180X_ConfigureDefault(void);
void VL6180X_SetScaling(uint8_t new_scaling);
void VL6180X_SetTimeout(uint16_t timeout);
#endif
这里笔者用的是I2C1,读者要是用其他I2C,只需要对
void VL6180X_WR_CMD(uint16_t cmd, uint8_t data)
void VL6180X_WR_CMD2(uint16_t cmd, uint8_t data)
uint8_t VL6180X_ReadByte(uint16_t reg)
中的HAL_I2C_Master_XXXXX进行修改就好了。
测试
笔者用的STM32F767进行开发的,然后用串口进行输出
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
while(!VL6180X_Init());
VL6180X_ConfigureDefault();
VL6180X_SetTimeout(2);
VL6180X_SetScaling(1);
printf("test!\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
uint16_t distance = VL6180X_ReadRangeSingleMillimeters();
printf("%04d\r\n", distance);
delay_ms(900);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
整个项目模板是有STM32CUBEMX生成的,所以这里我直接上main()函数了,注意需要串口重定向哦~
usart.c:
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART1->ISR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->TDR=(uint8_t)ch;
return ch;
}
串口输出情况:
缩放因子
上述实验是使用缩放因子为1的 VL6180X_SetScaling(1);
其测量距离 2-18cm
根据手册,其实在不同缩放因子下,测量距离范围是不一样的,并且误差大小也不一样。
这里第二个"Scaling factor = 1"应该是写错了,改成"Scaling factor = 2"
也就是最大测量范围的值,实际上到不了这个距离,大概80~90%。
事实上,不仅有上限范围,下线范围也有变化,这个是我多次测量后发现数据不对劲所总结出来的。大概:
Scaling factor = 2测量范围是20~40cm,需要做数据拟合矫正数据
Scaling factor = 3测量范围是40~60cm,需要做数据拟合矫正数据
似乎不是线性关系,应该要二次函数拟合
简评
可能TOF050F版本会少这些破事,能通过上位机直接矫正,TOF050C只有通过自己多次测量,去调整寄存器的矫正参数,确实不是那么的好用哈。还是建议大家使用TOF050F版本。