DWT基础应用与获取程序运行时间Debug练习(上)

张开发
2026/4/11 21:38:36 15 分钟阅读

分享文章

DWT基础应用与获取程序运行时间Debug练习(上)
前置介绍DWT (Data Watchpoint and Trace 数据观察点及跟踪)简介DWT 是 一个内核外设DWT 用于系统调试和跟踪DWT 中的寄存器 CYCCNT 记录内核时钟运行个数CYCCNT 精度非常高(可以达到纳秒, HAL_Delay 是毫秒级, TIM 定时器是微秒级)其几乎不受任何的中断和代码影响 (因为依赖的是硬件计数器)DWT 精度主频越高, DWT 精度越高当 CYCCNT 溢出之后, 会清零重新开始向上计数.DWT 相关寄存器DEMCRDWT_CYCCNT可以根据某一代码运行前后两次的数值求一个差值, 就能够得到这段代码运行的具体时间是多少.但是在 HAL 库中没有对 DWT 的封装, 因为 HAL 库主要封装的都是偏向外设的.而 DWT 属于 内核的一种外设, 所以需要我们自己去封装一些操作函数.CYCCNTENACYCCNTENA 是 DWT 控制寄存器 (DWT_CTRL) 的第一位通过向该寄存器写 1 使能, 以启用 CYCCNT 计数器项目配置请直接 Copy 上一节手动建立工程模板所创建的模板, 并进行如下修改创建对应目录和文件User/ ├── dwt/ # DWT 模块文件 │ ├── bsp_dwt.h │ └── bsp_dwt.c │ ├── led/ # LED 模块文件 │ ├── bsp_led.h │ └── bsp_led.c │ └── ... # 其他文件在 User (用户代码) 目录下创建对应模块目录 dwt, led分别在其中创建对应的 头文件和 C 文件附: BSP 为 Board Support Package 板级支持包Keil 中修改项目名, 添加对应文件代码部分#ifndef __MAIN_H #define __MAIN_H /* Includes ------------------------------------------------------------------*/ #include stm32f1xx_hal.h /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ #endif /* __MAIN_H *//** * brief DWT_BASE */ /* Includes ------------------------------------------------------------------*/ #include main.h #include led/bsp_led.h #include dwt/bsp_dwt.h /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ uint64_t begin 0; // 记录计时起始值 uint64_t end 0; // 记录计时结束值 uint64_t duration 0; // 记录程序运行所花的时间 (单位: DWT 计数器周期) uint64_t task_us 0; // 记录程序运行所花的时间 (单位: DWT 计数器周期) /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* Private functions ---------------------------------------------------------*/ int main(void) { uint8_t a 10; HAL_Init(); // 初始化 HAL 库 SystemClock_Config(); // 配置系统时钟, 设置为 72MHz DWT_Init(); // 启动 DWT 计数器, 用于精确测量程序运行时间 LED_GPIO_Config(); // 初始化 LED 引脚 while (1) { begin DWT_GetTick(); // 记录第一段程序开始的时间 LED_RGB_ALL_ON(); // 打开 RGB 灯, 模拟任务运行 DWT_DelayMs(1000); // 精确延时 1 秒 LED_RGB_ALL_OFF(); // 关闭 RGB 灯, 模拟任务结束 DWT_DelayMs(1000); // 精确延时 1 秒 end DWT_GetTick(); // 记录结束时间 duration end - begin; // 计算任务耗时 (单位位 DWT 周期) DWT_TickToMicrosecond(duration, SystemCoreClock); // 转为微秒 } } /** * brief System Clock Configuration * The system Clock is configured as follow : * System Clock source PLL (HSE) * SYSCLK(Hz) 72000000 * HCLK(Hz) 72000000 * AHB Prescaler 1 * APB1 Prescaler 2 * APB2 Prescaler 1 * HSE Frequency(Hz) 8000000 * HSE PREDIV1 1 * PLLMUL 9 * Flash Latency(WS) 2 * param None * retval None */ void SystemClock_Config(void) { RCC_ClkInitTypeDef clkinitstruct {0}; RCC_OscInitTypeDef oscinitstruct {0}; /* Enable HSE Oscillator and activate PLL with HSE as source */ oscinitstruct.OscillatorType RCC_OSCILLATORTYPE_HSE; oscinitstruct.HSEState RCC_HSE_ON; oscinitstruct.HSEPredivValue RCC_HSE_PREDIV_DIV1; oscinitstruct.PLL.PLLState RCC_PLL_ON; oscinitstruct.PLL.PLLSource RCC_PLLSOURCE_HSE; oscinitstruct.PLL.PLLMUL RCC_PLL_MUL9; if (HAL_RCC_OscConfig(oscinitstruct)! HAL_OK) { /* Initialization Error */ while(1); } /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ clkinitstruct.ClockType (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); clkinitstruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; clkinitstruct.AHBCLKDivider RCC_SYSCLK_DIV1; clkinitstruct.APB2CLKDivider RCC_HCLK_DIV1; clkinitstruct.APB1CLKDivider RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(clkinitstruct, FLASH_LATENCY_2)! HAL_OK) { /* Initialization Error */ while(1); } }#ifndef __BSP_DWT_H #define __BSP_DWT_H /* Includes ------------------------------------------------------------------*/ #include main.h // 引用 main.h 是因为需要用到很多的 HAL 库和定义. 所以也建议后续添加的每一个 .h 文件中都要引入 main.h /* 在 Cortex_M 里面有一个外设叫 DWT (Data Watchpoint and Trace), 该外设有一个32位的寄存器叫 CYCCNT, 它是一个向上的计数器, 记录的使内核时钟运行的个数, 假设内核频率为 72M, 内核跳一次的时间大概为 1/72M 14ns (最长能记录的时间为: 60s 2 的 32 次方 / 72 000 000) . 当 CYCCNT 溢出之后, 会清0重新开始向上计数 使能 CYCCNT 计数的操作步骤: 1. 先使能 DWT 外设设备, 这个由另外内核调试寄存器 DEMCR 的 位 24 控制, 写 1 使能 2. 使能 CYCCNT 寄存器之前, 先清0 3. 使能 CYCCNT 计数器, 这个由 DWT_CTRL (代码上宏定义为 DWT_CR) 的位0控制, 写 1 使能 4. CYCCNT 和 us 互相转换: 1 / SystemCoreCLock * CYCCNT (S) 1000 / SystemCoreClock * CYCCNT (MS) 1000000 / SystemCoreClock * CYCCNT (US) (X)US */ /* DWT时间戳相关寄存器定义 */ #define DEMCR *(uint32_t *) (0xE000EDFC) #define DWT_CTRL *(uint32_t *) (0xE0001000) #define DWT_CYCCNT *(uint32_t *) (0xE0001004) #define DEMCR_TRCENA (124) #define DWT_CTRL_CYCCNTENA (10) void DWT_Init(void); uint32_t DWT_GetTick(void); uint32_t DWT_TickToMicrosecond(uint32_t tick, uint32_t frequency); void DWT_DelayUs(uint32_t time); void DWT_DelayMs(uint32_t time); void DWT_DelayS(uint32_t time); #endif /* __BSP_DWT_H *//** * file bsp_dwt.c * brief 使用内核定时器函数接口 */ #include dwt/bsp_dwt.h /** * brief 初始化DWT计数器s * param 无 * retval 无 * note 使用延时函数前, 必须调用本函数 */ void DWT_Init(void) { /* 使能 DWT 外设 */ DEMCR | (uint32_t)DEMCR_TRCENA; /* DWT CYCNT 寄存器计数清0 */ DWT_CYCCNT (uint32_t)0U; // 使能 CYCCNT 寄存器之前, 先清0 /* 使能 Cortex-M DWT CYCCNT 寄存器 */ DWT_CTRL | (uint32_t)DWT_CTRL_CYCCNTENA; } /** * brief 读取当前时间戳 * param 无 * retval 当前时间戳, 即 DWT_CYCCNT 寄存器的值 */ uint32_t DWT_GetTick(void) { return ((uint32_t)DWT_CYCCNT); } /** * brief 节拍数转化时间间隔 (微秒单位) * param tick: 需要转换的节拍数 * param frequency: 内核时钟频率 * retval 当前时间戳 (微秒单位) */ uint32_t DWT_TickToMicrosecond(uint32_t tick, uint32_t frequency) { return (uint32_t)(1000000.0 / frequency * tick); } /** * brief DWT计数器实现精确延时, 32位计数器 * param time: 延迟长度, 单位: us * retval 无 */ void DWT_DelayUs(uint32_t time) { /* 将微秒转化为对应的时钟计数值 */ uint32_t tick_duration time * (SystemCoreClock / 1000000); uint32_t tick_start DWT_GetTick(); /* 刚进入时的计数器值 */ while(DWT_GetTick() - tick_start tick_duration); } /** * brief DWT计数器实现精确延时, 32位计数器 * param time: 延迟长度, 单位: ms */ void DWT_DelayMs(uint32_t time) { for (uint32_t i 0; i time; i) { DWT_DelayUs(1000); } } /** * brief DWT计数器实现精确延时, 32位计数器 * param time: 延迟长度, 单位: s */ void DWT_DelayS(uint32_t time) { for (uint32_t i 0; i time; i) { DWT_DelayMs(1000); } }#ifndef __BSP_LED_H__ #define __BSP_LED_H__ /* Includes ------------------------------------------------------------------*/ #include main.h /* ----------------------------- LED 引脚定义 ----------------------------- */ // 红灯 #define LED_R_Pin GPIO_PIN_1 // 绿灯 #define LED_G_Pin GPIO_PIN_2 // 蓝灯 #define LED_B_Pin GPIO_PIN_3 // 所有灯都连接在 GPIOA #define LED_Port GPIOA /* ----------------------------- 函数声明 ----------------------------- */ void LED_GPIO_Config(void); /* ----------------------------- LED控制宏 ----------------------------- */ // 红灯控制 #define LED_R_ON() HAL_GPIO_WritePin(LED_Port, LED_R_Pin, GPIO_PIN_RESET); // 点亮红灯 #define LED_R_OFF() HAL_GPIO_WritePin(LED_Port, LED_R_Pin, GPIO_PIN_SET); // 熄灭红灯 #define LED_R_TOGGLE() HAL_GPIO_TogglePin(LED_Port, LED_R_Pin); // 翻转红灯状态 // 绿灯控制 #define LED_G_ON() HAL_GPIO_WritePin(LED_Port, LED_G_Pin, GPIO_PIN_RESET); // 点亮绿灯 #define LED_G_OFF() HAL_GPIO_WritePin(LED_Port, LED_G_Pin, GPIO_PIN_SET); // 熄灭绿灯 #define LED_G_TOGGLE() HAL_GPIO_TogglePin(LED_Port, LED_G_Pin); // 翻转绿灯状态 // 蓝灯控制 #define LED_B_ON() HAL_GPIO_WritePin(LED_Port, LED_B_Pin, GPIO_PIN_RESET); // 点亮蓝灯 #define LED_B_OFF() HAL_GPIO_WritePin(LED_Port, LED_B_Pin, GPIO_PIN_SET); // 熄灭蓝灯 #define LED_B_TOGGLE() HAL_GPIO_TogglePin(LED_Port, LED_B_Pin); // 翻转蓝灯状态 /* ----------------------------- LED 组合控制宏 ----------------------------- */ // 三色灯全亮 #define LED_RGB_ALL_ON() LED_R_ON(); LED_G_ON(); LED_B_ON() // 三色灯全灭 #define LED_RGB_ALL_OFF() LED_R_OFF(); LED_G_OFF(); LED_B_OFF() // 仅亮红灯 #define LED_R_ON_ONLY() LED_R_ON(); LED_G_OFF(); LED_B_OFF() // 仅亮绿灯 #define LED_G_ON_ONLY() LED_R_OFF(); LED_G_ON(); LED_B_OFF() // 仅亮蓝灯 #define LED_B_ON_ONLY() LED_R_OFF(); LED_G_OFF(); LED_B_ON() #endif /* __BSP_LED_H__ *//** * file bsp_led.c * brief LED灯函数接口 */ #include led/bsp_led.h /** * brief 初始化控制LED的GPIO引脚 * note 配置为推挽输出, 默认全灭 */ void LED_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; /* 使能GPIOA端口时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 初始化时关闭所有LED (低电平点亮, 默认全设为高电平) */ HAL_GPIO_WritePin(GPIOA, LED_R_Pin|LED_G_Pin|LED_B_Pin, GPIO_PIN_SET); /* 设置GPIO引脚 : LED_R_Pin LED_G_Pin LED_B_Pin */ GPIO_InitStruct.Pin LED_R_Pin|LED_G_Pin|LED_B_Pin; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽输出 GPIO_InitStruct.Pull GPIO_NOPULL; // 不上拉不下拉 GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; // 低速输出 HAL_GPIO_Init(LED_Port, GPIO_InitStruct); }程序现象每一秒亮灯, 熄灯.

更多文章