【已解决】HC32F460 timer4例程 时钟频率的计算错误 以及 中断位未清零错误

使用HC32F460官方例程进行说明
版本:hc32f460_ddl_Rev2.2.0\example\ev_hc32f460_lqfp100_v1
例程:timer4\timer4_oco_single_high_ch

目录

一、定时器计时频率的计算

1.1 系统时钟频率

1.2 定时器频率

1.2 计数寄存器

1.3 周期基准寄存器

1.4 比较匹配寄存器

1.5 举例说明

1.5.1 周期基准寄存器

1.5.2 定时器频率设置

1.5.3 比较匹配寄存器

1.5.4 计算

二、中断位未清零错误

2.1 简介

2.2 错误原因

2.3 实验结果

三、总结

3.1 定时器计时频率的计算公式

3.2 中断标志位重置


一、定时器计时频率的计算

1.1 系统时钟频率

 在单片机中,由内部/外部时钟作为系统的时钟源,你可以理解为,单片机在供电后,时钟不停地在输出电压信号,波形为方波,该信号就像人的心脏,心脏跳动一次,为人体供血,而时钟的信号跳变一次,单片机内部各模块接到一个动作信号,单片机所有模块的动作都依赖这个时钟源输出的信号,所以系统时钟的频率会决定单片机各个模块的工作频率。

1.2 定时器频率

一般的定时器使用系统时钟,在此基础上进行分频,在HC32F460的timer0中,使用系统时钟中的pclk1(在用户手册可查询),460芯片的主频(也就是系统时钟频率)为200MHz,pclk1为100MHz,所以timer0的默认频率是100MHz,在此基础上分频。以100MHz为例,即timer0不停地在收到系统发过来的时钟信号,信号的频率为100MHz,也就是一个完整的信号周期事件为1/100M s,即100us,0.1ms。
不同单片机的不同计时器使用的时钟源不一样,默认的时钟频率也不一样(这一点可能是和内部的硬件电路有关系),比如timer4的默认频率为8MHz,在此基础上分频。

1.2 计数寄存器

计数寄存器counter,该寄存器在定时器收到系统一个时钟信号时,加一,该值在定时器运行过程中,是不停地在计数的。计数频率为定时器频率,在一个计数周期中,由零向上加一直加到设定的最大值MAX(不同波形不一样,三角波有向下计数的部分),随后清零,循环计数。在计数的过程中,可根据需要,产生相应的中断或者事件。

1.3 周期基准寄存器

周期基准寄存器,存储计数周期最大值MAX,用于规定定时器一个计时周期的最大值,在不同单片机中的缩写可能有所不同。

1.4 比较匹配寄存器

比较匹配寄存器,存储值一般在0和MAX之间(如果1.3和1.4都使用16位寄存器,如MAX设为0xABCD,而比较匹配寄存器设为0xFFFF,或大于MAX的任何值,则不会发生比较匹配),计数过程,当计数寄存器和比较匹配寄存器的设定值相等,可以产生比较匹配中断或事件。在主程序中对比较匹配寄存器进行连续的修改,可以对输出波形的占空比进行调制,常用于调制PWM占空比。

1.5 举例说明

华大timer4_oco_single_high_ch官方例程如下(选取核心代码,按代码顺序)

1.5.1 周期基准寄存器

#include "hc32_ddl.h"

/* Timer4 CNT */
#define TIMER4_UNIT                     (M4_TMR41)
#define TIMER4_CNT_CYCLE_VAL            (50000u)        /* Timer4 counter cycle value */

/* Timer4 OCO */
#define TIMER4_OCO_HIGH_CH              (Timer4OcoOuh)  /* only Timer4OcoOuh  Timer4OcoOvh  Timer4OcoOwh */ // even High odd low

/* Timer4 OCO interrupt number */
#define TIMER4_OCO_HIGH_CH_INT_NUM      (INT_TMR41_GCMUH)

 其中TIMER4_CNT_CYCLE_VAL值为50000u,即为周期基准寄存器的设定值,u表示整数

1.5.2 定时器频率设置

/* Timer4 CNT : Initialize CNT configuration structure */
stcCntInit.enBufferCmd = Disable;
stcCntInit.enClk = Timer4CntPclk;
stcCntInit.enClkDiv = Timer4CntPclkDiv16;   /* CNT clock divide */
stcCntInit.enCntMode = Timer4CntSawtoothWave;
stcCntInit.enZeroIntMsk = Timer4CntIntMask0;
stcCntInit.enPeakIntMsk = Timer4CntIntMask0;
TIMER4_CNT_Init(TIMER4_UNIT, &stcCntInit);                      /* Initialize CNT */
TIMER4_CNT_SetCycleVal(TIMER4_UNIT, TIMER4_CNT_CYCLE_VAL);      /* Set CNT Cycle value */

其中使用了16分频,定时器的波形为锯齿波 

1.5.3 比较匹配寄存器

uint16_t OcoHighChOccrVal = TIMER4_CNT_CYCLE_VAL / 2u;

