news 2026/9/20 20:16:10

RIOT OS 引脚定位工具:借助 soft_uart 逐引脚 bit-bang 引脚名定位 GPIO 映射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RIOT OS 引脚定位工具:借助 soft_uart 逐引脚 bit-bang 引脚名定位 GPIO 映射
  • 物联网
  • 嵌入式
  • 操作系统
  • 实时系统

【免费下载链接】RIOT

RIOT - The friendly OS for IoT

项目地址:https://gitcode.com/GitHub_Trending/riot/RIOT
点击查看免费下载

本指南深入讲解 RIOT OS 测试应用tests/periph/uart_locate_pins(源码位于 tests/periph/uart_locate_pins):它通过软件 UART(soft_uart模块)在每一个 GPIO 引脚上以 9600 Bd 的符号率逐位发送该引脚自身的名字,从而帮助开发者在没有板卡原理图或引脚标注不明时,仅凭一个 UART 适配器逐脚探测即可确认 GPIO 编号。读完本文,你将掌握该工具的工作原理、消息编码格式、构建与使用步骤,以及如何定制引脚探测列表、修改波特率,并从源码层面理解软件 UART 的位时序实现。

这个工具解决什么问题

在实际嵌入式开发中,经常遇到以下场景:

  • 拿到了没有原理图、或丝印不清的评估板,无法确认某个物理引脚对应的GPIO_PIN(port, pin)编号;
  • 芯片的硬件 UART 引脚被复用、或者硬件 UART 资源不足,想临时用任意 GPIO 输出调试信息;
  • 需要确认某个引脚是否可用作输出(例如确认其没有被连接到晶体、复位电路等特殊外设)。

uart_locate_pins正是为这种"盲探引脚"场景设计的:它在每个 GPIO 上通过软件模拟的 UART 反复发送一行文本,文本内容就是当前引脚的名字。只要把 UART 适配器依次接到各个引脚上,从串口终端读到的内容就能立刻告诉你"你现在接的是哪个引脚"。

工作原理:软件 UART 逐引脚广播引脚名

该应用的思路非常直接(见 main.c):

  1. 维护一个待探测引脚的列表(pins[]数组);
  2. 循环遍历该列表,对每个引脚调用soft_uart_init()初始化软件 UART;
  3. 构造一行包含引脚编号与引脚字母名的文本;
  4. soft_uart_write()把该文本逐位(bit-bang)发送出去;
  5. 发送完成后调用soft_uart_poweroff()关闭 UART,再继续下一个引脚,如此无限循环。

它没有使用芯片的硬件 UART 外设,而是完全依靠soft_uart驱动——一个纯软件实现的 UART,由通用定时器(timer)+ 普通 GPIO 配合产生串口波形,因此任何支持periph/gpioperiph/timer的 RIOT 板卡都可以运行,不依赖硬件 UART 引脚。根据 drivers/include/soft_uart.h 的说明,该模块正是用于"硬件 UART 不可用"的场合;需要注意的是当前实现收发不能同时进行,因此本工具只使用发送方向(TX)。

引脚消息的编码格式

main.c中发送的每行消息由两段组成(对应fmt模块的格式化函数,见 main.c):

P<port>.<pin> / P<字母><pin>\n

其中:

  • P<port>.<pin>:以数字形式给出端口号与引脚号,例如P0.0表示 port 0、pin 0;
  • P<字母><pin>:以字母形式给出端口('A' + port_num)与引脚号,例如PA0,即 GPIO A 的第 0 脚;
  • 行尾是\n换行符,便于串口终端逐行显示。

两种表示法同时输出,方便与芯片数据手册中的命名(如PA0)以及 RIOT 的GPIO_PIN(0, 0)写法互相印证。消息通过fmt_u16_dec()把无符号整数转成十进制字符串,再逐字节送入soft_uart_write(),整个过程不依赖printf和 stdio——这正是该测试在 Makefile 中引入stdio_null模块的原因(见下文)。

源码剖析:从引脚表到发送循环

1. 待探测引脚列表

所有被探测的引脚集中定义在main.c顶部的静态数组(main.c):

