
简介一套基于STM32F103VET6的智能停车场毕业设计资源面向嵌入式方向学生解决停车场无人化管理、远程状态查询与短信控制问题。项目按硬件职责划分为主板、控制台指示板及场内红外检测板实现了车辆进场自动播报欢迎并升起停车杆、完全驶入后自动降杆通过红外检测车位占用状态并在控制台与车位处实时显示屏幕同步呈现可用车位及环境温湿度同时借助SIM800L模块每1小时短信报告车位情况与近1小时进出车辆数并支持短信下发控制指令。资源共72个文件以C语言源码为主27个.c、30个.h另含STM32标准外设库、MDK工程文件、链接映射文件、README说明及License等压缩包仅304KB项目目录清晰可直接导入Keil进行编译与烧录验证。当前已有120人学习浏览适合需要综合运用GPIO、定时器、USART中断和传感器数据处理的毕设开发者。项目还前瞻性地规划了停车计费、冲卡报警与实时时钟短信安排等进阶功能可作为功能扩展与二次开发的清晰蓝本。1. 这是什么资源STM32 智能停车场项目的完整闭环基于 STM32 的智能停车场是电子类毕业设计里出场率很高的一个选题。它不像纯 LED 流水灯那么单薄也不像图像识别那么卷它恰好卡在一个很舒服的位置传感器、执行器、人机交互、通信调试四件事全都沾一点每一件都够你写进论文但又不会难到做不完。这份 zip 资源给你的是一整套可落地的设计思路从主控选型到硬件接线从核心代码到常见故障排查基本上照着走就能把系统跑起来。适合想把毕设做成“真能演示、能抗住答辩追问”的本科生也适合想快速搭一套小型停车场样机练手的工程师。2. 系统架构与主控选型为什么 STM32F103C8T6 是多数毕设的最优解我拆过不少毕设项目发现一个普遍规律一开始冲劲最足最容易在选型上摇摆。今天觉得 F407 好明天又想换国产 GD32最后光是移植工程就耗掉一半时间。做智能停车场主控的选择其实可以很干脆——STM32F103C8T6 是大多数情况下的最优解。它不是性能最强的但它是资料最多、上手路径最成熟、出问题最好查的。2.1 智能停车场的功能拆分与信号链路把整个停车场系统拆开看能分成四个层次。感知层负责判断“有没有车”入口处用红外对管或超声波模块检测车辆接近车位上方用超声波测距判断车位是否被占用。控制层跑核心逻辑比如车到入口、开闸抬杆、车进入后落杆、车位计数更新。执行层就是舵机模拟道闸、LED 指示灯模拟车位状态。人机层则是一块 OLED 或 LCD1602 屏幕显示剩余车位和计费信息按键用来模拟取卡入场。如果题目要求联网还可以加一个 ESP8266把数据通过串口发到云平台。信号链路由输入到输出非常清晰传感器信号进来经过 GPIO 或定时器捕获进入逻辑判断再输出到舵机和屏幕。这条链路越短排查越容易。反过来看也一样如果某一步输出不对从传感器开始往回查几乎不会走弯路。功能分层还有一个好处答辩时你可以直接讲“我的系统分四层”评委一听就知道你不是把代码全塞在 main 里。2.2 STM32F103C8T6 的关键资源与选型边界F103C8T6 是 Cortex-M3 内核主频 72MHzFlash 64KBSRAM 20KB。这个配置跑停车场逻辑绰绰有余。它提供 3 个通用定时器加 1 个高级定时器、1 个 12 位 ADC、多个 USART、SPI 和 I2C 接口。超声波测距用定时器输入捕获舵机用定时器 PWM 输出OLED 用 I2C 或 SPI这些外设全部够用不需要为了凑功能去选更高端的芯片。选型边界在于如果题目明确要求摄像头图像识别、大尺寸触摸屏或者以太网那 F103C8T6 就吃力了此时建议升到 F103ZET6 或 F407 系列。如果只是停车场这套玩法升到 F407 只会增加布线和调试成本课堂演示看不出差别。还需要注意一个细节F103C8T6 的 Flash 不大不要在工程里塞全字库或动画 GUI不然编译器会告诉你空间不够到那时候改代码很痛苦。下面是一个常见的模块与外设资源对照表帮助你在设计阶段就把引脚规划清楚功能模块推荐外设关键参数引脚建议超声波测距 Trig/EchoGPIO/TIM 输入捕获52ms 超时上限PA0/PA1TIM2_CH1舵机道闸TIM 通道 PWM50Hz0.5~2.5ms 脉宽PB0TIM3_CH3OLED 显示I2C1地址 0x3C 或 0x78PB8/PB9串口调试USART2115200-8-N-1PA2/PA3红外对管GPIO 输入遮挡电平翻转PA4这张表建议直接贴在电脑前写代码时对照着初始化引脚能少踩很多低级坑。2.3 工程初始化标准库还是 HAL 库、目录怎么组织选库的问题我每次都跟学生强调一遍因为它决定了你后面三个月的效率。用标准库手写能深入理解寄存器操作但初始化代码量很大每换一个外设都要翻阅参考手册。用 HAL 库配合 CubeMX图形化生成初始化代码开发效率高代码结构也更规范。毕设这种时间紧、还要保证演示稳定的场景我强烈建议 HAL 库加 CubeMX 的组合。工程目录我一般按三层组织Core/ 主函数、中断回调、系统时钟配置 Hardware/ 传感器、舵机、OLED 等外设驱动 App/ 停车场状态机、车位管理、计费逻辑每个文件夹只放一类东西排错的时候直接按目录找。比如 OLED 显示出了问题先看 Hardware 里的驱动函数是否正常不用去翻 App 层的业务代码。初始化顺序也要固定先时钟再 GPIO然后定时器、中断、串口最后才初始化传感器。这个顺序一旦乱了后面极难排查因为你不知道报错是引脚冲突还是时钟没起振。3. 硬件接线与传感器落地五个模块背后的三个共性问题硬件部分是这个项目里最容易让人抓狂的地方。程序写得再漂亮接线接错或供电不稳表现出来就是数据乱跳、反复重启、屏幕花屏。这类问题很难用代码修复只能靠硬件基本功解决。这一章把停车场核心模块的接线要点和参数边界讲清楚你照着接就能避开一半的坑。3.1 HC-SR04 超声波测距接线、时序与参数边界HC-SR04 是停车场车位检测最常用的超声波模块测距范围约 2cm 到 4m供电 5VTrig 和 Echo 都是电平信号。工作流程是Trig 收到一个至少 10us 的高电平脉冲后模块发送 8 个 40kHz 的方波随后 Echo 输出一个高电平高电平持续时间等于超声波往返的总时间。距离与时间的换算公式是距离等于时间乘声速再除以 2简化计算就是时间差us除以 58得到厘米数这里取的是声速 340m/s 下的近似值。接线上有三个注意点。Trig 接任意 GPIOEcho 必须接到支持输入捕获或外部中断的引脚。由于 HC-SR04 输出高电平是 5V而 STM32F103 的 GPIO 工作电压是 3.3V我一般会给 Echo 加一个分压电阻1k 和 2k 串联把 5V 分到约 3.3V避免长期电平倒灌伤引脚。第三模块的 5V 电源不要和单片机敏感电路混在一起至少要在模块电源入口并联一个 100uF 电容。注意HC-SR04 在测距超时或障碍物太远时Echo 高电平持续时间会异常长代码里必须加超时判断否则程序会卡死在等待 Echo 下降沿的中断里。代码里要处理的边界包括距离小于 2cm 时模块测不准大于 4m 时回波太弱都要在采集函数里做有效性校验。否则你会看到屏幕上偶尔蹦出一个 84cm 的假数据这就是边界没处理好。3.2 红外对管与光电检测车位状态判断的另一条路车位检测不只有超声波一种方案红外对管在判断车是否进入某个区域时更直接。一对红外对管由发射管和接收管组成车挡住光路时接收管电平翻转单片机只需读一次引脚电平就能判断“有车”。响应速度快、代码简单、成本低这是它的优点。但它也对安装位置有要求对阳光和强光敏感黑色车身对红外吸收强容易漏判。我通常的建议是入口检测用红外对管车位检测用超声波。入口只要判断车辆经过红外对管的响应速度优势正好发挥车位要精确判断“是否有车占用”超声波的距离数据更可靠。这种交叉选型还有一个隐性好处答辩时你可以顺理成章地说“我根据场景差异选择了不同的传感方案”比单纯堆同一种传感器有说服力得多。接线方面红外对管模块一般有 VCC、GND、DO 三个引脚DO 输出数字电平直接接 GPIO 输入。注意模块标准供电是 3.3V 到 5V部分模块在 5V 下输出高电平是 5V同样需要分压或用电平转换芯片别硬接。3.3 舵机、继电器与电机驱动执行机构的共地与反向电压模拟道闸最常用 SG90 舵机5V 供电转动角度由 PWM 脉宽决定。0.5ms 脉宽对应 0 度1.5ms 对应 90 度2.5ms 对应 180 度PWM 周期固定在 20ms也就是 50Hz。用 STM32 定时器的 PWM 输出通道很容易生成这个波形但舵机的问题不在信号而在电源。舵机启动瞬间电流可能到几百毫安如果和单片机共用同一个 5V 稳压源一抬杆单片机就复位。我见过太多学生遇到“舵机一动系统重启”的情况原因基本都是供电顶不住。解决方法是舵机电源单独取一路GND 和单片机共地PWM 信号线直接连单片机 GPIO。共地是必须的否则控制信号没有参考电平舵机根本不会动作。如果题目要求控制照明灯或风扇这类负载需要用继电器。继电器线圈在断电瞬间会产生反向电动势如果不加续流二极管高压可能打坏 GPIO 或稳压芯片。线圈两端并联一个 1N4148 或 1N4007方向要和电源相反。这个细节在原理图里很显眼但自己手焊板子的时候最容易漏。3.4 电源与接地网络把“反复重启”掐在源头整个智能停车系统里最隐蔽的故障源是供电架构。STM32 正常工作几十毫安OLED 背光亮起来也吃几十毫安舵机瞬间几百毫安蜂鸣器还要再加一笔。所有模块从一个稳压芯片后面取电满载时输出电压就会被拉低单片机检测到欠压就开始复位表现出来就是“刚上电正常一动作就重启”。可靠的供电方案是分级。外部 5V 适配器或充电宝作为总电源舵机和蜂鸣器直接挂 5V3.3V 用 AMS1117 单独从 5V 转出来给 MCU 和传感器供电。所有模块的 GND 必须严格连到同一网络不要靠杜邦线“顺手搭”在邻近 GND 上。地线不稳最直接的后果是 ADC 采样乱跳和超声波测距严重失真。电源指示灯建议加在总电源入口一眼能看出来当前系统是否在正常工作。4. 核心代码实现从 CubeMX 初始化到主状态机到串口调试硬件板子搭好接下来是代码。我把代码拆成四段工程初始化、超声波采集、主状态机、串口显示。每一段都给出关键代码块和参数说明你拿到资源包后可以直接对照着改。需要注意不同版本资源包里的函数命名可能不完全一样但逻辑结构基本是一致的。4.1 用 CubeMX 配置引脚映射与时钟树打开 CubeMX选 STM32F103C8T6在 Pinout 面板里配置引脚。PA0 设为 GPIO_Output接超声波 TrigPA1 设为 TIM2_CH1 输入捕获接 EchoPA2 和 PA3 设为 USART2_TX 和 USART2_RXPB8、PB9 设为 I2C1_SCL 和 I2C1_SDAPB0 设为 TIM3_CH3 复用推挽输出接舵机。引脚规划这一步建议严格按照模块表来不要想到哪个配哪个配乱了后面改起来非常痛苦。时钟树配置HSE 选择 8MHz 外部晶振PLL 倍频到 72MHz。这里有个高频踩坑点TIM3 挂载在 APB1 总线上APB1 时钟默认 36MHz如果 APB1 分频系数不是 1定时器时钟会翻倍变成 72MHz。计算 PWM 频率时必须把这个倍率算进去不然舵机输出频率不对。初始化代码生成后核心 GPIO 配置长这样void MX_GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_0; // PA0 Trig GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_0; // PB0 舵机 PWM GPIO_InitStruct.Mode GPIO_MODE_AF_PP; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); }这段代码做的事是打开 GPIOA、GPIOB 时钟然后把引脚配置成需要的模式。Trig 用推挽输出驱动能力足够舵机引脚要用复用推挽输出因为 PWM 信号由定时器硬件驱动不是 GPIO 翻转。如果这里误配成普通推挽输出你会看到舵机完全没有信号响应。4.2 超声波测距代码定时器输入捕获代替延时阻塞超声波采集有两种实现方案。一种是在主循环里用 delay 等待 Echo 高电平结束代码简单但会阻塞整个程序而且延时精度取决于主循环调度很容易到 10cm 左右就开始波动。另一种是让定时器工作在输入捕获模式记录 Echo 上升沿和下降沿的计数值差值乘以定时器分辨率就是时间。第二种方案不阻塞主流程数据稳定推荐直接用。volatile uint16_t rising_edge 0; volatile uint16_t falling_edge 0; volatile uint8_t capture_index 0; volatile uint32_t echo_diff 0; volatile float distance_cm 0.0f; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { if (capture_index 0) { rising_edge HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); capture_index 1; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); } else { falling_edge HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); capture_index 0; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); echo_diff (falling_edge rising_edge) ? (falling_edge - rising_edge) : 0; distance_cm echo_diff / 58.0f; } } } void HCSR04_Start(void) { HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(10); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); }这段代码的核心逻辑是触发后在中断回调里切换捕获极性。第一次捕获上升沿第二次捕获下降沿两次计数差值就是 Echo 高电平时长。除以 58 是经验公式因为声速 340m/s 时往返 1cm 约 58us。如果环境温度变化明显可以把声速换成温度补偿公式。注意TIM2 预分频必须设置成 72 分频让计数频率到 1MHz这样计数差值的单位才是微妙。预分频错了后面除 58 得到的距离全是错的而且错得没有任何规律。4.3 停车场主逻辑状态机入场、抬杆、落杆、计数主循环里如果直接堆 if-else代码会越写越长到后来加一个功能就要动十几处地方。停车场这种场景适合用状态机组织状态跳转清晰答辩时也容易口头描述流程。状态定义我用枚举实现typedef enum { STATE_IDLE 0, STATE_CAR_DETECTED, STATE_GATE_RAISING, STATE_CAR_ENTERING, STATE_GATE_LOWERING, STATE_PARKING_FULL } ParkingState;主逻辑状态机如下每隔几十毫秒调用一次根据当前状态执行对应动作并切换下一个状态void Parking_StateMachine(ParkingState *state) { switch (*state) { case STATE_IDLE: if (HCSR04_GetDistance(ENTRANCE_CH) 20.0f) { *state STATE_GATE_RAISING; Servo_SetAngle(90); // 抬杆 } break; case STATE_GATE_RAISING: if (delay_task_is_done(1500)) { *state STATE_CAR_ENTERING; } break; case STATE_CAR_ENTERING: if (HCSR04_GetDistance(ENTRANCE_CH) 50.0f) { parking_slots_count--; *state STATE_GATE_LOWERING; Servo_SetAngle(0); // 落杆 } break; default: *state STATE_IDLE; break; } }这段状态机的关键在于延时不能放在状态内部用阻塞方式等待否则整个系统在等待期间无法响应按键和串口。资源包里通常会有一个软延时任务模块用系统节拍判断时间是否到而不是死等。判定阈值也要注意进入判定用 20cm离开判定用 50cm中间留出缓冲区间避免车辆停在门口时数据轻微波动导致反复抬落杆。车位管理逻辑一般放在 App 层独立文件里。每个车位一个变量记录占用状态车进入后把对应车位置占用出口检测到车离开后释放。如果题目要做计费还需要记录入场时间戳出场时按小时数或停车时长计算费用显示在 OLED 上这个业务逻辑建议也做成独立模块。4.4 串口与 OLED 输出printf 重定向和可读状态显示串口打印是这个项目的生命线。数据不对时第一件事就是看串口日志。要让 printf 从串口输出必须先重定向 fputc。在 Keil 工程里加上这段#include stdio.h int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart2, (uint8_t *)ch, 1, 100); return ch; }重定向之后printf 就能直接输出到串口。但这里有个隐蔽的坑Keil 默认使用标准库的 printf体积大有时还会和 HAL 的底层实现冲突。解决方法是到 Options for Target 里勾选 Use MicroLIB用微库替换标准库输出立即变正常。这个坑几乎每个用 Keil 和 STM32 的新手都会踩一次建议从一开始就勾上。OLED 显示部分推荐 SSD1306 驱动的 0.96 寸 I2C 屏I2C 只要两根线接线简单。初始化调用 OLED_Init主循环里定期刷新剩余车位和计费信息。刷新频率要控制好全屏刷新一次大约需要几十毫秒如果每秒刷 20 次以上I2C 总线被占满超声波测距的时序就会受影响测量数据开始出现毛刺。一般每秒刷 2 到 5 次足够既能看到状态变化又不干扰核心逻辑。5. 避坑排查停车场项目中最常翻车的 5 个现场调试阶段才是真正考验人的地方。代码刚烧进去问题往往不是逻辑错误而是一些看起来没道理、查起来很耗时的硬件和配置问题。这一章我按“现象 → 原因 → 解决”的格式把你最可能遇到的五个翻车现场提前排一遍。5.1 超声波数据乱跳、距离显示明显不对现象OLED 或串口输出的距离值不停跳动车就停在正下方数据却从 10cm 跳到 4m或稳定在某个完全不符的值。原因最常见的是接线和接地问题Echo 引脚悬空感应噪声直接耦合进定时器捕获通道其次是波动回波声波打到斜面或边界角产生多次反射导致捕获到错误的时间戳还有代码的问题没有超时判断Echo 在高电平期间被一次新触发打断残留计数值被当作有效数据。解决先量一下 Echo 引脚空闲电平正常应该是低电平。然后检查模块 GND 与单片机是否同一网络。代码层测距函数里加有效范围判断距离小于 2cm 或大于 400cm 直接丢弃再做滤波连续采集 5 次去掉最大值和最小值后求均值打印出来的数据会稳定很多。超声波模块安装时还要保证探头前没有遮挡物探头平面朝向车位地面。5.2 上电即重启、舵机一动就复位现象USB 一接上OLED 刚点亮系统就重启或者每次抬杆屏幕闪一下串口开始打印乱码紧接着整体软复位。原因这是供电问题。舵机或继电器动作瞬间电流峰值很高把 5V 电源电压瞬间拉低单片机检测到供电电压异常后触发掉电复位。串口乱码是因为电压跌落期间 UART 电平信号失真错误数据被当作有效数据接收。解决把舵机从系统主电源上摘出去单独供电GND 仍然与单片机共地。入口总电源处并联一个 470uF 到 1000uF 的电解电容用来扛瞬态压降。用万用表测量舵机动作时 5V 网络的最低电压如果低于 4.5V说明供电架构必须改。这一条属于物理层问题调代码永远救不了。5.3 串口 printf 没有输出或输出乱码现象Keil 编译下载后串口助手界面无任何输出或者输出的全是乱码。程序看起来在运行OLED 正常刷新但收不到日志。原因printf 没有重定向到串口只是空调用串口参数不匹配波特率不一致是最常见的还有引脚复用冲突USART2 的 TX/RX 被配置成其他功能。有些调试口用的是 SWD剩余引脚被初始化成了普通 GPIO也会把 UART 线路占掉。解决按 4.4 节加入 fputc 重定向并勾选 MicroLIB然后确认串口助手参数是 115200、8 位数据、无校验、1 位停止位。用示波器或逻辑分析仪看 TX 引脚是否在空闲状态保持高电平如果空闲是低电平说明 GPIO 配置或硬件接线有问题。5.4 ADC 切换通道后数值错乱现象程序里先采集光敏电阻再切换到电位器通道切换到新通道后第一次读到的值明显偏低或偏高过一会儿才恢复正常。原因ADC 内部采样保持电容里还残留着上一个通道的电荷。切换通道后如果立即读取得到的是新旧混合结果尤其是通道间阻抗差异大时更明显。解决切换通道后先做一次空读或加一个几微秒的启动延时等待采样电容完成充电然后连续采样多次去掉最大值最小值取平均。另一种方案是用 ADC 的扫描模式加 DMA让硬件自动依次转换多个通道避免代码手动切换带来时序问题。如果你的代码用的是裸金属循环切换建议改成 DMA 模式。5.5 OLED 白屏或只在角落显示字符现象OLED 上电后白屏或者只有屏幕某一小部分显示字符其他区域全黑或全乱码。原因SSD1306 的 I2C 地址不对。不同卖家模块的 I2C 地址可能是 0x3C 或 0x78而驱动代码里写的是默认值两者对不上就白屏。另一类原因是复位时序不完整SSD1306 对复位引脚敏感如果复位脚悬空首次初始化可能失败。解决先用 I2C 扫描程序打印出实际设备地址然后修改驱动头文件里的地址宏定义。如果模块有 RST 引脚把它接到一个 GPIO初始化前先拉低 10ms 再拉高确保复位时序完整。刷新逻辑不要放在中断回调里I2C 传输过程被中断打断会造成数据错位表现为局部乱码。6. 进阶调试让毕设从“能跑”变成“能演示、能答辩”代码能跑只是第一步真正拉开差距的是调试规范和演示技巧。这一章给你三个实操习惯和验证方法都是从做过的项目里沉淀下来的。6.1 用串口协议打通数据验证与其用 OLED 实时观察数据不如把关键状态通过结构化的串口帧打出来。我一般会定义一套简单的文本协议printf(ENTRANCE:%d\n, entrance_cm); printf(SPOT_1:%d\n, spot1_status); printf(STATE:%d\n, current_state);上位机这边用串口助手或 Python 脚本直接读解析成表格。这样验证“传感器判定是否准确”时不需要盯着屏幕看直接把串口日志按时间排序阈值是否触发一目了然。这份日志还能直接放进论文附录作为实测数据比截图更有说服力。6.2 答辩演示的顺序与关键指标演示顺序不要乱来。我建议先展示 OLED 正常刷新剩余车位让评委建立系统在稳定运行的第一印象。然后用手或纸板靠近入口超声波等舵机抬杆再把障碍物拿走观察落杆动作和车位计数减少。最后切到串口助手指着日志解释每个状态切换的触发条件。被问到关键指标时记住这几个数字超声波测距量程 2cm 至 4m系统判定阈值 20cm 和 50cm舵机 PWM 频率 50Hz串口波特率 115200。这些参数能对答如流比你背十页论文都管用。6.3 我的三条调试血缘习惯最后讲三条我自己总结的调试习惯不是为了教你理论纯粹是踩坑换来的。第一改代码前先备份用 Git 或压缩包都行改崩了还有后悔药。第二上电前一定用万用表量电源和地之间是否短路这个习惯帮我避免了好几次烧传感器。第三传感器驱动、业务逻辑、调试输出分别放不同文件每个文件只做一件事排错时按文件定位而不是在一个几百行的 main.c 里反复翻。有过一次调了一整晚超声波数据跳变最后发现只是 Trig 引脚被误配置成了 ADC 模式之后从那以后我每次新建 STM32 工程都会强制走一遍引脚复用检查清单确认没有引脚冲突再开始写业务代码。这三个习惯不一定能让你的毕设拿高分但一定能让你少熬几个通宵。希望帮到你。本文还有配套的精品资源点击获取