本小节我们来学习使用高级定时器 PWM 输入模式,此模式是输入捕获模式的一个特例。 PWM 输入模式经常被应用于测量 PWM 脉宽和频率。下面我们结合这些文字,配合高级定 时器框图给大家介绍 PWM 输入的工作原理。
第一,确定定时器时钟源。本实验中我们使用内部时钟(CK_INT),F1 系列高级定时器挂 载在 APB2 总线上,按照 sys_stm32_clock_init 函数的配置,定时器时钟频率等于 APB2 总线时 钟频率,即 72MHz。计数器的计数频率确定了测量的精度。
第二,确定 PWM 输入的通道。PWM 输入模式下测量 PWM,PWM 信号输入只能从通道 1(CH1)或者通道 2(CH2)输入。
第三,确定 IC1 和 IC2 的捕获边沿。这里以通道 1(CH1)输入 PWM 为例,一般我们习惯 设置 IC1 捕获边沿为上升沿捕获,IC2 捕获边沿为下降沿捕获。
第四,选择触发输入信号(TRGI)。这里也是以通道 1(CH1)输入 PWM 为例,那么我们 就应该选择 TI1FP1 为触发输入信号。如果是通道 2(CH2)输入 PWM,那就选择 TI2FP2 为触 发输入信号。可以看到这里并没有对应通道 3(CH3)或者通道 4(CH4)的触发输入信号,所 以我们只选择通道 1 或者通道 2 作为 PWM 输入的通道。
第五,从模式选择:复位模式。复位模式的作用是:在出现所选触发输入 (TRGI) 上升沿 时,重新初始化计数器并生成一个寄存器更新事件。
第六,读取一个 PWM 周期内计数器的计数个数,以及高电平期间的计数个数,再结合计 数器的计数周期(即计一个数的时间),最终通过计算得到输入的 PWM 周期和占空比等参数。 以通道 1(CH1)输入 PWM,设置 IC1 捕获边沿为上升沿捕获,IC2 捕获边沿为下降沿捕获为 例,那么 CCR1 寄存器的值+1 就是 PWM 周期内计数器的计数个数,CCR2 寄存器的值+1 就是 PWM 高电平期间计数器的计数个数。通过这两个值就可以计算出 PWM 的周期或者占空比等 参数。
再举个例子,以通道 1(CH1)输入 PWM,设置 IC1 捕获边沿为下降沿捕获,IC2 捕获边 沿为上升沿捕获为例,那么 CCR1 寄存器的值+1 依然是 PWM 周期内计数器的计数个数,但是 CCR2 寄存器的值+1 就是 PWM 低电平期间计数器的计数个数。通过这两个得到的参数依然可 以计算出 PWM 的其它参数。这个大家了解一下就可以了,一般我们使用第六介绍的例子。
通过上面的描述,如果大家还不理解,下面我们结合 PWM 输入模式时序来分析一下。PWM 输入模式时序图如图 22.5.2 所示:
图 22.5.2 是以通道 1(CH1)输入 PWM,设置 IC1 捕获边沿为上升沿捕获,IC2 捕获边沿 为下降沿捕获为例的 PWM 输入模式时序图。
从时序图可以看出,计数器的计数模式是递增计数模式。从左边开始看,当 TI1 来了上升 沿时,计数器的值被复位为 0(原因是从模式选择为复位模式),IC1 和 IC2 都发生捕获事件。 然后计数器的值计数到 2 的时候,IC2 发生了下降沿捕获,捕获事件会导致这时候的计数器的 值被锁存到 CCR2 寄存器中,该值+1 就是高电平期间计数器的计数个数。最后计数器的值计数 到 4 的时候,IC1 发生了上升沿捕获,捕获事件会导致这时候的计数器的值被锁存到 CCR1 寄 存器中,该值+1 就是 PWM 周期内计数器的计数个数。
假设计数器的计数频率是 72MHz,那我们就可以计算出这个 PWM 的周期、频率和占空比 等参数了。下面就以这个为例给大家计算一下。由计数器的计数频率为 72MHz,可以得到计数 器计一个数的时间是 13.8ns(即测量的精度是 13.8ns)。知道了测量精度,再来计算 PWM 的周 期,PWM 周期 =(4+1)*(1/72000000) = 69.4ns,那么 PWM 的频率就是 14.4MHz。占空比 = (2+1)/(4+1) =3/5(即占空比为 60%)。
#include "./BSP/ATIM/atim.h"
TIM_HandleTypeDef g_timx_pwmin_chy_handle;/* 定时器x句柄 */
/* PWM输入状态(g_timxchy_cap_sta)
* 0,没有成功捕获.
* 1,已经成功捕获了
*/
uint8_t g_timxchy_pwmin_sta = 0; /* PWM输入状态 */
uint16_t g_timxchy_pwmin_psc = 0; /* PWM输入分频系数 */
uint32_t g_timxchy_pwmin_hval = 0; /* PWM的高电平脉宽 */
uint32_t g_timxchy_pwmin_cval = 0; /* PWM的周期宽度 */
void atim_timx_pwmin_chy_init(void)
{
TIM_SlaveConfigTypeDef slave_config = {0};
TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};
g_timx_pwmin_chy_handle.Instance = TIM8;
g_timx_pwmin_chy_handle.Init.Prescaler = 0;
g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
g_timx_pwmin_chy_handle.Init.Period = 65535;
HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);
/* 从模式配置,IT1触发更新 */
slave_config.SlaveMode = TIM_SLAVEMODE_RESET;/* 从模式:复位模式 */
slave_config.InputTrigger = TIM_TS_TI1FP1;/* 定时器输入触发源:TI1FP1 */
slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
slave_config.TriggerFilter = 0;
HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);
/* IC1捕获:上升沿触发TI1FP1 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;/* 选择输入端IC1映射到TI1 */
tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_RISING;
tim_ic_pwmin_chy.ICFilter = 0;
tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1;
HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1);
/* IC2捕获:上升沿触发TI1FP2 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI;/* 选择输入端IC2映射到TI1 */
tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_FALLING;
HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM8)
{
GPIO_InitTypeDef gpio_init_struct = {0};
__HAL_RCC_TIM8_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_6;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLDOWN;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &gpio_init_struct);
/* TIM1/TIM8有独立的输入捕获中断服务函数 */
HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
}
}
void atim_timx_pwmin_chy_restart(void)
{
HAL_NVIC_DisableIRQ(TIM8_CC_IRQn);
g_timxchy_pwmin_sta = 0;
g_timxchy_pwmin_hval = 0;
g_timxchy_pwmin_cval = 0;
HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
}
void TIM8_CC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_pwmin_chy_handle);
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM8)
{
if(g_timxchy_pwmin_sta == 0)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1 + 1;
g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1 + 1;
g_timxchy_pwmin_sta = 1;
}
}
}
}
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/GTIM/gtim.h"
#include "./BSP/ATIM/atim.h"
extern uint8_t g_timxchy_pwmin_sta; /* PWM输入状态 */
extern uint16_t g_timxchy_pwmin_psc; /* PWM输入分频系数 */
extern uint32_t g_timxchy_pwmin_hval; /* PWM的高电平脉宽 */
extern uint32_t g_timxchy_pwmin_cval; /* PWM的周期宽度 */
int main(void)
{
uint8_t t;
double ht, ct, f, tpsc;
HAL_Init(); /* 初始化 HAL 库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init();
gtim_timx_pwm_chy_init(72 -1, 10 - 1);
TIM3->CCR2 = 5;
atim_timx_pwmin_chy_init();
while(1)
{
delay_ms(10);
t++;
if (t >= 20) /* 每200ms输出一次结果,并闪烁LED1,提示程序运行 */
{
if (g_timxchy_pwmin_sta) /* 捕获了一次数据 */
{
printf("\r\n"); /* 输出空,另起一行 */
printf("PWM PSC :%d\r\n", g_timxchy_pwmin_psc); /* 打印分频系数 */
printf("PWM Hight:%d\r\n", g_timxchy_pwmin_hval); /* 打印高电平脉宽 */
printf("PWM Cycle:%d\r\n", g_timxchy_pwmin_cval); /* 打印周期 */
tpsc = ((double)g_timxchy_pwmin_psc + 1) / 72; /* 得到PWM采样时钟周期时间 */
ht = g_timxchy_pwmin_hval * tpsc; /* 计算高电平时间 */
ct = g_timxchy_pwmin_cval * tpsc; /* 计算周期长度 */
f = (1 / ct) * 1000000; /* 计算频率 */
printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高电平脉宽长度 */
printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期时间长度 */
printf("PWM Frequency :%.3fHz\r\n", f); /* 打印频率 */
atim_timx_pwmin_chy_restart(); /* 重启PWM输入检测 */
}
LED1_TOGGLE(); /* LED1(GREEN)闪烁 */
t = 0;
}
}
}