当前位置:首页 > 公众号精选 > 21ic电子网
[导读]这次和大家一起分享一下华大HC32F460的定时器。

出品  21ic论坛  binoo7

网站:bbs.21ic.com


这次和大家一起分享一下华大HC32F460的定时器。
首先说一下定时器的分类,一共有四种定时器分别是:
  • 高级控制定时器(Timer6)有3个;
  • 通用控制定时器(Timer4)有3个;
  • 通用定时器(TimerA)有6个;
  • 通用定时器(Timer0)有2个;

再次说一下这几个定时器有什么区别:
看名字就知道高级控制定时器肯定是功能最全的
  • 高级控制定时器 6(Timer6)是一个 16 位计数宽度的高性能定时器,可用于计数产生

不同形式的时钟波形,输出以供外部使用。
  • 该定时器支持三角波和锯齿波两种波形模式,可生成各种 PWM 波形;
  • 单元间可实现软件同步计数和硬件同步计数;
  • 各基准值寄存器支持缓存功能;
  • 支持 2 相正交编码和 3 相正交编码;
  • 支持 EMB 控制。
主要看一下基本框图


看框图能看,Timer6 的计数时钟可以有以下几种选择:
a) PCLK0 及 PCLK0 的 4、16、64、256、1024 分频(GCONR.CKDIV[2:0]设定)
b) 内部触发事件触发输入(HCUPR[17:16]或 HCDOR[17:16]设定)
c) TIM6_TRIGA-B 的端口输入(HCUPR[11:8]或 HCDOR[11:8]设定)
d) TIM6__PWMA 和 TIM6__PWMB 的 正交编码 输入( HCUPR[7:0] 或HCDOR[7:0]设定)
计数时钟源选择 a 时为软件计数模式,计数时钟源选择 b、c、d 时为硬件计数模式。
上述描述可以看到,b、c、d 时钟互相独立,可分别设定有效或无效,并且当选择 b、c、d 时钟时,a 时钟自动无效。
定时器6的时钟不能按照任意的分频系数进行分频,只能按照特定的频率进行分频,实话实说这一点就不如STM32方便了



计数方式有两种,一个是锯齿波,一个是三角波,两种波形的方式进行计数,当达到设定值后会清零或向下计数


自动重装载值这里叫做通用周期基准值寄存器,官方给的解释是设定每轮计数的计数周期值及对应缓存值 ,说实话这个官方的解释我看了一天也没搞懂是啥意思,很容易和STM32的周期计数混淆,大家要多注意了。



看寄存器的配置就能知道这是一个16位的定时器。


定时器可以产生的中断中官方给的解释如下:


  • 通用比较基准值寄存器(GCMAR-GCMFR)共计 6 个,可分别与计数值比较产生比较匹配。
  • 计数比较匹配时,状态标志寄存器(STFLR)中的 STFLR.CMAF~STFLR.CMFF位分别会被置为 1。
  • 此时若设定中断控制寄存器(ICONR)的 INTENA~INTENF 中相应位为 1 使能中断,则对应的中断请(TMR6_U_GCMA~F)也会被触发。
  • 在硬件捕获事件选择寄存器(HCPAR、HCPBR)选择的捕获输入有效条件产生时,捕获输入动作发生。
  • 此时若设置中断控制寄存器(ICONR)的 INTENA 或 INTENB 位为1 使能中断,则对应的中断请求(TMR6_U_GCMA~B)被触发。
  • 2 个专用比较基准值寄存器(SCMAR-SCMBR)也可分别与计数值比较产生比较匹配。
  • 计数比较匹配时,状态标志寄存器(STFLR)中的 STFLR.CMSPAF~CMSPBF 位分别会被置为 1。此时若设定中断控制寄存器(ICONR)的 INTENSAU或 INTENSBU中相应位为 1 使能中断,则对应的中断请求(TMR6_U_SCMA~B)也会被触发。

看上面的解释可能会云里雾里看不懂啥意思,还不如直接看中断控制寄存器,有多少中断都在中断控制器里写的明明白白,这样的话我们就能很容易的来根据需要配置中断的功能了,具体配置如下图所示



这里面比较值的设置就是根据两个比较基准寄存器的值来进行判断的,这两个寄存器分别是通用比较基准值寄存器,专用比较基准寄存器,其实还有一个是死区时间基准值寄存器



下面直接看代码,看看高级控制定时器的用法



官方给的例程挺多的,有耐心的小伙伴可以仔细分析一下MCU的TIMER6的PWM的锯齿波模式、无缓存输出功能,
看看比较输出的功能是怎么配置的

/******************************************************************************** Include files******************************************************************************/#include "hc32_ddl.h"
/******************************************************************************** Local type definitions ('typedef')******************************************************************************/
/******************************************************************************** Local pre-processor symbols/macros ('#define')******************************************************************************//* KEY0 (SW2)*/#define SW2_PORT (PortD)#define SW2_PIN (Pin03)/* KEY1 (SW4)*/#define SW4_PORT (PortD)#define SW4_PIN (Pin04)/* KEY2 (SW3)*/#define SW3_PORT (PortD)#define SW3_PIN (Pin05)/* KEY3 (SW5)*/#define SW5_PORT (PortD)#define SW5_PIN (Pin06)
/* LED0 Port/Pin definition */#define LED0_PORT (PortE)#define LED0_PIN (Pin06)
/* LED1 Port/Pin definition */#define LED1_PORT (PortD)#define LED1_PIN (Pin07)
/* LED2 Port/Pin definition */#define LED2_PORT (PortB)#define LED2_PIN (Pin05)
/* LED3 Port/Pin definition */#define LED3_PORT (PortB)#define LED3_PIN (Pin09)
/* LED0~1 toggle definition */#define LED0_TOGGLE() (PORT_Toggle(LED0_PORT, LED0_PIN))#define LED1_TOGGLE() (PORT_Toggle(LED1_PORT, LED1_PIN))#define LED2_TOGGLE() (PORT_Toggle(LED2_PORT, LED2_PIN))#define LED3_TOGGLE() (PORT_Toggle(LED3_PORT, LED3_PIN))
/******************************************************************************** Global variable definitions (declared in header file with 'extern')******************************************************************************/
/******************************************************************************** Local function prototypes ('static')******************************************************************************/
/******************************************************************************** Local variable definitions ('static')******************************************************************************/

