2、STM32基础回顾:GPIO、定时器、PWM、串口等外设的快速复习
在进入蓝牙机器人控制开发之前,我们需要先建立一个坚实的硬件操作基础。STM32作为机器人控制的核心MCU,其GPIO、定时器、PWM和串口四大外设构成了蓝牙控制链路中的关键环节:GPIO负责读取按键状态和驱动LED指示,定时器为系统提供精确的时间基准,PWM控制电机转速,串口则承担与蓝牙模块的双向数据通信。本章将围绕这四大外设,从寄存器级原理到HAL库实战操作进行系统回顾,为后续的指令解析、状态回传和模式切换功能打下扎实的硬件基础。
上图展示了STM32蓝牙控制系统的硬件外设链路关系。MCU通过GPIO感知外部按键输入并驱动LED状态指示,定时器提供时基和编码器测速功能,PWM通道输出电机控制信号,USART串口则与蓝牙模块进行双向数据交换。这四个外设并非孤立工作,而是通过中断系统和时钟树紧密协作,共同构成一个完整的机器人控制闭环。
2.1 GPIO:数字世界的“手”与“眼”
核心原理
GPIO(General Purpose Input/Output)是STM32最基础的外设,每个引脚都可以通过软件配置为输入或输出模式。在蓝牙机器人场景中,GPIO承担三类任务:读取按键/拨码开关状态(输入模式)、驱动LED指示灯(输出模式)、模拟I2C或SPI时序(复用模式)。STM32的GPIO内部结构包含施密特触发器、上拉/下拉电阻、输出驱动电路和复用功能选择器,理解这些结构有助于正确配置引脚。
配置与操作流程
- 使能GPIO时钟:通过RCC寄存器开启对应端口时钟,例如
__HAL_RCC_GPIOA_CLK_ENABLE()。 - 初始化引脚:使用
HAL_GPIO_Init()设置引脚号、模式(输入/输出/复用/模拟)、速度、上下拉。 - 读写操作:输出用
HAL_GPIO_WritePin()或HAL_GPIO_TogglePin(),输入用HAL_GPIO_ReadPin()。 - 外部中断:配置EXTI线,使能NVIC中断,在回调函数中处理按键事件。
实战要点:在机器人控制中,按键输入建议启用内部上拉电阻,按键另一端接地,这样按键按下时读到低电平,释放时读到高电平,逻辑清晰且无需外部上拉。LED输出建议采用推挽模式,灌电流驱动能力更强。
案例拆解:按键控制LED模式切换
假设机器人有两个按键:KEY1(PA0)用于切换控制模式(手动/自动),KEY2(PA1)用于急停。LED1(PC13)指示当前模式。初始化代码如下:
// 使能时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
// 按键输入配置
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// LED输出配置
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
在主循环中检测按键状态,使用软件消抖(延时20ms后再次确认),实现模式切换和急停功能。这个案例展示了GPIO输入输出配合的基本范式,后续蓝牙指令中的模式切换命令也会复用此逻辑。
常见误区:初学者容易忘记使能GPIO时钟,导致引脚无响应。另外,STM32大部分引脚在复位后处于浮空输入状态,如果直接读取未配置的引脚,可能得到不确定的电平值。务必在初始化时明确设置上下拉。
2.2 定时器:精确的时间管理者
核心原理
STM32定时器分为高级定时器(TIM1/8)、通用定时器(TIM2/3/4/5)和基本定时器(TIM6/7)。在蓝牙机器人中,定时器主要实现三个功能:产生周期性中断作为系统时基(如1ms调度)、编码器模式读取电机转速、输入捕获测量PWM信号脉宽。定时器的核心是16位(或32位)自动重装载计数器,时钟源经过预分频器后驱动计数器递增,当计数值达到自动重装载值时产生更新事件。
配置流程
| 步骤 | 操作 | HAL库函数 | 说明 |
|---|---|---|---|
| 1 | 使能定时器时钟 | __HAL_RCC_TIM2_CLK_ENABLE() | 根据定时器实例选择 |
| 2 | 设置时基参数 | HAL_TIM_Base_Init() | 预分频、计数周期、计数模式 |
| 3 | 配置中断 | HAL_TIM_Base_Start_IT() | 使能更新中断 |
| 4 | 编写回调 | HAL_TIM_PeriodElapsedCallback() | 在中断回调中处理周期任务 |
例如,要产生1ms中断,APB1时钟为36MHz,预分频器设为36-1=35,自动重装载值设为1000-1=999,则中断频率为36MHz/36/1000=1kHz。
案例拆解:编码器模式测速
在轮式机器人中,使用TIM3的编码器模式读取霍尔编码器输出。将编码器A相接入TIM3_CH1(PA6),B相接入TIM3_CH2(PA7),配置为编码器模式,计数器值即反映电机转动角度。通过定时器周期读取计数器值并计算差值,即可得到转速。
// 编码器模式初始化
TIM_Encoder_InitTypeDef EncoderConfig = {0};
EncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12;
EncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
EncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
HAL_TIM_Encoder_Init(&htim3, &EncoderConfig);
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
每10ms读取一次 __HAL_TIM_GET_COUNTER(&htim3),与上次值相减得到增量,乘以比例系数即可换算为实际速度。这种测速方式在蓝牙遥控中用于闭环速度控制,确保左右轮转速一致。
经验提醒:定时器预分频和重装载值的计算要特别注意时钟树。APB1上的定时器时钟是APB1的2倍(当APB1分频系数不为1时),APB2同理。使用CubeMX配置时,系统会自动计算,但手动配置时容易出错。
2.3 PWM:电机速度的“油门”
核心原理
PWM(Pulse Width Modulation)通过改变输出方波的占空比来控制电机平均电压,从而调节转速。STM32的定时器可以产生多路PWM信号,每路PWM由比较寄存器CCR决定占空比。当计数器值小于CCR时输出高电平,大于CCR时输出低电平。PWM频率通常选择10kHz~20kHz,既能避开人耳可听范围,又能保证电机驱动的平滑性。
配置与调整
- 初始化定时器:设置预分频和周期,确定PWM频率。
- 配置PWM通道:设置通道模式为PWM1或PWM2,极性为高或低。
- 启动PWM:调用
HAL_TIM_PWM_Start()。 - 动态调整占空比:修改CCR寄存器值,使用
__HAL_TIM_SET_COMPARE()。
对于电机驱动,通常还需要一个方向控制引脚(GPIO)配合PWM实现正反转。例如,IN1=1、IN2=PWM时正转,IN1=PWM、IN2=0时反转。
案例拆解:双电机差速控制
假设机器人使用L298N驱动两个直流电机,左电机PWM接TIM2_CH1(PA0),右电机PWM接TIM2_CH2(PA1),方向引脚分别接PB0/PB1和PB2/PB3。当蓝牙指令“前进”时,设置左右电机PWM占空比均为70%;“左转”时,左电机占空比30%,右电机70%。
// 设置左电机占空比70%
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 700);
// 设置右电机占空比70%
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 700);
这种开环控制方式简单直接,但负载变化时速度会波动。后续章节将引入编码器反馈实现闭环PID控制,PWM占空比作为PID输出量。
风险提示:PWM频率过低(低于1kHz)会导致电机产生明显噪音和振动;频率过高(超过50kHz)则可能超出驱动芯片的开关能力。另外,占空比从0突变到100%会产生冲击电流,建议在程序中使用斜坡函数平滑过渡。
2.4 串口USART:蓝牙通信的“咽喉”
核心原理
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)是STM32与蓝牙模块通信的关键外设。蓝牙模块(如HC-05)通过UART TTL电平与MCU连接,MCU的TX接蓝牙的RX,MCU的RX接蓝牙的TX,共地连接。串口通信以帧为单位,包含起始位、数据位(8/9位)、校验位(可选)和停止位。波特率决定传输速率,蓝牙模块通常默认9600bps。
接收策略:中断 vs DMA
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 轮询接收 | 简单 | 阻塞CPU | 调试阶段 |
| 中断接收 | 非阻塞,实时性好 | 每字节中断一次,CPU开销较大 | 指令较短(<20字节) |
| DMA接收 | CPU零干预,适合大数据 | 配置复杂,需要处理空闲中断 | 固件升级、日志传输 |
在蓝牙控制场景中,指令通常为“CMD:FORWARD;SPD:70;”这样的短字符串,推荐使用中断接收+环形缓冲区的方式。每收到一个字节就存入缓冲区,主循环解析缓冲区中的完整指令。
案例拆解:蓝牙指令解析框架
定义指令格式为“CMD:xxx;PARAM:yyy;”,例如“CMD:MODE;PARAM:AUTO;”表示切换自动模式。接收中断中逐字节存入缓冲区,当检测到分号“;”时置位标志位,主循环解析。
// 串口中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart1) {
rx_buffer[rx_index++] = rx_data;
if (rx_data == ';') {
rx_complete = 1; // 一帧指令接收完成
}
HAL_UART_Receive_IT(&huart1, &rx_data, 1); // 继续接收
}
}
主循环中检测到 rx_complete 标志后,解析缓冲区内容,提取CMD和PARAM字段,执行对应操作。这种设计能处理蓝牙模块可能发送的粘包、半包问题,为后续章节的指令系统打下基础。
经验提醒:蓝牙模块的波特率必须与MCU配置一致。HC-05默认9600,但有些模块可能被配置为115200。如果通信乱码,首先检查波特率。另外,蓝牙模块的VCC接3.3V还是5V取决于模块型号,HC-05的VCC可接5V,但逻辑电平是3.3V,与STM32兼容。
2.5 外设协同:中断系统与时钟树
核心原理
四大外设并非独立工作,它们通过中断系统和时钟树紧密耦合。STM32使用NVIC(嵌套向量中断控制器)管理外设中断优先级,支持抢占优先级和子优先级。在蓝牙机器人中,串口接收中断应设置为较高优先级(如抢占优先级1),定时器中断设为中等优先级(如抢占优先级2),GPIO外部中断设为较低优先级。这样即使定时器中断频繁触发,也不会丢失蓝牙数据。
时钟树配置
系统时钟源可选择HSI(8MHz内部)、HSE(外部晶振)或PLL倍频。典型配置为HSE 8MHz经过PLL倍频到72MHz,APB1分频为36MHz(定时器时钟72MHz),APB2分频为72MHz。外设时钟使能后,还需注意复用功能重映射(AFIO),例如USART1的TX/RX默认在PA9/PA10,但可以通过重映射到PB6/PB7。
案例拆解:完整初始化顺序
- 配置系统时钟(HSE→PLL→72MHz)。
- 使能各外设时钟(GPIO、TIM、USART)。
- 配置GPIO引脚模式(输入/输出/复用)。
- 初始化外设(定时器、PWM、串口)。
- 配置NVIC中断优先级。
- 启动外设(启动PWM、启动串口接收中断)。
这个顺序保证了每个外设工作时,其依赖的时钟和引脚已经就绪。如果顺序颠倒,可能导致外设初始化失败或引脚电平冲突。
常见误区:很多初学者在初始化串口后忘记调用 HAL_UART_Receive_IT() 启动第一次接收,导致中断永远不触发。另外,NVIC优先级分组必须在初始化外设之前设置,否则优先级配置不生效。
2.6 总结与硬件调试清单
本章回顾了STM32四大核心外设的基本原理和操作方法。GPIO是数字信号的出入口,定时器提供时间基准和测速能力,PWM控制电机动力输出,串口则承载蓝牙通信数据。在实际开发中,建议按照以下清单进行硬件自检:
- 用万用表测量VCC和GND电压,确保3.3V供电稳定。
- 用示波器或逻辑分析仪检查串口TX引脚是否有数据波形。
- 用LED测试GPIO输出是否正常,按键输入是否读到正确电平。
- 用PWM输出接LED观察亮度变化,验证占空比调整是否生效。
- 用定时器中断翻转LED,验证时基是否准确。
完成这些基础验证后,就可以进入下一阶段的蓝牙模块配置和通信协议设计了。扎实的外设操作能力是后续开发的前提,建议读者在开发板上逐一实践本章的代码示例,确保每个外设都能独立工作。