【应用笔记】TAE32G5800 定时器实现对外部信号周期和脉冲捕获
本篇笔记详细介绍了如何通过定时器模块实现对外部信号的周期和脉宽进行输入捕获。
该功能利用定时器的从模式以及两个定时器通道对一个外部信号的输入捕获,分别捕获外部信号的周期值以及占空比。通过选择定时器的输入通道作为定时器从模式的触发源,从模式选择复位模式,当定时器在输入通道中检测到外部信号产生上升沿的时候就会将计数器清 0 然后重新开始计数。当输入通道配置上升沿捕获的时候,捕获到的数值就是外部信号的周期值。当输入通道配置成下降沿捕获的时候,捕获到的数值就是外部信号的脉宽。因此我们可以通过设置定时器的两个通道连接到同一个输入脚上,然后设置不同的边沿捕获,就可以捕获到外部信号的周期值以及脉宽。
首先要介绍的是定时器的从模式,如图 2.1 所示,定时器通过另外一个定时器或者内部信号生成一个 TRGIN 信号给到从模式控制器这边,从模式控制器可以根据接收到的 TRIGIN 信号对定时器做出相应的动作。
以下将介绍从模式的几种模式。
从模式:复位模式。当输入触发信号产生上升沿跳变时,计数器及其预分频器将被重新初始化。如果 TMRx_CR0 寄存器中的 URS 位为 0 时,则还会生成更新事件 UEV。然后所有预装载寄存器 TMRx_CPR 和 TMRx_CCxR 都将更新。
从模式:门控模式。通过触发输入信号的电平来使能计数器,当输入信号为高电平时,计数器开始计数,当输入信号为低电平时,计数器保持不变。
从模式:触发模式。通过输入信号的上升沿来启动计数器,计数器开始计数。
本篇笔记利用定时器的输入通道,作为 TRIGIN 产生的源,当定时器接收到 TRIGIN 信号执行复位动作将定时器清 0。
定时器的输入捕获需要配置相关的定时器通道引脚,选择定时器通道为输入模式,配置定时器通道为输入模式后,还需要配置输入通道分频、滤波、以及需要捕获的边沿和触发输入选择等。
在输入捕获模式下,当在对应的 ICx 信号上检测到边沿跳变后,会将计数器的值锁存到捕获/比较寄存器(TMRx_CCxR)中。当发生捕获事件时,会将 TMRx_SR 寄存器中相应的 CCxMIF 标志位置 1,并触发定时器中断(如果对应中断已使能)。如果在发生捕获事件时 CCxMIF 标志位已经被置位,则会将 TMRx_SR 寄存器中相应的 CCxOIF 重复捕获标志位置 1。可通过软件对捕获标志位 CCxMIF 及重复捕获标志位 CCxOIF 写 1 来清零,或读取存储在 TMRx_CCxR 寄存器中的捕获数据时硬件自动清零。
通过配置 TMRx_CCMR 寄存器中的 CCxS 位来选择捕获输入通道,将 TMRx_CCER 寄存器中的 CCxE 位置 1,就可以使定时器工作在输入捕获模式下。
通过配置 TMRx_CCER 寄存器中的 CCxP 位来选择捕获模式下的触发边沿,配置 TMRx_CIR 寄存器的 CxTIS 位可选择捕获的引脚输入源或比较器模块(CMPx_OUT)的内部输出信号。
本篇笔记通过配置定时器的两个通道作为输入通道,选择捕获同一个外部信号不同的边沿实现对外部信号周期和脉宽的捕获。
本篇笔记通过直接操作寄存器的方式实现对外部信号的周期和脉宽捕获功能。代码的配置包括定时的初始化,定时器功能引脚和模块时钟以及中断服务函数使能初始化,中断服务函数内部执行函数配置和外部信号输出配置。
首先要配置的是定时器功能引脚和模式时钟以及中断服务函数使能初始化。
void LL_TMR_MspInit(TMR_TypeDef *Instance)
{
GPIO_InitTypeDef TMR9_CH0;
memset((void*)&TMR9_CH0,0x0,sizeof(TMR9_CH0));
TMR9_CH0.OType = GPIO_OTYPE_PP;
TMR9_CH0.Pull = GPIO_NOPULL;
TMR9_CH0.Speed = GPIO_SPEED_FREQ_LOW;
TMR9_CH0.Alternate = GPIO_AF7_TMR9;
TMR9_CH0.Pin = GPIO_PIN_8;
LL_GPIO_Init(GPIOA,&TMR9_CH0);
if (Instance == TMR9) {
//TMR9 Bus Clock Enable and Soft Reset Release
LL_RCU_TMR9_ClkEnRstRelease();
//NVIC TMR9 Interrupt Enable
LL_NVIC_EnableIRQ(TMR9_BRK_IRQn);
LL_NVIC_EnableIRQ(TMR9_UPD_IRQn);
LL_NVIC_EnableIRQ(TMR9_TRG_IRQn);
LL_NVIC_EnableIRQ(TMR9_CC_IRQn);
}
}
在对定时器的功能引脚配置完成以及使能模块时钟和中断后就需要配置 TMR 的初始化函数,TMR 初始化函数包含了几个部分,分别是 TMR 的时基,TMR 的从模式,TMR 的输入捕获还有 TMR 的中断使能和计数器使能。
在定时器初始化部分,首先要配置定时器的时基,时基部分需要配置的有预分频、周期值、预加载、定时器的工作模式以及定时器的更新源。
在完成对定时器的时基配置之后,我们还需要配置定时器的从模式功能,在这里我们选择定时器的 CH0 作为 TRIGIN 触发定时器的复位。
时基和定时器从模式都配置完之后我们就需要对定时器的通道进行配置,首先要配置的是让通道 0 和通道 1 的工作模式都是输入捕获模式,然后是输入信号的分频、滤波、触发边沿等功能配置。
在完成配置定时器的时基和输入通道的配置后使能定时器以及相关定时器中断。
代码清单 3.2 定时器初始化
void TMR_Init(void)
{
/*时基配置*/
LL_TMR_MspInit(TMR9); //模块时钟、中断使能配置
TMR9->CR0 |= (TMR9_CR0_ARE_Msk| //开启自动重载
TMR9_CR0_URS_Msk| //更新事件源选择只有上溢会生成更新事件
0<<TMR9_CR0_OPM_Pos); //使能单脉冲模式
TMR9->PSCR = 0; //定时器分频
TMR9->CPR = 5000; //计数周期值
/*定时器从模式配置*/
TMR9->SCR |= ((0x11<<TMR9_SCR_TS_Pos)| //选择定时器输入 CH0 触发
(1<<TMR9_SCR_FE_Pos)| //使能主从快速同步
(4<<TMR9_SCR_SMS_Pos)); //从模式选择复位模式
/*定时器输入捕获配置*/
TMR9->CCMR |= ((0<<TMR9_CCMR_CC0PE_Pos)| //输入捕获分频
(1<<TMR9_CCMR_OC0M_Pos) | //输入滤波
(1<<TMR9_CCMR_CC0S_Pos) | //定义通道 0 为输入通道
(0<<TMR9_CCMR_CC1PE_Pos)| //输入捕获分频
(1<<TMR9_CCMR_OC1M_Pos) | //输入滤波
(1<<TMR9_CCMR_CC1S_Pos)); //定义通道 1 为输入通道
TMR9->CCER |= ((0<<TMR9_CCER_CC0NE_Pos)| //互补通道不使能
(1<<TMR9_CCER_CC0P_Pos) | //输入信号边沿选择
(1<<TMR9_CCER_CC0E_Pos) | //通道 0 使能
(0<<TMR9_CCER_CC1NE_Pos)| //互补通道不使能
(2<<TMR9_CCER_CC1P_Pos) | //输入信号边沿选择
(1<<TMR9_CCER_CC1E_Pos)); //通道 1 使能
TMR9->CIR |= ((8<<TMR9_CIR_C0TIS_Pos)| //通道 0 的触发输入选择
(8<<TMR9_CIR_C1TIS_Pos)); //通道 1 的触发输入选择
TMR9->CR0 |= TMR9_CR0_CEN_Msk; //定时器使能计数
/*定时器中断配置*/
TMR9->IER |= ((TMR9_IER_C0MIE_Msk)| //通道 0 输入捕获中断使能
(TMR9_IER_C1MIE_Msk)| //通道 1 输入捕获中断使能
(TMR9_IER_OVIE_Msk)); //上溢中断使能
}
在配置完定时器初始化后我们还需要对捕获到的数据进行采集,这个时候我们需要利用输入捕获中断来获取我们需要的数据。
代码清单 3.3 定时器输入捕获中断服务函数
uint16_t captrue_buf[3];
void TMR9_CC_IRQHandler(void)
{
if((TMR9->SR)&&(TMR9_SR_CC0MIF_Msk))
{
TMR9->SR |= TMR9_SR_CC0MIF_Msk;
captrue_buf[0] = TMR9->CC0R; //周期值
captrue_buf[1] = TMR9->CC1R; //占空比
}
}
我们还需要一个外部信号给到定时的通道输入引脚,本篇笔记利用 HRPWM 模块发出的信号作为外部信号给到定时器,HRPWM 模块具体配置如下。
代码清单 3.4 LL_HRPWM_MspInit
void LL_HRPWM_MspInit(HRPWM_TypeDef *Instance)
{
GPIO_InitTypeDef HRPWM_GPIO_Init;
//Assert param
assert_param(IS_HRPWM_ALL_INSTANCE(Instance));
//HRPWM GPIO Common Config
memset((void *)&HRPWM_GPIO_Init, 0x00, sizeof(HRPWM_GPIO_Init));
HRPWM_GPIO_Init.IntMode = GPIO_INT_MODE_CLOSE;
HRPWM_GPIO_Init.OType = GPIO_OTYPE_PP;
HRPWM_GPIO_Init.Pull = GPIO_NOPULL;
HRPWM_GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
HRPWM_GPIO_Init.Alternate = GPIO_AF14_HRPWM;
//HRPWM Slave 0 Output Pinmux Config: PA9->OUT0B
HRPWM_GPIO_Init.Pin = GPIO_PIN_9;
LL_GPIO_Init(GPIOB, &HRPWM_GPIO_Init);
//HRPWM Bus Clock Disable and Soft Reset Assert
LL_RCU_HRPWM_ClkEnRstRelease();
//NVIC HRPAM Interrupt Enable
//LL_NVIC_EnableIRQ(HRPWM_MST_IRQn);
LL_NVIC_EnableIRQ(HRPWM_SLV0_IRQn);
//LL_NVIC_EnableIRQ(HRPWM_COM_IRQn);
}
和定时器配置流程一样,HRPWM 的配置要先配置 HRPWM 的功能引脚和模块时钟,然后再配 HRPWM 的初始化函数。
代码清单 3.5 HRPWM 初始化函数
void HRPWM_Init(void)
{
LL_HRPWM_MspInit(HRPWM);
HRPWM->PWM[0].REG.PWMCR0 |= ((5<<HRPWM_SLV0_PWMCR0_CKPSC_Pos)| //不分频
(HRPWM_SLV0_PWMCR0_CONT_Msk)| //连续模式
(HRPWM_SLV0_PWMCR0_UPDRST_Msk)| //复位更新
(HRPWM_SLV0_PWMCR0_PREEN_Msk)); //预加载使能
HRPWM->PWM[0].REG.PERR = 1000 - 1; //周期值
HRPWM->PWM[0].REG.CMPAR = 900 - 1; //CMPA
HRPWM->PWM[0].REG.CMPBR = 400 - 1; //CMPB
HRPWM->PWM[0].REG.CLRBR = 2; //CLRB 源:CMPA
HRPWM->PWM[0].REG.SETBR =4; //SETB 源:CMPB
HRPWM->Master.MCR1 |= ((HRPWM_MST_MCR1_MCEN_Msk)| //主定时器使能
(HRPWM_MST_MCR1_CEN0_Msk)); //SLV0 使能
HRPWM->Common.OENR |= HRPWM_COM_OENR_OEN0B_Msk; //OUT0B 输出使能
}
上述配置完成后需要完成外部接线实现该效果。本篇笔记使用的是 TAE32G5800 的 DEMO 板,然后需要我们把外部信号(PA9)接到定时器输入引脚(PA8)上,通过 Keil 的 DEBUG 模式查看效果。
首先我们可以看到通道 0 捕获到的值经过转换后得出来的值为 1000,接下来我们看一下外部信号 HRPWM 预先设定的周期值是多少。
从图 4.2 上可以看到,外部信号的周期值设定为 1000,即定时器通道 0 捕获的数值就是外部信号的周期值。然后是看定时器通道 1 捕获的值,从图 4.1 可以到看到 CCR1 捕获到的值为1F2,1F2的十进制数为498,然后从图4.2可以看到外部信号是CMPB置位,CMPA 复位,因此外部信号的脉宽是 CMPA 减 CMPB 得出来的值,该值为 498。因此 CCR1 捕获到的值就是外部信号的脉宽。