/* Timer4 OCO : Initialize OCO channel configuration structure */
stcOcoInit.enOcoIntCmd = Enable;
stcOcoInit.enPortLevel = OcPortLevelLow; //输出无效时,端口状态
stcOcoInit.enOcmrBufMode = OcmrBufDisable;
stcOcoInit.enOccrBufMode = OccrBufDisable;
TIMER4_OCO_Init(TIMER4_UNIT, TIMER4_OCO_HIGH_CH, &stcOcoInit);  /* Initialize OCO high channel */

/* Set OCO compare value */
TIMER4_OCO_WriteOccr(TIMER4_UNIT, TIMER4_OCO_HIGH_CH, OcoHighChOccrVal);

/* Enable OCO */
TIMER4_OCO_OutputCompareCmd(TIMER4_UNIT, TIMER4_OCO_HIGH_CH, Enable);

其中OcoHighChOccrVal为比较匹配寄存器,设定值为周期基准寄存器的1/2

1.5.4 计算

timer4
分频后的计数频率    16/8M    (counter每隔这么长时间+1)
周期基准寄存器      50000u  
计时时间           (16/8M) * 50000 = 0.1s    即10Hz

经过计算后,得到的频率是10Hz,即LED灯0.1s发生一次状态切换
而在华大timer4_oco_single_high_ch官方例程的readme.txt对例程的表述如下: 对频率的判断为5hz,也就是0.2s,LED_GREEN切换一次状态。

================================================================================
使用步骤
================================================================================
1)打开工程并重新编译;
2)启动IDE的下载和调试功能,全速运行;
3)LED_GREEN不断闪烁,频率为5Hz,测试结果符合预期。

================================================================================

至此,笔者产生疑惑,仔细检查后,不知道是哪里出错,也没有示波器,外接LED闪烁太快,肉眼识别不出,于是决定修改timer4的比较匹配中断函数代码,外接LED灯进行检测。然而又出现了新的错误。


二、中断位未清零错误

2.1 简介

例程中,使用了比较匹配中断,由于使用的是锯齿波,所以比较匹配的周期与定时器计数周期相同,中断回调函数如下:

//原代码
static void OcoIrqCallback(void)
{
	BSP_LED_Toggle(LED_GREEN);		
    TIMER4_OCO_ClearIrqFlag(TIMER4_UNIT, TIMER4_OCO_HIGH_CH);	
}

//修改后,代码A
uint16_t tick = 0 ;
static void OcoIrqCallback(void)
{
	tick++;
	if(tick>=10)
	{
		BSP_LED_Toggle(LED_GREEN);		
        TIMER4_OCO_ClearIrqFlag(TIMER4_UNIT, TIMER4_OCO_HIGH_CH);	
        tick=0;
	}		
}

笔者本意是,10次比较匹配中断后,切换一次LED状态,按理LED闪烁频率应该比原代码慢10倍,可是在实际测试过程中,闪烁频率并没有变化,和预期不符,为此困扰了很长时间,却不知道为何。今天在看另一个例程的延时tick写法时,茅塞顿开,对代码A进行了修改,修改如下:

//修改后,代码B
uint16_t tick = 0 ;
static void OcoIrqCallback(void)
{
	tick++;
	if(tick>=10)
	{
		BSP_LED_Toggle(LED_GREEN);        
        tick=0;
	}
    TIMER4_OCO_ClearIrqFlag(TIMER4_UNIT, TIMER4_OCO_HIGH_CH);	
}

你们能看出代码A和代码B的区别吗?

2.2 错误原因

定时器计数匹配产生中断,执行中断回调函数OcolrqCallback(),未及时清零中断标志位。

代码A,在前9次进入回调函数中,都没有清除中断标志位,所以在跳出中断回调函数后,立马就又进入了回调函数,回调函数的运行时间很短,远小于定时器的中断时间,tick++,很快就达到10次,随后切换了LED状态,并且清除了中断标志。所以在实验过程中,观察到的结果是,LED切换的时间并没有增加10倍,而是和时钟中断频率相近。
代码B,在每一次回调函数中,都清除了中断标志,所以在10次中断后,切换LED状态,LED切换的频率是时钟中断频率的10倍。

综上所述,代码A是在每一次中断+系统时钟频率运行10次后切换LED状态,而代码B是在每10次中断后切换LED状态。

2.3 实验结果

对代码B进行实验,发现LED每1s闪烁一次,因此定时器timer4的频率和笔者计算预期一致,为0.1s,笔者认为官方例程在readme.txt中的描述错误。如果华大的开发者看见该贴,可以进行实验,验证一下笔者是否错误。

三、总结

3.1 定时器计时频率的计算公式

计数周期    (分频系数/定时器时钟频率)*周期基准计数器值

3.2 中断标志位重置

编程中,切记手动清零中断标志位。

在大多数情况下,微处理器在处理完一个中断后会自动清除中断标志,然后恢复到处理主程序,并且会在下一次定时器中断到来时再次响应中断。即使在处理中断期间,中断标志没有被手动清除,它在处理完中断过后也会被自动清除。并且,一般来说,新的中断不会在当前的中断处理程序运行期间被触发,也就是说,处理程序不会被嵌套调用。 然而,如果中断标志不是自动清除的(这在一些定时器配置中是可能的),在定时器中断处理期间并没有清除中断标志,这可能会导致处理器在中断处理程序运行期间再次触发中断,而这可能导致处理程序被嵌套调用,并且可能会导致处理器陷入死循环。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值