1. 项目概述:从零到一的蓝牙遥控小车
最近整理硬盘,翻出来一个几年前带学生做的STM32蓝牙遥控小车项目。当时为了教学演示,从硬件选型、电路设计、代码编写到最后的调试优化,每一步都踩过不少坑,也积累了一整套完整的资料。这个项目麻雀虽小,五脏俱全,涵盖了嵌入式开发中单片机控制、外设驱动、无线通信、电机控制、电源管理等多个核心知识点,非常适合初学者作为第一个综合项目来练手,也适合有一定基础的开发者作为代码框架参考。今天就把这个项目的完整思路、关键代码、硬件连接图以及所有调试心得打包分享出来,目标是让你拿到资料后,能快速复现一辆属于自己的、稳定可靠的蓝牙遥控小车。
这辆小车的核心控制芯片是STM32F103C8T6,也就是常说的“蓝桥杯”或“最小系统板”核心,成本低、资源足。通过手机APP(或电脑串口助手)发送指令,指令经由HC-05或HC-06蓝牙模块传输给STM32,STM32解析指令后,通过L298N电机驱动模块控制两个直流电机的正反转和PWM调速,从而实现小车的前进、后退、左转、右转、停止以及变速功能。整个项目不涉及复杂的算法和操作系统,重点在于打通“感知-决策-控制”的完整链路,理解嵌入式系统如何与外部世界交互。
2. 硬件系统设计与核心器件选型
硬件是整个项目的骨架,选型合理与否直接决定了项目的稳定性、成本和开发难度。我们的设计原则是:在满足功能的前提下,优先选择成熟、通用、文档丰富的模块,以降低学习和调试门槛。
2.1 主控芯片:为什么是STM32F103C8T6?
选择STM32F103C8T6(以下简称F103)几乎是入门STM32的必然选择。它基于ARM Cortex-M3内核,主频72MHz,拥有64KB Flash和20KB RAM,对于小车控制来说绰绰有余。更重要的是,它的生态极其成熟:Keil MDK、STM32CubeMX等开发工具支持完善;网络上资料、例程、问题解答浩如烟海;其外设资源,如多个定时器(用于PWM生成)、USART(用于蓝牙通信)、GPIO等,完全契合本项目需求。
注意:市面上有些F103核心板标注的Flash可能是128KB,但实际只有64KB(后者是C8T6的真实容量)。在编写程序时,如果代码量较大,需要注意不要超过64KB,否则可能无法正常烧录或运行。我遇到过学生代码编译后超过64KB,下载失败,排查了半天才发现是芯片型号选错了。
2.2 无线通信模块:HC-05与HC-06的抉择
蓝牙模块负责无线指令的收发。HC-05和HC-06是最常见的两款,价格都在十元左右。
- HC-05:支持主从一体模式。既可以作为主机主动搜索并连接其他蓝牙设备,也可以作为从机被连接。功能更强大,但AT指令集稍复杂。
- HC-06:仅支持从机模式。只能等待手机等主机设备来连接它。AT指令集简单,对于只需要接收指令的小车项目来说,完全够用。
我们的选择是HC-06。理由很简单:小车只需要被动接收手机APP发来的指令,不需要主动去连接其他设备。HC-06配置更简单,出错的概率更低。购买时务必确认模块的通信电平是3.3V(与STM32 GPIO电平兼容),如果是5V电平的模块,需要加电平转换电路,否则可能损坏STM32芯片。
2.3 电机驱动模块:L298N的经典与替代方案
驱动直流电机需要较大的电流,单片机GPIO无法直接提供,因此必须使用电机驱动模块。L298N是一款双H桥驱动芯片,可以同时驱动两个直流电机,实现正反转和PWM调速,经典且皮实。
接线要点:
- 电源隔离:L298N的驱动电源(VCC)最好单独供电(如7-12V的电池),不要与STM32的逻辑电源(5V或3.3V)混用,否则电机启动时的电流冲击可能导致单片机复位。L298N上有一个5V输出引脚,可以反过来给STM32板子供电,但前提是你的驱动电源电压不要太高(建议不超过12V),且要做好滤波。
- 控制信号:IN1、IN2控制一个电机的方向,ENA接入PWM信号控制其速度。另一个电机对应IN3、IN4和ENB。我们将IN1/2/3/4连接到STM32的普通GPIO,将ENA/ENB连接到STM32的定时器PWM输出引脚。
替代方案:如果觉得L298N体积大、发热明显,可以考虑使用TB6612FNG驱动芯片。它的效率更高,体积更小,但价格稍贵,引脚定义需要重新适配。
2.4 电源系统:稳定供电是基石
小车的“心脏病”多源于电源。我们采用两套供电方案:
- 电机驱动电源:使用一块7.4V(2S)的锂电池组,直接接入L298N的VCC和GND。这块电池负责提供电机运转所需的大电流。
- 控制系统电源:从L298N的5V输出引脚取电,连接到STM32核心板的5V输入引脚。这样,只要电机电池有电,整个控制系统就有电。务必确保L298N的5V输出使能跳线帽插上。
实操心得:强烈建议在STM32的电源入口处增加一个100μF的电解电容和一个0.1μF的瓷片电容,用于滤除电源噪声。电机启停时会在电源线上产生很大的电压毛刺,这个简单的滤波电路能极大提高系统稳定性,避免单片机莫名其妙地重启。
3. 电路连接与PCB布局要点
有了器件,下一步就是按照“最小系统”的原则把它们连接起来。下面给出核心的连接表,并附上关键原理说明。
STM32F103C8T6 核心连接表
| STM32引脚 | 连接目标 | 功能说明 | 备注 |
|---|---|---|---|
| PA9 (USART1_TX) | HC-06 RXD | 串口发送数据到蓝牙模块 | 需串联1k电阻限流 |
| PA10 (USART1_RX) | HC-06 TXD | 串口接收蓝牙模块数据 | 直接连接 |
| PB6 (TIM4_CH1) | L298N ENA | 生成PWM控制电机A速度 | 必须为定时器PWM输出引脚 |
| PB7 (TIM4_CH2) | L298N ENB | 生成PWM控制电机B速度 | 必须为定时器PWM输出引脚 |
| PB8 | L298N IN1 | 控制电机A方向 | 高电平/低电平 |
| PB9 | L298N IN2 | 控制电机A方向 | 与IN1电平相反 |
| PB10 | L298N IN3 | 控制电机B方向 | 高电平/低电平 |
| PB11 | L298N IN4 | 控制电机B方向 | 与IN3电平相反 |
| 5V | L298N 5V Out | 系统电源输入 | 注意电源极性 |
| GND | L298N GND | 共地 | 至关重要!所有模块GND必须相连 |
接线详解与避坑指南:
- 串口连接:STM32的TX应接蓝牙模块的RX,RX接蓝牙模块的TX。这是一个常见的错误点。HC-06的RXD引脚逻辑电平是3.3V,虽然STM32的PA9输出也是3.3V,但为防止意外,我习惯在TX线上串联一个1kΩ的电阻,起到限流保护作用。
- PWM引脚选择:不是所有GPIO都能方便地输出PWM。需要查阅STM32的数据手册或引脚复用功能表,找到哪些引脚对应哪个定时器的哪个通道。我们选择TIM4的CH1和CH2,对应PB6和PB7。在代码中需要正确配置定时器和GPIO的复用功能。
- 共地!共地!共地!:这是所有电子项目中最重要也最容易被忽视的一点。STM32、L298N、HC-06、电池的负极(GND)必须全部连接在一起,形成一个统一的参考零电位。否则通信会乱码,控制会失灵。建议使用面包板或PCB的电源地线层来保证良好的共地。
关于PCB:如果想让小车更紧凑可靠,可以设计一块简单的PCB。布局时要注意:
- 电源路径尽量宽:电机驱动部分的走线要粗,以减少电阻和压降。
- 数字模拟分离:电机驱动(大电流模拟区)和单片机控制(数字区)在布局上尽量远离,地线可以在一点连接(单点接地),避免噪声串扰。
- 添加滤波电容:在每一个芯片的电源引脚附近,放置一个0.1μF的退耦电容,最近距离放置。
4. 软件架构与关键代码解析
软件部分我们采用模块化编程,将不同功能封装成独立的.c和.h文件,结构清晰,便于调试和复用。整个工程主要包含以下几个模块:系统初始化、蓝牙串口通信、电机PWM驱动、指令解析与控制逻辑。
4.1 开发环境搭建与工程创建
我们使用STM32CubeMX进行图形化初始化和Keil MDK-ARM进行代码编写和调试。这是目前最主流的STM32开发组合。
使用CubeMX初始化:
- 选择芯片型号:STM32F103C8Tx。
- SYS:Debug选择
Serial Wire(这是ST-LINK下载调试接口)。 - RCC:HSE选择
Crystal/Ceramic Resonator(如果你的核心板有外部8MHz晶振)。 - USART1:模式选择
Asynchronous,参数默认115200波特率,8位数据,无校验,1停止位。这需要与蓝牙模块的波特率匹配(HC-06默认通常是9600或115200,建议统一为115200)。 - TIM4:选择时钟源为
Internal Clock,通道1和通道2选择PWM Generation CH1/CH2。在下方参数设置中,设置Prescaler(预分频器)和Counter Period(自动重装载值ARR)以产生特定频率的PWM。例如,系统时钟72MHz,预分频器设为71,ARR设为999,则PWM频率为72MHz / (71+1) / (999+1) = 1kHz。这是一个常用的电机PWM频率。 - GPIO:将PB8、PB9、PB10、PB11设置为
GPIO_Output。 - 最后生成代码,选择MDK-ARM V5工具链。
工程结构:
Project/ ├── Core/ │ ├── Inc/ (头文件) │ │ ├── motor.h │ │ ├── bluetooth.h │ │ └── command.h │ └── Src/ (源文件) │ ├── motor.c │ ├── bluetooth.c │ └── command.c ├── Drivers/ ├── MDK-ARM/ └── .mxproject
4.2 电机驱动模块实现
motor.c和motor.h负责封装所有与电机控制相关的函数。
关键代码:PWM初始化与速度控制
// motor.h #ifndef __MOTOR_H #define __MOTOR_H #include "main.h" // 电机编号定义 typedef enum { MOTOR_A = 0, MOTOR_B } Motor_TypeDef; // 电机方向定义 typedef enum { MOTOR_STOP = 0, MOTOR_FORWARD, MOTOR_BACKWARD } Motor_Dir_TypeDef; void Motor_Init(void); // 初始化GPIO和PWM void Motor_SetSpeed(Motor_TypeDef motor, uint16_t speed); // 设置PWM占空比 (0-1000) void Motor_Run(Motor_TypeDef motor, Motor_Dir_TypeDef dir); // 控制电机启停和方向 #endif// motor.c #include "motor.h" #include "tim.h" // CubeMX生成的定时器句柄 // 控制引脚定义,根据你的实际接线修改 #define MOTOR_A_IN1_GPIO_Port GPIOB #define MOTOR_A_IN1_Pin GPIO_PIN_8 // ... 定义IN2, IN3, IN4 // PWM句柄,CubeMX生成 extern TIM_HandleTypeDef htim4; void Motor_Init(void) { // GPIO初始化代码已由CubeMX生成,此处通常无需额外操作 // 启动PWM输出 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); // ENA HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); // ENB // 初始状态:停止 Motor_Run(MOTOR_A, MOTOR_STOP); Motor_Run(MOTOR_B, MOTOR_STOP); } void Motor_SetSpeed(Motor_TypeDef motor, uint16_t speed) { // 限制速度值在0-1000之间(对应ARR=999) if(speed > 1000) speed = 1000; switch(motor) { case MOTOR_A: __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, speed); break; case MOTOR_B: __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, speed); break; default: break; } } void Motor_Run(Motor_TypeDef motor, Motor_Dir_TypeDef dir) { switch(motor) { case MOTOR_A: switch(dir) { case MOTOR_FORWARD: HAL_GPIO_WritePin(MOTOR_A_IN1_GPIO_Port, MOTOR_A_IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(MOTOR_A_IN2_GPIO_Port, MOTOR_A_IN2_Pin, GPIO_PIN_RESET); break; case MOTOR_BACKWARD: HAL_GPIO_WritePin(MOTOR_A_IN1_GPIO_Port, MOTOR_A_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_A_IN2_GPIO_Port, MOTOR_A_IN2_Pin, GPIO_PIN_SET); break; case MOTOR_STOP: default: HAL_GPIO_WritePin(MOTOR_A_IN1_GPIO_Port, MOTOR_A_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_A_IN2_GPIO_Port, MOTOR_A_IN2_Pin, GPIO_PIN_RESET); break; } break; case MOTOR_B: // 类似地控制IN3和IN4... break; } }提示:
__HAL_TIM_SET_COMPARE是HAL库提供的宏,用于快速设置定时器通道的比较寄存器值(即PWM脉冲宽度)。速度值speed越大,占空比越高,电机转速越快。
4.3 蓝牙通信与指令解析模块
bluetooth.c负责通过串口接收数据,command.c负责解析接收到的字符串指令并调用相应的电机控制函数。
关键代码:串口接收与指令解析我们采用中断方式接收串口数据,将接收到的字符存入缓冲区,当检测到行结束符(如\n或\r)时,认为一条指令接收完成,然后进行解析。
// bluetooth.h #define BLUETOOTH_RX_BUFFER_SIZE 64 extern uint8_t bluetooth_rx_buffer[BLUETOOTH_RX_BUFFER_SIZE]; extern volatile uint16_t bluetooth_rx_index; extern volatile uint8_t bluetooth_rx_complete_flag; void Bluetooth_Init(void); void Bluetooth_SendString(char *str);// bluetooth.c #include "bluetooth.h" #include "usart.h" uint8_t bluetooth_rx_buffer[BLUETOOTH_RX_BUFFER_SIZE]; volatile uint16_t bluetooth_rx_index = 0; volatile uint8_t bluetooth_rx_complete_flag = 0; // 重写HAL库的串口接收回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { uint8_t received_char; // 从串口数据寄存器读取一个字节(此部分HAL库已处理,我们通常在中断服务程序中直接处理) // 实际项目中,我们通常在main.c中调用HAL_UART_Receive_IT,并在回调中处理数据 } } // 更实用的做法:在main.c的while(1)循环中,使用HAL_UART_Receive进行轮询或中断接收 // 以下是一个简化的中断接收处理思路(需在main初始化后启动一次中断接收): // HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 然后在回调函数中: // void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // if(huart->Instance == USART1) { // if(rx_byte != '\n' && bluetooth_rx_index < BLUETOOTH_RX_BUFFER_SIZE-1) { // bluetooth_rx_buffer[bluetooth_rx_index++] = rx_byte; // } else { // bluetooth_rx_buffer[bluetooth_rx_index] = '\0'; // 字符串结束符 // bluetooth_rx_complete_flag = 1; // bluetooth_rx_index = 0; // } // HAL_UART_Receive_IT(&huart1, &rx_byte, 1); // 重新开启接收中断 // } // }// command.c #include "command.h" #include "motor.h" #include "string.h" // 使用strcmp等函数 void Command_ParseAndExecute(char *cmd) { if(strcmp(cmd, "FORWARD") == 0) { Motor_Run(MOTOR_A, MOTOR_FORWARD); Motor_Run(MOTOR_B, MOTOR_FORWARD); Motor_SetSpeed(MOTOR_A, 700); // 设置速度值,例如700/1000 Motor_SetSpeed(MOTOR_B, 700); } else if(strcmp(cmd, "BACK") == 0) { Motor_Run(MOTOR_A, MOTOR_BACKWARD); Motor_Run(MOTOR_B, MOTOR_BACKWARD); Motor_SetSpeed(MOTOR_A, 700); Motor_SetSpeed(MOTOR_B, 700); } else if(strcmp(cmd, "LEFT") == 0) { // 左转:右轮前进,左轮后退或停止 Motor_Run(MOTOR_A, MOTOR_BACKWARD); // 假设MOTOR_A是左轮 Motor_Run(MOTOR_B, MOTOR_FORWARD); // MOTOR_B是右轮 Motor_SetSpeed(MOTOR_A, 500); Motor_SetSpeed(MOTOR_B, 500); } else if(strcmp(cmd, "RIGHT") == 0) { // 右转:左轮前进,右轮后退或停止 Motor_Run(MOTOR_A, MOTOR_FORWARD); Motor_Run(MOTOR_B, MOTOR_BACKWARD); Motor_SetSpeed(MOTOR_A, 500); Motor_SetSpeed(MOTOR_B, 500); } else if(strcmp(cmd, "STOP") == 0) { Motor_Run(MOTOR_A, MOTOR_STOP); Motor_Run(MOTOR_B, MOTOR_STOP); } else if(strncmp(cmd, "SPEED:", 6) == 0) { // 解析速度指令,如 "SPEED:800" uint16_t speed = atoi(cmd + 6); Motor_SetSpeed(MOTOR_A, speed); Motor_SetSpeed(MOTOR_B, speed); } // 可以添加更多指令... }主循环逻辑(main.c中的关键部分)
// main.c #include "bluetooth.h" #include "command.h" uint8_t rx_byte; // 用于中断接收的变量 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM4_Init(); Motor_Init(); // 启动串口接收中断,每次接收1个字节 HAL_UART_Receive_IT(&huart1, &rx_byte, 1); while (1) { // 检查指令接收完成标志 if(bluetooth_rx_complete_flag) { bluetooth_rx_complete_flag = 0; // 解析并执行指令 Command_ParseAndExecute((char*)bluetooth_rx_buffer); } // 这里可以添加其他任务,如传感器读取 HAL_Delay(10); // 短暂延时,防止CPU空转过热 } } // 串口接收中断回调函数(放在main.c末尾或单独文件) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { static uint16_t index = 0; if(rx_byte != '\n' && index < BLUETOOTH_RX_BUFFER_SIZE-1) { bluetooth_rx_buffer[index++] = rx_byte; } else { bluetooth_rx_buffer[index] = '\0'; bluetooth_rx_complete_flag = 1; index = 0; } // 重新启动中断接收,等待下一个字节 HAL_UART_Receive_IT(&huart1, &rx_byte, 1); } }5. 手机APP设计与上下位机通信协议
为了让小车动起来,我们需要一个发送指令的终端。最简单的方法是使用手机上的蓝牙串口APP,如“蓝牙串口助手”、“Serial Bluetooth Terminal”等。这些APP可以连接HC-06模块,并发送字符串。
自定义简单通信协议: 为了控制更灵活,我们设计一个简单的文本协议。手机APP发送特定的字符串,STM32解析后执行对应动作。
F: 前进B: 后退L: 左转R: 右转S: 停止0~9: 速度等级(0最慢,9最快),APP端可以做成滑块或按钮。
在STM32的Command_ParseAndExecute函数中,相应地修改判断条件即可。例如:
if(cmd[0] == 'F') { // 单字符指令 // 前进操作 Motor_SetSpeed(MOTOR_A, speed_level * 100); // speed_level 由0-9映射到PWM值 }进阶:自己开发简易APP: 如果你会一点Android开发,可以使用Android Studio和蓝牙API开发一个专属遥控器界面。核心步骤是:
- 获取蓝牙适配器,搜索设备。
- 配对并连接名为“HC-06”的设备(HC-06的默认名称)。
- 通过蓝牙Socket获取输入输出流。
- 在按钮点击事件中,向输出流写入对应的指令字符串(如
"F\n")。
这样体验更好,界面可以自定义油门、方向盘等控件。
6. 系统调试与问题排查实录
调试是项目开发中耗时最长的部分。下面记录几个典型问题及其解决方法。
6.1 蓝牙模块无法连接或通信乱码
- 现象:手机搜不到蓝牙模块,或连接后发送指令小车无反应,串口助手显示乱码。
- 排查步骤:
- 供电检查:首先用万用表测量蓝牙模块VCC电压是否为3.3V或5V(视模块而定)。电压不足会导致模块无法正常工作。
- 接线检查:确认TX、RX是否交叉连接,GND是否共地。
- 波特率匹配:这是最常见的问题。HC-06默认波特率可能是9600或115200。使用USB转TTL模块,将蓝牙模块的TX接USB转TTL的RX,然后打开串口助手,尝试不同的波特率,给蓝牙模块通电,看是否能收到“OK”或类似就绪信息。也可以在STM32代码中,将串口初始化波特率改为9600试试。
- 模块状态灯:正常未连接时,LED慢闪;连接后,LED快闪或常亮。根据灯的状态可以初步判断。
- AT指令测试:有些模块需要进入AT模式设置参数(如名称、配对码)。具体操作是:在模块未连接状态下,按住模块上的按键(如果有)再上电,或将其KEY引脚接高电平再上电,此时LED慢闪进入AT模式,波特率固定为38400,用串口助手发送
AT(回车换行)应返回OK。
6.2 电机不转或只单向转动
- 现象:上电后电机不转,或只能一个方向转,PWM调速无效。
- 排查步骤:
- 电源功率:首先检查电机驱动电源(电池)电压是否足够,电量是否充足。电机启动瞬间电流很大,劣质电池或容量太小的电池会瞬间被拉低电压,导致系统重启。可以尝试单独用稳压电源给驱动部分供电测试。
- L298N使能:检查L298N的ENA和ENB跳线帽是否拔掉?如果跳线帽插着,ENA/ENB引脚的电平控制无效,电机会全速转动。我们的方案是通过PWM控制,所以跳线帽需要拔掉。
- 控制信号电平:用万用表或逻辑分析仪测量STM32输出到IN1/2/3/4和ENA/ENB的引脚电平。在发送控制指令时,电平是否按预期变化?如果ENA/ENB没有PWM波形,检查定时器PWM输出配置是否正确,是否调用了
HAL_TIM_PWM_Start。 - 电机接线:交换电机的两根线,可以测试电机本身是否完好。
- 软件逻辑:检查
Motor_Run函数中的GPIO控制逻辑是否正确。例如,前进时是否是一个GPIO置高,另一个置低?停止时是否两个都置低?
6.3 小车运行不稳定,时好时坏
- 现象:小车偶尔不受控制,或突然停止,单片机有时会重启。
- 排查步骤:
- 电源干扰:这是首要怀疑对象。确保按照前面所述,在STM32的电源入口处增加了滤波电容。电机电源线和控制板电源线尽量分开走。
- 机械结构:检查车轮是否安装牢固,底盘是否平衡,电机轴与轮子连接是否打滑。机械卡顿会导致电机堵转,电流急剧上升,可能触发驱动模块保护或拉低电压。
- 程序逻辑:在控制逻辑中加入“看门狗”(IWDG)。STM32内置独立看门狗,当程序跑飞或陷入死循环时,看门狗超时复位单片机。在CubeMX中启用IWDG,并在主循环中定时“喂狗”。
- 指令缓冲溢出:如果蓝牙指令发送过快,而STM32解析速度慢,可能导致串口接收缓冲区溢出。可以增加缓冲区大小,或简化指令解析逻辑。在串口接收中断中,加入缓冲区满的判断,防止写越界。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 手机搜不到蓝牙 | 模块未供电;模块损坏;手机蓝牙问题 | 检查模块VCC/GND;用其他手机测试;模块接USB-TTL测试AT指令 |
| 蓝牙已连接但无指令响应 | 串口线接反;波特率不匹配;代码未处理接收数据 | 检查TX/RX交叉;统一波特率(常用115200);检查串口中断是否开启,缓冲区是否正常 |
| 电机完全不转 | 电机电源未接;L298N使能跳线帽未拔;控制线未接 | 检查电机电源电压;拔掉ENA/ENB跳线帽;用万用表测控制引脚电平 |
| 电机只朝一个方向转 | 控制方向的一根信号线断路或程序逻辑错误 | 检查IN1/IN2(或IN3/IN4)的电平是否一高一低;调试Motor_Run函数 |
| PWM调速无效 | PWM未输出;ENA/ENB跳线帽未拔;PWM频率不对 | 用示波器或LED测PWM引脚;拔掉跳线帽;检查CubeMX中定时器PWM配置 |
| 小车跑起来一卡一卡 | 电源容量不足;机械阻力大;程序中有大延时 | 更换大容量电池;润滑机械结构;检查主循环中是否有HAL_Delay(500)这类长延时 |
| 单片机频繁重启 | 电机启动电流冲击;电源纹波大;程序跑飞 | 加强电源滤波电容;电机电源与控制电源分离;启用看门狗 |
7. 项目优化与功能扩展思路
基础功能实现后,你可以在此基础上添加更多功能,让小车变得更智能。
- 增加超声波避障:添加HC-SR04超声波模块,测量前方障碍物距离。当距离小于阈值时,自动停车或转向。这需要学习定时器输入捕获功能来测量高电平时间。
- 增加红外巡线:使用TCRT5000等红外传感器,让小车沿着黑色轨迹线自动行驶。这需要学习ADC采集或数字IO读取,实现PID控制算法则更高级。
- 改用WiFi或无线串口控制:将HC-06换成ESP8266或ESP32模块,实现通过WiFi在网页或手机APP上控制,控制距离更远,且可传输视频流(需额外摄像头)。
- 增加姿态传感器:接入MPU6050陀螺仪和加速度计,可以实现手势控制(通过手机APP发送传感器数据)或小车自身姿态监测。
- 升级电机与驱动:使用带编码器的直流电机(减速电机),搭配TB6612驱动,可以实现精确的转速测量和闭环控制,让小车速度更稳定,走直线更直。
- 设计更优雅的控制协议:使用二进制协议或更紧凑的文本协议(如
F,800表示前进,速度800),减少数据传输量,提高响应速度。
这个项目的所有源码、原理图、PCB文件(如有)、手机APP源码以及详细的文档,我已经整理打包。在复现过程中,最关键的是理解每个模块的工作原理和它们之间如何协同,而不是简单地照搬连线图和代码。遇到问题时,善用调试工具(万用表、逻辑分析仪、串口打印)和网络搜索,大部分坑前人都踩过。