2、STM32基础回顾:STM32系列选型、开发环境搭建(STM32CubeMX+Keil)、GPIO、定时器、PWM、ADC等外设基础

在开始巡线机器人的开发之前,我们需要先夯实STM32的基础知识。巡线机器人本质上是一个“感知-决策-执行”的闭环系统:灰度传感器负责感知地面线条位置,MCU负责计算偏差并生成控制指令,电机驱动模块负责执行指令。STM32作为整个系统的大脑,其选型是否合理、开发环境是否顺畅、外设使用是否熟练,直接决定了后续巡线算法的实现效率与稳定性。本章将围绕STM32的选型策略、CubeMX+Keil的工程搭建流程,以及GPIO、定时器、PWM、ADC四大核心外设展开,为后续的传感器数据采集和电机控制打下坚实基础。

STM32巡线机器人开发基础架构 STM32选型 F103/F407/G0系列 主频/Flash/外设资源 开发环境 STM32CubeMX + Keil 图形化配置/代码生成 核心外设 GPIO/定时器/PWM/ADC 传感器与电机控制 巡线机器人信号链路 灰度传感器 模拟电压/数字电平 ADC采集 多通道扫描 偏差计算 加权平均算法 PWM输出 定时器通道 电机 驱动 GPIO:传感器数字输入/电机方向控制 定时器:PWM生成/编码器模式/延时基准 ADC:灰度传感器模拟量采集(多通道) 图2-1 STM32巡线机器人开发基础架构与信号链路

2.1 STM32系列选型:从巡线需求出发的芯片选择

核心原理:STM32家族庞大,从低功耗的L0系列到高性能的H7系列,选型需要围绕“够用、好用、成本可控”三个维度展开。对于巡线机器人,核心需求包括:至少2路ADC通道(灰度传感器)、至少2路PWM输出(电机调速)、若干GPIO(方向控制、传感器数字输入)、以及足够的定时器资源(编码器模式或PWM生成)。此外,主频决定了偏差计算和控制算法的实时性,Flash容量则影响代码和调试信息的存储。

选型判断方法:我们可以通过一个简单的评分表来辅助决策。对于入门级巡线小车,STM32F103C8T6(72MHz主频,64KB Flash,20KB RAM)是最经典的选择,性价比高,资料丰富。如果希望预留更多性能余量(例如后续加入摄像头视觉巡线),可以选择STM32F407VET6(168MHz主频,带FPU,适合浮点运算)。如果追求低功耗和更小的体积,STM32G0B1系列也是不错的选择,主频64MHz,但外设配置更现代。

对比维度STM32F103C8T6STM32F407VET6STM32G0B1RET6
主频72MHz168MHz64MHz
Flash/RAM64KB/20KB512KB/128KB512KB/144KB
ADC通道数10通道(12位)16通道(12位)16通道(12位)
定时器资源3个通用+1个高级2个高级+8个通用多个通用+高级
适合场景基础巡线、入门学习视觉巡线、复杂算法低功耗、小型化

实战场景:假设我们设计一台两轮差速驱动的巡线小车,使用3路灰度传感器(模拟输出),需要2路PWM控制电机转速,2个GPIO控制电机方向。F103C8T6的10个ADC通道完全够用,3个通用定时器可以分配TIM2用于PWM输出,TIM3用于编码器模式(如果使用带编码器的电机)。如果后续要扩展蓝牙模块(UART)、OLED显示屏(I2C或SPI),F103的资源依然充足。

经验提醒:选型时不要只看引脚数量,要关注“外设复用冲突”。例如F103的PA0和PA1既可以作为ADC通道,也可以作为定时器PWM输出。在CubeMX中配置时,如果发现引脚冲突,需要优先保证传感器和电机控制的核心功能。
风险提示:避免选择过于冷门的型号,否则开发资料和第三方库支持会很少。对于初学者,F103系列是社区生态最完善的,遇到问题更容易找到解决方案。

2.2 开发环境搭建:STM32CubeMX + Keil的工程创建流程

核心原理:STM32CubeMX是ST官方提供的图形化配置工具,它通过可视化界面完成引脚分配、时钟树配置、外设参数设置,并自动生成初始化代码(基于HAL库或LL库)。Keil MDK则作为编译、下载和调试的IDE。两者配合,可以将开发者从繁琐的寄存器配置中解放出来,专注于业务逻辑(如巡线算法)的实现。

操作流程:搭建环境的步骤如下:

  1. 安装软件:安装STM32CubeMX(建议6.x以上版本)、Keil MDK(5.x版本)、以及对应的器件支持包(如Keil.STM32F1xx_DFP)。
  2. 新建工程:打开CubeMX,选择“Access to MCU Selector”,输入芯片型号(如STM32F103C8),双击创建工程。
  3. 配置时钟:在“System Core > RCC”中,将HSE设置为“Crystal/Ceramic Resonator”,然后在“Clock Configuration”中配置系统时钟为72MHz(F103最高主频)。
  4. 配置外设:在“Pinout & Configuration”界面,依次配置GPIO、ADC、定时器、PWM等外设(具体参数见后续小节)。
  5. 生成代码:在“Project Manager”中设置工程名称、编译工具链选择“MDK-ARM V5”,然后点击“GENERATE CODE”。
  6. 编译下载:用Keil打开生成的工程,编译无误后,通过ST-Link或J-Link下载到开发板。
