
简介这是一套基于STM32F103C8T6的停车场车位占用检测系统完整工程面向电子/嵌入式方向毕业设计、课程设计及项目实训人群解决车位状态实时监测、本地显示与远程控制等典型需求。系统以HC-SR04超声波模块和自锁开关共同判定车位占用情况配合蓝牙串口透传模块实现远程监控2.8寸触摸屏用于状态展示与交互操作硬件原理、判定逻辑和界面设计均可在工程中对应查看。资料包共857个文件、21.04MB包含C/H源码、原理图SchDoc、PCB工程、编译配置及工程备份等其中C/H源码对应主控逻辑SchDoc/PCB对应硬件设计Keil工程与配置文件便于直接打开编译和二次开发。核心代码经过测试可运行附带示意图、原理图与PCB文件便于按图接线并理解硬件连接。已有164人学习浏览适合作为毕设、课设、实训或竞赛练手的完整参考方案。1. 从车位状态到屏幕刷新先想清楚整套数据往哪走停车场车位检测这类毕设题最常见的翻车点不在传感器精度而是做完了才发现“数据从哪来、传去哪、谁来显示”三条路没打通。这套“基于 STM32蓝牙触摸屏”的检测系统本质是一条很清晰的数据链车位传感器把物理状态变成电平或距离值STM32 负责采样、去抖、按固定帧格式打包蓝牙模块负责把数据帧透传到另一端触摸屏端收到帧后解析并刷新车位图形。硬件选型、固件逻辑、蓝牙配对参数、串口屏指令全部围着这条链转。适合正在做毕业设计或课程设计的学生也适合想快速搭一套无线传感显示原型的开发者——不管你有没有写手机 App 的经验只要会用串口这套方案就能跑通。2. 车位检测系统的硬件分工与选型依据STM32 为什么够用2.1 三个核心器件的选型思路先定大方向。STM32 在这套系统里承担的是“采集 组帧 串口转发”不是跑算法所以对主频和 Flash 的要求很低。最常见的选型是 STM32F103C8T672MHz 主频、64KB Flash、20KB RAM带 3 个 USART几十块钱一块板子资料多到闭着眼都能搜到例程。如果预算敏感或者想体现一点低功耗意识用 STM32G030 系列也可以但 F103 的生态和调试工具更省心毕设答辩时讲起来不会卡壳。蓝牙模块的选择直接影响工作量。经典蓝牙 SPP 协议的模块比如 HC-05、HC-06、JDY-31上电之后就是一个“无线串口”手机装个蓝牙串口 App 就能收到数据。JDY-31 的底板兼容 HC-05/06 的 AT 指令未绑定版本上电自动连接省去主从配对这一层麻烦。BLE 模块HM-10 这类虽然功耗更低但手机端没有现成的“串口调试界面”要么自己写 App要么用厂商的私有工具对毕设周期不太友好。这套系统我推荐走 SPP 透传理由只有一条串口屏、USB-TTL、手机 App 全都能直接对接透传结果。触摸屏是另一个容易选错的点。威纶通、MCGS 这类工控组态屏走的是 Modbus 或网口协议和蓝牙 SPP 透传不是一套体系强行接入要自己写协议转换复杂度立刻上去了。真正合适的是串口屏比如淘晶驰 TJC 系列、迪文 DGUS 系列它们用 UART 接收指令和蓝牙模块的串口天然匹配。串口屏自带 HMI 编辑软件画好停车场平面图每个车位放一个矩形控件收到 0xA1 就把控件变红收到 0xA0 就变绿比组态屏省事得多。2.2 车位占用检测的三种传感器对比传感器决定了检测的“物理入口”。常见选择是超声波、地磁、红外反射各有各的适用位置传感器检测方式优点典型坑适用场景HC-SR04 超声波发射超声波并测回波时间便宜、例程多、原理直观探测角窄装斜了测不到户外风噪干扰室内模拟车位、安装在车位上方垂直向下地磁传感器QMC5883L/HMC5883L检测车位下方磁场扰动埋地安装不显眼、抗光线干扰需要校准、铁架干扰、I2C 接线麻烦模拟地下车库效果更接近真实工程红外反射传感器发射红外并检测反射强度电路最简单对颜色和灰尘敏感室内桌面演示容易受环境光影响毕设现场演示用 HC-SR04 最稳因为超声波测距的数字可以直接在串口日志里看到评委问“你怎么判断有车”时你可以拿出“距离小于阈值”的原始数据来讲说服力强。地磁传感器更贴近真实停车场方案但调试要多花一两天做校准如果时间紧不建议作为主传感器。2.3 最小系统的接线和供电细节以“STM32F103C8T6 HC-SR04 JDY-31 淘晶驰串口屏”为例接线分三段传感器到 STM32HC-SR04 的 Trig 接 PA0Echo 接 PA1VCC 接 5VGND 共地。Echo 返回的是 5V 电平STM32 的 GPIO 容忍 5V 输入可以直接接如果用其他单片机注意看数据手册。STM32 到蓝牙模块STM32 的 PA2USART2_TX接蓝牙 RXDPA3USART2_RX接蓝牙 TXD两条线交叉GND 必须共地。JDY-31 的 VCC 接 3.3V 或者 5V 要看具体底板丝印多数底板带稳压直接 5V 供电问题不大。STM32 到串口屏PA9USART1_TX接屏幕 RXDPA10USART1_RX接屏幕 TXD。串口屏一般 5V 供电但逻辑电平是 3.3V TTL也支持 5V注意屏和单片机不要两边同时供 5V统一从 USB 转 TTL 的 5V 引过去更安全。提示很多第一次做的人把 TX 接 TX、RX 接 RX结果一帧数据都收不到。串口接线的口诀是“发送接接收”STM32 的 TX 必须进蓝牙模块的 RXD。供电方面一块 STM32 最小系统板 传感器 蓝牙 串口屏同时工作的峰值电流大概 300-500mA用电脑 USB 口供电勉强能跑但 HC-SR04 上电瞬间电流尖峰可能让 3.3V 电压跌落导致复位。建议备一个 5V/2A 的充电头供电ST-Link 只保留下载和调试功能稳定性会好很多。3. 用定时器轮询驱动采集STM32 固件的状态机设计3.1 为什么不能用 while 死循环等传感器新手最容易写成这样程序启动后死循环触发超声波测距然后阻塞等 Echo 返回。这种做法在单任务系统里勉强能跑但只要接上蓝牙和串口屏问题立刻出现——蓝牙模块的数据在接收缓冲区里堆积没人处理串口屏的指令响应变慢整个系统就像卡住一样。正确做法是把系统改成“定时器中断 主循环状态机”。用一个 100ms 周期的定时器产生中断在中断里置一个采集标志位主循环检测到标志位后触发一次超声波测距然后非阻塞地等待 Echo 电平跳变超时则丢弃本次数据。这种结构的好处是主循环始终有空闲时间去处理串口接收和屏幕刷新。3.2 超声波测距和三次滤波的核心代码下面这段代码用 TIM2 作为 100ms 时基用 PA0/PA1 采集 HC-SR04// 使用 HAL 库定时器 TIM2 配置为 100ms 周期中断 volatile uint8_t sample_flag 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { sample_flag 1; // 置位采集标志主循环会去处理 } } uint32_t read_ultrasonic_distance_cm(void) { // 拉高 Trig 至少 10us触发 HC-SR04 发射超声波 HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(15); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); // 等待 Echo 变高带 50ms 超时保护防止传感器异常挂死程序 uint32_t timeout 50000; while (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) GPIO_PIN_RESET) { if (--timeout 0) return 0; } // 测量 Echo 高电平持续时间单位 us uint32_t start DWT-CYCCNT; // 使用 DWT 计时精度比循环计数高 timeout 300000; // 最大约 5ms 高电平对应约 170cm 距离 while (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) GPIO_PIN_SET) { if (--timeout 0) return 0; } uint32_t elapsed (DWT-CYCCNT - start) / (SystemCoreClock / 1000000); // 声速 340m/s往返除以 2得到厘米距离 return elapsed * 0.034 / 2; }代码里用了 DWT-CYCCNT 做精确计时比HAL_GetTick()的毫秒精度高得多测距误差可以控制在 1-2cm。如果你用的芯片不带 DWT 或者不熟悉这个寄存器也可以直接用输入捕获引脚测量高电平时间效果等价。有了距离值之后滤波逻辑要跟上#define DIST_THRESHOLD_CM 50 // 距离小于 50cm 判定为有车占用 #define FILTER_SAMPLES 3 // 连续 3 次一致才更新状态 // 车位状态0 空闲1 占用 uint8_t get_filtered_vehicle_presence(void) { static uint8_t stable_state 0; static uint8_t vote[FILTER_SAMPLES] {0}; static uint8_t index 0; uint32_t dist read_ultrasonic_distance_cm(); uint8_t current (dist 0 dist DIST_THRESHOLD_CM) ? 1 : 0; vote[index % FILTER_SAMPLES] current; index; // 三次采样结果一致才翻转状态避免飞鸟或行人闪避导致误判 if (vote[0] vote[1] vote[1] vote[2]) { if (stable_state ! vote[0]) { stable_state vote[0]; } } return stable_state; }3.3 蓝牙上报的帧格式定义STM32 和触摸屏之间是裸串口通信必须定义一套简单可靠的帧格式。常见做法是“帧头 长度 车位号 状态 校验”字节位置内容值举例说明0帧头0xAA固定用于找帧起始1长度0x03后面数据字节数不含帧头和校验2车位编号0x01可扩展到 0x01-0xFF3车位状态0xA1 占用 / 0xA0 空闲状态字避开 0xAA 防止误判4校验和0x65长度车位状态的累加和取低 8 位发送代码void send_parking_status(uint8_t slot_id, uint8_t occupied) { uint8_t frame[6]; frame[0] 0xAA; // 帧头 frame[1] 0x03; // 长度 frame[2] slot_id; frame[3] occupied ? 0xA1 : 0xA0; // 状态字 frame[4] frame[1] frame[2] frame[3]; // 累加和校验 HAL_UART_Transmit(huart2, frame, 5, 100); // 发往蓝牙模块 }状态字故意没有用 0x00 和 0x01是因为串口数据里出现连续 00 容易在调试时看不清0xA0/0xA1 在十六进制工具里非常显眼也避开了与帧头 0xAA 混淆。注意HAL_UART_Transmit是阻塞发送如果蓝牙模块的波特率是 9600发送 5 字节约耗时 5ms这个时间在 100ms 的采样周期内完全可以接受。不需要用 DMA 或者中断发送来优化。4. 蓝牙链路与触摸屏联调解决 HC-05 连不上和屏幕乱码4.1 两种常见的蓝牙拓扑怎么选“STM32蓝牙触摸屏”在物理上可以有两种接法。第一种是 STM32 发数据给手机手机蓝牙串口 App 显示触摸屏只是摆设或者根本不接。这种接法验收时没法在屏幕上看到车位图现场答辩效果差。第二种是蓝牙模块 A 接 STM32蓝牙模块 B 接串口屏两个蓝牙模块之间做 SPP 透传STM32 的数据帧最终进触摸屏。如果做第二种模块选型就要注意。HC-05 可以设为主模式或从模式两个 HC-05 配好主从关系后能自动回连。HC-06 只能做从机不能设为主机。JDY-31 的“未绑定版本”上电后自动搜索并连接对方省去 AT 指令配置。学生用 JDY-31 或“HC-05 主 HC-06 从”的组合都能跑但从答辩角度讲HC-05 主从配置过程更能体现你对蓝牙协议的理解。4.2 HC-05 配对失败时的三个必查参数“hc05蓝牙模块连接不上”是极高频问题九成是下面三个原因第一模块没有进入 AT 模式。HC-05 上电前按住板载按键再上电此时模块进入 AT 指令模式指示灯慢闪。接 USB-TTL 到电脑用串口助手打开对应 COM 口波特率设 38400发送字符串AT正确返回OK。如果返回乱码或者无响应先检查波特率是不是被改过或者 RXD/TXD 有没有接反。第二主从角色没配对。发送ATROLE1把模块 A 设为主机ATROLE0把模块 B 设为从机之后ATRESET复位。两个模块上电后会自动配对配对成功后指示灯从慢闪变成双闪或者常亮。如果一直不闪检查两个模块的波特率是否一致发送ATUART?查询。第三配对密码和连接码不匹配。发送ATPSWD1234把两边设成相同密码。注意 HC-05 的默认密码是 1234但有些商家发货前改过用ATPSWD?查询。验证链路是否打通有一个很实用的方法把接 STM32 的蓝牙模块 A 通过 USB-TTL 插到电脑另一个蓝牙模块 B 接 USB-TTL 插到另一台电脑两边各开一个串口助手A 端发字符串B 端能收到就说明无线链路本身是通的。这时候再分别接回 STM32 和串口屏能排除一半的故障面。4.3 触摸屏端解析脚本淘晶驰串口屏指令示例串口屏端的核心工作是“收帧、解析、改控件”。以淘晶驰 TJC 系列为例HMI 工程里放三个控件t0是文本控件显示“车位状态”b0是矩形背景控件显示颜色t1是文本控件显示原始距离值。在屏幕的“串口接收事件”里写脚本// 串口屏脚本解析 STM32 发来的 5 字节帧 var len; var slot_id; var status; var sum; var calc; // 每收到一帧完整数据自动进入该回调 if (uart_data[0] 0xAA) { var i; // 先做校验和验证防止无线传输中铁柱遮挡或干扰导致坏帧 for (i 1; i 4; i) { sum uart_data[i]; } if ((sum 0xFF) uart_data[3]) { slot_id uart_data[2]; status uart_data[3]; // 0xA1 表示占用0xA0 表示空闲 if (slot_id 1) { if (status 0xA1) { b0.bco 63488; // 红色 t0.txt 占用; } else { b0.bco 2016; // 绿色 t0.txt 空闲; } } } }脚本里的uart_data是串口屏自带的接收缓冲区数组具体字段名要看所用型号的指令手册。串口屏的脚本引擎语法接近 JavaScript但没有严格类型写的时候注意sum要在进入回调前复位否则第二次收帧时校验会越加越大。4.4 屏幕乱码和蓝牙断连的排查顺序触摸屏上出现乱码通常不是无线链路的问题而是波特率不匹配。蓝牙模块的数据波特率默认是 9600串口屏出厂默认波特率一般是 115200两边不统一屏幕收到的就是“天书”。处理方法是发 AT 指令把蓝牙模块的波特率改成和屏幕一致或者把屏幕的波特率改成 9600。我一般优先改屏幕因为屏幕波特率在 HMI 软件里下拉就能改不用动蓝牙模块的 AT 参数。还有一种隐蔽问题蓝牙模块和串口屏共用一个 5V 电源时蓝牙模块的 TX 输出电平是 3.3V串口屏的 RX 能识别但如果蓝牙模块的底板是宽电压设计TX 电平会被拉到 VCC也就是 5V而串口屏的 RX 标称 3.3V长期这样接有烧毁引脚的风险。稳妥做法是蓝牙模块选 3.3V 供电版本或者串口屏的 RX 串联 1kΩ 电阻做电平保护。5. 从单车位演示到多点检测扩展车位编号和低功耗唤醒的落地做法前面整套链路只跑通了一个车位。真实停车场是按“排”和“号”组织的触摸屏上要画 8 个甚至 16 个车位格子。要做的改动有两处一处是 STM32 端为每个车位分配唯一编号另一处是触摸屏端根据帧里的slot_id更新对应控件而不是固定更新b0。例如用数组管理 8 个控件的颜色值slot_id从 1 到 8解析帧后直接索引b[slot_id]代码结构不用大改只是把控件改成编号命名。低功耗这条路径毕设做出来是加分项。STM32 进入 Stop 模式的策略是把采样周期从 100ms 拉长到 2s传感器供电用 MOSFET 开关控制仅在采样前 50ms 上电采完立即断电。蓝牙模块是耗电大头SPP 模块一旦连接通常要 30-50mA 电流如果只是发送单方向数据可以选用低功耗从机模式但注意 HC-05 没有真正意义上的休眠模式只能靠 STM32 控制其电源。用 BLE 模块替代 SPP 模块能省一半功耗代价是手机端或对端需要一个 BLE 透传工具。最后给一个验证整条链路的实操步骤尤其适合答辩前检查把 STM32 的串口 2 同时接到蓝牙模块和 USB-TTLUSB-TTL 接电脑串口助手设置 9600-8-N-1。用手在 HC-SR04 前面晃一下串口助手应出现AA 03 01 A1 45手拿走应出现AA 03 01 A0 44。确认 STM32 发送正常后拔掉 USB-TTL只留蓝牙模块手机打开蓝牙串口 App 连接模块应看到同样内容。这时再去操作触摸屏屏幕状态与串口数据一致整个项目就可以交付评审了。本文还有配套的精品资源点击获取