用STM32和HC-05蓝牙模块,手把手教你给自平衡小车做个手机遥控器(附完整代码)

张开发
2026/4/18 11:03:49 15 分钟阅读

分享文章

用STM32和HC-05蓝牙模块,手把手教你给自平衡小车做个手机遥控器(附完整代码)
STM32与HC-05蓝牙模块实战打造高响应自平衡小车遥控系统在智能硬件开发领域自平衡小车一直是检验嵌入式系统综合能力的经典项目。当基础平衡功能实现后如何为其添加稳定可靠的无线控制能力成为进阶挑战。本文将聚焦HC-05蓝牙模块与STM32的深度整合从硬件配置到软件架构构建一套低延迟、高可靠的手机遥控方案。1. 蓝牙遥控系统架构设计一套完整的蓝牙遥控系统需要硬件、通信协议和应用层三个层面的协同工作。HC-05作为经典蓝牙2.0模块其最大优势在于稳定的串口透传功能和成熟的AT指令配置体系。核心组件交互流程手机APP → 蓝牙射频信号 → HC-05模块 → 串口数据 → STM32 → 电机驱动关键设计考量包括通信速率选择9600bps在传输效率与稳定性间取得平衡协议设计单字节指令可减少解析开销控制响应需与原有平衡控制环保持时序同步实际测试表明从手机发送指令到电机响应理想状态下延迟应控制在50ms以内。这要求串口中断处理必须高效。2. HC-05模块深度配置指南市面上的HC-05模块通常有两种工作模式自动连接模式和AT指令模式。要实现可靠控制必须进行以下关键配置# 进入AT指令模式按住模块按钮上电 ATNAMEBalanceCar # 设置设备名称 ATPSWD1234 # 设置配对密码 ATUART9600,0,0 # 设置波特率无校验位 ATROLE0 # 设为从机模式常见问题排查表现象可能原因解决方案无法进入AT模式按键时机不对上电前按住按键持续5秒手机搜索不到设备模块未供电检查VCC电压(3.3-5V)连接频繁断开波特率不匹配确认两端波特率一致数据乱码接线错误检查TX/RX交叉连接配置完成后建议使用串口助手工具发送测试指令验证模块基本功能正常后再进行代码开发。3. STM32端通信实现STM32需要通过USART外设与HC-05建立稳定数据通道。以下是经过优化的串口初始化代码// usart3.h #define BLUETOOTH_BUF_LEN 32 extern volatile uint8_t bluetooth_rx_buf[BLUETOOTH_BUF_LEN]; extern volatile uint8_t bluetooth_rx_flag; // usart3.c void USART3_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStruct {0}; USART_InitTypeDef USART_InitStruct {0}; // 启用时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); // 配置TX(PB10)为推挽输出 GPIO_InitStruct.GPIO_Pin GPIO_Pin_10; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStruct); // 配置RX(PB11)为浮空输入 GPIO_InitStruct.GPIO_Pin GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStruct); // USART参数配置 USART_InitStruct.USART_BaudRate baudrate; USART_InitStruct.USART_WordLength USART_WordLength_8b; USART_InitStruct.USART_StopBits USART_StopBits_1; USART_InitStruct.USART_Parity USART_Parity_No; USART_InitStruct.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART3, USART_InitStruct); // 启用接收中断 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); NVIC_EnableIRQ(USART3_IRQn); USART_Cmd(USART3, ENABLE); }中断服务程序中实现状态机解析可以提高系统可靠性void USART3_IRQHandler(void) { static uint8_t state 0; static uint8_t cmd_buffer[3]; static uint8_t index 0; if(USART_GetITStatus(USART3, USART_IT_RXNE) ! RESET) { uint8_t data USART_ReceiveData(USART3); switch(state) { case 0: // 等待帧头 if(data 0xAA) state 1; break; case 1: // 接收指令类型 cmd_buffer[0] data; state 2; break; case 2: // 接收数据 cmd_buffer[1] data; state 3; break; case 3: // 验证帧尾 if(data 0x55) { process_bluetooth_cmd(cmd_buffer[0], cmd_buffer[1]); } state 0; break; } USART_ClearITPendingBit(USART3, USART_IT_RXNE); } }4. 控制指令与平衡系统的融合将蓝牙指令无缝整合到现有平衡控制环是关键挑战。建议采用分层设计指令解析层将原始蓝牙数据转换为标准控制命令速度规划层根据指令生成目标速度曲线控制执行层融合平衡控制与速度控制// 控制指令定义 typedef enum { CMD_STOP 0x00, CMD_FORWARD 0x01, CMD_BACKWARD 0x02, CMD_LEFT 0x03, CMD_RIGHT 0x04 } RemoteCommand; // 全局控制变量 volatile struct { float target_speed; float turn_speed; uint8_t updated; } remote_ctrl {0}; void process_bluetooth_cmd(uint8_t cmd, uint8_t speed) { switch(cmd) { case CMD_FORWARD: remote_ctrl.target_speed speed * 0.1f; // 归一化 break; case CMD_BACKWARD: remote_ctrl.target_speed -speed * 0.1f; break; case CMD_LEFT: remote_ctrl.turn_speed speed * 0.05f; break; case CMD_RIGHT: remote_ctrl.turn_speed -speed * 0.05f; break; default: remote_ctrl.target_speed 0; remote_ctrl.turn_speed 0; } remote_ctrl.updated 1; }在平衡控制循环中整合遥控指令void balance_control_loop(void) { // 获取传感器数据 float roll get_mpu_roll_angle(); float gyro_x get_mpu_gyro_x(); // 处理遥控指令 if(remote_ctrl.updated) { set_target_velocity(remote_ctrl.target_speed); set_turn_rate(remote_ctrl.turn_speed); remote_ctrl.updated 0; } // 计算控制量 float vertical_out vertical_pid(roll, gyro_x); float velocity_out velocity_pid(get_encoder_speed()); float turn_out turn_pid(get_gyro_z()); // 电机输出 int16_t motor_left (int16_t)(vertical_out velocity_out - turn_out); int16_t motor_right (int16_t)(vertical_out velocity_out turn_out); set_motor_pwm(motor_left, motor_right); }5. 手机端APP开发与优化虽然市面上有现成的蓝牙串口助手APP但定制化开发能获得更好的控制体验。推荐使用MIT App Inventor快速构建控制界面关键设计要点虚拟摇杆控制将XY坐标转换为速度指令按钮防抖处理避免误触发指令压缩将多个参数打包为单帧发送示例指令格式帧头(1B) | 指令类型(1B) | 数据(1B) | 帧尾(1B) 0xAA | 0x01 | 0x50 | 0x55对于Android开发者核心蓝牙连接代码段// 蓝牙连接 BluetoothAdapter bluetoothAdapter BluetoothAdapter.getDefaultAdapter(); BluetoothDevice device bluetoothAdapter.getRemoteDevice(00:12:34:56:78:9A); BluetoothSocket socket device.createRfcommSocketToServiceRecord(UUID.fromString(00001101-0000-1000-8000-00805F9B34FB)); socket.connect(); // 发送指令 OutputStream outputStream socket.getOutputStream(); byte[] command { (byte)0xAA, 0x01, 0x50, (byte)0x55 }; outputStream.write(command);6. 系统调试与性能优化在实际部署中以下几个调试技巧能显著提升系统可靠性串口日志输出同时连接调试串口和蓝牙串口printf([BT] Received cmd: %02X, speed: %d\n, cmd, speed);无线信号质量监测// 定期发送心跳包 if(heartbeat_timer 1000) { USART3_Send_Data(0xAA); heartbeat_timer 0; }控制响应测试使用示波器测量从指令发送到电机响应的延迟逐步增加发送频率测试系统稳定性边界性能优化对比表优化措施延迟(ms)稳定性基础实现120易丢包中断优化80偶尔丢包协议简化60稳定DMA传输45非常稳定在资源允许的情况下启用DMA传输可以进一步降低CPU负载// 启用USART3 DMA传输 DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel2); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)USART3-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)tx_buffer; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize buffer_size; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel2, DMA_InitStructure); USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);7. 进阶功能扩展基础遥控功能稳定后可以考虑添加以下增强功能状态反馈将小车倾角、电池电压等数据回传手机显示使用JSON格式封装多参数数据自动重连机制void check_bluetooth_connection(void) { static uint32_t last_active 0; if(bluetooth_rx_flag) { last_active HAL_GetTick(); bluetooth_rx_flag 0; } else if(HAL_GetTick() - last_active 5000) { // 尝试重新初始化蓝牙模块 USART3_DeInit(); HC05_Reset(); USART3_Init(9600); } }多模式切换通过特定指令切换手动/自动模式在自动模式下实现巡线或避障功能OTA升级通过蓝牙传输固件更新包使用差分升级减少数据传输量在开发过程中遇到最棘手的问题是蓝牙模块在某些Android设备上连接不稳定。后来发现是手机蓝牙堆栈的省电策略导致通过在APP中定期发送保持连接的数据包解决了这个问题。另一个经验是对于控制类应用通信协议越简单越好复杂的校验机制反而会增加系统不确定性。

更多文章