1. 项目概述与核心设计思路
这个基于STM32F103C8的二驱蓝牙小车项目,是我在嵌入式开发学习过程中完成的一个综合性实践案例。它完美融合了单片机控制、电机驱动、无线通信和人机交互四大核心模块,特别适合想要从理论学习过渡到实际开发的嵌入式爱好者。
项目的核心设计思路是:通过蓝牙模块接收来自手机APP的控制指令,STM32解析指令后生成对应的PWM信号和方向控制信号,驱动两个直流电机实现差速转向。OLED屏幕则实时反馈小车的运行状态,形成一个完整的控制闭环。这种设计模式在实际工业控制中非常常见,比如AGV小车、服务机器人等场景都采用了类似架构。
选择STM32F103C8作为主控有几个关键考量:首先,它具备丰富的外设资源,包括多个定时器(用于PWM生成)、USART接口(连接蓝牙模块)和充足的GPIO;其次,作为Cortex-M3内核的经典款,其72MHz主频完全能满足实时控制需求;最重要的是,它的性价比极高,开发板价格通常在20元以内,非常适合学生和爱好者使用。
2. 硬件配置详解与选型建议
2.1 核心硬件清单与参数
完整的硬件系统需要以下组件协同工作:
-
主控单元:
- STM32F103C8T6最小系统板(核心参数:72MHz主频,64KB Flash,20KB RAM)
- 建议额外准备一个ST-Link V2下载器,用于程序烧录和调试
-
动力系统:
- TT减速电机(工作电压3-6V,减速比1:48)
- TB6612FNG双路电机驱动模块(最大电流1.2A,支持PWM调速)
- 18650锂电池两节(配电池盒,输出7.4V)
-
通信模块:
- HC-05蓝牙串口模块(经典蓝牙,支持AT指令配置)
- 注意:HC-06只能作为从机,建议选择HC-05主从一体模块
-
人机交互:
- 0.96寸OLED屏幕(SSD1306驱动,I2C接口)
- 可选:增加几个LED指示灯用于状态显示
-
辅助部件:
- 面包板及杜邦线(建议使用母对母线,连接更稳固)
- 轻量化车架(可用亚克力板自制或购买现成底盘)
- 电源模块(AMS1117-3.3V用于MCU供电)
关键提示:电机驱动模块的选择直接影响系统性能。L298N虽然便宜但效率较低(约70%),TB6612效率可达90%以上且体积更小。如果预算允许,强烈推荐TB6612。
2.2 硬件连接原理与注意事项
正确的硬件连接是项目成功的基础,以下是各模块的接线要点:
-
电源系统连接:
- 锂电池正极接TB6612的VM引脚(电机电源)
- 锂电池正极同时接AMS1117-5V的输入端
- AMS1117-5V输出给TB6612的VCC(逻辑电源)
- AMS1117-3.3V输出给STM32和蓝牙模块
-
电机驱动连接:
- 电机1:PWMA→TIM2_CH1(PA0),AIN1→PA4,AIN2→PA5
- 电机2:PWMB→TIM2_CH2(PA1),BIN1→PB10,BIN2→PB11
- STM32的GND与TB6612的GND必须共地
-
蓝牙模块连接:
- HC-05的TXD接STM32的PA10(RX)
- HC-05的RXD接STM32的PA9(TX)
- VCC接3.3V,GND共地
-
OLED屏幕连接:
- SCL→PB6,SDA→PB7(I2C1接口)
- 注意:部分OLED模块需要上拉电阻
实际接线时常见问题:
- 电机不转:首先检查TB6612的STBY引脚是否接高电平
- 蓝牙无法连接:确认模块已进入配对模式(LED快闪)
- OLED无显示:检查I2C地址是否正确(通常0x78或0x7A)
3. 软件架构设计与关键代码解析
3.1 工程目录结构与模块划分
采用模块化设计的思想,整个项目分为以下几个核心模块:
code复制Bluetooth_Car/
├── CMSIS/ // Cortex核心支持文件
├── STM32F10x_StdPeriph_Driver/ // 标准外设库
├── User/
│ ├── main.c // 主控制逻辑
│ ├── motor.c/h // 电机驱动封装
│ ├── pwm.c/h // PWM生成模块
│ ├── serial.c/h // 串口通信处理
│ ├── oled.c/h // OLED显示驱动
│ └── delay.c/h // 精确延时函数
└── Project/ // Keil工程文件
这种结构清晰划分了硬件抽象层(HAL)和应用逻辑层,方便后续功能扩展。例如要增加红外遥控功能,只需新增一个ir.c/h模块即可。
3.2 主程序逻辑流程
主程序采用经典的事件循环架构,核心流程如下:
-
硬件初始化:
- 系统时钟配置(72MHz)
- GPIO初始化(电机控制引脚、LED指示灯等)
- PWM定时器配置(TIM2,频率1kHz)
- USART1初始化(波特率9600)
- OLED初始化(I2C接口)
-
主循环逻辑:
c复制while(1) {
if(bluetooth_cmd_received) {
process_command(); // 解析蓝牙指令
update_motor_speed(); // 更新电机PWM
refresh_display(); // 更新OLED显示
clear_command_flag();
}
delay_ms(10); // 防止CPU占用率过高
}
- 中断处理:
- USART1中断:接收蓝牙数据
- 定时器中断:用于精确延时(可选)
3.3 电机控制核心代码实现
电机驱动采用典型的H桥控制方式,关键函数如下:
c复制// 设置电机速度和方向
void Motor_Set(uint8_t motor_id, int8_t speed) {
GPIO_TypeDef* port;
uint16_t pin1, pin2;
// 确定电机控制引脚
if(motor_id == MOTOR_LEFT) {
port = GPIOA;
pin1 = GPIO_Pin_4;
pin2 = GPIO_Pin_5;
} else {
port = GPIOB;
pin1 = GPIO_Pin_10;
pin2 = GPIO_Pin_11;
}
// 方向控制
if(speed > 0) { // 正转
GPIO_SetBits(port, pin1);
GPIO_ResetBits(port, pin2);
} else if(speed < 0) { // 反转
GPIO_ResetBits(port, pin1);
GPIO_SetBits(port, pin2);
} else { // 刹车
GPIO_ResetBits(port, pin1 | pin2);
}
// PWM调速
PWM_Set(motor_id, abs(speed));
}
经验之谈:实际测试中发现,当PWM占空比低于30%时,电机可能出现启动困难。解决方法是在代码中加入最小速度限制,或者采用分段式PWM策略,启动时短暂提高占空比。
3.4 蓝牙通信协议设计
为了确保通信可靠性,设计了一个简单的数据帧格式:
code复制[帧头][数据长度][指令类型][数据][校验和]
对应的解析代码示例:
c复制void USART1_IRQHandler(void) {
static uint8_t rx_buffer[10], index = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t data = USART_ReceiveData(USART1);
// 简单协议处理
if(data == 0xAA && index == 0) { // 帧头
rx_buffer[index++] = data;
} else if(index > 0 && index < sizeof(rx_buffer)) {
rx_buffer[index++] = data;
// 收到完整帧
if(index >= 3 && index == rx_buffer[1] + 2) {
if(check_sum(rx_buffer)) {
process_frame(rx_buffer);
}
index = 0;
}
} else {
index = 0;
}
}
}
实际项目中,可以使用开源库如protobuf或自定义更复杂的协议。但对于初学者,这种简单帧结构已经足够可靠。
4. 系统调试与性能优化
4.1 开发环境搭建要点
推荐使用Keil MDK作为开发环境,配置时需注意:
- 器件选择:STMicroelectronics → STM32F103C8
- 调试配置:使用ST-Link调试器,勾选"Reset and Run"
- 编译器优化:开发阶段使用-O0,发布时改为-O1
- 包含路径:正确添加标准外设库路径
常见编译问题解决:
- 提示"..\OBJ\XXX.axf: Error: L6218E" → 检查是否遗漏了某些.c文件的编译
- 程序无法下载 → 检查BOOT0/BOOT1引脚状态(都应接地)
- 调试时变量值不更新 → 在Watch窗口添加变量后右键选择"Decimal"显示
4.2 电机控制参数调优
通过实验测试,发现以下参数组合效果最佳:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| PWM频率 | 1kHz | 过低会导致电机啸叫 |
| 死区时间 | 500ns | 防止H桥直通 |
| 最小启动占空比 | 30% | 确保电机可靠启动 |
| 加速度限制 | 10%/100ms | 避免电流冲击 |
调优方法:
- 先用固定占空比测试电机响应
- 逐步调整PWM频率,选择噪音最小的点
- 测试不同负载下的最小启动占空比
- 最后加入加速度限制,使启停更平滑
4.3 蓝牙通信稳定性提升
在实际环境中,蓝牙通信可能受到以下干扰:
- 2.4GHz频段拥挤(WiFi、其他蓝牙设备等)
- 电源噪声导致模块工作不稳定
- 金属外壳屏蔽信号
解决方法:
- 在代码中加入重发机制:
c复制#define MAX_RETRY 3
void send_bt_command(uint8_t cmd) {
uint8_t retry = 0;
while(retry < MAX_RETRY) {
USART_SendData(USART1, cmd);
if(wait_ack(500)) break;
retry++;
}
if(retry == MAX_RETRY) {
// 触发错误处理
}
}
- 硬件上:
- 蓝牙模块电源并联100μF电容
- 尽量延长天线部分(不要被金属遮挡)
- 避免与WiFi路由器放在同一位置
4.4 系统功耗优化技巧
对于电池供电的小车,功耗优化尤为重要:
-
软件优化:
- 空闲时进入STOP模式
- 降低主频(通过修改时钟配置)
- 关闭不用的外设时钟
-
硬件优化:
- 选择高效率DC-DC转换器(如TPS63020)
- 增加电源开关,完全断电
- 使用低功耗蓝牙模块(如HC-08)
实测数据对比:
- 未优化:待机电流约50mA
- 软件优化后:待机电流约15mA
- 硬件+软件优化:待机电流可降至5mA以下
5. 项目扩展与进阶方向
5.1 功能扩展建议
基础项目完成后,可以考虑以下扩展:
-
增加传感器:
- 超声波模块实现避障
- 陀螺仪实现姿态控制
- 红外传感器实现巡线
-
改进控制方式:
- 手机APP增加重力感应控制
- 加入语音控制模块
- 实现PC端可视化控制
-
增强功能:
- 增加摄像头实现图传
- 添加机械臂扩展
- 实现自动充电功能
5.2 进阶开发建议
对于想深入学习的开发者,推荐以下方向:
-
改用HAL库开发:
- 学习STM32CubeMX工具
- 比较标准库与HAL库的差异
- 体验寄存器级编程
-
引入RTOS:
- 移植FreeRTOS
- 将不同功能划分为独立任务
- 学习任务间通信机制
-
开发自定义协议:
- 设计更高效的通信协议
- 加入数据加密功能
- 实现OTA无线升级
5.3 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 电机单边转动 | 某相MOS管损坏 | 更换电机驱动模块 |
| 蓝牙连接频繁断开 | 电源干扰 | 增加电源滤波电容 |
| OLED显示乱码 | I2C地址不匹配 | 修改初始化参数 |
| 小车跑偏 | 电机特性不一致 | 软件中加入校准参数 |
| 按键响应延迟 | 主循环处理时间过长 | 优化代码结构或引入中断 |
| 电池续航时间短 | LDO效率低 | 更换DC-DC转换器 |
5.4 项目心得与建议
经过这个项目的完整开发,我总结了以下几点经验:
-
模块化测试很重要:不要等全部硬件组装完再测试,应该分模块验证(先调通蓝牙通信,再测试电机驱动等)
-
电源设计是基础:很多奇怪的问题都源于电源不稳定,建议使用示波器观察各电源节点的纹波
-
版本控制很有必要:即使是个人项目,也建议使用Git管理代码,方便回溯和版本管理
-
文档记录不能少:在开发过程中及时记录接线图、参数配置等,后期调试时会节省大量时间
对于初学者,我的建议是从最简系统开始:先让一个电机转起来,再逐步添加蓝牙控制、OLED显示等功能。遇到问题时,善用STM32的调试功能,通过断点和变量观察往往能快速定位问题根源。
