基于 STM32F103 蓝牙遥控云台超声波避障小车
📅 2026/7/24 10:26:34
👁️ 阅读次数
📝 编程学习
- 项目概述
- 硬件系统设计
- 资源分配与硬件接线表
- 定时器资源规划
- 软件总体架构
- 模块详细原理说明
- 通信协议规范
- 程序运行流程
- 关键代码说明
- 系统调试要点与现存问题
- 功能扩展方案
1 项目概述
本系统为蓝牙遥控智能探测小车,搭载 SG90 舵机云台带动 HC-SR04 超声波传感器,实现多角度环境测距;支持手机蓝牙单字符指令远程控制小车运动、云台定位、云台自动往复扫描;搭载三色 LED 分级距离报警、0.96 寸 OLED 双页面状态显示;定时向上位机回传障碍物距离、舵机角度、危险预警信息。
核心功能列表
- HC-05 蓝牙无线指令遥控小车前进、后退、左转、右转、停止;
- TIM2 PWM 实现电机调速,支持高速 / 低速档位切换;
- SG90 舵机云台:手动角度定位(0°/90°/180°)、自动往复扫描;
- HC-SR04 超声波测距,测量单位 cm,超时无回波标记超量程;
- 三色 LED 按照障碍物距离分级常亮 / 闪烁预警;
- OLED 双页面循环刷新,实时展示系统运行状态;
- 串口节流上报传感数据,降低串口占用;
- 所有外设模块化驱动,便于调试与功能拓展。
2 硬件系统设计
2.1 硬件清单
表格
| 序号 | 器件名称 | 型号 | 功能 |
|---|---|---|---|
| 1 | 主控芯片 | STM32F103C8T6 | 系统核心控制器 |
| 2 | 蓝牙串口模块 | HC-05 | 无线通信,9600 波特率 |
| 3 | 电机驱动模块 | L298N | 两路直流减速电机驱动 |
| 4 | 舵机 | SG90(9g) | 超声波云台旋转 |
| 5 | 超声波传感器 | HC-SR04 | 障碍物距离检测 |
| 6 | 显示屏 | 0.96 寸 OLED(SSD1306) | 软件 I2C 显示 |
| 7 | 指示灯 | 红 / 黄 / 绿 LED | 距离分级报警 |
| 8 | 执行机构 | 直流减速电机 + 小车底盘 | 小车运动 |
| 9 | 供电系统 | 锂电池组 | 整机供电 |
2.2 系统总体框图
plaintext
手机蓝牙APP ←→ HC-05 ←→ USART1 ←→ STM32F103主控 ↓ ┌────────┬────────┬────────┬────────┬────────┐ │TIM3舵机│TIM2电机│HC-SR04 │OLED屏 │三色LED │ │PWM云台 │PWM调速 │测距模块│显示模块│报警指示│ └────────┴────────┴────────┴────────┴────────┘3 引脚资源分配与硬件接线表
重要预处理:上电执行
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);释放 PA15 引脚用作 TIM2_CH1 PWM 输出,同时保留 SWD 下载调试功能。
3.1 电机控制(GPIOA)
表格
| 信号 | 引脚 | 功能 |
|---|---|---|
| MOT_IN1 | PA0 | L298N IN1 方向控制 |
| MOT_IN2 | PA1 | L298N IN2 方向控制 |
| MOT_IN3 | PA2 | L298N IN3 方向控制 |
| MOT_IN4 | PA3 | L298N IN4 方向控制 |
| PWM 调速 | PA15(TIM2_CH1) | L298N 使能调速信号 |
3.2 LED 报警指示灯(GPIOB)
表格
| 信号 | 引脚 | 功能 |
|---|---|---|
| LED_GREEN | PB0 | 绿色安全指示灯 |
| LED_YELLOW | PB1 | 黄色预警指示灯 |
| LED_RED | PB2 | 红色危险指示灯 |
3.3 HC-SR04 超声波模块(GPIOB)
表格
| 信号 | 引脚 | 功能 |
|---|---|---|
| Trig | PB5 | 超声波触发输出 |
| Echo | PB6 | 超声波回波输入 |
3.4 SG90 舵机云台
表格
| 信号 | 引脚 | 功能 |
|---|---|---|
| 舵机 PWM | PA6(TIM3_CH1) | 50Hz 舵机控制信号 |
3.5 HC-05 蓝牙模块 USART1
表格
| 信号 | 引脚 | 功能 |
|---|---|---|
| TX(STM32→蓝牙) | PA9 USART1_TX | 串口发送 |
| RX(蓝牙→STM32) | PA10 USART1_RX | 串口接收中断 |
3.6 OLED 显示屏(软件 I2C,oled.h 内部宏定义)
SDA、SCL 使用 PA 口自定义引脚,由 oled 驱动文件内部宏控制。
4 定时器资源规划
系统时钟 72MHz
表格
| 定时器 | 功能 | 配置参数 | 输出引脚 |
|---|---|---|---|
| SysTick | 系统延时 delay_ms/delay_us | 72MHz,HCLK/8 | 无引脚 |
| TIM3 | SG90 舵机 50Hz PWM | PSC=71,ARR=19999 | PA6 CH1 |
| TIM2 | 电机调速 PWM 输出 | PSC=71,ARR=999 | PA15 CH1 |
| TIM4 | 超声波计时时基 | PSC=71,ARR=9,中断周期 10μs | 无输出引脚 |
说明:TIM4 中断服务函数代码暂时注释,可根据需求开启用于超声波脉宽计时。
5 软件总体架构
采用前后台裸机架构
- 后台(异步中断)USART1 接收中断:接收蓝牙单字符指令,置位接收标志位; TIM4 更新中断:提供 10μs 计时基准(可选开启)。
- 前台(主循环 while (1) 轮询)指令解析 → 超声波测距 → 云台角度控制 → 电机持续输出 → LED 报警逻辑 → 串口数据上报 → OLED 页面刷新。
工程文件划分
main.c:应用层主逻辑、状态调度、外设初始化SystemTime.h:SysTick 延时函数lanya.h/c:USART1 蓝牙通信驱动、接收中断servo.h/c:TIM3 舵机 PWM 驱动、角度设置HC_SR04:超声波触发、测距、距离换算oled.h/c:软件 I2C OLED 显存驱动、字符 / 数字显示- 电机驱动函数:GPIO 初始化、PWM 占空比配置、运动控制
6 模块详细原理说明
6.1 电机驱动与调速模块
- IN1~IN4 为方向控制 GPIO,控制电机正反转;
- PA15 输出 TIM2 PWM 信号接到 L298N 使能引脚实现调速;
- PWM 配置:ARR=999,PSC=71,频率 = 72M/(71+1)/(999+1)=1kHz;
SetPWM_Duty()修改 CCR1 寄存器,占空比范围 0~999;- 指令
V将占空比设置为 400,实现低速行驶;默认占空比 600。
电机动作逻辑:
F前进:IN1=1,IN2=0;IN3=1,IN4=0B后退:IN1=0,IN2=1;IN3=0,IN4=1L左转:IN1=0,IN2=1;IN3=1,IN4=0R右转:IN1=1,IN2=0;IN3=0,IN4=1S停止:全部引脚拉低
6.2 SG90 舵机云台控制
SG90 标准控制:50Hz PWM(周期 20ms) 脉宽计算公式: \(Pulse = 500 + angle × \frac{2000}{180}\)
- 0° → 500
- 90° → 1500
- 180° → 2500
自动扫描模式:角度 0°↔180° 往复,每 5ms 步进 10°;收到定位指令自动关闭自动扫描。
6.3 HC-SR04 超声波测距原理
- Trig 输出 15μs 高电平触发超声波发射;
- 等待 Echo 上升沿、下降沿,通过 TIM4 累计 10μs 计时;
- 音速取 25℃标准 346m/s,往返距离换算: \(Distance_{mm}=\frac{time×346}{200}\)
- 增加两级超时保护,无有效回波返回 0;上层代码将距离标记为 999.0cm(超量程);
- 主循环每 5 次轮询执行一次测距,降低 CPU 占用。
6.4 LED 分级报警策略
LED 为低电平点亮:
- distance > 100cm:绿灯常亮 → 安全区域
- 50cm ≤ distance ≤ 100cm:黄灯 5ms 周期闪烁 → 预警区域
- distance < 50cm:红灯 5ms 周期闪烁 → 危险区域
6.5 OLED 显示机制
双页面交替刷新,页面切换间隔 150ms:
- 页面 1:云台角度 Angle + 障碍物距离 Dist
- 页面 2:小车运动状态 MOT、云台扫描开关 SCAN、危险提示 WARN (SAFE/DANGER) 驱动采用显存缓存
OLED_GRAM数组,统一调用OLED_Refresh()刷新屏幕,减少 I2C 频繁通信。
7 通信协议规范
7.1 上位机下发控制指令(单字符指令)
表格
| 指令 | 功能说明 |
|---|---|
0 | 舵机定位 0°,关闭自动扫描 |
1 | 舵机定位 90°,关闭自动扫描 |
2 | 舵机定位 180°,关闭自动扫描 |
F | 小车前进 |
B | 小车后退 |
L | 小车左转 |
R | 小车右转 |
S | 小车停止 |
K | 开启云台自动往复扫描 |
G | 关闭自动扫描,舵机归零 0° |
V | 电机调速至低速(占空比 400) |
通信机制:串口中断接收单字节,接收完成置
usart_flag=1,主循环解析指令。
7.2 小车向上位机主动上报数据包
上报周期:200ms(节流控制,防止串口刷屏) 输出格式:
plaintext
DIST:XX.Xcm,WARN:X,ANG:XXX\r\nDIST:障碍物距离(cm)WARN:1 = 距离<50cm 危险;0 = 安全ANG:舵机当前角度(0~180)
8 程序运行流程
- 系统初始化顺序 释放 PA15 引脚 → SysTick 延时 → NVIC 优先级分组 → OLED → 舵机 → USART1 → HC-SR04 → TIM4 → 电机 GPIO → LED → TIM2 PWM
- OLED 打印启动提示,延时 800ms 清屏;上电舵机归位 0°;
- 进入主循环轮询: ① 判断蓝牙接收标志,解析指令更新小车状态、云台状态; ② 定时触发超声波测距,更新距离变量; ③ 若开启自动扫描,持续更新舵机角度; ④ 持续输出电机控制电平; ⑤ 根据距离阈值执行 LED 亮灭 / 闪烁逻辑; ⑥ 200ms 定时向上位机发送传感数据; ⑦ OLED 双页面交替刷新; ⑧ delay_ms (150),进入下一轮循环。
9 关键代码说明
- 全局状态变量
c
运行
u8 motor_run_state = 'S'; //小车运行状态 u8 scan_mode = 0; //云台自动扫描开关 uint16_t motor_pwm_duty = 600; //电机PWM占空比 u16 angle = 0; //舵机角度 float distance = 0.0f; //障碍物距离(cm)- 串口接收采用单字节接收,适合蓝牙简易遥控 APP;
- 超声波测距加入超时保护,避免程序卡死在 while 等待;
- OLED 采用显存缓存机制,所有绘图操作先写入数组,统一刷新;
- 数据上报增加时间戳节流,避免频繁占用串口总线。
10 系统调试要点与现存问题
调试注意事项
- PA15 复用冲突:必须执行
GPIO_Remap_SWJ_JTAGDisable,否则 TIM2_CH1 无法输出 PWM; - 整机所有模块必须共地,电机大电流干扰会导致串口、超声波数据异常;
- SG90 舵机负载不宜过重,云台轻量化,防止电压跌落导致单片机复位;
- HC-SR04 存在盲区,小于 2cm 无法测量;软材质障碍物测距误差较大;
当前代码存在缺陷
- 主循环使用
delay_ms(150)阻塞延时,降低系统实时响应; - 超声波未做多次采样滤波,测距数据波动较大;
- 蓝牙仅支持单字符指令,无法连续调速;
- 无指令超时看门狗,蓝牙断开后小车持续保持原有动作;
- TIM4 中断函数注释,若开启需要保证中断变量
time使用 volatile 修饰。
11 功能扩展方案
- 移除阻塞 delay,基于 tick_ms 实现非阻塞事件调度,提升实时性;
- 增加超声波多次采样均值滤波,稳定测距数据;
- 设计多字符通信协议,支持连续调速、角度连续控制;
- 添加指令超时检测,蓝牙失联后小车自动停止;
- 增加自动避障逻辑:距离<阈值自动转向避让;
- 增加蜂鸣器声音报警;
- 增加电池电压检测与低电量报警;
- 增加按键本地手动控制。
12 核心代码
#include "SystemTime.h" static u8 fac_us=0; //us延时倍乘数 static u16 fac_ms=0; //ms延时倍乘数 //初始化延迟函数 //SYSTICK的时钟固定为AHB时钟的1/8 //SYSCLK:系统时钟频率 void SysTick_Init(u8 SYSCLK) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us=SYSCLK/8; fac_ms=(u16)fac_us*1000; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk; } //延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } //延时nms //注意nms的范围 //SysTick->LOAD为24位寄存器,所以,最大延时为: //nms<=0xffffff*8*1000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms<=1864 void delay_ms(u16 nms) { u32 temp; SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 }
编程学习
技术分享
实战经验