红外测距传感器
GP2Y0A02YKOF是夏普的一款距离测量传感器模块。它由PSD(position sensitive detector)和IRED(infrared emitting diode)以及信号处理电路三部分组成。由于采用了三角测量方法,被测物体的材质、环境温度以及测量时间都不会影响传感器的测量精度。传感器输出电压值对应探测的距离。通过测量电压值就可以得出所探测物体的距离,所以这款传感器可以用于距离测量、避障等场合。
模块来源
采购链接:
https://item.taobao.com/item.htm?spm=a230r.1.14.16.7def2810sScrt7&id=580318855469&ns=1&abbucket=12#detail)
资料下载链接:
https://pan.baidu.com/s/11dDQHyYJfi0nNyC28vkpoA
资料提取码:qvpm
规格参数
工作电压:3.3-5V
工作电流:33MA
模块尺寸:37 x 21.6mm
输出方式: 模拟量输出
读取方式:ADC
管脚数量:3 Pin
移植过程
我们的目标是在天空星HC32F4A0PITB上能够判断前方障碍物的功能。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
查看资料
红外测距传感器的输出是非线性的。每个型号的输出曲线都不同。所以,在实际使用前,最好能对所使用的传感器进行一下校正。对每个型号的传感器创建一张曲线图,以便在实际使用中获得真实有效的测量数据。下图是测距距离为20-150CM型号的输出曲线图。
从上图中,可以看到,当被探测物体的距离小于大约 15cm 的时候,输出电压急剧下降,也就是说从电压读数来看,物体的距离应该是越来越远了。但是实际上并不是这样的,想象一下,你的机器人本来正在慢慢的靠近障碍物,突然发现障碍物消失了,一般来说,你的控制程序会让你的机器人以全速移动,结果就是,"砰"的一声。当然了,解决这个方法也不是没有,这里有个小技巧。只需要改变一下传感器的安装位置,使它到机器人的外围的距离大于最小探测距离就可以了。
红外测距传感器的输出数据线是通过电压的变化来确定距离,我们可以使用ADC功能获取传感器的电压变化,将其转换为实际距离即可。电压距离转换公式见官方代码库链接:https://github.com/zoubworldArduino/ZSharpIR 找到我们20-150CM型号的传感器,在下方有换算公式。
引脚选择
想要使用ADC,需要确定使用的引脚是否有ADC外设功能。可以通过数据手册进行查看。
在数据手册的第44页,是关于HC32F4A0系列芯片引脚的功能定义示意图。
当前只有AO引脚需要使用到ADC接口,所以DO引脚可以使用开发板上其他的GPIO。这里选择使用PA5的附加ADC功能。
移植至工程
移植步骤中的导入.c和.h文件与第二章的第1小节【DHT11温湿度传感器】相同,只是将.c和.h文件更改为bsp_IRdistance.c与bsp_IRdistance.h。这里不再过多讲述,移植完成后面修改相关代码。
在文件bsp_IRdistance.c中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-04-13 LCKFB-LP first version
*/
#include "bsp_IRdistance.h"
#include "board.h"
#include "stdio.h"
#include "math.h"
/**********************************************************
* 函 数 名 称:IRdistance_GPIO_Init
* 函 数 功 能:初始化ADC
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:lckfb.com
* 备 注:
**********************************************************/
void IRdistance_GPIO_Init(void)
{
// 关闭相关的寄存器写保护
LL_PERIPH_WE(LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU);
stc_gpio_init_t stcGpioInit;
// ADC引脚初始化
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinAttr = PIN_ATTR_ANALOG; // 设置引脚属性为模拟
stcGpioInit.u16PinState = PIN_STAT_RST; // 引脚状态为复位状态
stcGpioInit.u16PinDir = PIN_DIR_IN; // 引脚方向为输入
stcGpioInit.u16Latch = PIN_LATCH_OFF; // 关闭锁存器
stcGpioInit.u16PullUp = PIN_PU_OFF; // 关闭上拉电阻
stcGpioInit.u16Invert = PIN_INVT_OFF; // 不反相输入信号
stcGpioInit.u16ExtInt = PIN_EXTINT_OFF; // 关闭外部中断功能
stcGpioInit.u16PinInputType = PIN_IN_TYPE_SMT;
// AO 初始化
(void)GPIO_Init(PORT_IRDISTANCE, GPIO_IRDISTANCE_AO, &stcGpioInit);
// 使能 ADC 时钟
FCG_Fcg3PeriphClockCmd(FCG_ADC, ENABLE);
stc_adc_init_t stcAdcInit;
// 使用基本参数初始化ADC结构体
(void)ADC_StructInit(&stcAdcInit);
stcAdcInit.u16ScanMode = ADC_MD_SEQA_SINGLESHOT; // 设置ADC为单次扫描模式
stcAdcInit.u16Resolution = ADC_RESOLUTION_12BIT; // 设置ADC分辨率为12位
stcAdcInit.u16DataAlign = ADC_DATAALIGN_RIGHT; // 设置ADC数据右对齐
// 初始化ADC
(void)ADC_Init(IRDISTANCE_ADC, &stcAdcInit);
// 通道重映射
ADC_ChRemap(IRDISTANCE_ADC, IRDISTANCE_ADC_CHANNEL, GPIO_IRDISTANCE_AO_REMAP);
// ADC使能
ADC_ChCmd(IRDISTANCE_ADC, ADC_SEQ_A, IRDISTANCE_ADC_CHANNEL, ENABLE); // 使能ADC通道5
}
/******************************************************************
* 函 数 名 称:Adc_Get_Value
* 函 数 说 明:采集一次adc数据
* 函 数 形 参:无
* 函 数 返 回:ADC采集的数值
* 作 者:lckfb.com
* 备 注:
******************************************************************/
uint16_t Adc_Get_Value(void)
{
static uint16_t adcValue;
__IO uint32_t TimeOut = 0UL;
/* 只能启动序列 A 的转换。
序列 B 需要硬件触发才能启动转换。*/
ADC_Start(IRDISTANCE_ADC);
// 等待ADC转换完成
while(SET != ADC_GetStatus(IRDISTANCE_ADC, ADC_FLAG_EOCA))
{
// 如果超时
if(TimeOut > 500)
{
// 清除标志位
ADC_ClearStatus(IRDISTANCE_ADC, ADC_FLAG_EOCA);
// 停止ADC转换
ADC_Stop(IRDISTANCE_ADC);
// 打印错误信息
printf("ERROR = ADC 等待序列 A 转换【超时】!!\r\n");
// 返回0
return 0;
}
// 每次循环是延时1ms
TimeOut++;
delay_ms(1);
}
// 如果转换完成,清除标志位。
ADC_ClearStatus(IRDISTANCE_ADC, ADC_FLAG_EOCA);
// 采集一次数据
adcValue = ADC_GetValue(IRDISTANCE_ADC, IRDISTANCE_ADC_CHANNEL);
// 返回数据
return adcValue;
}
/******************************************************************
* 函 数 名 称:Get_Adc_Average
* 函 数 说 明:采集 __Count 次adc数据,然后求平均值。
* 函 数 形 参:__Count:采集次数
* 函 数 返 回:ADC采集的平均数值
* 作 者:lckfb.com
* 备 注:
******************************************************************/
uint16_t Get_Adc_Average(uint8_t __Count)
{
uint32_t adcValue = 0;
for(int i = 0; i < __Count; i++)
{
// 获取一次 ADC 数据
uint16_t tmp = Adc_Get_Value();
// 如果获取到的数据为0,说明采集失败,跳过本次循环
if(tmp == 0)
{
__Count--;
continue;
}
// 将采集到的数据累加到 adcValue 变量中
adcValue += tmp;
}
// 计算平均值
uint16_t value = adcValue / __Count;
printf("value: %d\r\n",value);
return adcValue / __Count;
}
/******************************************************************
* 函 数 名 称:Get_illume_Percentage_value
* 函 数 说 明:计算红外测距的测量距离
* 函 数 形 参:无
* 函 数 返 回:返回测量距离
* 作 者:lckfb.com
* 备 注:无
******************************************************************/
double Get_IRdistance_Distance(void)
{
double adc_average = Get_Adc_Average(10); // 获取 ADC 的平均值
double voltage = adc_average * (3.3 / 4095); // 将 ADC 数值转换为电压值
double distance_cm = 61.573 * pow(voltage, -1.106); // 根据传感器的校准公式计算距离(单位:厘米)
return distance_cm;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
在文件bsp_encoder.h中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-04-13 LCKFB-LP first version
*/
#ifndef _BSP_IRDISTANCE_H_
#define _BSP_IRDISTANCE_H_
#include "hc32_ll.h"
#define FCG_ADC FCG3_PERIPH_ADC1
#define IRDISTANCE_ADC CM_ADC1
#define IRDISTANCE_ADC_CHANNEL ADC_CH5
#define PORT_IRDISTANCE GPIO_PORT_A
#define GPIO_IRDISTANCE_AO GPIO_PIN_05
#define GPIO_IRDISTANCE_AO_REMAP ADC12_PIN_PA5
void IRdistance_GPIO_Init(void);
uint16_t Adc_Get_Value(void);
uint16_t Get_Adc_Average(uint8_t __Count);
double Get_IRdistance_Distance(void);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
移植验证
在自己工程中的main主函数中,编写如下。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-04-13 LCKFB-LP first version
*/
#include "board.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "bsp_IRdistance.h"
int32_t main(void)
{
board_init();
uart1_init(115200U);
// 关闭GPIO寄存器写保护
LL_PERIPH_WE(LL_PERIPH_GPIO);
IRdistance_GPIO_Init();
printf("ADC demo start\r\n");
while(1)
{
printf("Distance = %.2f\r\n", Get_IRdistance_Distance() );
delay_ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
上电现象(障碍物距离20CM):
会有误差,最后的计算公式可以根据实际的测量距离,进行校准和调整。
模块移植成功案例代码: