2. 开发环境搭建:Keil MDK安装、STM32CubeMX配置、HAL库工程模板建立、串口打印调试

做电机控制,说白了就是跟硬件和代码打交道。环境搭不好,后面全是坑。我见过不少新手,代码写得挺漂亮,结果一烧录就黑屏,最后发现是工程配置错了。所以这一章,咱们把地基打牢。

2.1 工具链总览

先看看我们要用哪些家伙。别急着装,心里有个数先。

工具 版本建议 干什么用
Keil MDK-ARM V5.38 或更新 写代码、编译、调试
STM32CubeMX V6.10+ 图形化配置外设、生成HAL初始化代码
HAL库 F4 V1.28+ 硬件抽象层,不用直接操作寄存器
串口助手 任意 看打印信息,调试PID参数

我个人习惯用 Keil V5.38,稳定,兼容性好。CubeMX 版本别太老,不然有些外设配置选项找不到。

2.2 Keil MDK 安装与注册

安装过程其实没啥好说的,下一步下一步就行。但有几点我得提醒你:

  • 路径不要有中文。我吃过这个亏,编译报错找半天,结果是路径里有个“项目”两个字。
  • 安装时勾选所有器件支持包。虽然我们只用 STM32F407,但万一以后换芯片呢?
  • 注册用管理员权限运行。不然注册信息写不进去,编译到一半弹 license 错误,很烦。
小技巧: 装完 Keil 后,先打开一次,让它自动创建默认目录。然后再装 Pack,这样不会出现“找不到器件”的提示。

2.3 STM32CubeMX 配置与工程生成

CubeMX 这东西,说白了就是个代码生成器。你点点鼠标,它帮你把 HAL 库的初始化代码全写好。省下的时间,用来调 PID 不香吗?

咱们的配置流程,我画了张图,你跟着走就行:

STM32CubeMX 配置流程 1. 新建工程 2. 选择芯片 STM32F407VGT6 3. 配置时钟 RCC 4. 配置定时器 TIM 5. 配置串口 USART 6. 生成代码 关键配置项 • HSE: 8MHz 外部晶振 • 主频: 168MHz • APB1: 42MHz, APB2: 84MHz

2.4 关键外设配置详解

这里我重点讲三个外设,电机控制离不开它们。

2.4.1 时钟配置(RCC)

F407 最高能跑 168MHz。怎么配?HSE 外部 8MHz 晶振,通过 PLL 倍频到 168MHz。我建议你直接用 CubeMX 的 Clock Configuration 页面,拖拽滑块就行。别手动算分频系数,容易算错。

注意: 配置完时钟后,一定要检查 APB1 和 APB2 的时钟频率。APB1 最高 42MHz,APB2 最高 84MHz。超了的话,外设可能工作不正常。我曾经把 APB1 设到 50MHz,TIM 定时器直接不走了。

2.4.2 定时器配置(TIM)

电机控制里,定时器干两件事:

  • 产生 PWM 波:驱动电机。用 TIM1 或 TIM8,它们是高级定时器,支持互补输出和死区插入。
  • 编码器接口:读取电机转速。用 TIM2~TIM5,它们支持编码器模式。

配置时,预分频器(PSC)和自动重装载值(ARR)决定了 PWM 频率。公式很简单:

PWM频率 = 定时器时钟 / (PSC + 1) / (ARR + 1)

比如 APB2 是 84MHz,想要 20kHz 的 PWM,PSC=0,ARR=4199。算出来刚好 20kHz。

2.4.3 串口配置(USART)

串口用来打印调试信息。PID 调参时,你要看当前转速、目标值、误差,全靠它。我习惯用 USART1,PA9(TX) 和 PA10(RX),波特率 115200。

经验之谈: 串口中断优先级不要设太高。电机控制的中断(比如 TIM 更新中断)才是老大。串口只是辅助,优先级设低一点,别抢了主中断的时间。

2.5 HAL 库工程模板建立

CubeMX 生成代码后,你会得到一个 MDK-ARM 工程。但别急着写代码,先做几件事:

  1. 整理文件结构:把用户代码放到 User 文件夹,别跟 HAL 库混在一起。我习惯建一个 App/ 目录,放 motor.c、pid.c 这些。
  2. 配置调试器:Keil 里点 Project → Options for Target → Debug,选 ST-Link Debugger。然后点 Settings,把 SWD 速度调到 4MHz,下载快很多。
  3. 添加宏定义:在 stm32f4xx_hal_conf.h 里,确保 HAL_TIM_MODULE_ENABLED 和 HAL_UART_MODULE_ENABLED 是打开的。

嗯,这里要注意。CubeMX 生成的 main.c 里有一段 /* USER CODE BEGIN */ 和 /* USER CODE END */ 的注释。你的代码一定要写在这两个注释之间。不然下次重新生成代码,你写的东西会被覆盖掉。我刚开始用 HAL 库时,没注意这个,写了一大堆初始化代码,重新生成后全没了……从那以后,我再也不敢乱放代码了。

2.6 串口打印调试实现

串口打印,说白了就是把数据从芯片里吐出来,让你看到。咱们用重定向 printf 的方式,方便。

在 usart.c 里添加这段代码:

/* USER CODE BEGIN 0 */
#include <stdio.h>

int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    return ch;
}
/* USER CODE END 0 */

然后在 Keil 里勾选 "Use MicroLIB"。不然 printf 会占用大量资源,电机控制这种实时性要求高的场景,扛不住。

测试一下:

/* USER CODE BEGIN 2 */
printf("STM32F407 电机控制工程启动\r\n");
printf("系统时钟: %lu Hz\r\n", HAL_RCC_GetSysClockFreq());
/* USER CODE END 2 */

打开串口助手,波特率 115200,你应该能看到打印信息。如果没显示,先检查接线,再检查波特率。我遇到过最奇葩的问题,是串口助手用的 USB 转 TTL 模块,TX 和 RX 接反了……

2.7 工程验证清单

环境搭完,别急着往下走。花 5 分钟验证一下:

检查项 预期结果 如果不对怎么办
编译无错误 0 Error, 0 Warning 检查 Pack 版本、路径
下载成功 Flash Download Finished 检查调试器驱动、接线
串口打印 看到系统时钟信息 检查波特率、TX/RX 是否接反
LED 闪烁 板载 LED 以 1Hz 闪烁 检查 GPIO 配置、时钟使能

核心要点: 环境搭建不是一次性的。每加一个新外设,都要重新确认工程配置是否正确。尤其是时钟树,改一次就要验证一次。我见过有人改了时钟忘了改定时器分频,电机转得忽快忽慢,还以为是 PID 参数没调好……