用一个反射式光电传感器,模拟“闯红灯“检测——STM32 物联网实验
用一个反射式光电传感器,模拟"闯红灯"检测——STM32 物联网实验
一、写在前面
这学期的物联网实验课上,我们拿到了一个小小的传感器模块——反射式光电传感器。老师给它安排的实验是:模拟"闯红灯"检测。
听起来好像很高大上,其实原理并不复杂:把传感器埋在"停止线"的位置,当有小车模型从它面前经过(相当于车辆越过停止线),传感器检测到遮挡,主控板就触发蜂鸣器报警,LCD 上显示"有遮挡",以此模拟一辆车在红灯时闯了过去。
这篇博客记录一下我做这个实验的完整过程。
二、反射式光电传感器是怎么工作的?
这个传感器(ST118,漫反射型)内部其实就两个核心元件:
- 红外发射管:持续向外发射红外光
- 接收管(光敏三极管):接收被物体反射回来的红外光
工作过程是这样的:
红外光
发射管 ──────► 物体(小车)
▲ │
│ │ 反射
│ ▼
└──────── 接收管
- 没有物体时:红外光射出去没有东西反射回来,接收管收不到光,输出一个电平(本模块约定为 高电平 1,表示"无遮挡")。
- 有物体经过时:红外光被物体表面反射回来,接收管接收到光后导通,输出电平跳变(低电平 0,表示"有遮挡")。
一句话总结:靠"有没有反射光"来判断前面有没有东西。
它和"对射式"光电传感器的区别在于:对射式是发射和接收分开放在两边,靠遮挡光路来检测;而反射式是发射接收做在一起,靠反射光来检测,安装更方便,适合做"有没有东西经过"这种检测。
三、硬件连接
实验用的是 STM32开发板,主控是 STM32L431VCT6。传感器信号脚接到 PD14,配置为 GPIO 输入模式。
整体连接关系如下:
| 模块 | 引脚/接口 | 作用 |
|---|---|---|
| 反射式光电传感器 ST118 | PD14(GPIO 输入) | 检测物体遮挡,输出数字电平 |
| TFT-LCD 彩屏 | SPI 接口 | 显示实验标题和"有/无遮挡"状态 |
| 有源蜂鸣器 | PE8 | 有遮挡时报警 |
| LED1 | PA4 | 每秒翻转,作为运行指示 |
| 串口 UART2 | 115200 波特率 | 向上位机打印传感器状态 |
系统拓扑大概是:传感器把电平信号送给 STM32,STM32 每 1 秒轮询一次,然后根据结果驱动 LCD、蜂鸣器、LED,并通过串口输出数据。
四、代码实现
工程用 STM32 HAL 库开发,结构很清晰,主要分三层:
main.c:系统初始化 + 主循环st118.c:传感器驱动(GPIO 初始化 + 状态读取)handle.c:业务逻辑(检测、显示、报警、串口输出)
4.1 主函数:初始化 + 死循环
int main(void)
{
HAL_Init();
SystemClock_Config();
LCD_Display_Init(); // 屏幕初始化显示
gpio_module_open(BEEP|LEDS); // 打开蜂鸣器、LED
uart_open(uart2, 115200); // 串口初始化
ST118_Init(); // 初始化反射式光电传感器
while(1)
{
ST118_Handle(); // 检测反射式光电传感器并显示
}
}
主函数做的事情很标准:时钟、屏幕、蜂鸣器/LED、串口、传感器依次初始化,然后在 while(1) 里反复调用 ST118_Handle() 做检测。
4.2 传感器驱动:读一个 GPIO 电平
void ST118_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ST118_Port_Clk_Enable; // 使能 GPIOD 时钟
GPIO_InitStruct.Pin = ST118_Pin; // PD14
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ST118_GPIO_Port, &GPIO_InitStruct);
}
uint8_t ST118_Read_State(void)
{
// 返回传感器状态:1=无遮挡,0=有遮挡
return HAL_GPIO_ReadPin(ST118_GPIO_Port, ST118_Pin);
}
驱动非常简单,就是把 PD14 配成输入,然后用 HAL_GPIO_ReadPin 读电平。返回值的约定写在注释里:1 表示无遮挡,0 表示有遮挡。
4.3 业务逻辑:1 秒轮询 + 多外设响应
这是整个实验的核心:
void ST118_Handle(void)
{
uint8_t st118_state = 0;
static uint8_t st118_state_rev = 2;
static uint32_t tickstart;
if((HAL_GetTick() - tickstart) >= 1000) // 1000ms 检测一次
{
tickstart = HAL_GetTick();
st118_state = ST118_Read_State();
if(st118_state == 1) // 有遮挡
{
LCD_draw_bmp(96,120,48,gImage_guangdian);
Show_Str(96,178,BLUE,WHITE,"有遮挡",16,1); // 显示有遮挡
beep_open(); // 蜂鸣器报警
}
else if(st118_state == 0) // 无遮挡
{
LCD_draw_bmp(96,120,48,gImage_guangdianhui);
Show_Str(96,178,BLUE,WHITE,"无遮挡",16,1); // 显示无遮挡
beep_close(); // 关闭蜂鸣器
}
if(st118_state != st118_state_rev) // 状态变化时串口打印
{
printf("** 读取反射式光电传感器数据\r\n");
printf("** st118_state:%d\r\n", st118_state);
st118_state_rev = st118_state;
}
led_toggle(LED1_PORT); // LED1 翻转指示
}
}
几个设计细节值得说一下:
- 用
HAL_GetTick()做 1 秒定时轮询,而不是HAL_Delay()死等。这样主循环虽然一直在跑,但只有到 1 秒才真正做一次检测,不浪费 CPU,也方便以后加别的任务。 - 状态变化才打印串口。用一个静态变量
st118_state_rev记录上一次的状态,只有和上次不一样时才printf,避免串口被刷屏。 - LED1 每秒翻转一次,不管传感器状态如何,作为"程序在正常运行"的心跳指示。
五、实验现象
编译下载到开发板后,上电看到的现象:
- 启动:LCD 显示"反射式光电检测实验(车胤)"标题和检测图标,串口打印实验提示信息。
- 无遮挡:传感器前方没有东西时,LCD 显示"无遮挡",蜂鸣器不响,LED1 每秒闪一下。
- 有遮挡(模拟闯红灯):用手或小车模型从传感器正前方划过,LCD 立刻变成"有遮挡",蜂鸣器"嘀——"地响起来,模拟检测到车辆违章通过。
- 移开物体:恢复"无遮挡",蜂鸣器停止。
- 串口输出:每次状态变化,串口会打印类似下面的内容:
反射式光电传感器数据检测实验
------------------------------
开始实验:
** 反射式光电传感器初始化
** 读取反射式光电传感器数据
** st118_state:0
** 读取反射式光电传感器数据
** st118_state:1
整体跑下来,"传感器感知 → 主控判断 → LCD/蜂鸣器/LED 响应"这条链路是通的,闯红灯的模拟效果也出来了。
六、踩坑记录:电平极性那点事
做这个实验的时候,我踩了一个不大不小的坑,值得记一下。
传感器驱动的注释里写得很清楚:
返回值:1 = 没有物体遮挡,0 = 有物体遮挡
但业务逻辑 ST118_Handle() 里的判断是:
if(st118_state == 1) // 有遮挡
{
Show_Str(...,"有遮挡",...);
beep_open();
}
else if(st118_state == 0) // 无遮挡
{
Show_Str(...,"无遮挡",...);
beep_close();
}
你看,代码里 state == 1 被当成了"有遮挡",和驱动注释的约定正好相反。
实际烧录后,如果发现"手挡上去显示无遮挡、拿开反而显示有遮挡",那就是电平极性反了。解决办法有两种:
- 软件取反:在判断条件里把
1和0的分支对调,或者读的时候!ST118_Read_State()。 - 硬件确认:查模块数据手册,看这个批次的模块到底是高电平有效还是低电平有效,有些模块板载比较器的输出极性可能和注释不一致。
这个坑让我意识到:嵌入式开发里,"注释说的"和"实际测的"不一定一致,一切以实测为准。 拿到一个新模块,先用万用表或串口打一下原始电平,确认极性再写逻辑,能省不少调试时间。
七、还能怎么玩?(扩展思考)
这个实验只是最基础的"检测到遮挡就报警",如果往真实的闯红灯检测系统靠,还有很多可以扩展的方向:
- 联动交通信号灯:现在的代码不管红灯绿灯,只要有车过就报警。真实场景应该加一个信号灯状态,只有红灯期间检测到车辆通过才判定为闯红灯,绿灯正常通行不报警。
- 双传感器测速 + 方向判断:沿道路方向放两个传感器,根据车辆先后遮挡的时间差可以算车速,还能判断行驶方向,比单传感器信息丰富得多。
- 数据上云:开发板上带了 BC35G NB-IoT 模块,可以把违章记录(时间、车道、状态)传到云端,做远程监管和统计。
- 图像取证:加个摄像头,闯红灯时抓拍一张,存上时间戳,形成完整的违章证据链。
- 多传感器融合:反射式光电 + 地磁 + 超声波组合用,复杂环境下检测更可靠,抗干扰能力更强。
八、总结
这次实验虽然小,但把物联网最基本的一条链路完整走了一遍:
传感器感知 → 主控处理 → 执行器响应 → 数据输出
具体收获:
- 搞懂了反射式光电传感器"发射—反射—接收"的工作原理;
- 练熟了 STM32 HAL 库下 GPIO 输入模式的配置和
HAL_GPIO_ReadPin读取; - 学会了用
HAL_GetTick()做非阻塞定时轮询,以及"状态变化才输出"的简单去抖/降频思路; - 踩了电平极性的坑,记住了"以实测为准"这条嵌入式铁律。
一个几块钱的小传感器,加上几十行代码,就能模拟出一个交通违章检测的雏形——这大概就是物联网实验的乐趣吧。
更多推荐


所有评论(0)