使用STM32 HAL库实现TOF050C模块的硬件I2C通信

前言

最近在倒腾毕业设计,需要用到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版本。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32 HAL库实现TOF050C模块的硬件I2C通信

    发表评论