Cortex M0+(SAML21)板周期计数问题:DWT无效求替代方案
解决SAML21(Cortex-M0+)DWT周期计数失效及替代方案
一、先搞定DWT计数为0的问题
你的DWT代码没出有效结果,大概率是这几个原因:
1. 寄存器地址未正确映射
直接用*DEMCR这种裸指针容易出错,Cortex-M0+的调试寄存器应该用CMSIS标准定义来访问,避免地址错误:
#include "core_cm0plus.h" // 正确的DWT初始化流程 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 开启跟踪功能 DWT->CYCCNT = 0; // 重置周期计数器 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 启用周期计数
2. 编译器优化导致代码被“吃掉”
如果你的测试代码段太简单,编译器可能会把它优化掉,导致CYCCNT没有变化。可以给count变量加volatile,或者在关键位置加内存屏障指令确保执行顺序:
volatile uint32_t count; // 执行测试代码前加屏障 __ISB(); __DSB(); // 你的测试代码段 // ..... // 读取计数前再加屏障 __ISB(); __DSB(); count = DWT->CYCCNT;
3. 调试配置或硬件限制
有些开发板的调试接口(比如SWD)如果没正确配置,可能会导致DWT功能被禁用。确保你在调试器(比如Atmel Studio、OpenOCD)里开启了“Enable Trace”相关选项,或者检查芯片的调试锁有没有解除。
二、类似Arduino millis()的定时器中断方案
如果DWT还是搞不定,用定时器中断实现毫秒级计数是非常可靠的方案,以SAML21的TC定时器为例:
1. 全局计数器与中断服务函数
#include "sam.h" volatile uint32_t millis_counter = 0; void TC0_Handler(void) { if (TC0->COUNT16.INTFLAG.bit.OVF) { // 捕获溢出中断 millis_counter++; TC0->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF; // 清除中断标志 } }
2. 定时器初始化函数
假设系统时钟是48MHz,我们配置TC0每1ms触发一次中断:
void millis_init(void) { // 使能TC0时钟 MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC0; GCLK->PCHCTRL[TC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN; // 配置TC0为16位模式,分频为48(48MHz/48=1MHz) TC0->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV48; // 设置自动重载值为999(1MHz下,每1000个周期就是1ms) TC0->COUNT16.PER.reg = 999; // 启用溢出中断 TC0->COUNT16.INTENSET.reg = TC_INTENSET_OVF; // 启用定时器 TC0->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; // 配置NVIC中断优先级 NVIC_SetPriority(TC0_IRQn, 3); NVIC_EnableIRQ(TC0_IRQn); }
3. 实现millis()函数
uint32_t millis(void) { uint32_t temp; // 禁用中断防止读取时计数器被修改 __disable_irq(); temp = millis_counter; __enable_irq(); return temp; }
使用时,先调用millis_init()初始化,之后就可以像Arduino一样用millis()获取自启动以来的毫秒数了。如果需要更精确的周期计数,也可以调整定时器的分频和重载值,或者使用32位模式的定时器。
内容的提问来源于stack exchange,提问作者MarkP
相关产品推荐
相关产品推荐

