RIOT 中 Arduino Nano 33 IoT 板级支持详解:从烧录流程到 SAMD21 外设配置
发布时间:2026/9/17 8:37:46来源:尧图网络
RIOT 中 Arduino Nano 33 IoT 板级支持详解从烧录流程到 SAMD21 外设配置【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT本文以 RIOT 的 boards/arduino-nano-33-iot 板级文档为主体围绕 Arduino Nano 33 IoT 这块 Sigfox/SAMD21 学习开发板完整讲解如何在 RIOT 中编译、烧录该板子make BOARDarduino-nano-33-iot以及 STDIO 通过 USB 直接访问的原理TERM_DELAY的含义。在此基础上结合板级源码深入剖析其时钟、UART、I2C、SPI、ADC 等外设配置、Arduino 引导加载器跳转烧录机制和 USB CDC-ACM 复位流程帮助读者从会用深入到理解为什么这样配。一、板子概述Sigfox 连接 SAMD21 主控根据 boards/arduino-nano-33-iot/doc.mdArduino Nano 33 IoT 是一块面向学习与开发的板子其特点是主控为 Atmel现 MicrochipSAMD21 微控制器板上集成 Sigfox 无线连接能力由板载的 NINA-W102 模块提供。这一硬件定位在 RIOT 的板级源码中可以得到印证boards/arduino-nano-33-iot/Makefile.features 声明了CPU samd21、CPU_MODEL samd21g18a即实际运行核是SAMD21G18ACortex-M0该文件同时列出板子提供的全部外设特性FEATURES_PROVIDEDperiph_adc、periph_i2c、periph_pwm、periph_rtc、periph_rtt、periph_spi、periph_timer、periph_uart、periph_usbdev以及高层特性highlevel_stdioUSB CDC-ACM 串口终端boards/arduino-nano-33-iot/Kconfig 中通过select CPU_MODEL_SAMD21G18A将 Kconfig 选项与 CPU 型号绑定保证新构建系统下选择BOARD_ARDUINO_NANO_33_IOT时自动选中对应 CPU 模型。值得注意的是板子没有把 Sigfox 射频模块本身声明为periph_netdev类特性——RIOT 对该板的定位是以 SAMD21 为主控的通用 RIOT 开发板无线连接更多是板子的卖点而非 RIOT 板级驱动的接口。二、编译与烧录make BOARDarduino-nano-33-iot2.1 基本命令原文档给出的烧录示例是make BOARDarduino-nano-33-iot -C examples/basic/hello-world flash即在 examples/basic/hello-world 示例下通过BOARDarduino-nano-33-iot指定目标板make flash会依次完成编译 → 擦除/复位进入烧录器 → 下载镜像。等价地也可以先make编译出固件再用make flash烧写。2.2 烧录器选择BossA 与 ROM_OFFSET该板与 Arduino 生态的一个关键差异点是板子出厂预置了Arduino Zero 系列引导加载器UF2 引导区。RIOT 的烧录流程需要跳过这段引导区这部分逻辑在共享的板级公共目录中实现该板通过 boards/arduino-nano-33-iot/Makefile 引入DIRS $(RIOTBOARD)/common/samdx1-arduino-bootloader对应 boards/common/samdx1-arduino-bootloader/Makefile.include 中的关键配置# Bossa is the default programmer PROGRAMMER ? bossa PROGRAMMERS_SUPPORTED bossa ifeq ($(PROGRAMMER),bossa) ifeq ($(CPU),samd5x) ROM_OFFSET ? 0x4000 else ROM_OFFSET ? 0x2000 endif BOSSA_ARDUINO_PREFLASH yes PREFLASH_DELAY ? 2 endif可以逐条读出其含义默认且目前唯一支持的烧录器是bossaAtmel 官方的 SAM-BA 命令行版因此使用前需要系统里安装bossa并让板子处于可识别状态ROM_OFFSET决定了镜像的起始地址。SAMD21本板为0x20008 KiBSAMD5x 为0x400016 KiB——正是 Arduino 引导加载器占用的 ROM 空间RIOT 固件必须从引导区之后开始布局BOSSA_ARDUINO_PREFLASH yes表示烧录前会走一次预烧录动作向引导区写入预烧录镜像再复位让预置的 Arduino 引导加载器重新进入烧录模式PREFLASH_DELAY ? 2秒是预烧录后等待设备重新枚举的延时与后文TERM_DELAY是同一套机制见makefiles/tools/usb_board_reset.mk中PREFLASH_DELAY/TERM_DELAY的定义。适用前提与限制该烧录路径假设板子保留出厂的 Arduino 引导加载器如果固件刷坏了引导区则需要通过 SWD/JTAG 调试口恢复本文档未覆盖该场景。2.3 USB 自动复位进烧录模式boards_common_samdx1-arduino-bootloader纯串口/USB 烧录时需要一个办法让正在运行的 RIOT 固件主动回到引导加载器。RIOT 用 USB CDC-ACM 串口实现这一复位相关依赖在 boards/common/samdx1-arduino-bootloader/Makefile.depinclude $(RIOTBOARD)/common/makefiles/stdio_cdc_acm.dep.mk # USB Board reset only works if CDC ACM is used ifneq (,$(filter usbus_cdc_acm,$(USEMODULE))) USEMODULE boards_common_samdx1-arduino-bootloader USEMODULE usb_board_reset endif也就是说只有当 STDIO 走 USB CDC-ACMusbus_cdc_acm时USB 板级复位功能才会被自动启用。这与原文档STDIO of RIOT is directly available over the USB port的表述是一体的两面——USB 既是终端又是烧录/复位的通道。复位动作的真正实现在 boards/common/samdx1-arduino-bootloader/reset.c其原理值得展开#define SAMD21_DOUBLE_TAP_ADDR (0x20007FFCUL) #define SAMD21_DOUBLE_TAP_MAGIC_NUMBER (0x07738135UL) void usb_board_reset_in_bootloader(void) { uint32_t *reset_addr (uint32_t *)SAMD21_DOUBLE_TAP_ADDR; *reset_addr (uint32_t)SAMD21_DOUBLE_TAP_MAGIC_NUMBER; usb_board_reset_in_application(); }Arduino Zero 系列引导加载器在重启后并不总是直接跳回应用区它会检查 SRAM 末尾0x20007FFC是否残留一个特定的魔数0x07738135若是则停留在引导加载器模式等待烧录UF2 场景下地址/魔数会变为SAMD21_DOUBLE_TAP_ADDR宏与0xF01669EF由BOOTLOADER_UF2切换。RIOT 固件先写入魔数再触发系统复位引导加载器随即看到魔数而进入烧录等待状态bossa 便可完成下载。这是 SAMD 系 Arduino 板MKR 系列、Nano 33 系列等通用的跳转机制理解了它就能解释为什么拔掉 USB 重插进不了烧录模式这类问题往往与魔数残留/清除有关。三、STDIO 通过 USB 访问与 TERM_DELAY原文档的第三部分说明了串口终端的接入方式STDIO of RIOT is directly available over the USB port. TheTERM_DELAYenvironment variable can be used to add a delay (in second) before opening the serial terminal. The default value is 2s which should be enough in most of the situation.落到源码板子提供highlevel_stdio特性Makefile.features配合usbus_cdc_acm模块后RIOT 的printf/终端输入输出直接映射到 USB CDC-ACM 虚拟串口无需外接 USB-UART 芯片插一根 USB 线即可完成交互TERM_DELAY的定义位于 makefiles/tools/usb_board_reset.mkTERM_DELAY ? 2make term终端在打开串口前会sleep $(TERM_DELAY)等待设备完成 USB 枚举、CDC-ACM 设备节点出现。默认 2 秒对多数情况够用如果系统串口节点挂载慢例如 Linux 下 udev 规则延迟可以导出export TERM_DELAY5再执行make term避免串口打不开的假性故障。从源码结构看usb_board_reset.mk还把它复用为TESTRUNNER_CONNECT_DELAY即自动化测试连接串口前的等待延时也遵循同一变量保证本地交互与 CI 行为一致。四、板级外设配置剖析periph_conf.hboards/arduino-nano-33-iot/include/periph_conf.h 是该板板级抽象层BSP的核心逐项定义了 RIOT 各外设驱动所依赖的硬件参数。4.1 时钟PLL 主频 48 MHz#define CLOCK_USE_PLL (1) #if CLOCK_USE_PLL #define CLOCK_PLL_MUL (47U) /* must be 31 95 */ #define CLOCK_PLL_DIV (1U) #define CLOCK_CORECLOCK (((CLOCK_PLL_MUL 1) * 1000000U) / CLOCK_PLL_DIV) #else #define CLOCK_DIV (1U) #define CLOCK_CORECLOCK (8000000 / CLOCK_DIV) #endif由公式CORECLOCK ((PLL_MUL 1) * 1MHz) / PLL_DIV可得本板核心频为(47 1) * 1MHz / 1 48 MHz。注释中明确了约束PLL 电路最低需要 32 MHz 才能锁定最高 96 MHz因此CLOCK_PLL_MUL必须在31~95之间关闭 PLLCLOCK_USE_PLL 0时直接用内部 8 MHz 振荡器分频CORECLOCK 8MHz / DIV功耗略优但时钟抖动更大文档明确警告知道自己在做什么再用且核心频率不建议低于 1 MHz。这一 PLL 选择同时影响定时器时基下面定时器配置中CLOCK_USE_PLL生效时通用时钟源选SAM0_GCLK_1MHZ1 MHz 精准时钟否则用SAM0_GCLK_MAIN。4.2 定时器TC3/TC4static const tc32_conf_t timer_config[] { { /* Timer 0 - System Clock */ .dev TC3, .irq TC3_IRQn, .pm_mask PM_APBCMASK_TC3, .gclk_ctrl GCLK_CLKCTRL_ID_TCC2_TC3, .gclk_src SAM0_GCLK_1MHZ, .flags TC_CTRLA_MODE_COUNT16, }, { /* Timer 1 */ .dev TC4, .irq TC4_IRQn, .pm_mask PM_APBCMASK_TC4 | PM_APBCMASK_TC5, .gclk_ctrl GCLK_CLKCTRL_ID_TC4_TC5, .gclk_src SAM0_GCLK_1MHZ, .flags TC_CTRLA_MODE_COUNT32, } }; #define TIMER_0_MAX_VALUE 0xffff #define TIMER_0_ISR isr_tc3 #define TIMER_1_ISR isr_tc4 #define TIMER_NUMOF ARRAY_SIZE(timer_config)两个硬件定时器TIMER_0用 TC316 位计数TIMER_0_MAX_VALUE 0xffff系统调度时钟TIMER_1用 TC432 位计数供ztimer等使用。注意 TC4 的电源掩码同时覆盖了 TC4/TC5PM_APBCMASK_TC4 | PM_APBCMASK_TC5因为 SAMD 系列成对的 TCx 共享时钟/电源域。4.3 UARTSERCOM5 占用 PB22/PB23static const uart_conf_t uart_config[] { { .dev SERCOM5-USART, .rx_pin GPIO_PIN(PB, 23), .tx_pin GPIO_PIN(PB, 22), .mux GPIO_MUX_D, .rx_pad UART_PAD_RX_3, .tx_pad UART_PAD_TX_2, .flags UART_FLAG_NONE, .gclk_src SAM0_GCLK_MAIN, } };板子只定义了 1 路硬件 UARTSERCOM5引脚复用为 D 口RXPB23、TXPB22。这与 USB 直连 STDIO 并不冲突——usbus_cdc_acm提供终端交互而这路 UART 可用于调试输出或其他串口外设如调试器重映射、扩展模块等。4.4 PWMTCC0/TCC1 共 4 通道static const pwm_conf_chan_t pwm_chan0_config[] { /* GPIO pin, MUX value, TCC channel */ { GPIO_PIN(PA, 4), GPIO_MUX_E, 0}, { GPIO_PIN(PA, 5), GPIO_MUX_E, 1}, }; static const pwm_conf_chan_t pwm_chan1_config[] { { GPIO_PIN(PA, 10), GPIO_MUX_E, 0 }, { GPIO_PIN(PA, 11), GPIO_MUX_E, 1 }, };PWM0 对应 TCC0PA4/PA5PWM1 对应 TCC1PA10/PA11均以SAM0_GCLK_MAIN48 MHz为时基。注意这些引脚在 Nano 33 IoT 上多引出到 32.768 kHz RTC 晶振相关脚位实际使用时要结合丝印确认走线用途。4.5 ADC8 个模拟通道A0~A7#define ADC_PRESCALER ADC_CTRLB_PRESCALER_DIV512 #define ADC_NEG_INPUT ADC_INPUTCTRL_MUXNEG_GND #define ADC_GAIN_FACTOR_DEFAULT ADC_GAIN_FACTOR_DEFAULT ADC_INPUTCTRL_GAIN_1X #define ADC_REF_DEFAULT ADC_REFCTRL_REFSEL_INT1V static const adc_conf_chan_t adc_channels[] { { .inputctrl ADC_INPUTCTRL_MUXPOS_PA02 }, /* A0 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PB02 }, /* A1 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PA11 }, /* A2 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PA10 }, /* A3 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PB08 }, /* A4 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PB09 }, /* A5 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PA09 }, /* A6 */ { .inputctrl ADC_INPUTCTRL_MUXPOS_PB03 }, /* A7 */ };默认参数组合预分频 512、反相端接地单端采样、增益 1×、基准为内部 1V 参考INT1V。因此满量程读数对应约 1V若用 3.3V 电源激励传感器需要按比例折算或改为外部基准。8 个通道与板子标号的 A0~A7 一一对应方便与 Arduino 侧的引脚命名对照。4.6 I2C 与 SPI一路 I2C 两路 SPIstatic const i2c_conf_t i2c_config[] { { .dev (SERCOM4-I2CM), .speed I2C_SPEED_NORMAL, .scl_pin GPIO_PIN(PB, 9), .sda_pin GPIO_PIN(PB, 8), .mux GPIO_MUX_D, .gclk_src SAM0_GCLK_MAIN, .flags I2C_FLAG_NONE } };I2C0 使用 SERCOM4SCLPB9、SDAPB8标准速率100 kbit/s 档的I2C_SPEED_NORMAL。SPI 定义了两路{ /* Connected to NINA W102 */ .dev SERCOM2-SPI, .miso_pin GPIO_PIN(PA, 13), .mosi_pin GPIO_PIN(PA, 12), .clk_pin GPIO_PIN(PA, 15), ... }, { .dev SERCOM3-SPI, .miso_pin GPIO_PIN(PA, 19), .mosi_pin GPIO_PIN(PA, 16), .clk_pin GPIO_PIN(PA, 17), ... }第一路 SPISERCOM2PA12/PA13/PA15的注释明确写着Connected to NINA W102——即 Sigfox 射频模块就是挂在这路 SPI 上的第二路SERCOM3PA16/PA19/PA17则供板载扩展使用。这意味着若 RIOT 侧要用periph_spi的 SPI0与 NINA-W102 存在引脚占用关系实际驱动 Sigfox 模组需要走模块私有协议而非通用 SPI 抽象选型时要留意。4.7 RTC 与 USBRTT 默认频率 32768 Hz板载 32.768 kHz 晶振与periph_rtt/periph_rtc特性对应USB 设备配置sam0_common_usb_config_t绑定 PA24DM/PA25DPMUX_G这正是 STDIO-over-USB 与 USB 复位机制的硬件基础periph_usbdev特性由此提供供usbus_cdc_acm、usb_board_reset等上层模块使用。五、LED 与 SAUL最小可运行入口boards/arduino-nano-33-iot/include/board.h 定义了板载 LED#define LED0_PIN GPIO_PIN(PA, 17) #define LED_PORT PORT-Group[PA] #define LED0_MASK (1 17) #define LED0_ON (LED_PORT.OUTSET.reg LED0_MASK) #define LED0_OFF (LED_PORT.OUTCLR.reg LED0_MASK) #define LED0_TOGGLE (LED_PORT.OUTTGL.reg LED0_MASK) #define LED0_NAME LED(Yellow)LED0 是板载黄色 LEDPA17直接通过 PORT 寄存器的OUTSET/OUTCLR/OUTTGL原子位操作翻转避免读-改-写的竞态。同时boards/arduino-nano-33-iot/include/gpio_params.h 把它注册进 SAULRIOT 的传感器/GPIO 统一抽象static const saul_gpio_params_t saul_gpio_params[] { { .name LED0_NAME, .pin LED0_PIN, .mode GPIO_OUT }, };配合 boards/arduino-nano-33-iot/Makefile.dep 中的ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE saul_gpio endif只要应用启用saul_default大多数示例默认启用saul_gpio会自动挂入从而saul传感器示例如examples/guides/saul类应用可以直接通过设备名LED(Yellow)找到并控制这枚 LED——不需要为板子写任何额外代码。六、实战小结一条完整的开发闭环综合以上分析在这块板子上跑 RIOT 的完整闭环是编译烧录make BOARDarduino-nano-33-iot -C examples/basic/hello-world flash。构建系统自动带上boards_common_samdx1-arduino-bootloader依赖bossa 以ROM_OFFSET0x2000跳过 Arduino 引导区完成下载打开终端make term或手动打开 USB CDC-ACM 虚拟串口如 Linux 下的/dev/ttyACM0。STDIO 直接走 USB无需串口芯片打开慢时export TERM_DELAY5再试复位再烧录应用层调用 USB 板级复位usb_board_reset后固件会把魔数0x07738135写入0x20007FFC并复位Arduino 引导加载器检测到魔数即停留于烧录模式等待下一次 bossa 下载。需要注意的前提该流程依赖板子保留出厂 Arduino Zero 引导加载器、系统已安装bossa且 STDIO/复位链路走 USB CDC-ACM。所有配置参数的依据均可在 boards/arduino-nano-33-iot 板级目录下的periph_conf.h、board.h、Makefile.features、Makefile.dep及共享的 boards/common/samdx1-arduino-bootloader 目录中核对。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网