上一篇开箱把物料点了一遍,这一篇按计划推进第一步:把开发链路打通。看起来这是最简单的一环,但真正动手才发现,时钟源、引脚、外设时钟源这几处只要有一处理解偏差,后面点灯、串口、I2C 全都在"看起来正常但数据不对"的状态里打转。所以这一篇我按自己实际配置的顺序,把每一步"为什么这么配"记下来。
我的整体流程拆成七步:工程创建 → 确认硬件外部时钟(24MHz)→ 配置为外部时钟源 → 配置引脚与开发板 BSP → 配置各时钟参数 → 配置 I2C 准备读传感器 → 生成代码。
一、软件工具链准备
工欲善其事,先把 ST 的三件套装齐:
• STM32CubeMX(或 STM32CubeIDE 内置的 CubeMX 视图):图形化配置 + 代码生成;
• STM32CubeIDE:编译与下载调试,社区免费,配合板载 STLINK-V3EC 免驱;
• STM32CubeH5 固件包:HAL 库、CMSIS、BSP 驱动和例程都在这里。注意这一步别漏,很多人配置完生成代码报错,就是固件包没下或者版本过老,导致 H5 系列的器件支持包不完整。
另外在 Device Manager 里把 X-CUBE-MCSDK / NanoEdge AI Studio 的入口先收藏好——那是后面两篇的活儿,本篇不涉及,但装 CubeMX 时顺手登记一下账号能省后面的等待时间。
二、工程创建:先建骨架,再谈外设
打开 CubeMX,走 File → New Project。这里有个岔路口值得说清楚:
方式 A:按芯片选型,直接搜 STM32H533RET6;
方式 B:按开发板选型,在 Board Selector 里搜 NUCLEO-H533RE。
我选了方式 B。原因是按板卡选型会自动关联 BSP(板级支持包)、默认帮你把板载 LED、用户按键、ST-LINK 串口的网络对应关系带出来,省掉大量对着原理图查表的时间。对本次这种"板子官方、外设标准"的场景,按板卡建工程几乎是唯一合理选择;等以后我们自己画板子,才需要回到方式 A。
建好后立刻到 Project Manager 把三件事先定下来,避免后面返工:
1. Project Name 与存放路径(路径中不要有中文和空格,这是老坑了);
2. Toolchain / IDE 选 STM32IDE;
3. Code Generator 里勾选 "Generate peripheral initialization as a pair of '.c/.h' files per peripheral",把每个外设的初始化拆成独立文件。工程一大,全部塞在 main.c 里的可读性会迅速崩塌,这条勾越早打越舒服。
三、先看硬件:外部高速时钟到底是多晶振
配置 RCC 之前,必须先把原理图打开确认外部晶振频率,绝不能凭"一般 Nucleo 都是 8MHz"的印象直接填。
翻 MB1814C 的板载原理图,在 U13 的 OSC_IN / OSC_OUT(板上网络名 RCC_OSC_IN / RCC_OSC_OUT)上挂的无源晶振标称值清晰可读:24 MHz(板上另一路 RCC_OSC32_IN/OUT 是 32.768kHz 的 LSE,给 RTC 用的,跟系统主频无关,别搞混)。
这一步的产出很简单——记住 24MHz 这个数字。因为 CubeMX 里的 HSE 频率只是"告诉软件:硬件上接的是多少",工具不会自己去量。填错的话不会有任何编译报错,它只会安静地用错误的频率去算 PLL,结果主频偏移、波特率跟着偏,串口打出一屏乱码,而新手的第一反应往往是"串口助手波特率设错了",方向就彻底偏了。

