2024艾迈斯欧司朗竞赛 - 基于TMF8821 TOF传感器实现角度距离测算
该项目使用了艾迈斯欧司朗TMF8821 TOF传感器,实现了角度距离测算的设计,它的主要功能为:将板卡组合后固定,并保证与面前的平面存在一定夹角,通过程序测算出板卡距离屏幕的夹角和垂直最小距离。
标签
Arduino
ToF传感器
艾迈斯欧司朗
不爱胡萝卜的仓鼠
更新2025-03-06
20

一.项目介绍

很高兴可以参加本次的2024艾迈斯欧司朗dToF传感器光电设计竞赛,本次将使用TMF8821传感器完成本次的题目

本次我选择第一题:角度测算

将板卡组合后固定,并保证与面前的平面存在一定夹角,通过程序测算出板卡距离屏幕的夹角和垂直最小距离


二.简短的使用到的硬件介绍

本项目使用到的硬件如下

1.TOF传感器:TMF8821模块

2.主控:Arduino Mega 2560

3.扩展板:Arduino Mega 2560扩展板(DFRobot)、TMF8821模块扩展板(自己绘制)


4.7针0.96寸OLED屏幕


组装好的状态:


三.方案框图和项目设计思路介绍

硬件系统框图如下

arduino mega 2560作为主控,通过IIC与TOF传感器通讯,再通过SPI向OLED屏发送显示内容,屏幕显示测距结果、计算得到的TOF传感器与平面的夹角和垂直最小距离


整体开发逻辑如下:

STEP1:完成TOF传感器驱动,初始化、配置、读取数据等

STEP2:使用U8G2库,在屏幕上显示测距结果

STEP3:利用测距结果计算得到TOF传感器与平面的夹角和垂直最小距离


其中计算的方案如下:

我使用的的是3X3的点阵,MAP图如下

可以看到垂直方向为32°,也就是说2号点与8号点之间的夹角是32°。下图为我绘制的一个侧视图

那么把这个场景转化成几何图形

∠ADC可以通过180°-∠DAC-∠ACD得到,现在需要求出∠ACD


∠ACD也就是∠ACB,那么可以利用三角形ABC


可以先用余弦定理计算得到BC的长度,因为AB、AC、∠BAC是知道的


知道BC长度后,可以利用正弦定理计算得到∠ACB

这样一来思路就清晰了


剩下还有垂直最小距离,这个就很简单了,就是AC



四.软件流程图和关键代码介绍

4.1软件流程图如下

初始化函数和主函数如下

void setup ( )
{
  init_serial();
  init_IIC();
  oled_init();


  TOF_init();


  TOF_start_measure();
}


void loop ( )
{
  uint8_t TOF_result_buffer[TOF_MEASURE_DATA_LEN] = {0};
  static uint8_t angle;
  static int distance;


  /**************看Host Driver手册4.6章节***************/
  /* 使用轮询的方式获取数据(轮询方案可以参考Host Driver手册4.11.2章节) */
  if (TOF_is_data_rerady_to_read())
  {
    TOF_read_measure_data(TOF_result_buffer);


    cal_angle_and_distance(TOF_result_buffer, &angle, &distance);


    oled_display_result(TOF_result_buffer, angle, distance);
  }
}


4.2TOF初始化流程图如下:

代码如下

void TOF_init()
{
  /* EN引脚 */
  pinMode(TOF_EN_PIN, OUTPUT);
  /* 打开EN */
  digitalWrite(TOF_EN_PIN, HIGH);
  delay(100);


  TOF_enable();
  TOF_wait_cpu_is_ready();
    TOF_read_appid_reg();
  TOF_download_firmware(IMAGE_START, tof_image, IMAGE_LENGTH);
    TOF_read_appid_reg();


  /* 配置参数 */
  TOF_config_param();


  /* 可以不管校准参数,可以用,就是可能会不准,测量结果给个警告,但结果能用 */
#if 0
  /* 加载工厂校准数据  */
  TOF_load_factory_calibration();
#endif
}


4.3 开始TOF采样的函数

