/* * 模块名称:System Timer * 模块功能:DWT CYCCNT 实现 64-bit 微秒级时间戳 * 在 SysTick 中断中检测 CYCCNT 溢出以扩展至 64-bit * 适用平台:STM32F407ZGTx (168MHz) * 作者:王建锋 * 创建日期:2026-07-19 * * 限定条件: * - CPU 主频 168MHz(若更改须同步 SYS_TIME_CYCLE_PER_US 宏) * - 须在 FreeRTOS 启动前调用 sys_time_init() * - sys_time_systick_hook() 须在 SysTick 中断中调用 * * 修改记录: * v1.0 2026-07-19 王建锋 创建初始版本 */ #include "sys_time.h" #include "app_cfg.h" #include "stm32f4xx.h" /* * 每微秒 CPU 周期数(168MHz → 168 cycles/µs) */ #define SYS_TIME_CYCLE_PER_US 168UL /* * CYCCNT 每溢出一次代表的微秒数(2^32 / 168 ≈ 25,564,904 µs ≈ 25.56s) */ #define SYS_TIME_OVERFLOW_US (uint64_t)(((uint64_t)1 << 32) / SYS_TIME_CYCLE_PER_US) static volatile uint32_t s_overflow_cnt; /* * 函数功能:初始化 DWT CYCCNT * 入口参数:无 * 返 回 值:0 - 成功 * 限定条件:须在 FreeRTOS 启动前调用 * 函数说明:启用 DWT 和 CYCCNT 计数器,计数器从 0 开始 */ int sys_time_init(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; s_overflow_cnt = 0; return 0; } /* * 函数功能:获取微秒级时间戳 * 入口参数:无 * 返 回 值:64 位微秒时间戳 * 函数说明:由当前 CYCCNT 值与溢出计数合成 64-bit 周期数再转换 */ uint64_t sys_time_us(void) { uint32_t cyccnt = 0; uint64_t ticks = 0; uint32_t ov = 0; uint32_t ov2 = 0; /* * 原子读取溢出计数与 CYCCNT,处理潜在的读取竞争 * 若两次读取溢出计数不一致则重读 CYCCNT */ do { ov = s_overflow_cnt; cyccnt = DWT->CYCCNT; ov2 = s_overflow_cnt; } while (ov != ov2); ticks = ((uint64_t)ov << 32) | cyccnt; return ticks / SYS_TIME_CYCLE_PER_US; } /* * 函数功能:获取毫秒级时间戳 * 入口参数:无 * 返 回 值:64 位毫秒时间戳 * 限定条件:sys_time_init() 已成功调用 * 函数说明:通过对 sys_time_us() 取整实现 */ uint64_t sys_time_ms(void) { return sys_time_us() / 1000; } /* * 函数功能:SysTick 中断钩子,检测 CYCCNT 溢出 * 入口参数:无 * 返 回 值:无 * 函数说明:必须在 SysTick_Handler() 中调用 * 每 1ms 检查一次 CYCCNT 是否已回绕 */ void sys_time_systick_hook(void) { static uint32_t s_last_cyccnt = 0; uint32_t cyccnt = DWT->CYCCNT; if (cyccnt < s_last_cyccnt) { s_overflow_cnt++; } s_last_cyccnt = cyccnt; }