STM8开发实战:HC-SR04超声波测距仪
前言
- 在经过前期理论的学习,懂得如何使用单片机的各项外设之后,我们便可以想做什么就做什么了,本文以制作一个基于HC-SRO4超声波测距模块的测距仪为例子,演示怎样通过单片机的各项外设来控制各种功能的外围电路,从而实现我们想要的各种功能
- 本程序已开源,提供了STM8S105C6与STM8S003F3两个版本的代码,开源地址:https://github.com/artless-artist/STM8_UltrasonicRangefinder
准备工作
硬件
- HC-SR04超声波测距模块
- 0.96寸OLED屏幕(集成了SSD1306显示驱动芯片)
- STM8S,推荐使用STM8S105C6这样的高级型号,若使用内存较小的型号会遇到开发困难
软件
- 使用IAR开发
HR-SR04超声波测距模块
工作原理
-
超声波测距原理
超声波模块会在触发测距后发射8个40khz的方波(这是为了让声波独特,避免与环境中的其他信号混淆),从声波接触到被测物体时就会被反射回来,被模块接收
那么,从发射声波到接收到反射声波的用时除以2就是声音从模块传播到被测物体所用的时间,再将这个时间乘以声速就是距离了 -
HC-SR04工作时序
1.在TRIG引脚上给至少10us的高电平信号后触发测距;
2.触发测距后模块自动发送8个40khz的方波,自动检测是否有信号返回;
3.若有信号返回,通过ECHO引脚输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。
STM8控制原理
-
设计思路
- 从工作时序来看,我们需要让STM8的一个引脚作为TRIG,输出高电平触发测距;另外需要一个ECHO引脚被配置为输入模式,用以测量超声波模块的ECHO引脚返回的高电平持续时间
- 测量ECHO引脚高电平持续时间是整个功能实现的核心,我们很容易想到通过STM8的定时器外设来实现这一点,具体方法如下:
-
使用基本型定时器
- 使用任意定时器的计时功能,在返回高电平信号后开始计时,当返回信号变回低电平后结束计时,并计算
- 由于适用轮询的数据传输方式,将单片机主频设置为16MHz以提高运行速度,而又由于计数值有限,为了扩展测量范围,设定TIM4预分频系数为128,让计数值每8us加一
//使用TIM4进行计时 void TIM4_Init(void) { TIM4_PSCR = 0x07; // 预分频器128,16MHz/128=125kHz(8us) TIM4_ARR = 255; // 自动重装载值 TIM4_CNTR = 0; // 计数器清零 TIM4_CR1 |= 0x01; // 使能计数器 } //详细的宏定义、初始化代码受限篇幅省略,需要完整驱动请到开源地址中查看 float HC_SR04_GetDistance(void) { float time = 0; float distance = 0; // 发送10us的高电平触发信号 TRIG = 1; Delay_us(15); // 延时一段时间保证触发测距 TRIG = 0; // 等待回波信号变高 while(ECHO == 0); // 开始计时 TIM4_CNTR = 0; // 等待回波信号变低 while(ECHO == 1); // 获取计时值 time = TIM4_CNTR * 8; distance = time * (0.0331 + 0.00006*temperature) /2; //声速加入温度补偿系数后的计算公式,单位为厘米 return distance; } -
使用高级计时器的输入捕获功能
- 我们的需求是在对信号的高电平持续时间进行测量,也就是在低电平变为高电平时从零开始计时,而高电平变回低电平时停止计时读数,这显然与我们曾学习过的定时器输入捕获与复位触发功能不谋而合,我们曾用这个功能进行信号占空比的测量
//使用TIM1输入捕获的复位触发模式对echo引脚返回的高电平持续时间进行测量 void TIM1_Init(void) { TIM1_PSCRH = 0x00; TIM1_PSCRL = 0x02; TIM1_ARRH = 0xFF; TIM1_ARRL = 0xFF; //利用输入时TIx产生的两路相同信号TIxFP1和TIxFP2,对上边沿与下边沿进行检测 TIM1_CCMR1 = 0x01; //CC1S=01 TI1FP1连接到IC1 TIM1_CCMR2 = 0x02; //CC2S=10 TI1FP2连接到IC2 TIM1_CCER1_CC1P = 0; //检测TI1FP1上升沿 TIM1_CCER1_CC2P = 1; //检测TI1FP2下降沿 TIM1_SMCR = 0x54; //TS=101(TI1FP1为触发信号), SMS=100(复位触发模式) //从TI1的上升沿触发复位,在捕获到TI2的下降沿时 TIM1_CCER1 |= 0x11; //使能两个捕获 TIM1_CR1 = 0x01; // 使能计数器 } float HC_SR04_GetDistance(void) { TIM1_SR1 &= 0xF9; //清除CC1IF和CC2IF标志位 TIM1_CCER1 |= 0x11; //开启捕获功能 float time = 0; float distance = 0; u16 pulse_width = 0; // 发送高电平触发信号 TRIG = 1; Delay_us(15); TRIG = 0; while((TIM1_SR1&0x02)==0); //等待捕获比较标志位CC1IF变为1(上升沿) while((TIM1_SR1&0x04)==0); //等待捕获比较标志位CC2IF变为1(下降沿) //IC1检测到上升沿时复位计时,到IC2检测到下降沿时即高电平脉宽 pulse_width = (u16)TIM1_CCR2H<<8; //取高电平脉宽的高8位 pulse_width |= TIM1_CCR2L; //取高电平脉宽的低8位 time=(float)(pulse_width)*0.1875;//主频=16M,TIM1进行三分频 distance = time * (0.0331 + 0.00006*temperature) /2; return distance; }
OLED(SSD1306驱动)屏幕
工作原理
- 为了简化开发,我们直接选用已经集成了显示驱动的OLED屏幕,商家已经提供了驱动库文件,调用其中函数即可进行显示,我们只需要对其进行修改使其适用于我们所用的单片机型号
- 为了节省引脚,我选用了使用IIC通信的版本,只需要占用两个引脚
STM8控制原理
- STM8具体控制原理本文不再阐述,在之前的文章中对IIC通信已有具体介绍
-
模拟IIC与硬件IIC
尽管在我的博文中使用了硬件IIC,但在实际开发时我使用了GPIO软件模拟IIC的工作时序,一来STM8的硬件IIC代码资料不多不易于开发,二来方便移植到更多没有硬件IIC的平台上
但是,这样就带来一个问题:如果配置了其他中断,中断会打断软件模拟的IIC时序,导致OLED屏幕无法正常显示,这也就是为什么在前文HC-SR04的驱动不使用定时器捕获中断进行更精确的计时;使用硬件IIC并且合理配置中断优先级能够解决这个问题 -
内存占用问题
本项目直接使用了硬件生产商提供的字库文件,但在诸如STM8S003这类入门级的单片机中存储空间不足,过大的字库会导致烧录失败,为此,我对原本的字库文件进行简化(只能显示6×8的符号与数字),并在头文件中将宏定义SIZE的值修改为6以使用更小(所需要的像素更少)的字体,可在开源地址中下载
成品展示
-
使用开发板与杜邦线搭建的测试平台

-
成品运行效果展示(OLED屏幕有屏闪,在肉眼观察时显示正常)

本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/artlessist/p/18855822
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外
浙公网安备 33010602011771号