前言
- 博文基于STM32F103ZET6和标准固件库V3.5.0在MDK5环境下开发;
- 本博文只讨论输入捕获的PWM输入模式,这个模式是STM32输入捕获检测脉宽和频率的一种硬件处理机制,说白了就是STM32芯片专门用来进行对PWM进行捕获的一个功能;此方法相比较于传统的PWM的捕获方法,大大减小了代码量,提高了检测效率,而实际上对于PWM信号的检测还有其他方法(下面我会介绍,就是原子教程提供的方法),并且关于繁琐的寄存器的介绍这里就不多说了,有兴趣的话可以直接去我的另外一个博客里去看: https://blog.csdn.net/wuyuzun/article/details/73135662 ;(备注:此链接里的博客里有一个配套的例程,例程的思想来自于源子教程,然而本博客是向突出介绍“PWM输入模式”这种我认为更好的方法)
- 本片博文只介绍单路(一个定时器)的PWM输入模式的,如果想实现多路PWM同时检测,只需要对其他具有输入捕获功能的定时器相同配置即可(我会在另外一个博客里进行介绍);
- 如有不足之处,多指教;
什么是PWM输入模式以及相应配置流程
(截图来自《STM32中文参考手册_V10》第216页)
介绍:(我们通过比较原子提供的思路来分析一下“PWM输入模式的比较模式”)
原子的思路:
如图为某个引脚输入的PWM波,把它分为4~5的阶段进行编程:
① 此时设置好上升沿为触发中断,以及其他定时器配置;
② 此时产生了上升沿,进行捕获操作,CNT内的值捕获到CCR内,此时使CNT=0,CNT继续开始计数;当完成捕获,并且修改捕捉极性为下降沿捕获,当下降沿来到时,将会再次触发此中断函数;
③ 此时产生了下降沿,触发了中断函数;
④ 清0 TIMx_SR寄存器中CCxOF和CCxIF位。
PWM输入模式:
(图片中为定时器输入捕获模式涉及到的功能模块,来自《STM32中文使用手册》P200页)
如图,举一个输入引脚为例(但是只能举TIMx_CH1和TIMx_CH2,因为TIMx_CH3和TIMx_CH4没有今天所要讨论的PWM输入模式这个功能,),假设这里对TIMx_CH1输入的信号同原子思路中的一样;那么我们同样可以用上面那种图来分析①~⑤五个步骤:
①:配置相对应的输入捕获引脚TIMx_CH1和GPIO管脚的映射关系;
②:选择TIMx_CCR1为有效输入:置TIMx_CCMR1寄存器的CC1S=01(这步的配置决定了此时的CCR1寄存器是输入用的还是输出用的,因为每个可以执行捕获和比较功能的定时器同一时间,只能执行捕获或者输出,两者不能同时进行);
③:选择TI1FP1的有效极性(用来捕获数据到TIMx_CCR1和清除计数器CNT),置CC1P=0(上升沿有效)
④:选择TIMx_CCR2的有效输入:置TIMx_CCMR1寄存器的CC2S=10(选中TI1)。(这里是需要特别注意的一个地方,从下图的配置选择或者从上面电路方框图的绿色剪头可以看出来,其实IC1和IC2配置时选择的输入信号都是来自于CH1通道进来的信号)
⑤:选择TI1FP2的有效极性(捕获CNT的数据到TIMx_CCR2);置CC2P=1(下降沿有效)。(图片参考了CC1P的配置,这里可以注意到一个现象就是IC2选择下降沿触发捕获,而IC1选择的是信号上升沿触发捕获)
⑥:选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS = 101(选择TI1FP1);
⑦:配置从模式寄存器为复位模式:置TIMx_SMCR中的SMS=100;(这一步决定每次上升沿出现,CNT就被清零)
⑧:使能捕获:配置TIMx_CCER寄存器中CC1E=1且CC2E=1;
从波形的角度分析工作原理
①:在一处完成上述配置(当然,上述的配置只是简单地寄存器配置,并没写中断函数等等,这个工作,可以放在后续):
②:一条路上的CH1,TI1,TI1FP1,TI1FP2的上升沿同时产生,CNT内的值被捕获到了CCR1内并且CNT被清零,从新开始计数;
③:一条路上的CH1,TI1,TI1FP1,TI1FP2的下降沿同时产生,CNT内的值被捕获到了CCR2内,不清零CNT;
④~⑤:在编写的中断函数内即可进行逻辑操作,获取此时CCR1(周期)和CCR2(高电平时间),即可计算占空比 = CCR2/CCR1;
下面即使相对应的代码:
/*---------------PWM输入对应管脚配置-----------------*/
static void PWM_Intput_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM1 对应 PA8
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*-------------中断嵌套配置------------*/
static void PWM_Intput_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*-------------TIM1模式配置------------*/
static void PWM_Intput_Mode_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*--------------------时基结构初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=0xffff; //ARR=65535us
TIM_TimeBaseStructure.TIM_Prescaler= 72-1; //PSC = 1M = 1us;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //分频因子为0,即不分频;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //CNT为向上计数模式;
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/*-------------------输入捕获结构体初始化------------------*/
TIM_ICInitTypeDef TIM_ICInitStructure;
//配置CH1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿触发
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //IC1直接连接TI1FP1
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //对输入的PWM信号不分频
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);
//配置CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //下降沿触发;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //IC2间接连接TI1FP2
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //对输入的PWM信号不分频
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);
//选择输入捕获的触发信号;
TIM_SelectInputTrigger(TIM1, TIM_TS_TI1FP1);
// 选择从模式
// PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT被复位清零;
TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM1,TIM_MasterSlaveMode_Enable);
// 使能捕获中断,这个中断主要针对的是主捕获通道(TI1FP1)
TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
// 清除中断标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
// 计数器开始计数
TIM_Cmd(TIM1, ENABLE);
}
/*---------------PWM输入对应管脚配置-----------------*/
static void PWM_Intput_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM1 对应 PA8
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*-------------中断嵌套配置------------*/
static void PWM_Intput_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM1_CC_IRQHandler(void)
{
uint8_t IC1_CCR1,IC2_CCR2;
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
IC1_CCR1 = TIM_GetCapture1(TIM1); //获取周期值CCR1
IC2_CCR2 = TIM_GetCapture2(TIM1); //获取高电平时间CCR2
/*逻辑代码*/
}
/*-------------TIM1模式配置------------*/
static void PWM_Intput_Mode_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/*--------------------时基结构初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=0xffff; //ARR=65535us
TIM_TimeBaseStructure.TIM_Prescaler= 72-1; //PSC = 1M = 1us;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //分频因子为0,即不分频;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //CNT为向上计数模式;
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/*-------------------输入捕获结构体初始化------------------*/
TIM_ICInitTypeDef TIM_ICInitStructure;
//配置CH1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿触发
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //IC1直接连接TI1FP1
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //对输入的PWM信号不分频
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);
//配置CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //下降沿触发;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //IC2间接连接TI1FP2
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //对输入的PWM信号不分频
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);
//选择输入捕获的触发信号;
TIM_SelectInputTrigger(TIM1, TIM_TS_TI1FP1);
// 选择从模式
// PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT被复位清零;
TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM1,TIM_MasterSlaveMode_Enable);
// 使能捕获中断,这个中断主要针对的是主捕获通道(TI1FP1)
TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
// 清除中断标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
// 计数器开始计数
TIM_Cmd(TIM1, ENABLE);
}