发新帖本帖赏金 10.00元(功能说明)我要提问
12下一页
返回列表
[综合信息]

华大HC32F460定时器介绍

[复制链接]
6493|33
手机看帖
扫描二维码
随时随地手机跟帖
binoo7|  楼主 | 2021-3-3 16:55 | 显示全部楼层 |阅读模式
#申请原创#@21小跑堂@21小跑堂@21小跑堂

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

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

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


73627603f3c86e6ffb.png
看框图能看,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_<t>_PWMA 和 TIM6_<t>_PWMB 的 正交编码 输入( HCUPR[7:0] 或HCDOR[7:0]设定)
计数时钟源选择 a 时为软件计数模式,计数时钟源选择 b、c、d 时为硬件计数模式。
上述描述可以看到,b、c、d 时钟互相独立,可分别设定有效或无效,并且当选择 b、c、d 时钟时,a 时钟自动无效。
定时器6的时钟不能按照任意的分频系数进行分频,只能按照特定的频率进行分频,实话实说这一点就不如STM32方便了

76709603f3ed9edd61.png
计数方式有两种,一个是锯齿波,一个是三角波,两种波形的方式进行计数,当达到设定值后会清零或向下计数
自动重装载值这里叫做通用周期基准值寄存器,官方给的解释是设定每轮计数的计数周期值及对应缓存值 ,说实话这个官方的解释我看了一天也没搞懂是啥意思,很容易和STM32的周期计数混淆,大家要多注意了。
40323603f3fb498e94.png
看寄存器的配置就能知道这是一个16位的定时器。
定时器可以产生的中断中官方给的解释如下:
  • 通用比较基准值寄存器(GCMAR-GCMFR)共计 6 个,可分别与计数值比较产生比较匹配。
  • 计数比较匹配时,状态标志寄存器(STFLR)中的 STFLR.CMAF~STFLR.CMFF位分别会被置为 1。
  • 此时若设定中断控制寄存器(ICONR)的 INTENA~INTENF 中相应位为 1 使能中断,则对应的中断请(TMR6_U<t>_GCMA~F)也会被触发。
  • 在硬件捕获事件选择寄存器(HCPAR、HCPBR)选择的捕获输入有效条件产生时,捕获输入动作发生。
  • 此时若设置中断控制寄存器(ICONR)的 INTENA 或 INTENB 位为1 使能中断,则对应的中断请求(TMR6_U<t>_GCMA~B)被触发。
  • 2 个专用比较基准值寄存器(SCMAR-SCMBR)也可分别与计数值比较产生比较匹配。
  • 计数比较匹配时,状态标志寄存器(STFLR)中的 STFLR.CMSPAF~CMSPBF 位分别会被置为 1。此时若设定中断控制寄存器(ICONR)的 INTENSAU<D>或 INTENSBU<D>中相应位为 1 使能中断,则对应的中断请求(TMR6_U<t>_SCMA~B)也会被触发。
看上面的解释可能会云里雾里看不懂啥意思,还不如直接看中断控制寄存器,有多少中断都在中断控制器里写的明明白白,这样的话我们就能很容易的来根据需要配置中断的功能了,具体配置如下图所示
98924603f41a03849f.png


这里面比较值的设置就是根据两个比较基准寄存器的值来进行判断的,这两个寄存器分别是通用比较基准值寄存器,专用比较基准寄存器,其实还有一个是死区时间基准值寄存器
69156603f42a32a28d.png
下面直接看代码,看看高级控制定时器的用法
18977603f43e023a63.png
官方给的例程挺多的,有耐心的小伙伴可以仔细分析一下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);
35726603f4570f3cc3.png
直接看源程序,就能找到三个时钟分别是61 62 63
这次我们用的是61也就是第一个时钟
第二步:是配置引脚为TIMER61的输出引脚 也就是端口复用
98002603f45e257b6d.png
第三步:配置定时器功能和预分频系数
91305603f4626735f4.png
定时器都有什么功能呢,看源代码能看到有三个
14996603f4664db127.png
那我们再看一下这个功能是配置的哪个寄存器呢?
34793603f46a1091d4.png
这个时候如果还是没看懂,那么我们回过头来看用户手册关于这个定时器是怎么说的
39880603f472fd0b6f.png
以后我们如果不知道代码中配置的是什么意思了,也可以这样来寻找答案。
第四步:设置通用周期基准值寄存器,也就是重装载值
u16Period = 33600u;
    Timer6_SetPeriod(M4_TMR61, Timer6PeriodA, u16Period);               //period set


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