static const struct { uint8_t port_num; uint8_t pin_num; } pins[] = { { .port_num = 0, .pin_num = 0, }, };

源码注释(main.c)特别提醒:请根据实际需要调整这个列表,不一定要包含所有引脚——例如连接到外部晶体的引脚被强制驱动为输出时可能表现异常,应当从列表中排除。默认模板只探测P0.0PA0),实际使用前需要把它展开成目标芯片的全部 GPIO。

2. 软件 UART 配置

应用为唯一的软件 UART 实例定义了配置(main.c):

soft_uart_conf_t soft_uart_config[] = { { .tx_pin = GPIO_UNDEF, .rx_pin = GPIO_UNDEF, .tx_timer = TIMER_DEV(0), .timer_freq = MHZ(1), }, };
  • tx_pin/rx_pin初始为GPIO_UNDEF,因为发送引脚会在主循环里被动态切换为当前探测的引脚;
  • tx_timer使用定时器 0(TIMER_DEV(0));
  • timer_freq = MHZ(1),即定时器以 1 MHz 工作。该值必须能被波特率整除才有良好的时序精度(soft_uart驱动对此有专门的校验,见下文"底层实现"一节)。

需要说明的是,这个配置文件覆盖soft_uart模块自带的默认配置。驱动默认参数位于 drivers/soft_uart/include/soft_uart_params.h:默认rx_pin/tx_pin均为GPIO_UNDEF、RX 定时器为 timer 0、TX 定时器为 timer 1、频率MHZ(1)、标志为 0。而本应用通过自定义的 include/soft_uart_params.h 重定义了soft_uart_config数组(extern声明)并固定SOFT_UART_NUMOF为 1,正如其文件头注释所说:这是为了在"每次切换引脚配置"时节省内存、避免使用驱动默认的静态参数数组。

3. 主循环:切换引脚 → 初始化 → 发送 → 关闭

主循环(main.c)核心逻辑如下:

while (1) { for (unsigned i = 0; i < ARRAY_SIZE(pins); i++) { gpio_t pin = GPIO_PIN(pins[i].port_num, pins[i].pin_num); soft_uart_config[0].tx_pin = pin; soft_uart_init(0, UART_SYMBOL_RATE, NULL, NULL); /* ...构造 "P0.0 / PA0\n" 消息... */ soft_uart_write(0, (void *)buf, (size_t)pos - (size_t)buf); soft_uart_poweroff(0); } }

关键点:

  • GPIO_PIN(port, pin)把 (端口号, 引脚号) 组合成 RIOT 的gpio_t句柄;
  • soft_uart_init(0, UART_SYMBOL_RATE, NULL, NULL)传入NULL回调,表示只初始化TX 发送模式(见 drivers/include/soft_uart.h 的说明);
  • soft_uart_write()是阻塞发送——它会等到全部字节发送完毕才返回(drivers/include/soft_uart.h),保证当前引脚发完消息后才切换到下一个;
  • 发送完毕调用soft_uart_poweroff(),把该引脚释放,接着探测下一个引脚,外层while(1)让整个扫描无限循环,因此你可以在任意时刻接入 UART 适配器,等待当前周期轮询到该引脚即可收到消息。

4. 波特率可配置

波特率通过编译期宏UART_SYMBOL_RATE提供,默认 9600(main.c):

#ifndef UART_SYMBOL_RATE # define UART_SYMBOL_RATE 9600 #endif

也就是说,即使不修改源码,也可以用CFLAGS += -DUART_SYMBOL_RATE=...make CFLAGS+=-DUART_SYMBOL_RATE=115200调整符号率。9600 Bd 是 UART 适配器与串口工具最通用的默认档位,绝大多数 USB-TTL 转换器无需额外配置即可使用。

构建与运行

Makefile 要点

应用的构建配置位于 Makefile,值得注意的有几点:

BOARD ?= nucleo-f767zi # 让本应用的 soft_uart_params.h 优先于驱动自带的版本被找到 INCLUDES += -I$(abspath $(CURDIR))/include include ../Makefile.periph_common USEMODULE += soft_uart USEMODULE += fmt # 不需要 stdio USEMODULE += stdio_null # 本应用不需要多线程 DISBALE_MODULE += core_thread
  • 默认板卡nucleo-f767zi(Nucleo-144 上的 STM32F767ZI),可通过BOARD=...覆盖;
  • 头文件路径注入INCLUDES += -I.../include保证链接器优先找到本应用自定义的 soft_uart_params.h,而不是驱动目录下的默认版本(Makefile 注释明确说明了这一目的);
  • 模块选择:启用soft_uartfmt(轻量整数格式化),并通过stdio_null关闭标准输出——因为本应用不需要 stdio;
  • 裁剪内核DISBALE_MODULE += core_thread移除了多线程支持,进一步减小镜像体积(注意此处是 Makefile 中实际的拼写,RIOT 的模块裁剪机制会据此跳过对应模块);
  • 同时继承 tests/periph/Makefile.periph_common 与 tests/Makefile.tests_common(后者默认BOARD ?= native、默认开启DEVELHELP,并给自动化测试注入交互同步与栈使用统计模块)。

编译、烧录与使用步骤

在 RIOT 仓库根目录下,按以下步骤操作:

# 1. 进入测试目录 cd tests/periph/uart_locate_pins # 2. 编译(默认板卡 nucleo-f767zi,也可显式指定其他板卡) make BOARD=nucleo-f767zi # 3. 烧录到目标板 make BOARD=nucleo-f767zi flash # 4. 打开串口终端(波特率 9600,8N1) # 例如:minicom -D /dev/ttyUSB0 -b 9600

然后:

  1. 把 USB-TTL UART 适配器的RX引脚连接到目标板上的任意一个 GPIO;
  2. 适配器的GND必须与目标板共地;
  3. 观察串口终端输出,例如收到P0.0 / PA0,就说明当前接的正是PA0
  4. 依次把适配器换到其他引脚,逐一记录每个引脚对应的编号;
  5. 若某引脚始终无输出,则该引脚可能是特殊用途引脚(如晶体/复位/电源),或已被从pins[]列表中排除。

每次换脚无需重新烧录——应用会无限循环轮询整个引脚表。

软件 UART 底层原理:位时序如何生成

要理解为什么这个工具能在任意 GPIO 上"模拟"出 9600 Bd 的串口,需要深入soft_uart驱动的实现(drivers/soft_uart/soft_uart.c)。

波特率到定时器节拍的换算

初始化时,驱动根据配置的定时器频率计算每一位对应的定时器 tick 数(soft_uart.c):

ctx->bit_time = (cfg->timer_freq + baudrate / 2) / baudrate; unsigned accuracy = (100 * cfg->timer_freq / ctx->bit_time) / baudrate; if (accuracy > 110 || accuracy < 90) { return UART_NOBAUD; }
  • bit_time= 每个数据位的定时器计数个数(四舍五入取整);
  • 驱动还校验换算精度:若(100 * timer_freq / bit_time) / baudrate落在 90%~110% 区间之外,则直接返回UART_NOBAUD拒绝该波特率。这正是配置里要求timer_freqMHZ(1)(1 MHz)的原因——1 MHz 对 9600 属于整除关系(1,000,000 / 9600 ≈ 104.17 tick/位),精度足够通过校验。

发送:定时器中断驱动逐位翻转 GPIO

发送一字节的逻辑在soft_uart_write_byte()(soft_uart.c)与定时器回调_tx_timer_cb()(soft_uart.c)中:

  1. 把待发字节左移 1 位,空出的最低位作为起始位(低电平)
  2. 按 8 数据位 + 1 停止位的帧结构,把 8 个数据位和停止位拼进byte_tx
  3. timer_set_periodic()让定时器以bit_time为周期周期性触发中断;
  4. 每次定时器回调把byte_tx的最低位写进gpio_write(cfg->tx_pin, ...),然后右移一位,直到整帧发完,清定时器并释放同步信号量;
  5. soft_uart_write()(soft_uart.c)在持有互斥锁的前提下逐字节调用上述过程,实现整条消息的阻塞发送。