void TOF_start_measure()
{
  /**************看Host Driver手册4.5章节***************/
  uint8_t TxBuffer[1] = {0};
  uint8_t RxBuffer[1] = {0};


  /* 设置INT_ENAB寄存器,把可以读取测量结束数据的int打开 */
  TxBuffer[0] = BIT_1; /* 可以读取测量结束数据的int对应bit2 */
  IIC_writeReg(PRINT_DEBUG_INFO_ENABLE, TOF_REG_ADDR_INT_ENAB, 1, TxBuffer);


  /* 清除INT_STATUS寄存器,用于清除所有旧的待处理中断 */
  TxBuffer[0] = 0xFF; /* bit写入1表示清除,所以为FF */
  IIC_writeReg(PRINT_DEBUG_INFO_ENABLE, TOF_REG_ADDR_INT_STATUS, 1, TxBuffer);


  /* 开始测量 */
  TxBuffer[0] = TOF_REG_VALUE_CMD_STAT__CMD_MEASURE;
  IIC_writeReg(PRINT_DEBUG_INFO_ENABLE, TOF_REG_ADDR_CMD_STAT, 1, TxBuffer);


  /* 检查开始测量指令是否被接受 */
  while(1)
  {
    IIC_readReg(PRINT_DEBUG_INFO_ENABLE, TOF_REG_ADDR_CMD_STAT, 1, RxBuffer);
    /* 回读值应该为0x01。如果回读值>=0x10,需要继续读该寄存器。如果<0x10且不是0x01,则表示出错 */
    if (RxBuffer[0] == TOF_REG_VALUE_CMD_STAT__STAT_ACCEPTED)
    {
      Serial.println("start measure cmd accept");
      break;
    }
    else if ((RxBuffer[0] < TOF_REG_VALUE_CMD_STAT__CMD_MEASURE) &&
      (RxBuffer[0] != TOF_REG_VALUE_CMD_STAT__STAT_ACCEPTED))
    {
      Serial.println("start measure ERROR");
      while(1);
    }
  }


  Serial.println("***start measure***");
}


4.4 等待采样数据可被读取、读取TOF数据的函数如下

uint8_t TOF_is_data_rerady_to_read()
{
  uint8_t RxBuffer[1] = {0};
  uint8_t TxBuffer[1] = {0};


  /* 读取INT_STATUS寄存器的值 */
  IIC_readReg(PRINT_DEBUG_INFO_DISABLE, TOF_REG_ADDR_INT_STATUS, 1, RxBuffer);


  /* 判断读取到的寄存器的值的bit1是否为1,为1表示有测量完成的数据可以被获取 */
  if (RxBuffer[0] & BIT_1)
  {
    TxBuffer[0] = BIT_1;
    /* 向INT_STATUS寄存器的bit1写入1,用于清除中断 */
    IIC_writeReg(PRINT_DEBUG_INFO_DISABLE, TOF_REG_ADDR_INT_STATUS, 1, TxBuffer);
    return 1;
  }


  return 0;
}


void TOF_read_measure_data(uint8_t *buffer)
{
  IIC_readReg(PRINT_DEBUG_INFO_DISABLE, TOF_REG_ADDR_CONFIG_RESULT, TOF_MEASURE_DATA_LEN, buffer);


  /* CONFIG_RESULT寄存器读取到的数据有很多种情况,要根据第一个字节(cid_rid)来确定一下是否为测量数据 */
  if(buffer[0] == TOF_REG_VALUE_CONFIG_RESULT__MEASUREMENT_RESULT)
  {
    // Serial.println("measure data success");
    print_distance_result(buffer);
  }
}


4.5 计算角度的函数如下,具体计算方案前面已经将分析过了

double calculate_angle_B(double a, double b, double angle_C_degree)
{
  // 将角度C从度转换为弧度
  double angle_C_radian = angle_C_degree * (PI / 180.0);
 
  // 使用余弦定理计算边c的长度
  double c = sqrt(a * a + b * b - 2 * a * b * cos(angle_C_radian));
 
  // 计算sin(B)
  double sin_B = (b * sin(angle_C_radian)) / c;
 
  // 使用反正弦函数计算B的大小(注意:结果是弧度)
  double angle_B_radian = asin(sin_B);
 
  // 将弧度转换回度
  double angle_B_degree = angle_B_radian * (180.0 / PI);
 
  return angle_B_degree;
}


五.功能展示图及说明

在OLED屏幕上会显示3X3的测距结果、计算得到的TOF传感器与平面的夹角和垂直最小距离

六.项目中遇到的难题和解决方法

在参加比赛前我觉得TOF传感器应该还是容易,以前也写过SHT30的驱动,很简单初始化一下,配置一下,读取参数即可。

但是实际开始写的时候看了Datasheets中的寄存器表,好大一堆寄存器,看的云里雾里,完全不知道应该从那边开始。

后来我在官网找到了一本用户指导手册(TMF882X Host Driver Communication AN001015)和arduino demo的源码,指导手册中从上电、初始化、下载固件、配置参数、读取数据完完整整的都讲了,同时我也阅读了arduino demo的源码,互相参照,理解整个传感器的工作逻辑。然后开始一步一步的写驱动


七.对本次竞赛的心得体会(包括意见或建议)

本次活动非常好玩,以前我玩过单点的激光测距传感器,这是第一次玩多点的,学习了多点传感器的工作原理及传感器特性,还顺便强化了我从0开始阅读文档,编写驱动的能力。




附件下载
ams-OSRAM_TMF8821.rar
代码
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号