// CubeMX生成的main.c中,外设初始化函数调用顺序示例
int main(void)
{
  HAL_Init();
  SystemClock_Config();    // 系统时钟配置
  MX_GPIO_Init();          // GPIO初始化
  MX_ADC1_Init();          // ADC1初始化
  MX_TIM2_Init();          // TIM2初始化(用于PWM)
  MX_TIM3_Init();          // TIM3初始化(用于编码器)
  // 用户代码:开始巡线主循环
  while (1)
  {
    // 巡线逻辑
  }
}

案例拆解:一位学员在配置时钟时,发现系统运行不稳定,电机转速忽快忽慢。排查后发现,他在CubeMX中误将PLL时钟源设置为HSI(内部高速时钟),导致系统时钟不是精确的72MHz,而是约64MHz。这直接影响了PWM频率和ADC采样时间的准确性。通过将HSE设置为外部晶振,并重新配置时钟树,问题解决。

关键检查点:生成代码后,务必检查SystemClock_Config()函数中的时钟参数。如果使用外部晶振,需要确保开发板上实际焊接了晶振,否则程序会卡死在HAL_RCC_ClockConfig的等待超时中。

2.3 GPIO外设:数字输入输出与巡线传感器接口

核心原理:GPIO(General Purpose Input/Output)是STM32最基础的外设。每个引脚可以配置为输入、输出、复用功能或模拟模式。对于巡线机器人,GPIO主要用于:读取数字灰度传感器的黑白电平信号、控制电机驱动芯片的IN1/IN2方向引脚、驱动LED指示灯等。

配置要点:在CubeMX中,配置GPIO需要关注以下几个参数:

  • GPIO mode:输入模式(Input mode)或输出模式(Output mode)。对于数字灰度传感器,选择输入模式;对于电机方向控制,选择输出模式。
  • GPIO Pull-up/Pull-down:如果传感器输出是开漏结构,需要启用内部上拉电阻;如果传感器输出是推挽结构,则选择“No pull-up/pull-down”。
  • Maximum output speed:对于PWM等高频信号,选择High或Very High;对于普通LED,选择Low即可。

实战场景:假设我们使用3路数字灰度传感器(输出TTL电平),分别连接到PB0、PB1、PB2。在CubeMX中,将这三个引脚配置为GPIO_Input,并启用内部上拉(因为传感器输出为低电平有效)。在代码中,通过HAL_GPIO_ReadPin()读取电平状态,判断传感器是否检测到黑线。

// 读取三个传感器状态
uint8_t s1 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
uint8_t s2 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
uint8_t s3 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
// 根据传感器组合判断偏差(示例:s2为中间传感器)
if (s2 == 0) { // 中间检测到黑线
  // 偏差为0,直行
} else if (s1 == 0) { // 左侧检测到黑线
  // 偏差为负,右转
} else if (s3 == 0) { // 右侧检测到黑线
  // 偏差为正,左转
}
常见误区:很多初学者忘记配置GPIO的时钟使能。在HAL库中,使用某个GPIO端口前,必须调用__HAL_RCC_GPIOB_CLK_ENABLE()。CubeMX生成的代码会自动处理,但如果手动添加代码,容易遗漏。

2.4 定时器与PWM:电机调速的核心实现

核心原理:定时器(Timer)是STM32中功能最丰富的模块之一。在巡线机器人中,定时器主要承担两个任务:一是生成PWM波形控制电机转速,二是作为编码器接口读取电机转速(用于闭环控制)。PWM(脉冲宽度调制)通过改变占空比来调节电机两端的平均电压,从而实现调速。

PWM配置方法:在CubeMX中,选择TIM2,将Channel1和Channel2配置为“PWM Generation CH1/CH2”。关键参数包括:

  • Prescaler(预分频):将定时器时钟分频,例如72MHz时钟,预分频为71,则计数频率为1MHz。
  • Counter Period(自动重载值):决定PWM频率。例如设置为999,则PWM频率为1MHz/1000=1kHz。
  • Pulse(脉冲宽度):即比较值,决定占空比。设置为500,则占空比为50%。
// 启动PWM输出
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
// 动态调整占空比(设置比较值)
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 700); // 占空比70%
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 300); // 占空比30%

案例拆解:在调试电机时,我们发现电机在低占空比(<20%)时无法启动,这是因为电机需要一定的启动电流。解决方案是:在程序启动时,先给一个较高的占空比(如60%)持续200ms,然后再降低到目标值。这种“软启动”策略可以有效避免电机堵转。

