拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RIOT 外设频率计(FREQM)测试应用解析:从 GPIO 输入测量时钟频率的原理与实践

物联网嵌入式操作系统实时系统【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址https://gitcode.com/GitHub_Trending/riot/RIOT点击查看免费下载导读本文围绕 RIOT 操作系统中的外围设备频率计Frequency MeterFREQM测试应用展开深入讲解该测试如何通过 GPIO 采集外部时钟信号、以片内参考时钟为基准计算被测信号频率并结合drivers/periph/freqm.h驱动接口与cpu/sam0_common/periph/freqm.c的底层实现说明同步阻塞与异步回调两种测量模式、溢出处理机制以及板级配置方法。读完本文你将掌握在 RIOT 上编写、编译、烧录并解读 FREQM 外设测试应用的全流程并能够依据源码原理自行调整测量时长与参考时钟配置。一、FREQM 测试应用概览FREQM 测试应用位于仓库的 tests/periph/freqm/其核心目标是用最简单的方式验证 RIOT 的 FREQM 外设驱动是否正常工作。该应用只有三个文件文件作用README.md说明测试原理与成功输出的预期格式Makefile构建配置声明依赖模块与默认目标板卡main.c测试主程序初始化 FREQM 并执行一次阻塞式频率测量从 README 的描述可以看出测试原理是将一个时钟信号连接到 GPIO 引脚利用片内已知频率的参考时钟去测定该信号的频率。也就是说被测频率 参考时钟频率 ×测量窗口内被测时钟的周期计数值 / 参考时钟计数值这一原理在后续源码分析中会得到完整印证。二、构建配置与运行环境测试应用的 Makefile 内容非常精简BOARD ? same54-xpro include ../Makefile.periph_common USEMODULE periph_freqm include $(RIOTBASE)/Makefile.include逐行解读BOARD ? same54-xpro默认目标板卡为 Microchip SAM E54 Xplained Prosame54-xpro。?表示只有在命令行未显式指定BOARD时才采用该默认值因此可以通过BOARDnative或BOARDxxx覆盖。include ../Makefile.periph_common引入 tests/periph 目录下所有外设测试共享的构建公共片段用于统一定义编译目标、flash/term 等公共目标。USEMODULE periph_freqm显式加入periph_freqm模块这是编译驱动头文件 drivers/include/periph/freqm.h 及其板级实现所必需的。include $(RIOTBASE)/Makefile.include引入 RIOT 主构建系统。编译与烧录的命令为在测试目录下执行make BOARDsame54-xpro make BOARDsame54-xpro flash term注意FREQM 属于需要硬件支撑的外设必须由板卡通过FEATURES_PROVIDED periph_freqm声明该特性后才能正常构建。例如 boards/same54-xpro/Makefile.features 第 18 行即声明了periph_freqm。在 README 中并未罗列所有支持板卡使用时请以目标板卡目录下的Makefile.features是否包含periph_freqm为准。三、测试主程序逐行解读main.c 的完整逻辑如下共约 50 行我们分段分析#include periph/freqm.h int main(void) { puts(FREQM peripheral driver test); /* Initialize frequency meter peripheral */ freqm_init(0); uint32_t period_us UINT32_MAX; uint32_t freq_hz 0; /* Measure in blocking mode */ if (!freqm_frequency_get(0, freq_hz, period_us)) { printf(Measured Clock Frequency: %ld Hz\n, freq_hz); } else { puts(Overflow occurred to the FREQM value counter!); return EXIT_FAILURE; } puts(Test run finished.); /* main thread exits */ return 0; }3.1 初始化freqm_init(0)freqm_init以配置索引idx为参数0表示使用板级配置数组freqm_config[]中的第 0 项。在默认板卡 same54-xpro 上对应的板级配置位于 boards/same54-xpro/include/periph_conf.hstatic const freqm_config_t freqm_config[] { { .pin GPIO_PIN(PB, 17), .gclk_src SAM0_GCLK_32KHZ } };即被测时钟信号接在PB17引脚上参考时钟源选用32 kHz 内部 GCLK。freqm_config_t结构体定义在 cpu/sam0_common/include/periph_cpu_common.htypedef struct { gpio_t pin; /** GPIO at which the frequency is to be measured */ uint8_t gclk_src; /** GCLK source select for reference */ } freqm_config_t;因此如果你的硬件把被测时钟接在了其他具备 GCLK 复用功能的引脚上或想更换参考时钟需要修改对应板卡的freqm_config[]前提是该板卡支持 FREQM 外设。3.2 测量时长period_us UINT32_MAX测试应用将测量时长period_us设为UINT32_MAX约 4294967295 微秒 ≈ 71.6 分钟。从 cpu/sam0_common/periph/freqm.c 的_us_to_ref_clock_counts()函数可以看到测量时长会先换算成参考时钟周期数再写入 FREQM 的CFGA.REFNUM寄存器static uint32_t _us_to_ref_clock_counts(uint32_t period_us, uint8_t clock_id) { uint64_t clk_cnt (uint64_t)period_us * sam0_gclk_freq(clock_id) / US_PER_SEC; if (clk_cnt UINT8_MAX) { return UINT8_MAX; /* 硬件 REFNUM 字段上限 */ } else if (clk_cnt 0) { return 1; /* 至少计 1 个参考周期 */ } else { return clk_cnt; } }这里揭示了两个重要的硬件约束REFNUM 是 8 位字段最大只能表示 255 个参考时钟周期。即便传入UINT32_MAX最终也会被钳位到UINT8_MAX255。当换算结果不足 1 个周期时会被强制设为 1保证测量窗口非零。因此在真实应用中测量时长应根据参考时钟频率合理设置测量窗口越长被测频率分辨率越高但窗口过长时被测计数器VALUE可能溢出从而触发-EOVERFLOW返回。3.3 阻塞式测量freqm_frequency_get()测试应用调用的是阻塞式接口freqm_frequency_get(0, freq_hz, period_us)。该接口的定义位于 drivers/include/periph/freqm.h/** * brief Read number of periods of measured clock and calculate its frequency * * This function uses a blocking mutex to wait for the measurement to finish. * * param[in] idx index of the configuration * param[out] result calculated frequency * param[in] period_us measurement duration in microseconds * * return -EOVERFLOW if FREQM sticky counter has an overflow * return 0 on success */ int freqm_frequency_get(freqm_t idx, uint32_t *result, uint32_t period_us);其底层实现cpu/sam0_common/periph/freqm.c并非真正的忙等待而是借助互斥锁把异步回调包装成同步调用int freqm_frequency_get(freqm_t idx, uint32_t *result, uint32_t period_us) { struct _sync_ctx ctx { .lock MUTEX_INIT_LOCKED }; /* Invoke non-blocking FREQM measure function */ freqm_frequency_get_async(idx, _sync_cb, ctx, period_us); /* Block until measurement is done */ mutex_lock(ctx.lock); *result ctx.hz; return ctx.overflow ? -EOVERFLOW : 0; }调用链为freqm_frequency_get()→freqm_frequency_get_async()→ 硬件启动测量 → DONE 中断irq_freqm()→ 计算频率并调用回调_sync_cb()→mutex_unlock()唤醒阻塞线程。在测试应用的单线程 main 中线程被挂起期间调度器会运行其他线程如 idle 线程因此该阻塞并不会锁死整个系统。3.4 返回值与错误处理freqm_frequency_get()返回 0 表示测量成功返回-EOVERFLOW表示 FREQM 的粘性计数器sticky counter发生溢出。测试应用对此的处理是if (!freqm_frequency_get(0, freq_hz, period_us)) { printf(Measured Clock Frequency: %ld Hz\n, freq_hz); } else { puts(Overflow occurred to the FREQM value counter!); return EXIT_FAILURE; }从实现看溢出标志来自 FREQM 状态寄存器的粘性 OVF 位FREQM_STATUS_OVF_Msk一旦被测频率过高、在测量窗口内使 VALUE 计数器溢出该位即被置位并在读取后清除bool _freqm_get_measurement(uint32_t *result) { const freqm_config_t *config freqm_config[freqm_obj.idx]; /* Calculate measured frequency */ uint64_t result_tmp FREQM-VALUE.reg * (uint64_t)(sam0_gclk_freq(config-gclk_src)); result_tmp result_tmp / freqm_obj.period_cnt; *result (uint32_t)result_tmp; /* Read overflow status */ bool overflow_condition ((int)FREQM-STATUS.reg FREQM_STATUS_OVF_Msk); /* Clear overflow status */ FREQM-STATUS.reg FREQM_STATUS_OVF_Msk; return overflow_condition; }这段代码同时给出了频率计算公式的硬件实现证据被测频率 VALUE被测时钟周期计数值× 参考时钟频率 / REFNUM参考周期计数值其中sam0_gclk_freq(config-gclk_src)返回参考 GCLK 的实际频率MHz 级换算后的 Hz 值运算以 64 位中间量进行以避免乘法溢出最终截断为 32 位结果。四、预期输出与验收标准根据 README.md测试成功的串口输出格式如下main(): This is RIOT! (Version: INSERT VERSION HERE) FREQM peripheral driver test Measured clock frequency: MEASURED CLOCK FREQUENCY Hz Test run finished.其中main(): This is RIOT!是 RIOT 启动时由auto_init/main框架打印的常规启动信息INSERT VERSION HERE会被实际版本号替换FREQM peripheral driver test来自main.c第 28 行的puts(FREQM peripheral driver test)Measured clock frequency: MEASURED CLOCK FREQUENCY Hz对应printf(Measured Clock Frequency: %ld Hz\n, freq_hz)Test run finished.表示测量流程正常走完。需要注意的是README 中的输出示例使用了小写Measured clock frequency:而 main.c 实际打印的是大写Measured Clock Frequency:两者存在细微的大小写差异。判定测试是否通过应以实际源码输出为准即程序能正常打印出频率数值并输出Test run finished.且退出码为 0若打印Overflow occurred to the FREQM value counter!并返回EXIT_FAILURE则说明发生了计数器溢出。五、驱动接口全景同步与异步两种测量模式FREQM 驱动对外暴露的接口集中在 drivers/include/periph/freqm.h共三个函数和一个回调类型5.1 回调类型freqm_cb_ttypedef void (*freqm_cb_t)(uint32_t result, bool overflow, void *context);参数含义result为测量得到的频率Hzoverflow为粘性计数器是否溢出context为调用者自定义上下文指针原样回传。5.2 异步测量freqm_frequency_get_async()void freqm_frequency_get_async(freqm_t idx, freqm_cb_t freqm_cb, void *context, uint32_t period_us);调用后立即返回测量完成后通过中断调用回调。底层实现cpu/sam0_common/periph/freqm.c的关键步骤是由period_us换算 REFNUM 并写入FREQM-CFGA.reg使能 DONE 中断FREQM-INTENSET.reg FREQM_INTENSET_DONE_Msk并启动 FREQM写入FREQM-CTRLB.reg FREQM_CTRLB_START_Msk启动测量测量完成触发irq_freqm()中断计算频率后回调。整个测量过程受全局互斥锁msr_lock保护_freqm_enable加锁、_freqm_disable解锁因此同一时刻只允许一个测量任务占用 FREQM 外设。5.3 中断处理irq_freqm()void irq_freqm(void) { /* Clear the Done Interrupt flag */ FREQM-INTFLAG.reg FREQM_INTFLAG_DONE_Msk; uint32_t result 0; bool overflow_condition _freqm_get_measurement(result); /* Invoke the callback function */ freqm_obj.callback(result, overflow_condition, freqm_obj.context); _freqm_disable(); }该中断服务函数先清除 DONE 标志再读取 VALUE 与溢出状态、计算频率最后调用注册的回调并释放互斥锁。回调执行完成后测量周期即告结束。5.4 设备类型freqm_t#ifndef HAVE_FREQM_T typedef uint_fast8_t freqm_t; #endiffreqm_t是设备实例标识类型默认实现为uint_fast8_t如果某 CPU 需要自定义类型可通过定义HAVE_FREQM_T覆盖。六、底层原理GCLK 引脚复用与参考时钟FREQM 能正常工作的前提是被测信号必须接到具备 GCLK 输入复用功能的 GPIO上。在 cpu/sam0_common/periph/freqm.c 中_freqm_gpio_init()完成了三件事遍历gclk_io_pins数组校验引脚是否具有 GCLK 外设复用功能_freqm_pin()通过gpio_init_mux(msr_gpio_src, GPIO_MUX_M)将引脚切换到 GCLK/IO 复用模式调用_gclk_connect()将对应的GCLK_IO[x]与外部输入GCLKIN相连。源码中还有一条值得注意的硬件约束GCLK_IO[0]与GCLK_IO[1]不能用于 FREQM因为它们分别关联到GCLKGEN[0]主时钟与GCLKGEN[1]32 kHz 时钟已被SAM0_GCLK_MAIN和SAM0_GCLK_32KHZ占用代码通过assert(*gclk_io_id 1)强制保证这一点。参考时钟的接入则通过_freqm_clock_init()完成它把FREQM_GCLK_ID_MSR被测时钟通道和FREQM_GCLK_ID_REF参考时钟通道在较新的 vendor 头文件中定义为FREQM_GCLK_ID_MSR 1分别关联到对应的 GCLK 发生器并等待同步完成。七、在自定义板卡上运行该测试若要在非默认板卡上运行本测试需要满足以下条件CPU 支持 FREQM目前仓库中 FREQM 的驱动实现位于 cpu/sam0_common/periph/freqm.c即 sam0 系列SAM D/E/L 等CPU 提供支持并通过periph_cpu_common.h定义freqm_config_t。板卡声明特性板卡目录的Makefile.features中需包含FEATURES_PROVIDED periph_freqm。提供板级配置板卡头文件如include/periph_conf.h中需定义freqm_config[]数组指定被测引脚pin与参考 GCLK 源gclk_src且被测引脚必须支持 GCLK 复用。硬件接线将待测时钟信号接到配置指定的 GPIO 引脚默认板卡为 PB17信号电压需符合板卡 GPIO 电平规范。运行时通过make BOARD你的板卡 flash term即可观察输出。结语通过本文可以看到tests/periph/freqm/这个看似只有 50 行主程序的外设测试应用背后串联起了 RIOT 外设驱动架构中的关键环节板级配置freqm_config_t、驱动接口抽象同步/异步测量、GCLK 时钟复用、硬件中断与互斥锁同步以及溢出处理。无论是验证自己板卡的 FREQM 外设还是以此为模板编写自定义的频率测量程序都可以直接从 tests/periph/freqm/main.c 出发结合 drivers/include/periph/freqm.h 与 cpu/sam0_common/periph/freqm.c 按需裁剪与扩展。赞分享物联网嵌入式操作系统实时系统【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址https://gitcode.com/GitHub_Trending/riot/RIOT点击查看免费下载相关推荐RIOT 外设 ADC 测试应用解析从编译运行到源码级原理RIOT 外设 ADC 测试应用解析从编译运行到源码级原理 导读 本文以 RIOT OS 中的 tests/periph/adc 测试应用为线索完整讲解如何物联网嵌入式操作系统实时系统科学论文摘要关键词提取keyphrase-extraction-kbir-semeval2017 终极性能评测报告科学论文摘要关键词提取keyphrase extraction kbir semeval2017 终极性能评测报告 在当今信息爆炸的时代 关键词提取技术 已物联网嵌入式操作系统实时系统RIOT 外设 PIO 测试应用深入解析指令内存分配与状态机管理RIOT 外设 PIO 测试应用深入解析指令内存分配与状态机管理 导读 PIOProgrammable IO可编程 IO是 RP2040/RP2350物联网嵌入式操作系统实时系统上一篇GitDiagram用户案例成功企业应用实践分享下一篇告别PDF解析难题用olmocr一键生成可交互文档预览创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门