基于 STM32F103 蓝牙遥控云台超声波避障小车
  1. 项目概述
  2. 硬件系统设计
  3. 资源分配与硬件接线表
  4. 定时器资源规划
  5. 软件总体架构
  6. 模块详细原理说明
  7. 通信协议规范
  8. 程序运行流程
  9. 关键代码说明
  10. 系统调试要点与现存问题
  11. 功能扩展方案

1 项目概述

本系统为蓝牙遥控智能探测小车,搭载 SG90 舵机云台带动 HC-SR04 超声波传感器,实现多角度环境测距;支持手机蓝牙单字符指令远程控制小车运动、云台定位、云台自动往复扫描;搭载三色 LED 分级距离报警、0.96 寸 OLED 双页面状态显示;定时向上位机回传障碍物距离、舵机角度、危险预警信息。

核心功能列表

  1. HC-05 蓝牙无线指令遥控小车前进、后退、左转、右转、停止;
  2. TIM2 PWM 实现电机调速,支持高速 / 低速档位切换;
  3. SG90 舵机云台:手动角度定位(0°/90°/180°)、自动往复扫描;
  4. HC-SR04 超声波测距,测量单位 cm,超时无回波标记超量程;
  5. 三色 LED 按照障碍物距离分级常亮 / 闪烁预警;
  6. OLED 双页面循环刷新,实时展示系统运行状态;
  7. 串口节流上报传感数据,降低串口占用;
  8. 所有外设模块化驱动,便于调试与功能拓展。

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_IN1PA0L298N IN1 方向控制
MOT_IN2PA1L298N IN2 方向控制
MOT_IN3PA2L298N IN3 方向控制
MOT_IN4PA3L298N IN4 方向控制
PWM 调速PA15(TIM2_CH1)L298N 使能调速信号

3.2 LED 报警指示灯(GPIOB)

表格

信号引脚功能
LED_GREENPB0绿色安全指示灯
LED_YELLOWPB1黄色预警指示灯
LED_REDPB2红色危险指示灯

3.3 HC-SR04 超声波模块(GPIOB)

表格

信号引脚功能
TrigPB5超声波触发输出
EchoPB6超声波回波输入

3.4 SG90 舵机云台

表格