/******************************************************************************** Function implementation - global ('extern') and local ('static')******************************************************************************//*********************************************************************************** \brief Callback function of external interrupt ch.0********************************************************************************/
void Timer6_OverFlow_CallBack(void){
}
/********************************************************************************** \brief Initialize the system clock for the sample**** \param None**** \return None******************************************************************************/static void SysClkIni(void){ en_clk_sys_source_t enSysClkSrc; stc_clk_sysclk_cfg_t stcSysClkCfg; stc_clk_mpll_cfg_t stcMpllCfg; stc_clk_output_cfg_t stcOutputClkCfg;

MEM_ZERO_STRUCT(enSysClkSrc); MEM_ZERO_STRUCT(stcSysClkCfg); MEM_ZERO_STRUCT(stcMpllCfg); MEM_ZERO_STRUCT(stcOutputClkCfg);
/* Set bus clk div. */ stcSysClkCfg.enHclkDiv = ClkSysclkDiv1; // 168MHz stcSysClkCfg.enExclkDiv = ClkSysclkDiv2; // 84MHz
stcSysClkCfg.enPclk0Div = ClkSysclkDiv1; // 168MHz (timer6 cnt) stcSysClkCfg.enPclk1Div = ClkSysclkDiv2; // 84MHz (timer6 logic) stcSysClkCfg.enPclk2Div = ClkSysclkDiv4; // 42MHz stcSysClkCfg.enPclk3Div = ClkSysclkDiv4; // 42MHz stcSysClkCfg.enPclk4Div = ClkSysclkDiv2; // 84MHz CLK_SysClkConfig(&stcSysClkCfg);
CLK_HrcCmd(Enable); //Enable HRC
/* MPLL config. */ stcMpllCfg.pllmDiv = 2ul; //HRC 16M / 2 stcMpllCfg.plln = 42ul; //8M*42 = 336M stcMpllCfg.PllpDiv = 2ul; //MLLP = 168M stcMpllCfg.PllqDiv = 2ul; //MLLQ = 168M stcMpllCfg.PllrDiv = 2ul; //MLLR = 168M CLK_SetPllSource(ClkPllSrcHRC); CLK_MpllConfig(&stcMpllCfg);
/* flash read wait cycle setting */ EFM_Unlock(); EFM_SetLatency(EFM_LATENCY_4); EFM_Lock();
/* Enable MPLL. */ CLK_MpllCmd(Enable);
/* Wait MPLL ready. */ while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy)) { ; }
/* Switch system clock source to MPLL. */ CLK_SetSysClkSource(CLKSysSrcMPLL);}
/*********************************************************************************** \brief Main function of project**** \param None**** \retval int32_t return value, if needed********************************************************************************/int32_t main(void){ uint16_t u16Period; uint16_t u16Compare; stc_timer6_basecnt_cfg_t stcTIM6BaseCntCfg; stc_timer6_port_output_cfg_t stcTIM6PWMxCfg; stc_timer6_gcmp_buf_cfg_t stcGCMPBufCfg; stc_port_init_t stcPortInit; stc_irq_regi_conf_t stcIrqRegiConf;
MEM_ZERO_STRUCT(stcTIM6BaseCntCfg); MEM_ZERO_STRUCT(stcTIM6PWMxCfg); MEM_ZERO_STRUCT(stcGCMPBufCfg); MEM_ZERO_STRUCT(stcPortInit); MEM_ZERO_STRUCT(stcIrqRegiConf);
SysClkIni();
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIM61, Enable);
PORT_SetFunc(PortE, Pin09, Func_Tim6, Disable); //Timer61 PWMA PORT_SetFunc(PortE, Pin08, Func_Tim6, Disable); //Timer61 PWMB
stcTIM6BaseCntCfg.enCntMode = Timer6CntSawtoothMode; //Sawtooth wave mode stcTIM6BaseCntCfg.enCntDir = Timer6CntDirUp; //Counter counting up stcTIM6BaseCntCfg.enCntClkDiv = Timer6PclkDiv1; //Count clock: pclk0 Timer6_Init(M4_TMR61, &stcTIM6BaseCntCfg); //timer6 PWM frequency, count mode and clk config
u16Period = 33600u; Timer6_SetPeriod(M4_TMR61, Timer6PeriodA, u16Period); //period set
u16Compare = 10000u; Timer6_SetGeneralCmpValue(M4_TMR61, Timer6GenCompareA, u16Compare); //Set General Compare RegisterA Value
u16Compare = 20000u; Timer6_SetGeneralCmpValue(M4_TMR61, Timer6GenCompareB, u16Compare); //Set General Compare RegisterB Value
stcTIM6PWMxCfg.enPortMode = Timer6ModeCompareOutput; //Compare output function stcTIM6PWMxCfg.bOutEn = true; //Output enable stcTIM6PWMxCfg.enPerc = Timer6PWMxCompareLow; //PWMA port output Low level when CNTER value match PERAR stcTIM6PWMxCfg.enCmpc = Timer6PWMxCompareHigh; //PWMA port output High level when CNTER value match with GCMAR stcTIM6PWMxCfg.enStaStp = Timer6PWMxStateSelSS; //PWMA output status is decide by STACA STPCA when CNTER start and stop stcTIM6PWMxCfg.enStaOut = Timer6PWMxPortOutLow; //PWMA port output set low level when CNTER start stcTIM6PWMxCfg.enStpOut = Timer6PWMxPortOutLow; //PWMA port output set low level when CNTER stop stcTIM6PWMxCfg.enDisVal = Timer6PWMxDisValLow; Timer6_PortOutputConfig(M4_TMR61, Timer6PWMA, &stcTIM6PWMxCfg);
stcTIM6PWMxCfg.enPortMode = Timer6ModeCompareOutput; //Compare output function stcTIM6PWMxCfg.bOutEn = true; //Output enable stcTIM6PWMxCfg.enPerc = Timer6PWMxCompareLow; //PWMB port output Low level when CNTER value match PERAR stcTIM6PWMxCfg.enCmpc = Timer6PWMxCompareHigh; //PWMB port output High level when CNTER value match with GCMBR stcTIM6PWMxCfg.enStaStp = Timer6PWMxStateSelSS; //PWMB output status is decide by STACB STPCB when CNTER start and stop stcTIM6PWMxCfg.enStaOut = Timer6PWMxPortOutLow; //PWMB port output set low level when CNTER start stcTIM6PWMxCfg.enStpOut = Timer6PWMxPortOutLow; //PWMB port output set low level when CNTER stop stcTIM6PWMxCfg.enDisVal = Timer6PWMxDisValLow; Timer6_PortOutputConfig(M4_TMR61, Timer6PWMB, &stcTIM6PWMxCfg);
/*config interrupt*/ /* Enable timer61 GOVF interrupt */ Timer6_ConfigIrq(M4_TMR61, Timer6INTENOVF, true);

