一、实验目的一句话让开发板“看见”前面有没有东西距离多远。用的是一款叫HCSR04的超声波模块。它就像蝙蝠一样给它一个“开始”的指令它就往前“喊”一嗓子发出一串超声波声音碰到东西会反弹回来它收到回声后会告诉我们“从喊出去到听到回声中间花了多少时间”有了时间再乘个音速除以 2就是距离啦。公式距离 声音跑的总时间 ÷ 58.8做完这个实验屏幕上每隔一秒刷新一次距离约定的是距离大于 100cm→ 认为“没东西挡着”距离小于等于 0cm→ 认为“有东西挡着了”这时候让 LED 一闪一闪提示。二、先看整体是怎么跑的main.c 里其实就是按顺序把每个“功能模块”叫起来intmain(void){HAL_Init();// 先把ST给的HAL库初始化好SystemClock_Config();// 给芯片定一个“心脏跳动频率”80MHzLCD_Display_Init();// 把屏幕点亮写上标题gpio_module_open(LEDS);// 打开LED模块的电源uart_open(uart2,115200);// 打开串口波特率115200方便打印调试DWT_Init();// 启动一个“精确到微秒”的计时器很重要HCSR04_GPIO_Init();// 配置超声波模块的引脚和中断printf(开始实验...\r\n);while(1){Distance_Handle();// 不断循环测距 - 显示 - 判断}}一开始我看到HAL_Init()、DWT_Init()这些完全看不懂后来才明白每个函数就是在把一个小零件“接好”接完了芯片才能干活。先别纠结细节往下看就懂了。三、时钟因为没有学习过所以使用的是现成的代码来实现四、屏幕上的标题LCD_Display_Init屏幕初始化后我们要让它显示标题。看这段voidLCD_Display_Init(void){LCD_Init();// 点亮屏幕LCD_Fill(0,lcddev.height-20,lcddev.width,lcddev.height,BLUE);// 底部一条蓝底LCD_Fill(0,0,lcddev.width,20,BLUE);// 顶部一条蓝底Gui_StrCenter(0,2,BLUE,BLUE, ,16,0);// 占位Gui_StrCenter(0,40,BLUE,WHITE,超声波检测实验,24,1);// 标题Gui_StrCenter(0,lcddev.height-18,BLUE,BLUE, ,16,0);// 占位}教你认得每个参数Gui_StrCenterx, y从屏幕哪个位置开始画居中它会自己算fc字的颜色前景色这里是白色 WHITEbc背景色这里是蓝色 BLUEstr要写的文字也就是标题“超声波检测实验”就在这儿改size字号。填 16、24、32分别对应不同大小的字模mode0 是正常1 是叠加。⚠️ 新手又一个大坑中文标题不是随便写就能显示的屏幕显示中文靠一张“字模表”FONT.H 里的 tfont24。如果这个表里没有你想写的那个汉字屏幕上就会是“空白”。我之前想把名字“徐成”也放上去结果“徐”字表里没有显示不出来。后来补了字模才解决。五、最核心的测距函数5.1 一个结构体记录测量过程hcsr04.h 里定义了一个结构体HCSR04记录测量中间用到的数据成员干嘛用的trigger_flag记录“回声开始了吗”flag标志“这次测量算完了吗”等于1才会去算距离start_time回声开始的时刻end_time回声结束的时刻value回声持续了多久就是 end - startdistence最终算出来的距离不用死记看函数怎么用它就明白了。5.2 触发并测一次HCSR04_HandlefloatHCSR04_Handle(void){HCSR04_TRIG_Clr();HCSR04_TRIG_Set();Delay_us(12);// 拉高12微秒触发模块发射HCSR04_TRIG_Clr();if(HCSR04.flag1)// 中断已经把时间算好了{HCSR04.flag0;HCSR04.distenceHCSR04.value/SYSCLOCK/58.8;// 时间 - 距离if(HCSR04.distence400)HCSR04.distence400;// 最远4米超出就按4米算DWT_CYCCNT0;// 清零防止下次测量有干扰}returnHCSR04.distence;// 把距离返回出去}一步步看先把 TRIG 拉低、拉高、延时、再拉低 —— 这一下就是在“喊一嗓子”然后等flag 1这说明中断函数已经把回声时间测好放进value里了用公式算距离超出 4 米就封顶HCSR04 最多只能测 4 米最后把距离返回。5.3 中断回声什么时候到EXTI9_5_IRQHandlervoidEXTI9_5_IRQHandler(void){if(__HAL_GPIO_EXTI_GET_IT(ECHO_Pin)!RESET){if(HAL_GPIO_ReadPin(ECHO_GPIO_Port,ECHO_Pin)){HCSR04.trigger_flag1;// 回声开始(上升沿), 记录时刻HCSR04.start_timeDWT_CYCCNT;}elseif(HCSR04.trigger_flag){HCSR04.end_timeDWT_CYCCNT;// 回声结束(下降沿)if(HCSR04.end_timeHCSR04.start_time){HCSR04.valueHCSR04.end_time-HCSR04.start_time;HCSR04.flag1;// 告诉主函数可以算距离了}HCSR04.trigger_flag0;}__HAL_GPIO_EXTI_CLEAR_IT(ECHO_Pin);}}这段跟新手讲就是ECHO 引脚突然变高 → 说明回声来了记下“开始时间”ECHO 引脚变低 → 说明回声结束了记下“结束时间”两个时间一减就是“声音跑的总时间”放到value把flag置 1告诉主循环“数据好了拿去算”。5.4 为什么强调 DWT你一定奇怪为什么用 DWT 这个“精确计时器”不用普通的延时函数因为回声时间非常短可能几十微秒就到头了。你用普通的 while 延时去“等电平”根本来不及误差极大。DWT 是芯片内核自带的计数器能精确到芯片周期测微秒级时间特别准。所以DWT_Init()一定要在主程序里先调用。六、放在主循环里的业务Distance_Handle屏幕刷新、判断逻辑都在这个函数里它每 1000 毫秒1 秒刷新一次voidDistance_Handle(void){staticfloatdistance0;// 存距离staticcharstr[20];// 存要显示的字符串staticuint32_ttickstart;// 记录上次刷新的时间点if((HAL_GetTick()-tickstart)1000){// 满1秒才执行一次tickstartHAL_GetTick();/* 这里一般会 1. 调 HCSR04_Handle() 拿到距离 2. 用 sprintf 显示函数把 Distance:xxcm 显示出来 3. 判断 distance 20 就翻转 LED */led_toggle(LED1_PORT);}}新手要能改动的地方1000想让它刷新快一点、慢一点就改这个单位毫秒20判断“有车/没车”的阈值改成别的也行LED1_PORT想让哪个 LED 闪过就这样改。七、新手最容易踩的坑重要测出来全是 0 或很差先查两件事。一SYSCLOCK是不是等于你的真实主频二回声中断到底进没进在中断里面打断点看看。中文标题显示空白去 FONT.H 里找tfont24看看你要写的汉字在不在里面。没有就必须补字模。刷新太快或太慢改Distance_Handle里的1000。测不到 100厘米以上HCSR04 本来最远也就 100厘米因为我就设置这么远的上限。回波测不准别用普通延时去等待电平一定要用 DWT 精确计时。对了最重要的是把主板插紧没插紧的主板很容易只拷录而不能实际检测数据—完—