RIOT OS 正交解码器(QDEC)外设测试指南:X4 模式下的脉冲计数验证
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
正交解码器(Quadrature Decoder,QDEC)是旋转编码器、电机转速测量等场景的核心外设。本文以 RIOT OS 仓库中的tests/periph/qdec测试程序为对象,完整讲解该测试的设计思路、硬件接线、编译烧录方法与预期结果,并结合drivers/include/periph/qdec.h接口定义与 nRF5x/STM32 平台的底层实现,帮助读者理解 QDEC 驱动在 RIOT 中的工作方式,以及如何将该测试移植到自己的板卡上。
测试目标与预期结果
tests/periph/qdec是 RIOT 操作系统中针对底层 QDEC 驱动(low-level QDEC driver)的测试程序。其核心目的正如 tests/periph/qdec/README.md 所述:
如果一切按预期运行,你应该在每一个配置了 QDEC 的设备上看到 X4 模式的正交解码。
也就是说,该测试的唯一预期结果是:程序正确初始化所有已声明的 QDEC 设备,并在 X4 模式下持续输出计数器数值。只要每个 QDEC 通道每秒都能打印出符合输入信号的计数值,测试即视为通过。
程序行为:初始化、计数与回调
测试程序本体位于 tests/periph/qdec/main.c,逻辑非常简洁,由"初始化"和"周期读取"两个阶段组成。
阶段一:初始化所有 QDEC 设备
程序遍历全部 QDEC 设备,逐一调用qdec_init():
for (i = 0; i < QDEC_NUMOF; i++) { int32_t error = qdec_init(QDEC_DEV(i), QDEC_X4, handler, (void *)(uintptr_t)i); if (error) { fprintf(stderr, "Mode not supported!\n"); return error; } }这里的关键要素包括:
QDEC_NUMOF:由板级配置(periph_conf.h)中定义的设备数量宏,见 drivers/include/periph/qdec.h 中的默认访问宏QDEC_DEV(x)。QDEC_X4:解码模式,即对 A、B 两路信号的上升沿与下降沿全部计数。handler:溢出中断回调。QDEC 计数器溢出时会触发中断并调用该回调,回调内调用qdec_read_and_reset()将计数器清零:
static void handler(void *arg) { qdec_t qdec = (qdec_t)(uintptr_t)arg; printf("QDEC %u counter overflow : reset counter\n", qdec); qdec_read_and_reset(QDEC_DEV(qdec)); }若某个设备不支持请求的模式,qdec_init()返回错误码,程序打印Mode not supported!并退出。这正与 README 中"X4 模式解码"的预期一致:本测试严格要求在 X4 模式下运行。
阶段二:每秒读取并清零计数
初始化完成后,主循环每秒对每个 QDEC 通道执行"读值—清零—打印":
while (1) { for (i = 0; i < QDEC_NUMOF; i++) { value = qdec_read_and_reset(QDEC_DEV(i)); printf("QDEC %lu = %ld\n", (unsigned long int)i, (long int)value); } xtimer_sleep(1); }由此可以得到每秒的脉冲计数增量,方便直接对比输入信号的频率验证解码是否正确。xtimer_sleep(1)依赖xtimer模块,已在 tests/periph/qdec/Makefile 中通过USEMODULE += xtimer引入。
QDEC 驱动接口与三种解码模式
qdec_init()、qdec_read()、qdec_read_and_reset()、qdec_start()、qdec_stop()五个 API 构成了 RIOT 的 QDEC 外设接口,定义在 drivers/include/periph/qdec.h。
正交信号与方向判定原理
正交编码器输出两路相位相差 90° 的方波 A 与 B。驱动通过检测一路信号的边沿并检查另一路信号的电平来判断旋转方向。qdec.h的文档给出了完整的 8 种判定情形(节选核心规则):
- A 上升沿且 B 为高 → 顺时针,计数加一;
- A 上升沿且 B 为低 → 逆时针,计数减一;
- B 下降沿且 A 为高 → 顺时针,计数加一;依此类推。
据此衍生出三种解码模式:
| 模式 | 计数边沿 | 对应情形 |
|---|---|---|
QDEC_X1 | 仅 A 上升沿 | 情形 1、2 |
QDEC_X2 | A 上升沿 + 下降沿 | 情形 1~4 |
QDEC_X4 | A、B 全部边沿 | 全部 8 种情形 |
qdec_mode_t枚举即对应这三种模式:
typedef enum { QDEC_X1, /* X1 mode */ QDEC_X2, /* X2 mode */ QDEC_X4, /* X4 mode */ } qdec_mode_t;X4 模式分辨率最高(每个编码器周期产生 4 个计数脉冲),这也是本测试指定使用该模式的原因。
初始化前置条件
接口文档特别强调:调用qdec_init()前,目标设备必须处于非活动状态——要么从未初始化过,要么已通过qdec_stop()停止。换言之,重复初始化前必须先停止设备,这是驱动层保证状态一致性的约束。
硬件接线:以 nucleo-f401re 为例
测试程序打印的接线说明(对应 STM32 平台)为:
QDEC0 : signal A on PA6 and signal B on PA7 QDEC1 : signal A on PB6 and signal B on PB7即默认板卡nucleo-f401re(tests/periph/qdec/Makefile 中BOARD ?= nucleo-f401re)配置了两个 QDEC 通道。若使用该板卡,应将编码器的 A/B 两路信号分别接到上述引脚,并保证共地。STM32 平台上的 QDEC 实现基于定时器的编码器接口模式,相关代码见 cpu/stm32/periph/qdec.c。
编译与烧录运行
在仓库根目录执行:
make -C tests/periph/qdec flash termmake -C指定测试目录;flash负责编译并烧录,term打开串口终端查看输出。- 若交叉编译工具链未加入
PATH,可先执行source /path/to/RIOT/RIOT/dist/tools/export之类的方式导出工具链环境(以实际部署为准)。 - 通过
BOARD=...可覆盖默认板卡,例如make -C tests/periph/qdec BOARD=nrf52840dk flash term。
正常运行时应看到类似输出:
Welcome into Quadrature Decoder (QDEC) test program. This program will count pulses on all available QDEC channels ... QDEC 0 = 42 QDEC 1 = 0每秒打印一次各通道计数值,用手转动编码器时对应计数值应随之增减,即为 README 所述"X4 模式解码正常"的直观体现。
移植到其他板卡:nRF52840 DK 的定制示例
测试通过EXTERNAL_BOARD_DIRS机制附带了一个定制板卡目录,用于在默认没有 QDEC 配置的板卡上快速启用该外设:
EXTERNAL_BOARD_DIRS = $(CURDIR)/boards_modded定制板卡的组织结构
boards_modded/nrf52840dk_mod目录包含:
Makefile:将模块命名为board_nrf52840dk_qdec(刻意与native实现的板卡名区分),并通过DIRS += $(RIOTBOARD)/nrf52840dk复用官方 nRF52840 DK 板卡目录;Makefile.features:FEATURES_PROVIDED += periph_qdec,向系统声明该板卡提供 QDEC 外设功能;Makefile.dep:USEMODULE += board_nrf52840dk_qdec引入定制模块;include/periph_conf.h:定义 QDEC 的引脚与参数配置。
QDEC 配置结构体
periph_conf.h中通过qdec_conf_t数组声明设备,nRF52840 DK 定制示例的配置为:
static const qdec_conf_t qdec_config[] = { { .a_pin = GPIO_PIN(0, 11), /* Button 1 */ .b_pin = GPIO_PIN(0, 12), /* Button 2 */ .led_pin = GPIO_PIN(0, 13), /* And the first LED */ .sample_period = QDEC_SAMPLEPER_SAMPLEPER_128us, .debounce_filter = true, .led_active_state = false }, }; #define QDEC_NUMOF ARRAY_SIZE(qdec_config)各字段含义与 nRF5x 平台实现(cpu/nrf5x_common/periph/qdec.c)对应如下:
a_pin/b_pin:正交信号 A、B 输入引脚,初始化时被配置为上拉输入(GPIO_IN_PU),并写入PSEL.A/PSEL.B寄存器;led_pin:可选 LED 指示引脚。为有效引脚时写入PSEL.LED并依据led_active_state设置LEDPOL;为GPIO_UNDEF时则断开(写入CONNECT掩码);sample_period:采样周期,用于设置SAMPLEPER寄存器,决定信号采样的时间分辨率;debounce_filter:使能硬件去抖滤波,写入DBFEN寄存器,适合有抖动干扰的机械编码器场景。
QDEC_NUMOF由ARRAY_SIZE(qdec_config)自动推导,与main.c的遍历循环直接联动。此外qdec_init()内部通过断言校验sample_period不超过QDEC_SAMPLEPER_SAMPLEPER_131ms,移植时应注意取值边界。
nRF5x 平台实现要点
从 cpu/nrf5x_common/periph/qdec.c 可以看出该平台的几个关键约束与行为:
- 仅支持 X4 模式:
if (mode != QDEC_X4) return -EINVAL;,nRF5x 的 QDEC 外设天然对所有边沿计数,不支持 X1/X2; - 溢出回调可选:传入非空
cb时使能ACCOF中断并注册isr_qdec,否则关闭中断。isr_qdec只服务唯一可用的 QDEC 设备(QDEC_DEV(0)); - 读值即清零:
qdec_read_and_reset()通过触发TASKS_RDCLRACC任务寄存器读取并清除ACC累加寄存器,实现原子性的"读后清零"; - 停止语义:
qdec_stop()触发TASKS_STOP后忙等EVENTS_STOPPED事件,确保外设真正停止后才返回。
这也解释了 README 的措辞:既然 nRF5x 只支持 X4,那么"在每个配置了 QDEC 的设备上看到 X4 模式解码"就是该平台测试通过的唯一标准。
如何扩展测试到更多板卡
若要在其他支持periph_qdec功能的板卡上运行本测试,只需:
- 确认板卡
periph_conf.h中定义了qdec_config[]数组与QDEC_NUMOF宏(可在FEATURES_PROVIDED中声明periph_qdec,或参考nrf52840dk_mod通过EXTERNAL_BOARD_DIRS定制); - 按
qdec_conf_t结构体字段配置 A/B 引脚、采样周期、去抖与 LED 选项; - 以
BOARD=<板卡名>编译运行,观察每秒打印的计数是否随编码器输入变化。
仓库中另提供native与 STM32 平台的参考实现(cpu/native/periph/qdec.c、cpu/stm32/periph/qdec.c),可作为理解接口语义与移植实现的对照素材。
总结
tests/periph/qdec是一个聚焦"X4 模式正交解码"的轻量级外设冒烟测试:初始化全部 QDEC 通道、注册溢出回调、每秒读值清零并打印。通过它既能快速验证硬件接线与板级periph_conf.h配置是否正确,也能从 drivers/include/periph/qdec.h 与 nRF5x/STM32 实现中理解正交信号解码、三种模式差异、溢出中断回调等底层机制,是学习 RIOT 外设驱动与进行板卡移植验证的良好起点。
【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考