stcIrqRegiConf.enIRQn = Int002_IRQn; //Register INT_TMR61_GOVF Int to Vect.No.002 stcIrqRegiConf.enIntSrc = INT_TMR61_GOVF; //Select Event interrupt of timer61 stcIrqRegiConf.pfnCallback = &Timer6_OverFlow_CallBack; //Callback function enIrqRegistration(&stcIrqRegiConf); //Registration IRQ
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn); //Clear Pending NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);//Set priority NVIC_EnableIRQ(stcIrqRegiConf.enIRQn); //Enable NVIC

/*start timer6*/ Timer6_StartCount(M4_TMR61);
while(1) { ; }
}


我们直接分析主函数部分


第一步:先开启时钟 :PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIM61, Enable);

直接看源程序,就能找到三个时钟分别是61 62 63
这次我们用的是61也就是第一个时钟


第二步:是配置引脚为TIMER61的输出引脚 也就是端口复用


第三步:配置定时器功能和预分频系数
定时器都有什么功能呢,看源代码能看到有三个

那我们再看一下这个功能是配置的哪个寄存器呢?

这个时候如果还是没看懂,那么我们回过头来看用户手册关于这个定时器是怎么说的

以后我们如果不知道代码中配置的是什么意思了,也可以这样来寻找答案。


第四步:设置通用周期基准值寄存器,也就是重装载值

u16Period = 33600u; Timer6_SetPeriod(M4_TMR61, Timer6PeriodA, u16Period); //period set


这里我们用的是A通道,timer6有三个通道,分别如下图所示


第五步:设置比较值

 u16Compare = 10000u; Timer6_SetGeneralCmpValue(M4_TMR61, Timer6GenCompareA, u16Compare); //Set General Compare RegisterA Value
u16Compare = 20000u; Timer6_SetGeneralCmpValue(M4_TMR61, Timer6GenCompareB, u16Compare); //Set General Compare RegisterB Value

看到这里我们来画个图说明一下

我们制定好波形,波形的频率就是根据预分频值算出来的,然后设置好重装载值和比较值,再设置中断类型,就可以产生响应的中断

第六步:设置输出功能

stcTIM6PWMxCfg.enPortMode = Timer6ModeCompareOutput; //Compare output function stcTIM6PWMxCfg.bOutEn = true; //Output enable stcTIM6PWMxCfg.enPerc = Timer6PWMxCompareLow; //PWMA port output Low level when CNTER value match PERAR stcTIM6PWMxCfg.enCmpc = Timer6PWMxCompareHigh; //PWMA port output High level when CNTER value match with GCMAR stcTIM6PWMxCfg.enStaStp = Timer6PWMxStateSelSS; //PWMA output status is decide by STACA STPCA when CNTER start and stop stcTIM6PWMxCfg.enStaOut = Timer6PWMxPortOutLow; //PWMA port output set low level when CNTER start stcTIM6PWMxCfg.enStpOut = Timer6PWMxPortOutLow; //PWMA port output set low level when CNTER stop stcTIM6PWMxCfg.enDisVal = Timer6PWMxDisValLow; Timer6_PortOutputConfig(M4_TMR61, Timer6PWMA, &stcTIM6PWMxCfg);

根据以上的代码我们就设置好了比较输出的功能,怎么算比较输出呢,就是当计数值经过比较值的时候输出引脚进行翻转,由高电平变为低电平,或者有低电平变为高电平


这样就产生了占空比可调的PWM波形输出了,看下面的图,大家就清楚了



每次遇到比较,就翻转输出,这样我们设置不同的比较值,就可以有不同占空比的PWM了,这样的控制方式对于步进电机控制上好处多多,我推荐大家可以看看硬石老师的电机开发例程和野火老师的例程,他们用的都是定时器的比较输出的功能来控制步进电机产生梯形加减速、S形加减速。


