一、前言
本篇基于 STM32F103C8T6 最小系统板,搭配 LM393 红外避障模块、I2C OLED 屏幕完成障碍物检测显示工程,完整讲解硬件接线、红外模块底层原理、STM32 数字输入配置、5 次采样多数表决软件防抖滤波、OLED 状态显示、故障调试全套内容,适合单片机入门、课堂实训、课程设计直接使用,代码逻辑清晰,接线简单可直接复刻。

实现功能:红外模块检测前方障碍物,STM32 读取 PB5 引脚电平,软件滤波消抖后,OLED 实时打印引脚电平、有无障碍物状态。
视频讲解:STM32F103C8T6驱动红外避障模块+0.96寸OLED教程(含硬件接线、原理、软件滤波、现象调试)
https://www.bilibili.com/video/BV1ngu26sEVu/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb
二、整体工作流程
物体靠近 / 远离 → 红外反射光强度改变 → 接收管模拟信号变化 → LM393 比较器输出高低电平 → STM32 PB5 读取电平 → 5 次采样多数表决滤波消抖 → OLED 刷新显示引脚电平与障碍物状态
三、硬件接线说明
主控:STM32F103C8T6 外设:LM393 红外避障模块、0.96 寸 I2C OLED 屏幕

3.1 引脚对应表
| 红外避障 VCC | 3.3V | 模块供电,优先 3.3V 保证电平兼容 |
| 红外避障 GND | GND | 全局共地,必不可少 |
| 红外避障 OUT | PB5 | 障碍物数字信号输出输入引脚 |
| OLED SCL | PB8 | 软件 I2C 时钟线 |
| OLED SDA | PB9 | 软件 I2C 数据线 |
| OLED VCC | 3.3V | 屏幕供电 |
| OLED GND | GND | 屏幕电源地 |
3.2 接线拓扑简图
红外避障模块 OUT ───────── PB5
红外避障模块 GND ─┬─────── STM32 GND
OLED GND ─────────┘
OLED SCL ────────────────── PB8
OLED SDA ────────────────── PB9
3.3 关键接线注意事项
5V模块 GND → STM32 GND
四、红外避障模块硬件工作原理
4.1 模块内部组成
4.2 信号流转逻辑图
红外发射管发射红外光 → 前方物体反射光线 → 接收管采集反射光生成模拟信号 电位器设置阈值电压 → LM393 对比两路电压 → OUT 引脚输出高低电平 → STM32 PB5 采集信号 → OLED 显示结果
4.3 电平逻辑(本工程标准)
| 无障碍物 | 高电平 | 1 | HWBZ_NO_OBSTACLE |
| 检测到障碍物 | 低电平 | 0 | HWBZ_OBSTACLE |
补充:不同厂家红外模块输出电平逻辑相反,若实物检测状态颠倒,只需修改Hwbz.h中HWBZ_ACTIVE_LEVEL为 Bit_SET 即可。
五、代码配置
红外模块已经通过片载比较器将模拟信号转为数字电平,本工程无需 ADC 采集,直接将 PB5 配置为上拉数字输入模式。
5.1 main.c代码
/*
* Infrared obstacle-avoidance teaching demonstration
* Sensor OUT -> PB5
* OLED SCL -> PB8
* OLED SDA -> PB9
*/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "Hwbz.h"
#define DISPLAY_REFRESH_MS 50U
static void HWBZ_ShowState(u8 out_level, u8 obstacle);
int main(void)
{
u8 out_level;
u8 obstacle;
u8 last_out_level = 0xFFU;
u8 last_obstacle = 0xFFU;
delay_init();
NVIC_Configuration();
OLED_Init();
HWBZ_Init();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
while(1)
{
out_level = HWBZ_ReadOutLevel();
obstacle = HWBZ_IsObstacle();
/* Redraw only after a state change to avoid visible OLED flicker. */
if((out_level != last_out_level) || (obstacle != last_obstacle))
{
HWBZ_ShowState(out_level, obstacle);
last_out_level = out_level;
last_obstacle = obstacle;
}
delay_ms(DISPLAY_REFRESH_MS);
}
}
static void HWBZ_ShowState(u8 out_level, u8 obstacle)
{
OLED_Clear();
OLED_ShowString(0, 0, (u8 *)"IR Obstacle Demo", 16);
OLED_ShowString(0, 16, (u8 *)"OUT Pin: PB5", 12);
OLED_ShowString(0, 28, (u8 *)"OLED: PB8/PB9", 12);
if(out_level == 0U)
{
OLED_ShowString(0, 40, (u8 *)"OUT Level: LOW", 12);
}
else
{
OLED_ShowString(0, 40, (u8 *)"OUT Level: HIGH", 12);
}
if(obstacle == HWBZ_OBSTACLE)
{
OLED_ShowString(0, 52, (u8 *)"OBSTACLE: YES", 12);
}
else
{
OLED_ShowString(0, 52, (u8 *)"OBSTACLE: NO", 12);
}
OLED_Refresh();
}
5.2 Hwbz.c代码
#include "Hwbz.h"
#include "delay.h"
#define HWBZ_FILTER_SAMPLES 5U
#define HWBZ_FILTER_INTERVAL_MS 2U
/*
* 函数名称:HWBZ_Init
* 功 能:初始化红外避障模块的数字输出引脚 PB5
* 说 明:PB5 配置为上拉输入,避免模块未连接时引脚电平悬空
*/
void HWBZ_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = HWBZ_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(HWBZ_GPIO_PORT, &GPIO_InitStructure);
}
/*
* 函数名称:HWBZ_ReadOutLevel
* 功 能:读取红外避障模块 OUT 引脚的实际电平
* 返 回 值:0 表示低电平,1 表示高电平
*/
u8 HWBZ_ReadOutLevel(void)
{
return (GPIO_ReadInputDataBit(HWBZ_GPIO_PORT, HWBZ_GPIO_PIN) == Bit_SET) ? 1U : 0U;
}
/*
* 函数名称:HWBZ_IsObstacle
* 功 能:判断红外避障模块是否检测到障碍物
* 返 回 值:HWBZ_OBSTACLE 表示有障碍物,HWBZ_NO_OBSTACLE 表示无障碍物
*
* 连续采集 5 次 OUT 电平,并采用少数服从多数的方式判断。
* 这种处理参考了 51 示例中的延时确认思路,可抑制信号抖动,
* 防止教学演示时 OLED 上的检测状态频繁闪烁。
*/
u8 HWBZ_IsObstacle(void)
{
u8 i;
u8 active_count = 0U;
for(i = 0U; i < HWBZ_FILTER_SAMPLES; i++)
{
if(GPIO_ReadInputDataBit(HWBZ_GPIO_PORT, HWBZ_GPIO_PIN) == HWBZ_ACTIVE_LEVEL)
{
active_count++;
}
/* 两次采样之间保留短暂间隔,以过滤瞬时干扰 */
if(i < (HWBZ_FILTER_SAMPLES – 1U))
{
delay_ms(HWBZ_FILTER_INTERVAL_MS);
}
}
return (active_count >= 3U) ? HWBZ_OBSTACLE : HWBZ_NO_OBSTACLE;
}
5.3 Hwbz.h代码
#ifndef __HWBZ_H
#define __HWBZ_H
#include "sys.h"
/*
* 红外避障模块接线说明:
* OUT -> PB5
*
* 常见的 LM393 型红外避障模块在检测到障碍物时输出低电平。
* 如果教学中使用的模块输出逻辑相反,只需修改 HWBZ_ACTIVE_LEVEL。
*/
#define HWBZ_GPIO_PORT GPIOB
#define HWBZ_GPIO_PIN GPIO_Pin_5
#define HWBZ_ACTIVE_LEVEL Bit_RESET
/* 红外避障检测结果 */
#define HWBZ_NO_OBSTACLE 0U /* 未检测到障碍物 */
#define HWBZ_OBSTACLE 1U /* 检测到障碍物 */
/* 初始化红外避障模块的 PB5 输入引脚 */
void HWBZ_Init(void);
/* 读取 OUT 引脚的实际电平:返回 0 表示低电平,返回 1 表示高电平 */
u8 HWBZ_ReadOutLevel(void);
/* 对 OUT 信号进行滤波并返回是否检测到障碍物 */
u8 HWBZ_IsObstacle(void);
#endif
六、软件防抖:5 次采样多数表决滤波
6.1 为什么需要滤波?
物体处于检测临界距离、环境强光干扰、接线接触不良时,OUT 电平会快速跳变,单次读取会导致 OLED 屏幕频繁闪烁、状态反复切换,必须做软件滤波消抖。
6.2 滤波实现逻辑
6.3 滤波执行流程
单次读取 PB5 → 记录电平状态 → 判断是否采满 5 次 未采满:延时 2ms 重复采样;采满后统计有效电平次数 次数≥3:判定有障碍物;反之无障碍物 → 更新 OLED 显示
6.4 工程文件函数对应关系
| Hwbz.c | HWBZ_Init() | 初始化 PB5 为上拉输入 |
| 同上 | HWBZ_ReadOutLevel() | 读取引脚原始电平 0/1 |
| 同上 | HWBZ_IsObstacle() | 5 次采样 + 多数表决滤波判断障碍物 |
| main.c | HWBZ_ShowState() | OLED 打印全部状态信息 |
七、OLED 屏幕显示内容说明
程序运行后屏幕固定显示 4 行信息,刷新机制:主循环 50ms 扫描一次,仅电平 / 障碍物状态变化时刷新屏幕,降低屏幕闪烁。
7.1 OLED 标准显示格式
IR Obstacle Demo
OUT Pin: PB5
OLED: PB8/PB9
OUT Level: LOW
OBSTACLE: YES
7.2 显示字段释义
八、检测距离调节 & 电位器使用教程
电位器作用:调整 LM393 比较器阈值电压,间接控制检测距离,无法设置精准厘米数值,调节步骤:

常见故障:一直检测 / 一直无障碍物排查清单

九、环境与被测物体对检测效果的影响
红外避障依靠红外光反射强度判断有无物体,不测量精确距离,检测效果受多重因素制约:
适用场景:仅适合近距离有无障碍物判断;需要精准测距请更换超声波传感器、激光测距模块。



