STM32 - 如何启用DWT周期计数器

17

我正在使用STM32F7-Discovery开发板,并且一直在尝试启用DWT循环计数器。从我在网上看到的信息来看,以下代码应该足以启用它:

CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL  |= 1;

然而,每当我运行那段代码时,值没有被改变或操作被跳过(我不太确定发生了什么)。

我也尝试过直接制作内存地址的指针并直接更改它们,但同样没有成功。例如:

volatile uint32_t *DWT_CONTROL = (uint32_t *) 0xE0001000;
volatile uint32_t *DWT_CYCCNT = (uint32_t *) 0xE0001004;
volatile uint32_t *DEMCR = (uint32_t *) 0xE000EDFC;
*DEMCR = *DEMCR | 0x01000000;
*DWT_CYCCNT  = 0;
*DWT_CONTROL = *DWT_CONTROL | 1;

目前,我仅在使用Visual Studios调试器(带有VisualGDB)逐步执行时获得了它,如果我将DWT->CTRL的值更改为ON值,则循环计数器开始计数。 除此之外,在代码中似乎无法使该值发生变化。

编辑:是什么原因导致这些代码行不执行任务但也不崩溃并继续运行。

CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL  |= 1;
在运行这些代码之后,所有那些内存位置的值都保持不变,并未被应该执行的操作所改变。
例如:
//DWT_CTRL_CYCCNTENA_Msk = 1
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk 
应该导致DWT->CTRL的值为0x40000001,但它仍然保持其默认值0x40000000。以下图片是运行时发生的示例。 BeforeBefore AfterAfter

“值没有被改变或操作被跳过” - 在询问之前为什么不先找出来呢?并使用CMSIS头文件。不要定义自己的寄存器。顺便说一句,这些定义会生成更多的代码,因为你将指针定义为变量。(甚至不要考虑const限定符,它们仍然是变量。) - too honest for this site
应根据此链接进行操作。请注意,在使用调试器时,调试器将使用DWT来完成其自身的目的。因此,您实际上不能使用调试器来调试此代码。 - user3386109
@user3386109:这取决于调试器。OpenOCD不使用计数器,在STM32F4上至少可以完美地工作。 - too honest for this site
@Olaf 是的,你说得对,我应该说调试器可能正在使用DWT来实现自己的目的。 - user3386109
@KenQueso:完全没有。但我使用CMSIS头文件。请注意,您的变量可能会被错误的代码覆盖(正如我所写的),这会增加相当多的代码量。 - too honest for this site
显示剩余5条评论
5个回答

10

也许是因为未解锁dbg寄存器(DWT->LAR = 0xC5ACCE55)导致的:

以下步骤已成功解决该问题:

      CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
      DWT->LAR = 0xC5ACCE55; 
      DWT->CYCCNT = 0;
      DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

有没有想法在哪里可以找到文档来解释这个神奇的访问词:DWT->LAR = 0xC5ACCE55 - Gabriel Staples
@GabrielStaples 请参阅 ARMv7-M 架构参考手册 的 D1 章节,其中详细说明了每个 CoreSight 组件都有这些特殊寄存器之一是软件锁。然后您需要查看 ARM CoreSight 架构规范,在 B2.3.10 章节中描述了 LSR 和 LAR 寄存器以及关键信息。 - Enbyted

9

不确定STM32F7是否相同,但是使用CMSIS头文件正确完成此操作的方法如下所示,在STM32F4上(实际上可在任何提供此模块的Cortex-M3/4(/7?)上工作):

CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

你还需要启用跟踪模块。注意,该代码不是中断安全的!通常情况下,你应该让计数器自由运行,并对快照进行时间差分析。

确保你的工具链不会干扰你的代码。OpenOCD/gdb 不会干扰,不确定提供手动分析功能的其他工具是否会干扰。

正如我在评论中强调的那样:不要使用某些自定义寄存器的定义。ST(和ARM)提供CMSIS标头文件,用于标准外设模块(DWT和CoreDebug实际上是ARM IP),你应该使用它们。这包括不使用“魔法数字”,而是已定义的常量/宏。

更多信息可以在“架构参考手册”中找到。注意:还有一个“架构应用级参考手册”,但那不是你想要的。


