2、STM32基础回顾:GPIO、定时器、PWM、串口等外设的快速复习

在进入蓝牙机器人控制开发之前,我们需要先建立一个坚实的硬件操作基础。STM32作为机器人控制的核心MCU,其GPIO、定时器、PWM和串口四大外设构成了蓝牙控制链路中的关键环节:GPIO负责读取按键状态和驱动LED指示,定时器为系统提供精确的时间基准,PWM控制电机转速,串口则承担与蓝牙模块的双向数据通信。本章将围绕这四大外设,从寄存器级原理到HAL库实战操作进行系统回顾,为后续的指令解析、状态回传和模式切换功能打下扎实的硬件基础。

STM32蓝牙控制硬件外设链路图 STM32F103 MCU ARM Cortex-M3 @72MHz GPIO · TIM · PWM · USART APB1: 36MHz / APB2: 72MHz NVIC中断优先级管理 GPIO 通用输入输出 按键输入 / LED输出 模式:推挽/开漏/上拉/下拉 速度:2/10/50MHz 定时器 TIM 时基:1ms/1us中断 编码器模式:测速 输入捕获:PWM脉宽测量 PWM 脉宽调制 TIM_CH1~CH4输出 频率:1kHz~20kHz 占空比:0%~100% USART 串口 TX/RX 异步通信 波特率:9600~115200 中断接收 + DMA发送 HC-05 / HC-06 蓝牙模块 UART TTL电平 · 波特率9600 TX/RX 图:STM32蓝牙控制硬件外设链路关系

上图展示了STM32蓝牙控制系统的硬件外设链路关系。MCU通过GPIO感知外部按键输入并驱动LED状态指示,定时器提供时基和编码器测速功能,PWM通道输出电机控制信号,USART串口则与蓝牙模块进行双向数据交换。这四个外设并非孤立工作,而是通过中断系统和时钟树紧密协作,共同构成一个完整的机器人控制闭环。

2.1 GPIO:数字世界的“手”与“眼”

核心原理

GPIO(General Purpose Input/Output)是STM32最基础的外设,每个引脚都可以通过软件配置为输入或输出模式。在蓝牙机器人场景中,GPIO承担三类任务:读取按键/拨码开关状态(输入模式)、驱动LED指示灯(输出模式)、模拟I2C或SPI时序(复用模式)。STM32的GPIO内部结构包含施密特触发器、上拉/下拉电阻、输出驱动电路和复用功能选择器,理解这些结构有助于正确配置引脚。

配置与操作流程

  1. 使能GPIO时钟:通过RCC寄存器开启对应端口时钟,例如 __HAL_RCC_GPIOA_CLK_ENABLE()。
  2. 初始化引脚:使用 HAL_GPIO_Init() 设置引脚号、模式(输入/输出/复用/模拟)、速度、上下拉。
  3. 读写操作:输出用 HAL_GPIO_WritePin() 或 HAL_GPIO_TogglePin(),输入用 HAL_GPIO_ReadPin()。
  4. 外部中断:配置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,既能避开人耳可听范围,又能保证电机驱动的平滑性。

配置与调整

  1. 初始化定时器:设置预分频和周期,确定PWM频率。
  2. 配置PWM通道:设置通道模式为PWM1或PWM2,极性为高或低。
  3. 启动PWM:调用 HAL_TIM_PWM_Start()。
  4. 动态调整占空比:修改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。

案例拆解:完整初始化顺序

  1. 配置系统时钟(HSE→PLL→72MHz)。
  2. 使能各外设时钟(GPIO、TIM、USART)。
  3. 配置GPIO引脚模式(输入/输出/复用)。
  4. 初始化外设(定时器、PWM、串口)。
  5. 配置NVIC中断优先级。
  6. 启动外设(启动PWM、启动串口接收中断)。

这个顺序保证了每个外设工作时,其依赖的时钟和引脚已经就绪。如果顺序颠倒,可能导致外设初始化失败或引脚电平冲突。

常见误区:很多初学者在初始化串口后忘记调用 HAL_UART_Receive_IT() 启动第一次接收,导致中断永远不触发。另外,NVIC优先级分组必须在初始化外设之前设置,否则优先级配置不生效。

2.6 总结与硬件调试清单

本章回顾了STM32四大核心外设的基本原理和操作方法。GPIO是数字信号的出入口,定时器提供时间基准和测速能力,PWM控制电机动力输出,串口则承载蓝牙通信数据。在实际开发中,建议按照以下清单进行硬件自检:

  • 用万用表测量VCC和GND电压,确保3.3V供电稳定。
  • 用示波器或逻辑分析仪检查串口TX引脚是否有数据波形。
  • 用LED测试GPIO输出是否正常,按键输入是否读到正确电平。
  • 用PWM输出接LED观察亮度变化,验证占空比调整是否生效。
  • 用定时器中断翻转LED,验证时基是否准确。

完成这些基础验证后,就可以进入下一阶段的蓝牙模块配置和通信协议设计了。扎实的外设操作能力是后续开发的前提,建议读者在开发板上逐一实践本章的代码示例,确保每个外设都能独立工作。