经验总结:PWM频率的选择很重要。对于直流减速电机,1kHz-10kHz是常用范围。频率太低,电机会发出明显的“嗡嗡”声;频率太高,电机驱动芯片的开关损耗会增加。建议从10kHz开始调试。

2.5 ADC外设:灰度传感器模拟量采集

核心原理:ADC(模数转换器)将模拟电压转换为数字量。巡线机器人使用模拟灰度传感器时,传感器输出的电压值随反射光强度变化,ADC可以精确测量这个电压,从而判断线条位置。STM32的ADC是12位分辨率,输入范围通常为0-3.3V。

配置与读取流程:

  1. 在CubeMX中,选择ADC1,将IN0、IN1、IN2(对应PA0、PA1、PA2)勾选为“Enabled”。
  2. 设置扫描模式(Scan Mode)为Enabled,连续转换模式(Continuous Mode)为Disabled(使用单次转换+软件触发)。
  3. 设置采样时间(Sampling Time)为55.5 Cycles或更高,以保证足够的采样精度。
// 单次轮询读取ADC值(非中断方式)
uint32_t adc_values[3];
HAL_ADC_Start(&hadc1);
for (int i = 0; i < 3; i++) {
  HAL_ADC_PollForConversion(&hadc1, 10); // 等待转换完成
  adc_values[i] = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);

实战场景:假设灰度传感器在白色地面输出2.8V,在黑色线条上输出1.0V。我们可以设定一个阈值(如1.8V),将ADC值转换为二值化信号。但更高级的做法是,直接使用ADC的原始值进行加权计算,得到更精确的偏差值。例如,使用3路传感器,偏差计算公式为:

// 假设s0、s1、s2为三路ADC值,数值越小表示越黑
float error = (s0 * (-1) + s1 * 0 + s2 * 1) / (s0 + s1 + s2);
常见问题:ADC的参考电压必须稳定。如果使用开发板上的3.3V供电,且电机大电流工作时导致电压跌落,ADC读数会漂移。建议为传感器和ADC参考电压使用独立的LDO稳压,或在电源输入端增加大容量电容。

2.6 综合案例:从零搭建一个巡线小车的底层驱动

案例背景:我们设计一台两轮差速巡线小车,使用3路模拟灰度传感器(接PA0-PA2),两个直流减速电机(带编码器,接TIM3的CH1/CH2),电机驱动芯片为TB6612(方向控制接PB4/PB5,PWM接TIM2的CH1/CH2)。

实施步骤:

  1. CubeMX配置:完成时钟(72MHz)、GPIO(PB4/PB5输出)、ADC1(3通道)、TIM2(PWM输出)、TIM3(编码器模式)的配置。
  2. 编写初始化代码:在main.c中,调用各外设的初始化函数。
  3. 编写巡线逻辑:在主循环中,读取ADC值,计算偏差,通过PID算法(后续章节详述)计算左右电机的PWM占空比,并设置电机方向。
// 主循环中的核心逻辑(简化版)
while (1) {
  // 1. 读取ADC
  uint16_t s0 = Read_ADC(0);
  uint16_t s1 = Read_ADC(1);
  uint16_t s2 = Read_ADC(2);
  // 2. 计算偏差(加权平均)
  float error = ((float)s2 - s0) / (s0 + s1 + s2 + 1); // 避免除零
  // 3. 根据偏差调整PWM(比例控制示例)
  int base_speed = 500; // 基础占空比
  int correction = (int)(error * 200);
  int left_speed = base_speed + correction;
  int right_speed = base_speed - correction;
  // 4. 限制PWM范围
  left_speed = constrain(left_speed, 0, 999);
  right_speed = constrain(right_speed, 0, 999);
  // 5. 设置电机方向和速度
  Set_Motor_Direction(LEFT, left_speed > 0);
  Set_Motor_Direction(RIGHT, right_speed > 0);
  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, abs(left_speed));
  __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, abs(right_speed));
  HAL_Delay(10); // 10ms控制周期
}

结果与复盘:该底层驱动成功实现了小车沿黑线行驶的基本功能。在调试过程中,我们发现ADC的采样值存在约±20的噪声波动,导致偏差计算不稳定。解决方案是在ADC采样后增加一个简单的滑动平均滤波(取5次采样的平均值)。此外,电机PWM的占空比在接近0和999时,会出现明显的非线性,因此在实际控制中,我们将PWM范围限制在100-900之间,以保证电机响应的线性度。

本章总结:STM32的开发核心在于“配置”而非“编码”。通过CubeMX正确配置时钟、GPIO、定时器和ADC,可以大幅减少底层调试时间。巡线机器人的底层驱动,本质上就是ADC读取传感器、GPIO控制方向、PWM控制速度的循环。掌握这些基础外设后,后续的偏差计算和PID控制算法就有了可靠的执行基础。