我尝试使用预定义的定义头来解决上述问题,但似乎仍然存在无法编辑值的问题。CoreDebug->DEMCR也不会改变,这非常奇怪。我能想到的唯一其他可能是FreeRTOS或LwIP是否使用了CMSIS头文件,但如果是这种情况,随着程序运行,我不应该看到CYCCNT值增加吗? - KenQueso
1
@KenQueso:这并不是downvote我的答案的理由!我不确定你在评论中的意思,据我所知,在这些方面IP是相同的。你说的“值无法编辑”的意思是什么?这完全不清楚。而且这与FreeRTOS等无关。头文件可以在没有任何操作系统的情况下使用,只需要正确的编译器即可。也许你缺乏使用工具链的一些基础知识。请提供一个[mcve]。 - too honest for this site
我没有对你的答案进行负投票,我甚至没有足够的积分来对问题进行投票。无论如何,我在原帖中添加了一张前后照片,试图澄清正在发生的事情。 - KenQueso
5
我不知道我该如何更清楚地表达。我已经准确地概述了我的问题,并明确指出了问题所在。我并不是在寻求导师的帮助。我想知道可能导致我上面清楚概述的行为的原因。 - KenQueso
赞同建议使用HAL头文件而不是硬编码的地址。注意:我在STM32F4上尝试过这个方法,它不需要DWT->LAR,这是其他答案中提到的,只适用于使用该处理器的人。另外,对于那些经验不太丰富的人,你可能需要在你的代码中添加#include stm32f417xx.h或类似的内容。 - undefined
显示剩余3条评论

5

你做得一切都正确,除了你忘记解锁DWT寄存器(就像Howard指出的那样)。 在你的代码中,应该像这样:

volatile uint32_t *DWT_CONTROL = (uint32_t *) 0xE0001000;
volatile uint32_t *DWT_CYCCNT = (uint32_t *) 0xE0001004;
volatile uint32_t *DEMCR = (uint32_t *) 0xE000EDFC;
volatile uint32_t *LAR  = (uint32_t *) 0xE0001FB0;   // <-- added lock access register

*DEMCR = *DEMCR | 0x01000000;     // enable trace
*LAR = 0xC5ACCE55;                // <-- added unlock access to DWT (ITM, etc.)registers 
*DWT_CYCCNT = 0;                  // clear DWT cycle counter
*DWT_CONTROL = *DWT_CONTROL | 1;  // enable DWT cycle counter

请注意,如ARMv7-M架构参考手册所述,锁定机制仅适用于软件访问。始终允许DAP访问(这就是为什么可以使用调试器启用周期计数器的原因)。
请注意,STM32F7文档ARM文档都有一个错别字,并将Lock Access寄存器的地址给出为0xE0000FB0(请参见此处)。 使用提供的CMSIS核心寄存器定义(core_cm7.h)可以避免这个问题,因为它们是正确的,当然也更有效率,如Olaf所说 ;)

1

我知道我有点晚了,但如果还有其他人想知道如何正确设置DWT,可以查看https://developer.arm.com/documentation/ddi0337/e/ch11s05s01
在我的例子中,使用stm32f1,只需按照以下方式设置DWT即可满足我的需求:

DWT->CTRL = DWT_CTRL_CYCEVTENA_Msk | DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0;

-1
这对我有用:
//address of the register
volatile unsigned int *DWT_CYCCNT   = (volatile unsigned int *)0xE0001004;     

//address of the register
volatile unsigned int *DWT_CONTROL  = (volatile unsigned int *)0xE0001000;     

//address of the register
volatile unsigned int *DWT_LAR      = (volatile unsigned int *)0xE0001FB0;     

//address of the register
volatile unsigned int *SCB_DEMCR    = (volatile unsigned int *)0xE000EDFC;

...

*DWT_LAR = 0xC5ACCE55; // unlock (CM7)
*SCB_DEMCR |= 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL |= 1 ; // enable the counter

...

x = *DWT_CYCCNT;

...测试代码:

y = *DWT_CYCCNT;
x = (y - x); // Elapsed clock ticks, at SystemCoreClock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接