(图1:原理图中 24MHz HSE 晶振部分截图)
四、把工程配置为外部时钟源
进入 System Core → RCC:
• High Speed Clock (HSE):板上是有源晶振(Crystal/Ceramic Resonator),不是外部有源时钟输入,所以选 Crystal/Ceramic Resonator。如果这里误选 BYPASS Clock Source,等于让芯片去接收一个外部送进来的时钟信号,HSE 起振失败,程序最终会退回 HSI 内部振荡器运行;
• Low Speed Clock (LSE):本次暂不做 RTC,保持 Disabled,等后面需要精确时间戳时再打开。
顺手把 SYS → Debug 设为 Serial Wire(SWD)。这条同样是"不配也能跑第一次,但第二次就烧不进去"的经典坑:一旦调试引脚被复用成 GPIO,芯片下载完立刻接管 PA13/PB3/PB4 之类的调试口,再点下载就报 "No target connected",只能靠按住 Reset 抢在程序运行前擦除来救。所以从第一个工程开始就把 SWD 打开。
五、引脚与开发板 BSP 配置
到 Pinout & Configuration 视图,按本次任务的需求清单逐项落地:
• USART(ST-LINK 虚拟串口):从原理图上找 TXD/RXD 那一对连到 STLINK-V3EC 的网络,在 CubeMX 里点 USART 的 TX 引脚,工具会自动把对应的复用功能与引脚配好(H5 系列不同串口/不同板卡批次映射会有差异,务必以手上这份原理图为准,不要照抄别人帖子)。模式选 Asynchronous,默认 115200 8N1。板载 VCP 的好处是不用额外 USB 转 TTL 线,Type-C 一插就有串口。
• 用户 LED(LD2)与用户按键(B1):板卡选型已经带了标签,直接确认 GPIO 标签为 LED / USER_KEY 即可;顺手在 GPIO 页把 LED 设为 GPIO_Output,并给个初始电平,方便后面判断程序到底跑没跑起来。
• I2C 的 SDA/SCL:本次 LIS3DH 走 Grove 接口,最终引到 Arduino 扩展座上的 I2C(原理图网络 SDA/SCL)。我的做法是不手写引脚号——直接在芯片图上点 Arduino 座对应的那两个引脚,让 CubeMX 自己列出可用的复用功能再挑 I2C,这样 AF 编号永远不会配错。
• 未使用的引脚:全部保持默认的 Analog 状态,不要随手拉成输出,省功耗也避免和后面外接模块打架。
配置过程中要养成看右下角 Messages 的习惯,任何引脚冲突(黄色感叹号/红叉)都会在那里给出原因,比生成代码后再编译报错好定位十倍。
六、配置各时钟参数(时钟树)
切到 Clock Configuration,这是整个环境搭建里最需要"懂一点原理"的一页。目标很明确:让 HCLK 跑到 250MHz 上限,同时让各个外设总线时钟落在允许范围内。
我的配置思路(HSE = 24MHz):
1. MUX 选择 PLL 作为 SYSCLK 源,PLL 源选 HSE;
2. PLL 输入分频 M 先把 24MHz 分进 PLL 推荐输入区间(我取到 4MHz 左右),倍频 N 把 VCO 拉到 500MHz 附近,再由 P 分频得到 250MHz 作为 SYSCLK / HCLK;
3. APB1、APB2 按规定分频得到总线时钟(H5 的 APB 上限要留意,别为了省事全部分 1);
4. 给后面要用的外设预留时钟:ADC/SAI 这类需要干净独立时钟的,宁可先留 HSI/PLL-Q 备用,也不要在没搞清楚时乱挂。
配置时页面上任何一条超限的分支都会用红色标出来。判断标准就一条:时钟树里不能有任何红色,同时 48MHz 那一路(如果启用 USB)必须凑出精确 48MHz。这里我特意把 CubeMX 算出的实际 HCLK 与 24MHz 手工核对了一遍——这也正是第三步非要看原理图的原因:软件里假设的频率对了,算出来的一切才是真的。
(图2:Clock Configuration 时钟树配置截图)
七、配置 I2C,为读取传感器做准备
下一篇的主线是"LIS3DH 采振动数据",所以本篇先把 I2C 打通。
Connectivity → I2C(本次使用的那一路) 配置要点:
• 模式:I2C(不是 SMBus);
• Speed Mode:Fast Mode 400kHz。LIS3DH 支持到 400kHz,而 Grove 线很短,跑快速模式没问题。不过我第一版仍先按 Standard 100kHz 起调——调不通时先降速排障,跑通后再提速,这个顺序能省下很多"到底是代码错还是时序错"的纠结;
• 7-bit 地址、General Call 关闭;
• Clock Source:这一项在 H5 上特别容易被忽略。可以选择 PCLK(APB 总线时钟)也可以选择 HSI/PLL 等独立时钟源,选了之后一定要确认下面算出来的 SCL 实际频率和设定值一致。总线上电后 SDA/SCL 必须被拉高,Grove LIS3DH 板上已带上拉,但如果哪天自己飞线,忘了 2.2k~4.7k 上拉,症状就是"初始化不报错,读出来全是 0xFF 或 0x00";
• 暂时不开 DMA 和中断,先用最简单的阻塞轮询把通信验通,后面为了高吞吐采样再上 DMA + FIFO,别一上来就把变量叠满。
同时把 USART 的波特率与刚才的时钟树复核一次:串口实际波特率由当前 PCLK 分频得出,改了时钟树之后一定要回头看 USART 页有没有提示 "Baud Rate mismatch"。
八、生成代码与第一次验证
回到 Project Manager,点 GENERATE CODE。生成后的目录结构先花两分钟认一遍,后面所有代码都要往里放:
• Core/Src Core/Inc:自己写的业务代码写在这里(main.c、stm32h5xx_hal_msp.c、各 MX_XXXX_Init());
• Drivers/STM32H5xx_HAL_Driver:HAL 库,不要手改,改了下一次重新生成就被冲掉;
• Drivers/CMSIS 与 Drivers/BSP/NUCLEO_H533RE:板级驱动,BSP 里已经把 LED/按键的读写封装好了,直接用。
CubeMX 只会生成"初始化",业务逻辑要自己写,而且要写在 USER CODE BEGIN/END 区块之间,这样下次重新生成代码不会被覆盖——这条规矩第一天就该养成。
第一次上电的验证只写两件事,越少越好:
1. 点灯:HAL_GPIO_TogglePin() 翻转 LD2,配合 HAL_Delay(500)。灯闪说明芯片起来了、时钟跑起来了、延时基准(SysTick)也基本正确;
2. 串口打印:重定向 printf(CubeIDE 里勾选 "Generate support files" 并在 syscalls.c/sysmem.c 中启用 _write),然后打印一行字符串和 HAL_GetTick() 计数。串口能看到递增的 tick,就代表烧写链路、时钟、串口三条链路全通。
为了后面读传感器时能顺手调试,我顺手加了个 I2C 扫描的小函数:遍历 0x08-0x77 用 HAL_I2C_IsDeviceReady() 试探,把应答的设备地址打印出来。第一次跑就看到 0x19(LIS3DH,SA0 接高时的 7 位地址),说明总线上拉、接线、时钟三样都对,再往下就是读 WHO_AM_I 寄存器核对 0x33 的事了。


