RIOT 板级支持详解:STM32 Nucleo-F412ZG(nucleo-f412zg)移植与开发指南
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
导读
本文围绕 RIOT 操作系统对STM32 Nucleo-F412ZG开发板的板级支持(Board Support Package)展开,完整介绍该板在 RIOT 中的识别信息、MCU 外设规格、periph_conf.h中的引脚与时钟配置、构建系统集成方式,以及基于 OpenOCD / ST-Link MSD / stm32flash 的烧录流程和 ST-Link VCP 串口 Shell 接入方法。读完本文,你将能够在nucleo-f412zg目标上从零编译并烧录 RIOT 应用,并清楚了解板级配置文件中每个外设项的含义与修改方法。
板卡概览:Nucleo 家族中的 Cortex-M4 高配成员
nucleo-f412zg是 RIOT 对 ST 公司 Nucleo-144 系列开发板STM32 Nucleo-F412ZG的官方板级支持目标。在 RIOT 源码树中,该板的完整支持位于 boards/nucleo-f412zg/,它通过@ingroup boards_common_nucleo144与所有 Nucleo-144 板共享公共配置,公共部分见 boards/common/nucleo144/(包括Makefile.include、Makefile.features以及board.h、arduino_iomap.h、gpio_params.h等公共头文件)。
核心板卡信息如下表所示:
| MCU 项 | 参数 |
|---|---|
| MCU 型号 | STM32F412ZG |
| 内核架构 | ARM Cortex-M4F(带 FPU) |
| 厂商 | STMicroelectronics |
| RAM | 256 KiB |
| Flash | 1 MiB |
| 最高主频 | 100 MHz |
| FPU | 支持 |
| 定时器 | 17 个(2×看门狗、1×SysTick、2×32 位、12×16 位) |
| ADC | 1× 12 位(最多 16 通道) |
| UART | 4 个(USART) |
| I2C | 4 个 |
| SPI | 5 个 |
| CAN | 2 个 |
| RTC | 1 个 |
| 数据手册 | STM32F412ZG Datasheet(st.com 资源) |
| 参考手册 | RM0402(STM32F412 高级 ARM 32 位 MCU 参考手册) |
| 编程手册 | PM0214(STM32 Cortex-M4 MCU/MPU 编程手册) |
| 板卡手册 | UM1974 / DM00244518(STM32 Nucleo-144 板用户手册) |
注:原文档中的 Datasheet、Reference Manual、Programming Manual 与 Board Manual 均为 ST 官方公开文档,读者可在 st.com 按上述编号检索获取。RIOT 文档本身通过 Doxygen
@defgroup boards_nucleo-f412zg将该板归入boards_common_nucleo144分组,便于在 API 文档中统一索引。
在 RIOT 中,板卡名即为nucleo-f412zg,后续所有make命令均通过BOARD=nucleo-f412zg指定该目标。
MCU 与板级功能:从 Makefile 看板卡能力声明
RIOT 的板级支持由一组Makefile.*文件声明,nucleo-f412zg通过这些文件宣告它提供了哪些外设与特性。
外设特性声明(Makefile.features)
boards/nucleo-f412zg/Makefile.features 首先固定 CPU 型号,再按字母序声明该板提供的外设特性:
CPU = stm32 CPU_MODEL = stm32f412zg # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_dma FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart FEATURES_PROVIDED += periph_usbdev # Put other features for this board (in alphabetical order) FEATURES_PROVIDED += tinyusb_device # load the common Makefile.features for Nucleo-144 boards include $(RIOTBOARD)/common/nucleo144/Makefile.features这些FEATURES_PROVIDED条目意味着在编译任意应用时,RIOT 的依赖解析机制(见 makefiles/features_check.inc.mk)会认可该板已自带periph_adc、periph_dma、periph_i2c、periph_pwm、periph_rtc、periph_spi、periph_timer、periph_uart、periph_usbdev以及tinyusb_device等能力,应用代码无需再额外声明即可直接使用这些外设 API。
值得注意的是,板卡虽然支持tinyusb_device(USB 设备栈),但 ST-Link 的 VCP(虚拟串口)由板载调试器独立提供,二者互不干扰。末尾的 include 将公共的 Nucleo-144 特性声明并入构建,具体可查看 boards/common/nucleo144/Makefile.features。
构建目录与依赖(Makefile / Makefile.dep / Makefile.include)
- boards/nucleo-f412zg/Makefile:将板卡模块目录指向
$(RIOTBOARD)/common/nucleo,即复用 Nucleo 系列公共的板级 C 源码。 - boards/nucleo-f412zg/Makefile.dep:包含
$(RIOTBOARD)/common/nucleo/Makefile.dep,拉入公共依赖(如 GPIO、时钟等基础模块)。 - boards/nucleo-f412zg/Makefile.include:包含
$(RIOTBOARD)/common/nucleo144/Makefile.include,负责将 ST-Link、OpenOCD 等烧录/调试工具链参数引入构建环境。
Kconfig 集成
boards/nucleo-f412zg/Kconfig 将该板接入 RIOT 的 Kconfig 配置体系:
config BOARD default "nucleo-f412zg" if BOARD_NUCLEO_F412ZG config BOARD_NUCLEO_F412ZG bool default y select BOARD_COMMON_NUCLEO144 select CPU_MODEL_STM32F412ZG source "$(RIOTBOARD)/common/nucleo144/Kconfig"当选择该板时,会自动选中BOARD_COMMON_NUCLEO144与CPU_MODEL_STM32F412ZG,从而在 Kconfig 层面锁定 CPU 型号并继承 Nucleo-144 公共配置项(如时钟树、电源配置等),保证与 Makefile 体系的行为一致。
引脚与外设映射:periph_conf.h 深度解析
板级外设的具体引脚映射定义在 boards/nucleo-f412zg/include/periph_conf.h 中,这是将物理引脚与 RIOT 外设抽象(UART/SPI/PWM/ADC/DMA)对接的核心文件。下面逐项解析。
时钟源与公共配置
文件开头声明该板同时提供 LSE 与 HSE 晶振,并引入 STM32 公共时钟配置与复用配置头:
#ifndef CONFIG_BOARD_HAS_LSE #define CONFIG_BOARD_HAS_LSE 1 #endif #ifndef CONFIG_BOARD_HAS_HSE #define CONFIG_BOARD_HAS_HSE 1 #endif #include "periph_cpu.h" #include "clk_conf.h" #include "cfg_i2c1_pb8_pb9.h" #include "cfg_timer_tim5.h" #include "cfg_usb_otg_fs.h"cfg_i2c1_pb8_pb9.h:I2C1 复用到 PB8/PB9,这是 Nucleo 板上 Arduino 兼容接头常用的 I2C 引脚。cfg_timer_tim5.h:选用 TIM5 作为系统高精度定时器(ztimer)的时钟源。cfg_usb_otg_fs.h:配置 USB OTG FS 外设,与FEATURES_PROVIDED += periph_usbdev/tinyusb_device呼应。
UART 配置(含 ST-Link VCP 串口)
uart_config[]定义了 3 个可用 UART 实例,均未绑定 DMA(DMA_STREAM_UNDEF/UINT8_MAX):
| 实例 | 外设 | RX 引脚 | TX 引脚 | 总线 | 中断 |
|---|---|---|---|---|---|
| UART_0 | USART3 | PD9 | PD8 | APB1 | USART3_IRQn |
| UART_1 | USART6 | PG9 | PG14 | APB2 | USART6_IRQn |
| UART_2 | USART2 | PD6 | PD5 | APB2 | USART2_IRQn |
对应的中断处理函数绑定为isr_usart3、isr_usart6、isr_usart2。其中USART3(UART_0)正是 Nucleo-144 板上 ST-Link VCP 的连接口,RIOT 默认的stdio即经由它输出。因此通过 USB 连接板子后,在宿主机打开对应串口(115200、8N1)即可看到 RIOT Shell 提示符(详见下文"访问 RIOT Shell"一节)。
PWM 配置
pwm_config[]提供两个 PWM 设备,引脚均标注了 Nucleo 丝印(D3/D5/D6/D9),方便对照板面使用:
| 设备 | 通道映射 | AF | 总线 |
|---|---|---|---|
| TIM1 | PE9→cc0(D6)、PE11→cc1(D5)、PE13→cc2(D3) | GPIO_AF1 | APB2 |
| TIM4 | PD15→cc3(D9) | GPIO_AF2 | APB1 |
未使用的通道统一填GPIO_UNDEF,避免误初始化。
SPI 配置
提供 1 个 SPI 设备(SPI1),同时支持 DMA 传输:
static const spi_conf_t spi_config[] = { { .dev = SPI1, .mosi_pin = GPIO_PIN(PORT_A, 7), .miso_pin = GPIO_PIN(PORT_A, 6), .sclk_pin = GPIO_PIN(PORT_A, 5), .cs_pin = GPIO_PIN(PORT_A, 4), .mosi_af = GPIO_AF5, .miso_af = GPIO_AF5, .sclk_af = GPIO_AF5, .cs_af = GPIO_AF5, .rccmask = RCC_APB2ENR_SPI1EN, .apbbus = APB2, #ifdef MODULE_PERIPH_DMA .tx_dma = 0, .tx_dma_chan = 3, .rx_dma = 1, .rx_dma_chan = 3, #endif } };PA4~PA7 对应 Nucleo 板 Arduino 接头的 D10/D12/D11/D13(CS/MISO/MOSI/SCLK),可直接对接标准 SPI 模块。
ADC 配置
ADC 只配置了 6 个模拟输入通道 + 1 个内部 VBAT 通道,对应 Arduino 接头 A0~A5:
| 引脚 | ADC 线号 |
|---|---|
| PA3 | 3(A0) |
| PC0 | 10(A1) |
| PC3 | 13(A2) |
| PC1 | 11(A3) |
| PC4 | 14(A4) |
| PC5 | 15(A5) |
| (内部)VBAT | 18 |
VBAT_ADC被定义为ADC_LINE(6),应用可通过adc_sample(VBAT_ADC, ...)读取电池电压。源文件注释明确说明:此处并未按 STM32F412ZG 的全部通道顺序逐一配置,而是仅覆盖 Nucleo Arduino 头 A0~A5 与 VBAT,避免不必要的配置膨胀。
DMA 配置
DMA 用于 SPI1 的收发,使用 DMA2 的两个 Stream:
static const dma_conf_t dma_config[] = { { .stream = 11 }, /* DMA2 Stream 3 - SPI1_TX */ { .stream = 10 }, /* DMA2 Stream 2 - SPI1_RX */ }; #define DMA_0_ISR isr_dma2_stream3 #define DMA_1_ISR isr_dma2_stream2 #define DMA_NUMOF ARRAY_SIZE(dma_config)当应用启用periph_dma后,SPI1 的收发将自动走 DMA 通道,降低 CPU 负载。
编译一个应用:从 hello-world 开始
以 RIOT 自带的 hello-world 示例为例,在仓库根目录执行:
make BOARD=nucleo-f412zg -C examples/basic/hello-world该命令会依据CPU_MODEL = stm32f412zg引入 cpu/stm32/ 下的 STM32 公共实现,并生成可烧录的.elf、.hex与.bin文件。你可以随后用make BOARD=nucleo-f412zg -C examples/basic/hello-world term连接串口查看输出。
提示:Nucleo-144 公共头文件位于 boards/common/nucleo144/include/board.h(LED、按键等板载器件定义),Arduino 引脚映射见同目录的 arduino_iomap.h,需要将外设映射到 Arduino 引脚编号时可参考它们。
烧录与调试:三种方式详解
Nucleo-144 板载 ST-Link 调试器,RIOT 官方对 STM32 板的烧录指引见 doc/guides/board_specific/stm32.md。针对nucleo-f412zg,有以下三种烧录路径。
方式一:OpenOCD(默认推荐)
Nucleo32/64/144 均板载 ST-LINK 编程器,可直接用 OpenOCD 烧录,且 OpenOCD 是 Nucleo 板的默认PROGRAMMER,无需额外设置环境变量。至少需要 OpenOCD 0.11.0:
make BOARD=nucleo-f412zg flash -C examples/basic/hello-world如果你的环境未将 OpenOCD 设为默认,可显式指定:
make BOARD=nucleo-f412zg PROGRAMMER=openocd flash注意事项(摘自官方 STM32 烧录指南):
- Nucleo 板出厂时 ST-Link 固件可能很旧,若烧录失败,建议先升级 ST-Link 固件(使用 STM32CubeProgrammer 或官方升级工具 STSW-LINK007)。
- OpenOCD 版本过旧时不支持新的 ST-Link V3,可能出现
Error: libusb_open() failed with LIBUSB_ERROR_ACCESS之类的错误;新发布芯片也可能暂未被最新 OpenOCD 支持(例如早期 OpenOCD 无法识别 STM32C0/G0/G4/L4/L5 等系列)。
方式二:ST-Link 大容量存储设备(cpy2remed)
Nucleo 板插入 USB 后,ST-Link 会同时枚举为一个 U 盘(Mass Storage Device)。将 HEX 文件复制进该盘即可触发 ST-Link 自动烧录。RIOT 提供了cpy2remed(copy to removable media)脚本封装此流程:
make BOARD=nucleo-f412zg PROGRAMMER=cpy2remed flash注意:此方式在 WSL 下不便使用——标准 WSL 内核不支持大容量存储设备,且无法同时通过 usbipd 访问 VCP。
方式三:stm32flash(串口引导加载)
stm32flash通过 STM32 片内 ROM 引导加载器经串口烧录,需要将 RST 引脚接 DTR、BOOT0 接 RTS。RIOT 提供STM32FLASH_RESET与STM32FLASH_RESET_INVERT两个变量控制复位时序:
make BOARD=nucleo-f412zg PROGRAMMER=stm32flash STM32FLASH_RESET=1 flashSTM32FLASH_RESET=1:先拉高 BOOT0 复位进入引导加载器,烧录完成后再次复位(BOOT0 拉低)进入应用。STM32FLASH_RESET_INVERT:默认1,表示假定 TTL 适配器的 RTS/DTR 信号为反相;若你的适配器为非反相,则设为0。
访问 RIOT Shell:ST-Link VCP 串口
Nucleo-144 公共文档 boards/common/nucleo144/doc.md 明确说明:默认的 RIOT Shell 通过 ST-Link VCP(虚拟串口)访问。不同 Nucleo 型号使用的 USART 各不相同,具体实例需查看对应板的include/periph_conf.h。对nucleo-f412zg而言,这个串口就是上文 UART 配置表中的USART3(UART_0,PD8/PD9)。
连接参数:
- 波特率:115200
- 数据格式:8N1(8 数据位、无校验、1 停止位)
在 Linux 下可用minicom -D /dev/ttyACM0(设备节点可能因系统而异)或执行:
make BOARD=nucleo-f412zg termmake term会自动读取Makefile.include中继承的串口配置打开终端。进入 Shell 后即可执行help查看可用命令,或运行ps、ifconfig(需网络模块)等 RIOT 内置命令验证系统状态。
与姊妹板 nucleo-f413zh 的关系
nucleo-f412zg与 boards/nucleo-f413zh/ 为同一 PCB 上的孪生兄弟:两者共用同一份引脚图(原文档中的 pinout 图即为 "nucleo-f412zg-and-f413zh.svg"),差异仅在 MCU 型号(F412ZG 与 F413ZH)及随之而来的外设能力。在阅读 ST 官方 UM1974 用户手册第 34 页的 144 引脚排布图时,两份文档可互相参考;两板的periph_conf.h结构也基本同源,便于对比学习。
小结与扩展阅读
nucleo-f412zg板级支持完整覆盖了 RIOT 从构建到烧录再到 Shell 交互的整条链路:Makefile.features声明外设能力,periph_conf.h落实引脚映射与时钟,Kconfig 统一配置入口,ST-Link/OpenOCD/cpy2remed/stm32flash 提供多路径烧录方案,而 VCP 串口(USART3,115200 8N1)则是默认的调试交互通道。
如需进一步深入,推荐在仓库中继续阅读:
- boards/common/nucleo144/doc.md:Nucleo-144 系列公共说明与 Shell 访问说明;
- boards/nucleo-f413zh/doc.md:孪生板卡文档,对照阅读更易理解差异;
- doc/guides/board_specific/stm32.md:STM32 全系烧录指南(OpenOCD / MSD / stm32flash);
- doc/guides/getting-started/flashing.mdx 与 doc/guides/build-system/flashing.md:RIOT 通用烧录与构建系统机制;
- doc/guides/advanced_tutorials/porting_boards.mdx:若想将自己的开发板移植进 RIOT,本文介绍的整套文件结构即是最佳模板。
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考