• 正文
  • 相关推荐
申请入驻 产业图谱

为什么 HRTIM 的 TIMx 输出总是无效 ?

06/29 08:34
174
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在基于 STM32H7 系列芯片的项目开发中,有些问题乍一看像是配置没做好,但真查下去才会发现,原因往往藏在 HAL 接口细节、CubeMX 生成代码、或库代码缺陷里。

本文整理三个实际开发中较有代表性的案例:

    HRTIM 的 TIMx 配了 PWM,但输出始终无效;DMAMUX 的 Overrun 中断反复触发,导致程序异常;STM32H723 的 TIM23/TIM24 实际支持中心对齐计数模式,测试却表现为仅支持向上计数;

这三个问题的共同特点是:

a、代码能编译;

b、配置表面上看没问题;

c、现象完全不符合预期。

如果你在 STM32项目里也遇到类似情况,希望这篇文章能够给你些提醒或启示,愿你少走一些弯路。

一、为什么 HRTIM 的 TIMx 输出总是无效?

有人使用STM32H743芯片的HRTIM外设,在利用TIMx做PWM输出时,发现输出总是毫无动静【编译时没有任何出错提示】。

先是对HRTIM的相关配置做一通检查确认,无果。

在调试状态下观察TIMx相关寄存器的状态,TIMx的计数器根本没有计数动作,岿然不动。难道是调用的API库代码有问题?

参照他人的相关代码,发现调用的API函数代码并无差别,都是调用这两个函数:

HAL_HRTIM_WaveformOutputStart()//启动hrtim-timx的输出通道

HAL_HRTIM_WaveformCountStart()//启动hrtim-timx的计数器

现在计数器不工作,重点检查HAL_HRTIM_WaveformCountStart()函数,最后发现是在给这个函数赋参数时出错了。刚开始的写法是这样的:

HAL_HRTIM_WaveformCountStart(&hhrtim, HRTIM_TIMERINDEX_TIMER_A)

而实际上呢,应该是这样写:

HAL_HRTIM_WaveformCountStart(&hhrtim, HRTIM_TIMERID_TIMER_A)

调整参数后,输出立即正常。

这些宏定义、代码写法都是库代码中别人约定好的,对于我们使用者来说,只能是自己保证正确使用。要保证正确使用往往需要在调用前阅读下函数的说明。STM32库函数在这方面做得还是不错的,针对每个库函数都有很详细而清晰的说明。如果无视,偶尔踩坑也难免。

又比方,有人想启动HRTIM里的master相关中断时,结果调用的是启动其它TIMx中断的API函数。即本来应该是调用:

__HAL_HRTIM_MASTER_ENABLE_IT();

结果调用的__HAL_HRTIM_TIMER_ENABLE_IT();

当然,个人认为这里也有个比较客观原因,有些宏定义、函数、参数的名字很接近,作为生物人可能看错,这个也没办法,人嘛,哪有不出错呢!只能哪里跌倒哪里爬起来。

二、为什么 DMAMUX 的 Overrun 中断没完没了?

有用户在使用 STM32H743 芯片开发产品时,用到了 DMAMUX 和 DMA 功能模块。调试过程中,一度发现程序总是莫名其妙地“卡死”。

随着调试深入,逐渐发现问题很可能是由于中断频繁产生导致的。

一个很直观的现象是,当禁用掉 DMA 相关中断后,程序“卡死”问题就消失了。由此可以基本锁定:问题与 DMA 相关中断有关。

当时和 DMA 有关的中断主要有 3 类:

    用于 UART 接收的 DMA1 Stream0 传输中断(含错误中断);DMAMUX 的 DMA Generator 申请的 DMA1 Stream1 传输中断(含错误中断);DMAMUX Overrun 中断,即 DMA 请求未能及时响应时触发的中断;

工程代码是通过 CubeMX 配置并生成的,DMA相关配置如下图所示。

经过一番调试验证,最终锁定问题来源于DMAMUX Overrun 中断。

更准确地说,是 Overrun 中断标志没有被及时清理,导致中断反复触发。

CubeMX 自动生成的中断服务函数如下:


void DMAMUX1_OVR_IRQHandler(void){ /* USER CODE BEGIN DMAMUX1_OVR_IRQn 0 */  /* USER CODE END DMAMUX1_OVR_IRQn 0 */ HAL_DMAEx_MUX_IRQHandler(&hdma_uart4_rx); /* USER CODE BEGIN DMAMUX1_OVR_IRQn 1 */  /* USER CODE END DMAMUX1_OVR_IRQn 1 */}

一般来说,使用 CubeMX 生成工程代码后,库函数对中断事件标志的管理通常已经比较完善了,包括:

标志检查;

标志清零;

状态机维护;

用户回调入口预留;

很多时候,用户只需要在回调函数中处理业务逻辑,并不需要自己额外去管中断标志。

但这里的问题是,DMAMUX Overrun 中断一直反复触发。这基本可以说明,至少有一个 Overrun 标志没有被清除。

继续细看这个中断服务函数,会发现它只处理了UART申请的DMA请求溢出事件。

HAL_DMAEx_MUX_IRQHandler(&hdma_uart4_rx);

也就是说,代码只对 UART4 RX 所对应的 DMA 请求做了 Overrun 管理。