言归正传我们继续分析代码

第七步:配置中断功能

/*config interrupt*/ /* Enable timer61 GOVF interrupt */ Timer6_ConfigIrq(M4_TMR61, Timer6INTENOVF, true);

stcIrqRegiConf.enIRQn = Int002_IRQn; //Register INT_TMR61_GOVF Int to Vect.No.002 stcIrqRegiConf.enIntSrc = INT_TMR61_GOVF; //Select Event interrupt of timer61 stcIrqRegiConf.pfnCallback = &Timer6_OverFlow_CallBack; //Callback function enIrqRegistration(&stcIrqRegiConf); //Registration IRQ
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn); //Clear Pending NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);//Set priority NVIC_EnableIRQ(stcIrqRegiConf.enIRQn); //Enable NVIC
复制代码


第八步:使能定时器

 /*start timer6*/ Timer6_StartCount(M4_TMR61);

这里我们讲解的是高级定时器的功能,剩下的定时器,就是在这个基础上进行简配得到的,所以就不再一一列举了,大家有时间可以看看手册和代码,关键的注意事项在上面都介绍了,大家应该配置没有问题,如果遇到问题了,可以在本帖留言,我看到后会及时回复 谢谢大家了



本文系21i c论坛网友binoo7的回忆 原创

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

21ic电子网

扫描二维码,关注更多精彩内容

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Holtek新推出BS67F2432具备触控按键、高精准度HIRC与LCD驱动器Flash MCU。主要特色为内建高精准度4MHz HIRC振荡电路、8路触控按键及最大支持4COM×15SEG LCD驱动器。适用于触控接...

关键字: MCU LCD驱动器 定时器

Holtek持续扩展Touch A/D Flash MCU产品,新增系列成员BS86C12CA,延续优良抗干扰特性,提供丰富的定时器资源并支持LXT振荡器。引脚与BS86C08C及BS86D12C相容,具高性价比,适合需...

关键字: MCU LXT振荡器 定时器

采用MCU(微控制器单元)模块实现定时器的设计是通过利用MCU内部的定时器/计数器资源来实现的。定时器是MCU中的一个重要功能模块,它可以在特定的时间间隔内执行特定的操作,如产生中断、更新定时器值、触发其他设备等。

关键字: mcu模块 定时器

单片机的外设是指与单片机核心处理部分相连的附加硬件模块,它们能够扩展单片机的功能和能力。这些外设包括各种模块和接口,用于处理特定的任务或实现特定的功能。

关键字: 单片机 定时器

PIC单片机是基于RISC系统结构的单片机,最初的设计是支持PDP(编程数据处理器)计算机。大量的操作可以用来控制外围设备。PIC单片机比微控制器具有更快的程序执行能力。它是由微芯片技术公司于1889年发明的,是一种8位...

关键字: PIC单片机 定时器 中断

外部输入、输出继电器、内部继电器、定时器、计数器等器件的接点可多次重复使用,无需用复杂的程序结构来减少接点的使用次数。

关键字: plc编程 定时器 计数器

单片机可以通过“定时/计数模式选择位C/T”令定时/计数器工作于定时或计数模式下,也可通过“工作方式选择位M1M0”设定其工作方式。C/T和M1M0等与定时/计数器有关的位在寄存器TCON或TMOD中,见表4-8和表4-...

关键字: 寄存器 计数器 定时器

在家电产品和工业应用系统中,定时和计数是两种常用的功能,如:微波炉加热计时和流水线上产品数目统计等。MCS-51单片机内部集成的两个可编程定时/计数器T0和T1使用灵活、方便,在仪器仪表等工业产品中应用广泛。

关键字: 计数器 定时器 单片机

TMOD 的地址是 89H ,它不能位寻址 ,它里面的内容被称为方式字,设置时一次写入,其各位的定义如图 6.2 所示。高 4 位用于定时器 T1 ,低 4 位用于定时器 T0 。

关键字: 定时器 计数器 单片机

单片机定时器其实跟我们平时常说的计数器,是同一个电子元件,只不过计数器记录的是单片机外部情况,所接收的也是外部脉冲,而定时器则是由单片机自身提供的一个非常稳定的计数器,这个稳定的计数器就是单片机上连接的晶振部件。

关键字: 定时器 计数器 单片机
关闭
关闭