(图3:代码Code+ 串口打印 + I2C 扫描结果截图)
九、踩到的几个坑
• HSE 频率填错:一开始按印象填了 8MHz,串口全是乱码,查了半天才想起去看原理图。教训是"先量硬件,再配软件",永远先看原理图;
• 忘了开 SWD:第一个实验烧进去后第二次连不上,按住 Reset 才抢回来,之后所有工程都固定打开 SWD;
• 改完时钟树没回头看串口:把 HCLK 从 96MHz 提到 250MHz 后,CubeMX 里 USART 的 OverSampling/分频值跟着变,串口又乱了一次,实际是配置页面已经给了提示而我没注意;
• I2C 初调直接上 400kHz + 未确认上拉:降回 100kHz 才稳定,排障顺序应该是"接线 → 上拉 → 降速 → 再谈软件"。
十、小结与下一步
至此,开发环境基础搭建完成:CubeMX 按板卡建工程、HSE 按 24MHz 配置为外部晶振、SWD 与串口打开、时钟树拉到 120MHz 无红线、I2C 就绪并扫到器件地址、代码生成后点灯与串口双验证通过。链路通了,接下来就有了一个稳定的"底座"。
下一步计划:
• 用 I2C 读 LIS3DH 的 WHO_AM_I 与 CTRL_REG1,配置成 100Hz/±2g 的 ODR 与量程,把三轴加速度原始值按固定周期串口打印;
• 打通 FIFO 读取路径,保证一次批量取出多个采样点,避免频繁单字节读造成的时间抖动;
• 用 ULN2003 + 28BYJ-48 制造"停止 / 正常转 / 堵转"等几种可复现状态,同步采集加速度数据,并按 NanoEdge AI Studio 要求的目录与文件格式整理——那才是这个项目真正有意思的部分。
感谢 DigiKey 与 EEPW 提供的支持,敬请期待下一篇。
我要赚赏金
