ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STC15单片机超声波测距OLED显示实战:从原理到原理图设计

2026/9/16 13:18:19 拓冰建站 浏览量
STC15单片机超声波测距OLED显示实战:从原理到原理图设计 简介这份面向STC15单片机初学者的超声波测距项目集成了测距算法、OLED显示与硬件原理图适用于嵌入式课程设计、竞赛备赛或实际避障模块开发也可作为毕业设计参考。方案基于IAP15系列8051内核MCU通过HC-SR04类超声波传感器完成脉冲发射与回波捕获依靠往返时间差计算目标距离并用I2C/SPI接口驱动OLED实时显示整个过程覆盖了单片机定时器、外部中断、通信协议等核心知识点与程序架构。压缩包内共12个文件类型包含C程序源码、头文件、Keil工程文件uvproj/uvopt、编译生成的hex与m51以及Altium Designer绘制的超声波测距原理图整体约70KB便于直接打开查看和二次开发。目前已有327人浏览学习说明该方案具有一定参考价值也印证其便于上手。资料不仅提供可运行固件和电路图纸还体现了四路探测设计思路可扩展为多方向同时测距对智能车避障、环境监测等场景很有帮助是单片机综合实践的不错选择。1. 从测距到看得见STC15、超声波与OLED该怎么组合STC15系列单片机做超声波测距并驱动OLED显示是很多工程师从纯逻辑仿真走向实物调试的第一站。这个组合的优势在于它绕开了复杂的外设接口——STC15内部集成高精度时钟省掉外部晶振超声波模块用两个普通IO就能完成触发和回波读取OLED走I2C只需要两根线。比起用串口把数据扔给上位机直接把距离显示在0.96寸屏幕上整个系统脱机就能跑非常适合做车载防撞提示、液位监测或者机器人避障的前期原型验证。这篇内容会从硬件连接、测距算法、OLED驱动三个层面展开最后给出原理图设计时的关键细节和精度优化手段确保画完板子烧录程序就能稳定工作。2. 硬件电路基础STC15如何驱动超声波模块和OLED屏幕2.1 超声波模块的选型常识HC-SR04与STC15的IO口匹配目前市面上最常见的超声波测距模块是HC-SR04它的工作电压是5V而STC15系列单片机大部分型号支持2.5V到5.5V宽电压供电。如果系统用5V供电模块和单片机的IO电平是兼容的如果为了低功耗把MCU降到3.3V那么模块的ECHO回波引脚输出的高电平就可能超过MCU承受范围需要分压电阻处理。我一般习惯用电阻分压将5V降到3.3V串联1kΩ和2kΩ电阻中间抽头接MCU引脚同时并联一个100pF电容滤除高频噪声。TRIG触发引脚是输入方向模块内部有上拉3.3V高电平已经满足触发阈值可以直接连接。HC-SR04的测量范围从2cm到400cm分辨率标称0.3cm但实际受环境温度和反射面材质影响很大。模块上有两个晶振一个是40kHz的发射晶振一个是用于计时校准的晶振。STC15内部的定时器时钟来自系统时钟分频可以精确到微秒级别配合外部中断捕获回波下降沿能实现更好的时间戳。如果选型时在意体积可以改用HY-SRF05或者US-015它们的引脚定义和HC-SR04基本一致只是测距范围和电平逻辑略有不同替换代码时只需要调整超时阈值即可。2.2 OLED显示模块的接口选择I2C与SPI的取舍0.96寸OLED模块有两种常见接口I2C和SPI。SPI速率更高刷新大字体或显示波形时优势明显但至少需要占用4个IO口SCLK、MOSI、DC、CS部分模块还额外要求RST引脚。I2C只需要SDA和SCL两根线地址通常为0x3C或0x3D适合STC15这种引脚资源紧张的场景。STC15没有硬件I2C外设需要软件模拟I2C时序但OLED显示内容是静态距离数字加单位后缀刷新频率不高软件模拟完全够用CPU占用率极低。I2C模式下OLED的像素数据写入依赖SSD1306控制器内部GRAM。SSD1306预留128×64 bit显存分8页每页8个像素高度。写入数据时先设置页地址和列地址再连续发送数据字节每一bit对应一个像素的亮灭。这里有个容易踩坑的地方I2C在连续写多字节时SSD1306会自动递增列地址但到达列边界如127后会回卷到当前页的起始列并不会自动换页。因此显示跨页内容时必须手动切换页地址。比如要显示一个16×16的汉字需要先写上半部分到第0页再写下半部分到第1页很多新手在画图取模时没有做跨页切分导致汉字上下部分错位或重复。void OLED_WR_Byte(uint8_t dat, uint8_t cmd) { I2C_Start(); I2C_SendByte(0x78); // I2C地址0x3C左移一位写入方向 if (cmd) { I2C_SendByte(0x40); // 数据模式 } else { I2C_SendByte(0x00); // 命令模式 } I2C_SendByte(dat); I2C_Stop(); // 每次只传一字节确保命令和数据不混淆 }这段代码是I2C通信的基础封装。OLED模块的从机地址将0x3C左移一位后得到0x78。如果模块背面的地址电阻焊接位置不同地址可能变为0x7A对应0x3D此时需要修改I2C_SendByte的入参。发送单字节数据时命令模式和数据模式需要分别设置控制字节SSD1306通过这个控制字节区分接收到的数据是要写寄存器还是写显存。对于显示实时距离的场景建议把字符和数字的全部取模数据存放在STC15的内部Flash中因为STC15的Flash容量通常在4KB到64KB之间足够存放字库。如果使用外置字库芯片I2C总线上就会多挂一个从机地址冲突和数据竞争的风险会增加不建议入门阶段这样做。3. STC15上的C语言实现从触发测距到OLED渲染的完整流程3.1 定时器0配置与微秒级延时函数设计超声波测距的核心是测量声波发出到返回的时间差。HC-SR04模块的时序相对简单给TRIG引脚拉高10微秒以上模块自动发射40kHz脉冲并等待回波回波到达后ECHO引脚输出一个高电平脉冲高电平持续时长就是声波往返总时间。高电平持续时间范围在150微秒到25毫秒之间对应2cm到4m的测量范围。如果MCU用阻塞式等待在测距期间做不了其他事如果换成定时器输入捕获就能边显示边等待回波同时还能统计多次测量的平均值。STC15系列内部有一个可配置的时钟分频器系统时钟最高可达24MHz甚至更高。我在项目中通常将定时器0设置为16位自动重装模式装载一个固定初值然后开启定时器中断在中断里维护一个全局变量来累加微秒计数。这种定时器溢出软件计数的方式比直接用16位定时器单次计时更稳因为单次计时上限只有65535微秒拆成多次溢出后可以无限扩展时间窗口。void Timer0_Init(void) { AUXR | 0x80; // T0工作在1T模式即定时器时钟不进行分频 TMOD 0xF0; // 清零T0模式位保留T1设置 TMOD | 0x01; // 设置T0为16位定时器 TH0 0; // 初始值为0等待溢出中断 TL0 0; ET0 1; // 使能T0中断 TR0 1; // 启动定时器 EA 1; // 打开总中断 } volatile uint16_t g_time_us 0; // 微秒计数器从0递增 void Timer0_ISR(void) interrupt 1 { g_time_us; }代码中的AUXR寄存器是STC15特有的辅助寄存器将第7位置1后定时器0的工作时钟从传统的12分频改为1分频直接使用系统时钟。如果系统时钟是12MHz那么定时器0每增加1次溢出实际耗时是65536微秒g_time_us变量记录的就是溢出次数真正的微秒数等于g_time_us乘以65536再加上TH0和TL0的当前值。读取时为了防止中断在读取高低字节之间触发需要先读TH0再读TL0读完后判断TH0是否变化如果变化则重新读一遍这是8位单片机读取16位变量的典型同步步骤。3.2 回波时间测量的两种方式阻塞等待与外部中断最简单的方式是用循环等待ECHO引脚变高然后清零定时器并循环等待引脚变低。这种方式代码直观但在等待期间如果回波丢失或模块未响应程序会陷入死循环。必须加上超时判断——比如当g_time_us计数超过30毫秒时强制退出否则MCU会一直卡在测距函数里OLED无法刷新整个系统看起来像死机。超时值设置为30毫秒是保守的因为声波在空气中的速度约340米每秒往返30毫秒对应约5米距离已经超过HC-SR04的测量范围。外部中断方式会更省心。ECHO引脚连接到STC15的INT0引脚配置上升沿和下降沿都触发外部中断。上升沿到来时记录定时器当前值作为起始时间下降沿到来时再次读取定时器值算出时间差。这样MCU在测距期间可以安心刷新OLED不用阻塞在IO轮询上。void EX0_Init(void) { IT0 0; // 设置INT0为上升沿和下降沿都触发 EX0 1; // 使能外部中断0 EA 1; } uint16_t g_t0_start 0; volatile uint32_t g_distance_cm 0; volatile uint8_t g_measure_done 0; void EX0_ISR(void) interrupt 0 { uint16_t current; uint32_t elapsed; if (ECHO_PIN) { // 读取引脚电平高电平表示是上升沿 g_t0_start (uint16_t)(TH0 8 | TL0); // 记录起始时间 } else { current (uint16_t)(TH0 8 | TL0); elapsed (uint32_t)(current - g_t0_start); // 计算时间差考虑溢出回绕 g_distance_cm (elapsed * 34) / 200; // 声速340m/s往返时间转距离 g_measure_done 1; } }外部中断代码里最关键的是elapsed的计算。定时器是16位循环计数假如起始时间是60000结束时间是2000直接相减会得到负数。利用C语言无符号整数的溢出回绕特性uint16_t类型的60000减去2000实际结果等于65536-58000也就是7536这恰好是从60000到65536再回绕到2000的时间差。STC15是8位单片机但C编译器在处理uint16_t类型时自动生成多字节运算指令效率足够高。最终距离计算公式来自声速的换算声波速度约340米每秒往返时间转化为单程距离需要除以2emsp;elapsed单位是微秒所以距离等于elapsed乘0.034再除以2简化成(elapsed * 34) / 200是为了避免浮点数运算只保留整数部分单位是厘米。3.3 OLED显示缓冲区的设计与动态刷新机制距离值是实时变化的如果每次都直接往OLED显存里写数据会产生闪烁和拖影。原因在于写入新数字时旧数字的残留像素没有清除干净或者先清屏再写字导致短暂空白。正确做法是在MCU内部维护一块128字节的显存缓冲区每次更新距离值后先修改缓冲区对应位置的数据再一次性把整个缓冲区推送到SSD1306的GRAM中。uint8_t oled_buf[128 * 64 / 8]; // 1KB显存缓冲区 void OLED_Refresh(void) { uint8_t page, col; for (page 0; page 8; page) { OLED_WR_Byte(0xB0 page, 0); // 设置页地址 OLED_WR_Byte(0x00, 0); // 列地址低4位 OLED_WR_Byte(0x10, 0); // 列地址高4位 for (col 0; col 128; col) { OLED_WR_Byte(oled_buf[page * 128 col], 1); } } }刷新函数遍历8页和128列每页对应显存中的一行。SSD1306允许连续写入整页数据前提是正确设置起始列地址。列地址分两次写入低4位在前高4位在后命令0x00和0x10职责不同。这里有一个细节每次写满128字节后如果下一个循环要切换到下一页必须重新发送页地址和列地址命令否则内部列地址指针会指向当前页的越界位置。全屏刷新一次需要发送1024个数据字节按I2C时钟100kHz计算约耗时100毫秒对于显示静态距离来说足够但如果后续要显示波形动画就需要改造成部分区域刷新只更新距离数字所在的矩形区域将刷新时间缩短到个位数毫秒级。缓冲区数组使用code关键字可以存放到Flash但这里作为可读写变量必须放在RAM中STC15的RAM大小从256字节到4KB不等128字节的缓冲区占用不算大在编译时要留意data区不能超过256字节否则会编译报错。4. 原理图设计要点从单片机最小系统到测距电路的完整链路4.1 STC15最小系统的供电与复位电路STC15系列单片机的最大亮点是内部集成复位电路和高精度RC时钟传统51单片机必须外接的晶振和复位电容都可以省略。但为了保证上电瞬间的稳定性和抗干扰能力我仍然会在原理图上保留一个10kΩ上拉电阻接到RST引脚同时并联一个0.1μF电容到地。STC15的RST引脚是低电平复位和传统51的高电平复位逻辑相反这一点在绘制原理图时容易搞错方向需要注意。如果直接用STC-ISP软件下载程序时提示单片机型号不匹配多半是复位电路误触发了下载模式。供电部分建议在VCC引脚附近放置两个电容100μF电解电容负责低频稳压0.1μF陶瓷电容负责高频滤波。如果系统里还有舵机或电机等大功率负载超声波模块的5V供电需要单独走线避免电机启动瞬间将VCC电压拉低导致OLED显示花屏或单片机复位。OLED模块上的稳压芯片是3.3V LDO因此OLED允许5V供电但I2C接口的高电平会被内部上拉到3.3VSTC15的IO输出高电平是5V两者之间最好加一个330Ω串联电阻做电平钳制不过实际使用中很多工程师直接相连也没出问题这取决于OLED模块的兼容性设计。4.2 原理图中的IO分配与信号流向标注STC15的IO口是准双向模式和传统51的P1口特性类似。驱动超声波模块时TRIG引脚设置为推挽输出模式可以获得更强的驱动能力但HC-SR04的触发阈值并不高普通准双向模式也能可靠触发。ECHO引脚必须设置为高阻输入模式因为模块输出的高电平是5V如果设置成准双向模式引脚下拉能力会分压信号导致回波高电平幅度不足触发不了外部中断。我在原理图中会将ECHO引脚标注为INT0输入提醒自己这个引脚存在中断功能映射。OLED的SDA和SCL引脚分配时尽量选择没有第二功能的IO口避免后续扩展功能时引脚冲突。STC15系列的部分型号引脚和P3.0、P3.1复用为串口下载引脚如果OLED接在这两个引脚上下载程序时OLED会收到乱码数据虽然不会损坏屏幕但会显示随机雪花点。常见的做法是把OLED接在P2口超声波模块接在P3.2INT0和一个空闲IO上。4.3 原理图检查清单连接验证与常见绘图错误对照原理图进行硬件调试前先用万用表测量关键节点电压VCC和GND之间应为5VOLED的VCC和GND之间应有3.3V或5V取决于模块上LDO的类型。如果OLED模块自带LDO测量SDA引脚对地电压正常应为3.3V左右这个测量值可以确认模块上的上拉电阻是否起作用。再检查超声波模块的VCC引脚部分模块标注的是5V但内部没有稳压直接接3.3V也能工作但发射功率下降测距范围会缩短。检查项正常值异常处理办法MCU VCC对地4.8V~5.2V检查稳压芯片和滤波电容虚焊OLED SDA对地3.0V~3.6V测量模块LDO输出确认上拉电阻连接HC-SR04 VCC4.5V~5.5V确认模块未接反观察背面二极管方向TRIG引脚对地待机0V初始化代码没用GPIO_MODE_OUT_PP改用推挽输出ECHO引脚待机0V模块VCC未上电检查供电走线原理图绘制时经常出现的页码重复问题比如多张原理图页面都设置成Page Number1在生成网表时会报错。用嘉立创EDA时要检查工程属性中每个页面的页码是否递增排列否则导出的PCB网表会提示元件丢失或网络悬空。另外OLED模块的I2C地址选择电阻在原理图中只标注了默认0x3C实际焊接时如果用户将电阻焊到另一个位置地址变成0x3D程序里就要同步修改建议在原理图备注栏写明地址与电阻的对应关系。5. 精度优化与显示刷新一个提升测量稳定性的实用技巧5.1 距离数据的滤波算法与温度补偿单一一次的测距结果受环境噪声和反射面特性影响较大直接显示会出现末位数字跳动。常见的做法是连续测量5次去掉最大值和最小值取剩下3次的平均值。这个中值平均滤波算法在STC15上实现非常轻松因为数据量小不需要额外的数组排序库手动比较即可。每次测距间隔建议在50毫秒以上避免超声波模块连续触发时上一次的余振干扰下一次测量。温度补偿是提升精度最重要的手段。声速在0摄氏度时约331米每秒每升高1摄氏度增加约0.6米每秒。如果系统在室内恒温环境工作误差可以忽略但在露天环境冬天和夏天的测距误差能达到3%。STC15没有内置温度传感器如果不想外接DS18B20可以在代码里提供一个宏定义根据季节手动修改声速值。如果接上DS18B20则可以在主循环里每隔1秒读取一次温度实时代入声速计算公式。uint16_t speed_of_sound(int8_t temp) { // 返回声速值单位是0.1米每秒 return 3312 (uint16_t)(temp * 6); } void calc_distance_with_temp(uint32_t elapsed_us, int8_t temp) { uint32_t speed speed_of_sound(temp); // 0.1m/s uint32_t dist_mm (elapsed_us * speed) / 20000; // 结果单位为毫米 g_distance_mm dist_mm; }这段代码将声速以0.1米每秒为单位存储算出的距离直接是毫米精度比用厘米保留一位小数更精准。除以20000的推导过程是elapsed_us乘以speed再除以1000得到微秒×0.1米每秒结果再转换为毫米最终单位换算系数固定为20000。在OLED上显示时可以将毫米值拆分成整数部分和小数部分显示如123.4cm格式比单纯显示123cm在近距离场景下更能体现真实变化。5.2 OLED局部刷新与显示内容布局全屏刷新1KB显存耗时约百毫秒级别如果再加上测距算法的等待时间整体帧率会掉到5fps以下人眼能明显察觉数字滑动卡顿。优化思路是只更新距离数值所在的区域。定义一个结构体记录当前显示数字在屏幕上的坐标和宽度每次测距完成后先把旧数字的区域清空再在这个坐标上写入新数字。清空区域时只需将对应显存缓冲区byte置0再推送到该区域即可不需要动其他区域。页地址范围0~7 列地址范围0~127 区域更新时需要重新组合发送流程建议封装为函数 OLED_UpdateArea(page_start, col_start, page_end, col_end)OLED上可以规划两行显示第一行显示Distance:第二行用大字号显示数字和单位。大字号取模方式通常选择16×16、24×24或32×64。32×64字号的数字显示效果最醒目但每帧需要传输256字节局部刷新下也能保持流畅。如果使用Proteus仿真调试STC15的仿真模型在I2C时序上可能与实物有微小差异仿真中正常显示的图形实物可能间距错位这通常是延时参数不足导致的需要在I2C_Start和I2C_Stop之间增加几个空操作指令延长时钟低电平时间。5.3 低功耗模式下的测距策略优化如果系统准备做成电池供电的便携设备STC15支持空闲模式和掉电模式。掉电模式下CPU和定时器全部停止外部中断可以唤醒。测距周期可以设计为10秒一次平时让单片机进入掉电模式定时器T1配置为唤醒源唤醒后执行一次测距并更新OLED然后再进入掉电。OLED模块也支持软件关闭显示进入掉电前发送0xAE命令关闭屏幕唤醒后重新发送0xAF开启。这样一个5V/2000mAh的电池组可以支撑数周连续工作。测量数据的合法性校验也值得重视。当距离超过400cm或者模块遮挡导致回波丢失时程序要能识别异常值。代码中可以用if (g_distance_mm 4000)判断是否超量程超量程时OLED显示Out of Range而不是直接显示一个靠运气得到的随机数。同理如果多次测量结果跳变超过50cm视为数据异常保留上次的显示值。这种稳定性处理让产品在恶劣环境下不会给出误导性读数是工程方案和课程设计的关键差异点。本文还有配套的精品资源点击获取