信号引脚功能
舵机 PWMPA6(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_us72MHz,HCLK/8无引脚
TIM3SG90 舵机 50Hz PWMPSC=71,ARR=19999PA6 CH1
TIM2电机调速 PWM 输出PSC=71,ARR=999PA15 CH1
TIM4超声波计时时基PSC=71,ARR=9,中断周期 10μs无输出引脚

说明:TIM4 中断服务函数代码暂时注释,可根据需求开启用于超声波脉宽计时。

5 软件总体架构

采用前后台裸机架构

  • 后台(异步中断)USART1 接收中断:接收蓝牙单字符指令,置位接收标志位; TIM4 更新中断:提供 10μs 计时基准(可选开启)。
  • 前台(主循环 while (1) 轮询)指令解析 → 超声波测距 → 云台角度控制 → 电机持续输出 → LED 报警逻辑 → 串口数据上报 → OLED 页面刷新。

工程文件划分

  1. main.c:应用层主逻辑、状态调度、外设初始化
  2. SystemTime.h:SysTick 延时函数
  3. lanya.h/c:USART1 蓝牙通信驱动、接收中断
  4. servo.h/c:TIM3 舵机 PWM 驱动、角度设置
  5. HC_SR04:超声波触发、测距、距离换算
  6. oled.h/c:软件 I2C OLED 显存驱动、字符 / 数字显示
  7. 电机驱动函数:GPIO 初始化、PWM 占空比配置、运动控制

6 模块详细原理说明

6.1 电机驱动与调速模块

  1. IN1~IN4 为方向控制 GPIO,控制电机正反转;
  2. PA15 输出 TIM2 PWM 信号接到 L298N 使能引脚实现调速;
  3. PWM 配置:ARR=999,PSC=71,频率 = 72M/(71+1)/(999+1)=1kHz;
  4. SetPWM_Duty()修改 CCR1 寄存器,占空比范围 0~999;
  5. 指令V将占空比设置为 400,实现低速行驶;默认占空比 600。

电机动作逻辑:

  • F前进:IN1=1,IN2=0;IN3=1,IN4=0
  • B后退:IN1=0,IN2=1;IN3=0,IN4=1
  • L左转:IN1=0,IN2=1;IN3=1,IN4=0
  • R右转:IN1=1,IN2=0;IN3=0,IN4=1
  • S停止:全部引脚拉低

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 超声波测距原理

  1. Trig 输出 15μs 高电平触发超声波发射;
  2. 等待 Echo 上升沿、下降沿,通过 TIM4 累计 10μs 计时;
  3. 音速取 25℃标准 346m/s,往返距离换算: \(Distance_{mm}=\frac{time×346}{200}\)
  4. 增加两级超时保护,无有效回波返回 0;上层代码将距离标记为 999.0cm(超量程);
  5. 主循环每 5 次轮询执行一次测距,降低 CPU 占用。

6.4 LED 分级报警策略

LED 为低电平点亮:

  1. distance > 100cm:绿灯常亮 → 安全区域
  2. 50cm ≤ distance ≤ 100cm:黄灯 5ms 周期闪烁 → 预警区域
  3. 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\n
  • DIST:障碍物距离(cm)
  • WARN:1 = 距离<50cm 危险;0 = 安全
  • ANG:舵机当前角度(0~180)

8 程序运行流程

  1. 系统初始化顺序 释放 PA15 引脚 → SysTick 延时 → NVIC 优先级分组 → OLED → 舵机 → USART1 → HC-SR04 → TIM4 → 电机 GPIO → LED → TIM2 PWM
  2. OLED 打印启动提示,延时 800ms 清屏;上电舵机归位 0°;
  3. 进入主循环轮询: ① 判断蓝牙接收标志,解析指令更新小车状态、云台状态; ② 定时触发超声波测距,更新距离变量; ③ 若开启自动扫描,持续更新舵机角度; ④ 持续输出电机控制电平; ⑤ 根据距离阈值执行 LED 亮灭 / 闪烁逻辑; ⑥ 200ms 定时向上位机发送传感数据; ⑦ OLED 双页面交替刷新; ⑧ delay_ms (150),进入下一轮循环。

9 关键代码说明

  1. 全局状态变量

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)
  1. 串口接收采用单字节接收,适合蓝牙简易遥控 APP;
  2. 超声波测距加入超时保护,避免程序卡死在 while 等待;
  3. OLED 采用显存缓存机制,所有绘图操作先写入数组,统一刷新;
  4. 数据上报增加时间戳节流,避免频繁占用串口总线。

10 系统调试要点与现存问题

调试注意事项

  1. PA15 复用冲突:必须执行GPIO_Remap_SWJ_JTAGDisable,否则 TIM2_CH1 无法输出 PWM;
  2. 整机所有模块必须共地,电机大电流干扰会导致串口、超声波数据异常;
  3. SG90 舵机负载不宜过重,云台轻量化,防止电压跌落导致单片机复位;
  4. HC-SR04 存在盲区,小于 2cm 无法测量;软材质障碍物测距误差较大;

当前代码存在缺陷

  1. 主循环使用delay_ms(150)阻塞延时,降低系统实时响应;
  2. 超声波未做多次采样滤波,测距数据波动较大;
  3. 蓝牙仅支持单字符指令,无法连续调速;
  4. 无指令超时看门狗,蓝牙断开后小车持续保持原有动作;
  5. TIM4 中断函数注释,若开启需要保证中断变量time使用 volatile 修饰。

11 功能扩展方案

  1. 移除阻塞 delay,基于 tick_ms 实现非阻塞事件调度,提升实时性;
  2. 增加超声波多次采样均值滤波,稳定测距数据;
  3. 设计多字符通信协议,支持连续调速、角度连续控制;
  4. 添加指令超时检测,蓝牙失联后小车自动停止;
  5. 增加自动避障逻辑:距离<阈值自动转向避让;
  6. 增加蜂鸣器声音报警;
  7. 增加电池电压检测与低电量报警;
  8. 增加按键本地手动控制。

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; //清空计数器 }