第五步:设置比较
    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


看到这里我们来画个图说明一下
35172603f49ae4b0ca.png
我们制定好波形,波形的频率就是根据预分频值算出来的,然后设置好重装载值和比较值,再设置中断类型,就可以产生响应的中断

第六步:设置输出功能
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波形输出了,看下面的图,大家就清楚了
3707603f4d0db35c4.png
每次遇到比较,就翻转输出,这样我们设置不同的比较值,就可以有不同占空比的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


55755603f4e6404e2d.png


第八步:使能定时器
    /*start timer6*/
    Timer6_StartCount(M4_TMR61);

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





























使用特权

评论回复

打赏榜单

21小跑堂 打赏了 10.00 元 2021-03-05
理由:恭喜通过原创文章审核!请多多加油哦!

里面有晴雨| | 2021-3-3 19:16 | 显示全部楼层
这个介绍的太详细了,不错的,感谢楼主的分享。

使用特权

评论回复
lidi911| | 2021-3-3 20:58 | 显示全部楼层
楼主讲解详细,谢谢。

使用特权

评论回复
海滨消消| | 2021-3-5 15:32 | 显示全部楼层
这个介绍的太详细了,不错的

使用特权

评论回复
duo点| | 2021-3-5 15:34 | 显示全部楼层
楼主很厉害啊 ,介绍的这么详细

使用特权

评论回复
usysm| | 2021-3-6 22:43 | 显示全部楼层
     

使用特权

评论回复
jkl21| | 2021-3-6 22:43 | 显示全部楼层
HC32F460性能怎么样   

使用特权

评论回复
wwppd| | 2021-3-6 22:43 | 显示全部楼层
      

使用特权

评论回复
maqianqu| | 2021-3-6 22:44 | 显示全部楼层
定时器功能比较多了。   

使用特权

评论回复
dspmana| | 2021-3-6 22:44 | 显示全部楼层
驱动舵机的程序有吗   

使用特权

评论回复
eefas| | 2021-3-6 22:44 | 显示全部楼层
华大HC32F460非常棒。  

使用特权

评论回复
pl202| | 2021-3-6 22:45 | 显示全部楼层
四种定时器?   

使用特权

评论回复
typeof| | 2021-3-6 22:45 | 显示全部楼层
2 相正交编码和 3 相正交编码呢

使用特权

评论回复
yujielun| | 2021-3-6 22:45 | 显示全部楼层
这个兼容stm32哪个产品呢   

使用特权

评论回复
htmlme| | 2021-3-6 22:46 | 显示全部楼层
很详细的代码。         

使用特权

评论回复
typeof| | 2021-3-6 22:46 | 显示全部楼层
这个功能真的是非常强大了。   

使用特权

评论回复
pl202| | 2021-3-6 22:46 | 显示全部楼层
一共有几个定时器呢   

使用特权

评论回复
eefas| | 2021-3-6 22:46 | 显示全部楼层
华大HC32F460可以申请到吗啊  

使用特权

评论回复
dspmana| | 2021-3-6 22:46 | 显示全部楼层
楼主来一些实际应用的资料吧。

使用特权

评论回复
maqianqu| | 2021-3-6 22:46 | 显示全部楼层
可以做比价器的。        

使用特权

评论回复
发新帖 本帖赏金 10.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

454

帖子

8

粉丝