而系统里实际上还存在另一条请求链路,即DMAMUX Generator 申请的 DMA 请求。如果这一路请求产生了 Overrun,而中断服务函数里没有对应处理,那么它的 Overrun 标志就不会被清掉。若是这样,程序将不断重进 DMAMUX1_OVR_IRQHandler(),最终看起来就像程序“卡死”了。

客观地说,如果我们对 DMAMUX 的工作机制和请求映射关系不够熟悉的话,这个问题确实不太容易一眼发现。最后,当我在 DMAMUX1_OVR_IRQHandler() 中再补上一行对应的处理代码后,DMA 溢出中断反复触发的问题便消失【至于如何消除DMA请求溢出那是另外的事情】。

调整的代码见下图【注:这里的参数需要结合实际工程场景填写】:

从分析来看,这个问题应该算是 CubeMX 在特定配置下生成代码不完整 的问题。期待后续版本能够进一步完善。

三、TIM23 / TIM24 是否支持中心对齐计数模式?

有用户在使用 STM32H723 芯片开发产品时,需要用到多个定时器之间的同步输出功能。工程通过 STM32CubeMX 进行初始化配置,并基于 STM32H7 系列 HAL 库生成代码。

根据参考手册描述和 CubeMX 配置界面,TIM23 和 TIM24 都是 32 位定时器,并且应该是支持中心对齐计数模式的。

但实际调试中却发现:

TIM23 / TIM24 似乎并不支持中心对齐计数模式,只表现为单向向上计数模式。

这就让人产生疑问:

是手册描述有误?

还是 CubeMX 创建的工程代码有问题?

或者是 HAL 库本身存在局限?

为了确认问题,找一块 STM32H723 开发板 做实际验证。

测试过程中,在时基参数保持不变的情况下,仅调整计数模式,观察 PWM 输出特征。结果发现:

对于 TIM23 / TIM24,无论如何调整计数模式,PWM 输出特征几乎没有变化;

具体表现为:输出始终呈现 单向向上计数模式下的特征。

为了排除测试方法问题,再拿 TIM2 做对比。因为 TIM2 同样是 32 位定时器,并且明确支持中心对齐计数模式。

对 TIM2 做同样测试后,结果就完全不同了。

在保持时基参数不变的情况下,调整计数模式时,PWM 输出特征会明显随之变化。

既然 TIM2 可以,而 TIM23 / TIM24 不可以,那么接下来就很自然会想到,是不是初始化代码有差异?

于是开始比较 TIM2 与 TIM23 / TIM24 的配置代码。比对半天,并没有发现实质差别。再进一步观察寄存器,重点看 TIMx->CR1 中的 CMS 位域。

调试发现:

TIM2 的 CMS 位域值会随着代码配置改变;

TIM23 / TIM24 的 CMS 位域值却始终不动。

也就是说,前期配置虽然看起来写进去了,但实际上并没有真正落实到 TIM23 / TIM24 的硬件寄存器中。

为了确认 TIM23 / TIM24 到底是“硬件不支持”,还是“代码没配进去”,最直接的办法就是手动在代码中直接修改寄存器的控制字段。

结果发现,只要直接修改相关寄存器,TIM23 / TIM24 的确可以工作在中心对齐计数模式。

既然硬件支持,问题就只能出在软件配置流程上了。

接下来,对 TIM23 / TIM24 的初始化代码逐行跟踪。最终在 TIM_Base_SetConfig() 函数中找到了问题原因。

在进行定时器计数模式配置时,代码会先通过一个 if 判断,检查当前定时器实例是否支持中心对齐模式。如果判断认为“不支持”,就会直接跳过对 CMS 参数的配置。

问题就出在这个if检查这个地方,我们可以继续跟踪下去看看这个检查代码。

这个检查是一个在stm32h723xx.h定义的宏:

#defineIS_TIM_COUNTER_MODE_SELECT_INSTANCE(INSTANCE)(((INSTANCE) ==TIM1)    || ((INSTANCE) ==TIM2)    || ((INSTANCE) ==TIM3)    || ((INSTANCE) ==TIM4)    || ((INSTANCE) ==TIM5)    || ((INSTANCE) ==TIM8)

看到目前版本的这个宏定义就真相大白了。

原来TIM23/TIM24根本就没有被定义进来,针对二者的检查自然返回失败,导致前期的中心对齐配置参数没法落实到硬件寄存器,CMS字段的默认值0对应的就是单向的向上计数模式。

客观上讲,这应当属于当前库代码中的一个 bug。硬件支持,但当前库代码有缺陷。

STM32 系列型号众多、更新快、外设差异细节也多,库里出现个别实例判断遗漏并不算特别意外。只能说,遇到这种现象时,除了看手册和 CubeMX 配置之外,也要敢于进一步追到 HAL 宏定义和底层判断逻辑里去。

小结

本文的三个案例分别涉及HRTIM、DMAMUX 和 TIM模块,背后反映的原因有三:

    接口名或参数名相近,容易导致误用;CubeMX 自动生成的代码不一定覆盖所有场景;HAL 库本身也可能存在缺陷。

像这些“配置看起来没问题,但现象明显不对”时,通常也只能是看现象,查配置,看寄存器,再辅以灵活应对。有些问题或许并不是你不会使用或配置,而是某个细节没有真正衔接上。这也没办法,哪能样样完美、事事一帆风顺呢。只能平常多积累,问题来了也平常心对待,冷静去处理就好。

OK,今天的分享就到这里,下次再聊~!

相关推荐