一.项目介绍
很高兴可以参加本次的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开始阅读文档,编写驱动的能力。