当前位置: 首页 > news >正文

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

前置介绍

DWT (Data Watchpoint and Trace 数据观察点及跟踪)简介

  1. DWT 是 一个内核外设
  2. DWT 用于系统调试和跟踪
  3. DWT 中的寄存器 CYCCNT 记录内核时钟运行个数
  4. CYCCNT 精度非常高(可以达到纳秒, HAL_Delay 是毫秒级, TIM 定时器是微秒级)
  5. 其几乎不受任何的中断和代码影响 (因为依赖的是硬件计数器)
DWT 精度

  1. 主频越高, DWT 精度越高
  2. 当 CYCCNT 溢出之后, 会清零重新开始向上计数.
DWT 相关寄存器
DEMCR

DWT_CYCCNT

可以根据某一代码运行前后两次的数值求一个差值, 就能够得到这段代码运行的具体时间是多少.

但是在 HAL 库中没有对 DWT 的封装, 因为 HAL 库主要封装的都是偏向外设的.

而 DWT 属于 内核的一种外设, 所以需要我们自己去封装一些操作函数.

CYCCNTENA

  1. CYCCNTENA 是 DWT 控制寄存器 (DWT_CTRL) 的第一位
  2. 通过向该寄存器写 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 (1<<24) #define DWT_CTRL_CYCCNTENA (1<<0) 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); }

程序现象

每一秒亮灯, 熄灯.

http://www.cnnetsun.cn/news/1833165.html

相关文章:

  • WinCC7.5+Telegraf+Influxdb 3.0 Core:通过OPC UA方式WinCC数据直接同步数据到Influxdb
  • Ubuntu 22.04 + PyTorch 2.3 环境:PyG 2.5+ 一键安装与版本兼容性实战
  • 从手机充电器到电动车BMS:图解多电源系统中电流回路的3个关键设计原则
  • 我用 AI 辅助开发了一系列小工具():文件提取工具邮
  • 如何轻松玩转怀旧Flash游戏?这个免费工具帮你搞定一切!
  • Mysql的行级锁到底是怎么加的?谌
  • CSS变量与自定义属性
  • 我们为什么放弃了ActiveMQ,转向了Pulsar?
  • RAG详解:让大模型看见你的私有知识
  • 整合素家族核心靶点解析:CD51(Integrin αv)的分子机制与药物研发技术前瞻
  • Pandas显示设置全攻略:如何让数据表格在Jupyter Notebook中更美观(附常见问题解决)
  • 事件驱动化技术中的事件发布事件订阅与事件处理
  • 计算机毕业设计:Python空气质量智能评估与预测平台 Django框架 Spark 线性回归 可视化 大数据 机器学习 深度学习(建议收藏)✅
  • Upscayl图像放大GPU加速深度优化:告别Vulkan兼容性困扰
  • Gemma-3-270m应用场景:政务公文润色、政策文件要点速读生成案例
  • 酷安UWP桌面客户端:Windows平台上的酷安社区完整体验指南
  • 日本Shopify消费趋势分析:樱花季与新生活季正在带火哪些品类?
  • 打字不如说话,说话不如截图——AI 代码助手的多模态输入实践兑
  • 2026届必备的五大降重复率平台解析与推荐
  • LeetCode 删除无效的括号:python 题解檀
  • 【仅限首批200位架构师开放】:可运行的AI原生配置中心参考实现(含Kubernetes Operator + WASM沙箱 + 配置Diff-AI比对引擎)
  • 从零构建 嵌入式AT指令解析器 与命令行控制台
  • ESP居然能当 DNS 服务器用?内含NCSI欺骗和DNS劫持实现竟
  • Ostrakon-VL-8B智能Agent设计:自动化餐饮供应链巡检机器人
  • gitru:一个由 Rust 打造的零依赖 Git 提交信息校验工具强
  • 3步掌握OCAuxiliaryTools:让黑苹果配置从复杂到简单的智能解决方案
  • 如何用Illustrator脚本集合在5分钟内提升设计效率300%?设计师必备的22个免费神器
  • 不用装软件!这款MicroPython浏览器 IDE :让你在手机上也能调试树莓派 Pico晌
  • 2026最权威的五大降重复率工具解析与推荐
  • 技术判断力之AI三问嘶