文章目录
- 超声波测距+OLED显示
- 超声波
- STM32引脚配置
- 代码
- 启动超声波模块
- 接收超声波数据回传
超声波测距+OLED显示
- 超声波模块使用的是HC-SR04模块,单片机使用的是STM32F407
超声波
- 超声波时序图
- 以上时序图表明你只需要提供一个 10uS 以上脉冲触发信号,该模块内部将发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号 。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。
- 公式:uS/58=厘米或者 uS/148=英寸;或是:距离=高电平时间*声速(340M/S)/2;建议测量周期为 60ms 以上,以防止发射信号对回响信号的影响。
- 模块需要和STM32工地
STM32引脚配置
- Trig引脚使用的是PE0,使用推挽输出即可
- Echo引脚,就是回传信号的引脚,使用的是TIM10的输入通道1,就是PF6引脚。因为要测量输入高电平的时长,直接使用定时计数器的输入捕获的功能会比较方便。STM32F407ZEt6的TIM10的输入通道一,只对应一路捕获通道
- 不要忘记打开中断
- 输入捕获的原理和计数的原理差不多,先规定捕获通道是上升沿触发,还是下降沿触发,还是双边都触发;
- 触发之后,申请一次捕获中断,同时将CNT的数值锁存到捕获寄存器CCR中,我们可以把此时的CCR的数值记录到value1中,还要清楚捕获中断标志
- 然后等待第二次触发,又会将CNT的数值锁存到CCR中,再把此时的数值记录到value2中,还要清楚中断标志位,把两个数值相减,再乘计一个数的时间,就是电平持续的时间或者周期等。
代码
启动超声波模块
-
根据上面超声波的时序,写出启动信号,即向Trig引脚发送一段不低于10微秒的高电平
void Ultrasonic_start(void) {HAL_TIM_Base_Start(&htim10);//启动定时计数器10HAL_TIM_IC_Start_IT(&htim10,TIM_CHANNEL_1);/* 启动定时计数器捕获通道1,并开启中断 */HAL_GPIO_WritePin(Tr1g_1_GPIO_Port,Tr1g_1_Pin,GPIO_PIN_RESET);delay_us(50);HAL_GPIO_WritePin(Tr1g_1_GPIO_Port,Tr1g_1_Pin,GPIO_PIN_SET);delay_us(50);HAL_GPIO_WritePin(Tr1g_1_GPIO_Port,Tr1g_1_Pin,GPIO_PIN_RESET);TIM10->CNT=0; //可以不对CNT进行清零,此处清零是为了不再后面处理计数溢出的情况 }
接收超声波数据回传
-
在硬件上Echo引脚连接的是TIM10的通道1,并在捕获通道1进行输入捕获,初始化时极性设置的是上升沿有效;
-
所以在第一次捕获时,捕获的是上升沿,说明回传信号的高电平开始;
-
在第一次捕获中断的中断函数中应该读取CCR中的数值并把捕获通道的极性进行修改为下降沿有效,用来检测高电平的结束
-
在第二次捕获中断到来时也要读取CCR中的数值,并将捕获通道的极性设置为上升沿有效,准备检测下一阶段的高电平信号
-
在第二次捕获完成后,可以先关闭计数器,等待计算完成再打开,避免这次还没有计算完,下一次的就已经到了
-
最重要的是不要忘记清除中断标志位,如果使用的是HAL库的回调函数,那么就不需要手动清除了,HAL库会帮你完成
-
计算的函数如下,会使用OLED显示:
uint16_t flag=0; //第二次捕获结束,即高电平已经结束了 /*capture[0]用来存放第一次捕获的数值;capture[1]用来存放第二次捕获的数值,capture[2]存放差值*/ uint16_t capture[3]={0}; /*使用OLED来进行输出的数组*/ char shuchu0[12]={0}; unsigned char shuchu[12]={0}; void Ultrasonic_Receive_Display(void) {float data=0;if(flag==1){flag=0;capture[2]=capture[1]-capture[0];/*计算数值*/data=((capture[2])*1.0)/58.0;for(int k1=0;k1<3;k1++){capture[k1]=0;}/*进行显示*/int m=0;sprintf(shuchu0,"%0.2f cm",data); //保留小数点后两位for(m=0;shuchu0[m]!='\0';m++){shuchu[m]=(unsigned char)shuchu0[m];}shuchu[m]='\0';OLED_CLS();OLED_ShowStr(0,3,"distance:",1);OLED_ShowStr(0,4,shuchu,1);HAL_Delay(100);}else{OLED_CLS();OLED_ShowStr(0,4,"please wait",2);HAL_Delay(100);}}
-
捕获中断的回调函数如下:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {uint16_t cnt=1; //用来区分是第一次还是第二次的数值if(TIM10==htim->Instance){switch (cnt){case 1:/*第一次通道捕获触发,高电平信号开始*/capture[0]= HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);/*用HAL库时要先清除通道的极性,再更改通道的极性*/TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1); TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); cnt++;Start_Flag=0; //外部定义,发送超声波启动信号的标志,此时还不能启动break;case 2:/*第二次表示高电平的结束*/capture[1]=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);cnt=1;flag=1; //外部定义,开始进行就算并发送给LED显示的标志Start_Flag=1; /*关闭定时计数器,防止下次的干扰*/HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_1);HAL_TIM_Base_Stop(htim);/*清除通道极性,并设置为上升沿触发,为下次做准备*/TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1); TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); break;}} }
-
主体部分写入一个循环里面就可以了
for(;;){while(Start_Flag==1){Ultrasonic_start();osDelay(100);Ultrasonic_Receive_Display();}}