作者:马一飞 QQ:791729359
在学习过程中有什么问题可以联系
(原本资料是以网盘的方式公开提供给任何蓝桥杯爱好者进行下载的,但是最近临近蓝桥杯考试,遇到了很多伸手党,我无偿给爱好者们解答学习上的问题,回复得比较慢居然还有同学来说我这样那样,所以决定不再随意提供自己写的源码和资料。博主已经开始参加工作了,也不是闲着没事干盯着电脑手机为你们服务。记住,不要成为伸手党,要自己多动手实现。)
我们的stm32不仅可以使用输出比较产生PWM波,也可以捕获PWM波,那么这个功能就叫做输入捕获。在蓝桥杯的比赛中,这个功能也是经常会考到的,赛题会让你捕获一个方波,并测出占空比和频率。
如果大家想详细了解这个捕获的过程,可以看看数据手册通用定时器章节的比较/捕获部分。
实际上捕获/比较都是用到了一个捕获/比较寄存器来进行作用的。那么接下来就从代码的层面看看如何把一个定时器配置成捕获模式。
在学习捕获之前,首先把输出比较部分掌握好,我们捕获部分直接从上一个文档的输出比较代码进行增加。
我们用了TIM2来做了比较输出PWM,那么我们捕获就用TIM3,那么TIM3对应的就是PA6、PA7引脚
。分别能够复用成TIM3的通道1、TIM3的通道2 。
1. 我们先使能TIM3,和PA引脚的时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
2. 我们把PA6、PA7设置为上拉输入(或者浮空输入)。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
3. 使能TIM3中断(在捕获方波的过程中必定有跳边沿的变化,因此我们需要在中断中去处理)。
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
4. 配置TIM3。
TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseInitStructure.TIM_CounterMode = 0x0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
跟比较模式一样,我们把计数值设置到最大,同时71分频。
5. 配置输入通道。
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
我们要使用到这个库函数
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
这个函数的原型可以在TIM库函数里面找到。
TIM_Channel是选择的通道
TIM_ICSelsction 我们必须选择指向TI寄存器
TIM_Polarity 代表触发方式,我们要选择选择上升沿触发,因为我们在初始化之后,我们要从采集到上升沿,开始计数。
不需要分频,也不需要滤波。
同样通道2的配置也是一样的。
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
6.使能定时器和中断
TIM_Cmd(TIM3, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);
因为我们使用到了通道1与通道2,因此他的比较值分别保存在CCR1寄存器和CCR2寄存器上。
那么我们初始化好捕获模式后,在中断函数中我们应该如何对数据进行捕获呢和计算占空比和频率呢?
这个过程需要大家去理解一下,我这里给大家画一个捕获的简图。
图中紫色部分为我们输入的方波信号,首先我们刚开始时要把捕获通道设置为上升沿触发,假如我们捕获到了一个上升沿,也就是到达了A点,那么我们的定时器就开始计数,随后我们还要吧捕获通道设置成为下降沿触发。当我们捕获了一个下降沿时,也就是到达了B点,那么我们获取一个计数值,定时器还得继续计数,同时重新把捕获通道设置为上升沿触发。直到再次捕获到了一个上升沿,那么就代表已经到达C点了,我们再获取一次计数值。
那么我们通过获取的这两个计数值,就很容易的算出周期和占空比。
占空比 = 第一次捕获值 / 第二次捕获值
周期 = 1000000 / 第二次捕获值
那么这个过程在程序里面到底如何设计呢?我们先用通道1来举例子,通道2的配置是一模一样的。
我们先定义三个变量。
u8 TIM3_CH1_CAPTURE_MODE = 0;
u32 TIM3_CH1_CAPTURE_H = 0;
u32 TIM3_CH1_CAPTURE_HL = 0;
TIM3_CH1_CAPTURE_MODE 用来记录当前捕获在哪个位置
TIM3_CH1_CAPTURE_H 用来记录第一次捕获的时间
TIM3_CH1_CAPTURE_HL 用来记录第二次捕获的时间
那么我们再中断里是这么处理的
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) == 1)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if(CAPTURE_MODE)
{
switch(TIM3_CH1_CAPTURE_MODE)
{
case 0: TIM3_CH1_CAPTURE_H = 0;
TIM3_CH1_CAPTURE_HL = 0;
TIM3_CH1_CAPTURE_MODE = 1;
TIM_SetCounter(TIM3, 0);//计数值清0
TIM_OC1PolarityConfig( TIM3,TIM_ICPolarity_Falling);//下降沿触发
break;
case 1: TIM3_CH1_CAPTURE_H = TIM_GetCounter(TIM3);//第一次计数值
TIM3_CH1_CAPTURE_MODE = 2;
TIM_OC1PolarityConfig( TIM3,TIM_ICPolarity_Rising);//上升沿触发
break;
case 2: TIM3_CH1_CAPTURE_HL = TIM_GetCounter(TIM3);//第二次获取计数值
TIM3_CH1_CAPTURE_MODE = 3;
TIM_OC1PolarityConfig( TIM3,TIM_ICPolarity_Rising);//上升沿触发
break;
default:break;
}
}else
{
TIM3_CH1_CAPTURE_MODE = 0;
}
}
CAPTURE_MODE 大家可以先不用看,这个标志位的作用是为了防止两个捕获通道互不干扰,相当于一个分时复用的效果,大致的意义就是,通道1进行捕获的时候,通道2不能作用;通道2捕获时,通道1不能作用。如果大家有更好的方法,可以私聊我一起来讨论讨论喔。
当我们TIM3_CH1_CAPTURE_MODE=0时,如果发生了中断,那么就代表我们捕获到了上升沿,当前在上面简图的A点,我们把定时器计数值清0,让定时器重新计数,同时还要把触发方式设置为下降沿触发。
当TIM3_CH1_CAPTURE_MODE=1时候,发生了中断,那么就代表我们捕获到了下降沿,当前的位置在上面简图的B点,我们获取一次定时器的计数值,同时还得把我们触发方式设置为上升沿触发。
当TIM3_CH1_CAPTURE_MODE=2时候,发生中断,那么就代表我们一个周期已经捕获完毕了,当前位置在上面简图的C点,我们再获取一次定时器的计数值,随后就可以根据我们两次获取的计数值,算出我们方波的频率和周期。
当TIM3_CH1_CAPTURE_MODE=3时,就意味着我们可以开始计算周期和频率,当我们处理好数据后,我们就可以把TIM3_CH1_CAPTURE_MODE清0,准备开始下一次捕获。
那么我们在主函数中就是计算出周期和频率的值,然后显示到LCD上。随后开启下一次捕获。
if(TIM3_CH1_CAPTURE_MODE == 3)
{
sprintf((char*)string,"ch1_fre:%d ",1000000 / TIM3_CH1_CAPTURE_HL);
LCD_DisplayStringLine(Line3, string);
sprintf((char*)string,"ch1_duty:%d ",TIM3_CH1_CAPTURE_H * 100/TIM3_CH1_CAPTURE_HL);
LCD_DisplayStringLine(Line4, string);
TIM3_CH1_CAPTURE_MODE = 0;
}
接下来我们把程序烧录到CT117E开发板看看效果到底是怎样的。
我们把PA1与PA6相接,PA2与PA7相接,我们使PA1产生1KHZ,40%占空比的方波;PA2产生5KHZ,80占空比的方波。
我们也能看到,捕获计算出的频率和占空比,与我们产生的PWM是完全一致的,那么就代表我们这个捕获程序是完全没有问题的。
也许你还有更好的方法与改进,欢迎一起讨论。
(以上仅属于个人观点)