尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

esp_hal_rtc_timer HAL 测试应用解析:RTC 计时器计数器、时间换算与闹钟中断验证

esp_hal_rtc_timer HAL 测试应用解析:RTC 计时器计数器、时间换算与闹钟中断验证 esp_hal_rtc_timer HAL 测试应用解析RTC 计时器计数器、时间换算与闹钟中断验证【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfesp_hal_rtc_timer是 ESP-IDF 中针对 RTC Timer 外设的统一硬件抽象层HAL在旧款芯片的 RTC_CNTL 外设与新架构芯片的低功耗LP定时器之间提供了一致的 API。本文以该组件自带的测试应用 test_apps/README.md 为主体结合 test_rtc_timer.c、rtc_timer_hal.h 及各芯片的rtc_timer_ll.h底层实现逐行剖析测试用例的验证思路、RTC slow clock 与微秒之间的换算原理、闹钟中断的置位/清除流程以及该测试应用在 CI 中的自动化运行方式帮助读者理解并复用这套 HAL 测试体系。测试应用概览验证对象与覆盖范围该测试应用对esp_hal_rtc_timer组件进行直接黑盒 白盒混合验证不经过上层esp_timer或电源管理栈而是直接调用 HAL 与 LL 层接口操作 RTC Timer 硬件。按照 test_apps/README.md 的说明其验证重点有三项读取 RTC timer 计数器确认rtc_timer_hal_get_cycle_count()返回的 slow clock tick 计数值能够随硬件持续递增RTC slow clock tick 到时间的换算将两次采样之间的 tick 差通过rtc_time_slowclk_to_us()换算为微秒再与esp_timer_get_time()实测的宿主时间延迟比对验证换算精度闹钟中断的置位与清除仅在配备新一代多目标multi-targetRTC timer 硬件的芯片上执行验证 alarm 中断 raw 状态能被正确触发raise与清除clear。支持的芯片目标应用通过 CMake 级别的supported_targets机制覆盖 ESP-IDF 支持的全部 13 个 RTC timer 目标芯片Supported TargetsESP32ESP32-C2ESP32-C3ESP32-C5ESP32-C6ESP32-C61ESP32-H2ESP32-H21ESP32-H4ESP32-P4ESP32-S2ESP32-S3ESP32-S31这 13 个目标在 esp_hal_rtc_timer/README.md 中被划分为三套硬件实现SOC_RTC_TIMER_V1传统 RTC timer 功能位于 RTC_CNTL 外设中对应 ESP32、ESP32-C2、ESP32-C3、ESP32-S2、ESP32-S3SOC_RTC_TIMER_V2LP timer 外设对应 ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2、ESP32-H21、ESP32-H4、ESP32-P4SOC_RTC_TIMER_V3RTC timer 外设对应 ESP32-S31。测试代码通过SOC_RTC_TIMER_SUPPORTED宏决定是否编译见 test_rtc_timer.c通过SOC_RTC_TIMER_V2 || SOC_RTC_TIMER_V3决定闹钟中断用例是否启用见 test_rtc_timer.c因此一份源码即可在所有目标上自适应运行。测试应用工程结构测试应用遵循 ESP-IDF 测试应用的统一布局位于 components/esp_hal_rtc_timer/test_appstest_apps/ ├── main/ │ ├── CMakeLists.txt # 组件注册SRCS 与 WHOLE_ARCHIVE │ ├── test_app_main.c # unity 测试入口与内存泄漏检查 │ └── test_rtc_timer.c # 两个核心 TEST_CASE ├── CMakeLists.txt # 项目级 CMakeproject(test_rtc_timer) ├── pytest_rtc_timer.py # CI 自动化测试脚本 ├── sdkconfig.ci.default # CI 专用 sdkconfig当前为空文件 └── sdkconfig.defaults # 默认配置CONFIG_ESP_TASK_WDT_ENn构建配置要点项目级 CMakeLists.txt 通过include($ENV{IDF_PATH}/tools/cmake/project.cmake)接入 IDF 构建系统项目名为test_rtc_timer。主组件 main/CMakeLists.txt 有两个值得注意的细节set(srcs test_app_main.c test_rtc_timer.c) idf_component_register(SRCS ${srcs} INCLUDE_DIRS . WHOLE_ARCHIVE)WHOLE_ARCHIVE注释明确指出为了让TEST_CASE宏定义的用例被链接进最终 ELF组件必须以 WHOLE_ARCHIVE 方式注册。这是 Unity 测试用例默认被链接器丢弃时的标准解决方案INCLUDE_DIRS .使test_rtc_timer.c能直接#include unity.h等头文件。测试入口与内存泄漏检测test_app_main.c 展示了 ESP-IDF Unity 测试框架的标准骨架#define TEST_MEMORY_LEAK_THRESHOLD (0) void setUp(void) { unity_utils_record_free_mem(); } void tearDown(void) { esp_reent_cleanup(); unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); } void app_main(void) { unity_run_menu(); }每个用例执行前setUp()记录空闲内存基线执行后tearDown()调用esp_reent_cleanup()释放 newlib 重入结构再通过unity_utils_evaluate_leaks_direct()评估泄漏泄漏阈值设为 0即任何可检测到的内存泄漏都会导致测试失败保证 HAL 测试路径不产生运行时分配或未释放资源unity_run_menu()启动交互式用例菜单同时兼容 pytest 自动化驱动的run_all_single_board_cases()。sdkconfig.defaults中只有一行CONFIG_ESP_TASK_WDT_ENn用于关闭任务看门狗避免在较长延时采样如 50 ms 的esp_rom_delay_us循环或中断轮询期间被 WDT 复位。用例一计数器读取与 slow clock 时间换算第一个用例rtc timer counter can be read and converted to elapsed time标签[rtc_timer]验证测试点 1 和 2见 test_rtc_timer.cTEST_CASE(rtc timer counter can be read and converted to elapsed time, [rtc_timer]) { assert_counter_elapsed_matches_time(0); }核心验证函数逻辑assert_counter_elapsed_matches_time()是双通道时间比对的核心test_rtc_timer.cstatic void assert_counter_elapsed_matches_time(uint8_t timer_id) { const uint32_t period get_rtc_timer_period(); const int64_t start_host_us esp_timer_get_time(); const uint64_t start_cycles rtc_timer_hal_get_cycle_count(timer_id); esp_rom_delay_us(RTC_TIMER_SAMPLE_DELAY_US); const uint64_t end_cycles rtc_timer_hal_get_cycle_count(timer_id); const int64_t end_host_us esp_timer_get_time(); const uint64_t host_elapsed_us end_host_us - start_host_us; const uint64_t timer_elapsed_us rtc_time_slowclk_to_us(end_cycles - start_cycles, period); const uint64_t tolerance_us max_u64(host_elapsed_us / 4, RTC_TIMER_CONVERSION_MIN_TOLERANCE_US); const uint64_t delta_us abs_diff_u64(timer_elapsed_us, host_elapsed_us); ... }执行流程分为五步获取 slow clock 校准周期get_rtc_timer_period()调用esp_clk_slowclk_cal_get()头文件esp_private/esp_clk.h返回经校准的 slow clock 周期单位为 1 μs 内的周期计数或微周期并用TEST_ASSERT_NOT_EQUAL_UINT32(0, period)断言其非零防止除零与未校准问题记录起点同时记录宿主侧时间esp_timer_get_time()微秒和硬件侧计数器rtc_timer_hal_get_cycle_count(timer_id)slow clock tick固定延时调用esp_rom_delay_us(RTC_TIMER_SAMPLE_DELAY_US)等待 50 msRTC_TIMER_SAMPLE_DELAY_US 50000该 ROM 延时为忙等待不依赖调度器记录终点再次读取两侧时间计算宿主经过时间host_elapsed_us与硬件经过 tick 数end_cycles - start_cycles换算并比对rtc_time_slowclk_to_us(ticks, period)将 tick 差值换算为微秒得到timer_elapsed_us随后与host_elapsed_us求绝对差并与容差比较。容差设计自适应 下限保护容差计算体现了对嵌入式计时器抖动来源中断、缓存、慢时钟校准误差的务实处理const uint64_t tolerance_us max_u64(host_elapsed_us / 4, RTC_TIMER_CONVERSION_MIN_TOLERANCE_US);容差取宿主经过时间 / 4与RTC_TIMER_CONVERSION_MIN_TOLERANCE_US5000 μs 5 ms两者中的较大值这保证短延时如系统刚启动时有 5 ms 的最低宽容度而长延时下按比例放宽到 25%最后通过abs_diff_u64计算无符号绝对差delta_us tolerance_us时用例通过。失败诊断信息每次断言失败前都会先打印采样数据例如timer0 counter 123456 - 789012 cycles, host50000us timer49983us tol12500us同时TEST_ASSERT_TRUE_MESSAGE提供了可读的失败消息计数器未增长时报timer0 counter did not increase: ...换算偏差超限时报timer0 elapsed mismatch: host... timer... delta... tol...。这类输出对在真实硬件上定位慢时钟校准问题非常有价值。值得注意的是该用例仅验证timer_id 0的计数器主计时器而 V2/V3 芯片上的第二个计数器LP timer由闹钟中断用例间接覆盖。用例二闹钟中断的置位与清除V2/V3 专属第二个用例rtc timer alarm interrupts can be raised and cleared标签[rtc_timer]验证测试点 3仅在SOC_RTC_TIMER_V2 || SOC_RTC_TIMER_V3芯片上编译执行test_rtc_timer.cTEST_CASE(rtc timer alarm interrupts can be raised and cleared, [rtc_timer]) { assert_alarm_interrupt_round_trip(0); if (get_alarm_intr_mask(1) ! 0) { assert_alarm_interrupt_round_trip(1); } }timer 0 无条件测试timer 1 仅在芯片提供了对应中断 raw 位掩码RTC_TIMER_MAIN_TIMER_LP_INT_RAW或LP_TIMER_MAIN_TIMER_LP_INT_RAW时才测试体现了不同目标上 LP 定时器通道能力差异的兼容处理。中断掩码的芯片差异适配get_alarm_intr_mask()test_rtc_timer.c展示了寄存器宏在不同代际芯片间的命名差异static uint32_t get_alarm_intr_mask(uint8_t timer_id) { if (timer_id 0) { #if SOC_RTC_TIMER_V3 return RTC_TIMER_SOC_WAKEUP_INT_RAW; #else return LP_TIMER_SOC_WAKEUP_INT_RAW; #endif } #if defined(RTC_TIMER_MAIN_TIMER_LP_INT_RAW) return RTC_TIMER_MAIN_TIMER_LP_INT_RAW; #elif defined(LP_TIMER_MAIN_TIMER_LP_INT_RAW) return LP_TIMER_MAIN_TIMER_LP_INT_RAW; #else return 0; #endif }timer 0主定时器/唤醒定时器的 raw 中断位在 V3 芯片上叫RTC_TIMER_SOC_WAKEUP_INT_RAW在 V2 芯片上叫LP_TIMER_SOC_WAKEUP_INT_RAWtimer 1 的掩码则通过#if defined探测宏是否存在来兼容两代寄存器命名。闹钟往返测试round-trip流程assert_alarm_interrupt_round_trip()完整演示了 LL 层闹钟 API 的使用顺序test_rtc_timer.c第一阶段——预清除与状态确认rtc_timer_ll_alarm_intr_enable(LP_TIMER, timer_id, false); rtc_timer_ll_clear_alarm_intr_status(LP_TIMER, timer_id); TEST_ASSERT_EQUAL_HEX32(0, rtc_timer_ll_get_intr_raw(LP_TIMER, timer_id) raw_mask);先禁用闹钟中断使能、清除中断状态再断言 raw 状态中对应位确实为 0确保测试从干净状态开始。第二阶段——设定唤醒时间并轮询触发const uint64_t start_cycles rtc_timer_hal_get_cycle_count(timer_id); rtc_timer_hal_set_wakeup_time(timer_id, start_cycles delay_ticks); bool fired false; const int64_t deadline esp_timer_get_time() RTC_TIMER_ALARM_TIMEOUT_US; while (esp_timer_get_time() deadline) { if (rtc_timer_ll_get_intr_raw(LP_TIMER, timer_id) raw_mask) { fired true; break; } }delay_ticks由rtc_time_us_to_slowclk(RTC_TIMER_ALARM_DELAY_US, period)将 4000 μsRTC_TIMER_ALARM_DELAY_US换算为 slow clock tickrtc_timer_hal_set_wakeup_time()写入目标 tick 值触发比较器随后在 100 msRTC_TIMER_ALARM_TIMEOUT_US超时窗口内以轮询方式等待 raw 中断位置位避免在测试环境依赖中断向量注册。第三阶段——清理与状态复核rtc_timer_ll_set_target_enable(LP_TIMER, timer_id, false); rtc_timer_ll_alarm_intr_enable(LP_TIMER, timer_id, false); rtc_timer_ll_clear_alarm_intr_status(LP_TIMER, timer_id); TEST_ASSERT_EQUAL_HEX32(0, rtc_timer_ll_get_intr_raw(LP_TIMER, timer_id) raw_mask);触发成功后依次关闭比较器使能、关闭中断使能、清除中断状态最后再次断言 raw 位归零构成完整的置位→清除闭环验证。从测试回看 HAL 与 LL 层的 API 映射测试用例直接调用的rtc_timer_hal_*接口在 rtc_timer_hal.h 中全部定义为对 LL 层的薄封装宏HAL API统一接口展开为 LL API语义rtc_timer_hal_set_wakeup_time(timer_id, ticks)rtc_timer_ll_set_wakeup_time(timer_id, ticks)设定唤醒定时器目标值tick 单位rtc_timer_hal_clear_wakeup_time(timer_id)rtc_timer_ll_clear_wakeup_time(timer_id)解除比较器武装并清除挂起的中断状态rtc_timer_hal_get_cycle_count(timer_id)rtc_timer_ll_get_cycle_count(timer_id)读取当前 RTC 时间slow clock tick三个宏均受#if SOC_RTC_TIMER_SUPPORTED保护未实现该外设的芯片不会暴露这些接口。V2 芯片以 ESP32-C6 为例的 LL 实现esp32c6/include/hal/rtc_timer_ll.h 展示了 LP timer 外设的寄存器操作方式rtc_timer_ll_set_alarm_target()将 64 位目标值拆分为高 16 位与低 32 位分别写入target[timer_id].hi与.lo寄存器rtc_timer_ll_set_target_enable()通过target[timer_id].hi.enable位控制比较器使能rtc_timer_ll_counter_snapshot()置update.update 1触发计数器快照保证高低位读取的一致性防止 64 位读撕裂rtc_timer_ll_get_intr_raw()/rtc_timer_ll_clear_alarm_intr_status()按timer_id区分主定时器timer 0int_raw/int_clr与 LP 定时器timer 1lp_int_raw/lp_int_clr的寄存器组。测试中使用的rtc_time_slowclk_to_us()/rtc_time_us_to_slowclk()换算接口由soc/rtc.h提供见 test_rtc_timer.c 的包含关系二者都以校准周期period为换算基准构成 tick ↔ 微秒的双向转换。V1 芯片的差异ESP32、ESP32-C2 等 V1 芯片的 rtc_timer_ll.h 直接操作 RTC_CNTL 外设寄存器。由于SOC_RTC_TIMER_V2 || SOC_RTC_TIMER_V3分支不满足闹钟中断用例在这些芯片上不参与编译只有计数器读取与时间换算用例运行——这正是 README 中仅在新型多目标硬件上验证闹钟行为的技术原因。自动化测试与 CI 集成pytest 驱动pytest_rtc_timer.py 是标准的 ESP-IDF pytest 测试脚本pytest.mark.generic pytest.mark.parametrize(config, [default], indirectTrue) idf_parametrize(target, [supported_targets], indirect[target]) def test_rtc_timer(dut: Dut) - None: dut.run_all_single_board_cases()pytest.mark.generic标记为通用非特殊环境测试config参数化指向default配置即sdkconfig.defaultsidf_parametrize(target, [supported_targets])会在所有受支持的目标芯片上自动展开测试矩阵dut.run_all_single_board_cases()自动发现并依次运行固件中所有TEST_CASE与unity_run_menu()的非交互模式配合。本地构建与运行在 IDF 环境已导出source export.sh的前提下可按以下方式在指定目标上构建并运行该测试应用# 以 ESP32-C6 为例切换到测试应用目录并构建 cd components/esp_hal_rtc_timer/test_apps idf.py set-target esp32c6 idf.py build # 烧录并打开串口监视器通过 Unity 菜单选择用例运行 idf.py flash monitor运行时会看到两类用例输出timer0 counter 123456 - 789012 cycles, host50000us timer49983us tol12500us timer0 alarm raw0x00000001 mask0x00000001以PASS收尾的用例即代表该目标上 HAL/LL 计时器链路工作正常。小结这套测试应用能教会我们什么从源码结构看esp_hal_rtc_timer测试应用是一个小而完整的 HAL 验证范本值得借鉴的设计包括双通道时间比对法以esp_timer_get_time()宿主侧为参照系验证 slow clock tick → 微秒换算的准确性容差采用比例 下限的自适应策略硬件能力条件编译通过SOC_RTC_TIMER_SUPPORTED/SOC_RTC_TIMER_V2/SOC_RTC_TIMER_V3宏让一份用例源码兼容 13 个目标芯片并针对寄存器命名差异RTC_TIMER_SOC_WAKEUP_INT_RAWvsLP_TIMER_SOC_WAKEUP_INT_RAW做了防御式探测中断行为的确定性验证不依赖中断向量而是轮询 raw 状态使闹钟置位/清除逻辑可在无 ISR 注册的裸环境下被稳定断言零泄漏的测试纪律TEST_MEMORY_LEAK_THRESHOLD 0配合esp_reent_cleanup()确保 HAL 调用路径不引入运行时内存泄漏。对于需要在自有固件中直接操作 RTC Timer 或 LP 定时器的开发者该测试应用既是 API 用法的活文档也是验证 slow clock 校准是否正常、闹钟链路是否完整的现成回归套件。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表