因此,每个引脚的"bit-bang"本质上是:GPIO 被配置为普通推挽输出,由定时器中断在每个位时刻精确翻转电平,从而在引脚上产生标准的 UART 帧波形。这也解释了为什么soft_uart_config里必须指定一个可用的硬件定时器,以及为什么tx_timer/timer_freq的选取直接决定能否在目标波特率下稳定工作。

periph/uart的接口一致性

从 drivers/include/soft_uart.h 可以看到,soft_uart_initsoft_uart_modesoft_uart_writesoft_uart_poweron/poweroff的签名刻意与periph/uart保持一致,并复用uart_rx_cb_t等类型。这意味着soft_uart可以作为硬件 UART 的即插即用替代品,本测试应用正是利用了这一设计,把"任意 GPIO"临时变成串口 TX。

定制指南

根据你的目标板,通常需要改动三处:

定制项位置说明
探测引脚列表main.c 的pins[]展开为芯片全部(或目标)GPIO 的 (port, pin) 对;剔除晶体等特殊引脚
符号率main.c 的UART_SYMBOL_RATE,或编译期-DUART_SYMBOL_RATE=...默认 9600;改大需同步调整串口终端设置,并注意soft_uart的精度校验
软件 UART 定时器/频率main.c 的soft_uart_config[]更换tx_timertimer_freq,频率应为波特率的倍数

此外,include/soft_uart_params.h 通过SOFT_UART_NUMOF 1固定实例数量、以extern复用main.c中可变的配置数组,这一模式在你想把本应用改造成"动态引脚输出调试口"时同样可以复用。

总结

tests/periph/uart_locate_pins是 RIOT 生态中一个非常实用的小工具:它把soft_uart驱动的"任意 GPIO 可变身为串口 TX"能力发挥到极致,用逐引脚 bit-bang 引脚名的方式,把未知板卡的引脚映射问题转化为"一根 UART 适配器 + 一个串口终端"就能解决的简单探测过程。其核心价值在于:

  • 零硬件 UART 依赖:完全基于软件 UART,任何支持 timer + GPIO 的板卡可用;
  • 消息自解释:同时输出P0.0PA0两种格式,方便对照数据手册;
  • 即改即用:通过pins[]数组、UART_SYMBOL_RATE宏与自定义soft_uart_config[]即可适配任意目标板。

如果你正在为一款引脚标注不明的开发板编写板级支持,或者想快速确认某颗新芯片的 GPIO 编号,这个工具可以让你在几分钟内拿到完整的引脚对照表。

  • 物联网
  • 嵌入式
  • 操作系统
  • 实时系统

【免费下载链接】RIOT

RIOT - The friendly OS for IoT

项目地址:https://gitcode.com/GitHub_Trending/riot/RIOT
点击查看免费下载

相关推荐

上一篇:Open NSynth Super多GPU加速:如何高效生成音频嵌入
下一篇:如何在macOS上快速安装Brooklyn屏幕保护程序:终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 20:09:26

STM32F103C8T6驱动AS608指纹模块实战:从硬件选型到识别优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 20:07:05

Buzz 实战指南:免费离线转录,把本地语音转成文字和字幕

Buzz 实战指南&#xff1a;免费离线转录&#xff0c;把本地语音转成文字和字幕 【免费下载链接】buzz Buzz transcribes and translates audio offline on your personal computer. Powered by OpenAIs Whisper. 项目地址: https://gitcode.com/GitHub_Trending/buz/buzz …

作者头像 李华
网站建设 2026/9/20 20:06:42

Simulink AD/DA转换器仿真:采样量化与串并转换链路实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 20:03:11

Lucky 部署与功能实操指南:端口转发、DDNS 与反向代理配置

Lucky 部署与功能实操指南&#xff1a;端口转发、DDNS 与反向代理配置 【免费下载链接】lucky 软硬路由公网神器,ipv6/ipv4 端口转发,反向代理,DDNS,WOL,ipv4 stun内网穿透,cron,acme,rclone,ftp,webdav,filebrowser 项目地址: https://gitcode.com/GitHub_Trending/luc/luck…

作者头像 李华