stm32 定时器pwm输入捕获
输入捕捉的功能是记录下要捕捉的边沿出现的时刻,如果你仅仅捕捉下降沿,那么两次捕捉的差表示输入信号的周期,即两次下降沿之间的时间。
如果要测量低电平的宽度,你应该在捕捉到下降沿的中断处理中把捕捉边沿改变为上升沿,然后把两次捕捉的数值相减就得到了需要测量的低电平宽度。
如果要的测量低电平太窄,中断中来不及改变捕捉方向时,或不想在中断中改变捕捉方向,则需要使用PWM输入模式,或使用两个TIMx通道,一个通道捕捉下降沿,另一个通道捕捉上升沿,然后对两次捕捉的数值相减。PWM输入模式也是需要用到两个通道。使用两个通道时,最好使用通道1和通道2,或通道3和通道4,这样上述功能只需要使用一个I/O管脚,详细请看STM32技术参考手册中的TIMx框图。
、、//0-----------------------
一、概念理解
PWM输入捕获模式是输入捕获模式的特例,自己理解如下
1. 每个定时器有四个输入捕获通道IC1、IC2、IC3、IC4。且IC1IC2一组,IC3 IC4一组。并且可是设置管脚和寄存器的对应关系。
2. 同一个TIx输入映射了两个ICx信号。
3. 这两个ICx信号分别在相反的极性边沿有效。
4. 两个边沿信号中的一个被选为触发信号,并且从模式控制器被设置成复位模式。
5. 当触发信号来临时,被设置成触发输入信号的捕获寄存器,捕获“一个PWM周期(即连续的两个上升沿或下降沿)”,它等于包含TIM时钟周期的个数(即捕获寄存器中捕获的为TIM的计数个数n)。
6. 同样另一个捕获通道捕获触发信号和下一个相反极性的边沿信号的计数个数m,即(即高电平的周期或低电平的周期)
7. 由此可以计算出PWM的时钟周期和占空比了
frequency=f(TIM时钟频率)/n。
duty cycle=(高电平计数个数/n),
若m为高电平计数个数,则duty cycle=m/n
若m为低电平计数个数,则duty cycle=(n-m)/n
注:因为计数器为16位,所以一个周期最多计数65535个,所以测得的最小频率=TIM时钟频率/65535。
二、程序设计与分析
1. 程序概述:选择TIM3作为PWM输入捕获。IC2设置为上升沿,并设置为有效的触发输入信号。所以IC2的捕获寄存器捕获PWM周期,
IC1的捕获寄存器捕获PWM的高电平周期。
2.程序代码如下:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_7; //GPIO配置
PIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel= TIM3_IRQn; //NVIC配置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ICInitStructure.TIM_Channel =TIM_Channel_2; //通道选择
TIM_ICInitStructure.TIM_ICPolarity= TIM_ICPolarity_Rising; //上升沿触发
TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI; //管脚与寄存器对应关系
//TIM_ICSelection_DirectTI指TIM Input 1, 2, 3, 4对应 IC1, IC2, IC3, IC4;//
TIM_ICSelection_IndirectTI指TIM Input 1, 2, 3, 4对应IC2, IC1, IC4, IC3
TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
//
输入预分频。意思是控制在多少个输入周期做一次捕获,如果
//输入的信号频率没有变,测得的周期也不会变。比如选择4分频,则每四个输入周期才做一次捕获,这样在输入信号变化不频繁的情况下,
//可以减少软件被不断中断的次数。
TIM_ICInitStructure.TIM_ICFilter= 0x0; //滤波设置,经历几个周期跳变认定波形稳定0x0~0xF
TIM_PWMIConfig(TIM3,&TIM_ICInitStructure); //根据参数配置TIM外设信息
TIM_SelectInputTrigger(TIM3,TIM_TS_TI2FP2); //选择IC2为始终触发源
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//TIM从模式:触发信号的上升沿重新初始化计数器和触发寄存器的更新事件
TIM_SelectMasterSlaveMode(TIM3,TIM_MasterSlaveMode_Enable); //启动定时器的被动触发
TIM_Cmd(TIM3,ENABLE); //启动TIM2
TIM_ITConfig(TIM3,TIM_IT_CC2, ENABLE); //打开中断
中断处理函数
voidTIM3_IRQHandler(void)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2); //清楚TIM的中断待处理位
IC2Value =TIM_GetCapture2(TIM3); //读取IC2捕获寄存器的值,即为PWM周期的计数值
if (IC2Value != 0)
{
DutyCycle= (TIM_GetCapture1(TIM3) * 100) / IC2Value; //读取IC1捕获寄存器的值,并计算占空比
Frequency= 72000000 / IC2Value; //计算PWM频率。
}
else
{
DutyCycle= 0;
Frequency= 0;
}
}
注(一):若想改变测量的PWM频率范围,可将TIM时钟频率做分频处理
TIM_TimeBaseStructure.TIM_Period= 0xFFFF; //周期0~FFFF
TIM_TimeBaseStructure.TIM_Prescaler = 5; //时钟分频,分频数为5+1即6分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割
TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up;//模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//基本初始化
注注(二):定时器TIM的倍频器X1或X2。在APB分频为1时,倍频值为1,否则为2
、、---------------------------------------------------------------------------------------=============================================
再来看看捕获/比较模式寄存器 1:TIMx_CCMR1,这个寄存器在输入捕获的时候,非常有
用,有必要重新介绍,该寄存器的各位描述如图 15.1.1 所示:
图 15.1.1 TIMx_CCMR1 寄存器各位描述
当在输入捕获模式下使用的时候,对应图 15.1.1 的第二行描述,从图中可以看出,
TIMx_CCMR1 明显是针对 2 个通道的配置,低八位[7:0]用于捕获/比较通道 1 的控制,而高八
位[15:8]则用于捕获/比较通道 2 的控制,因为 TIMx 还有 CCMR2 这个寄存器,所以可以知道
CCMR2 是用来控制通道 3 和通道 4(详见《STM32 参考手册》290 页,14.4.8 节) 。
//==============================================================================================================
其中 CC1S[1:0],这两个位用于 CCR1 的通道配置,这里我们设置 IC1S[1:0]=01,也就是配
置 IC1 映射在 TI1 上(关于 IC1,TI1 不明白的,可以看《STM32 参考手册》14.2 节的图 98-
通用定时器框图) ,即 CC1 对应 TIMx_CH1。
//=========================================================================================================================
输入捕获 1 预分频器 IC1PSC[1:0],这个比较好理解。我们是 1 次边沿就触发 1 次捕获,所
以选择 00 就是了。
输入捕获1滤波器IC1F[3:0], 这个用来设置输入采样频率和数字滤波器长度。 其中,
是定时器的输入频率(TIMxCLK) ,一般为 72Mhz,而 则是根据 TIMx_CR1 的 CKD[1:0]
的设置来确定的,如果 CKD[1:0]设置为 00,那么 = 。N 值就是滤波长度,举个简
单的例子:假设 IC1F[3:0]=0011,并设置 IC1 映射到通道 1 上,且为上升沿触发,那么在捕获
到上升沿的时候,再以 的频率,连续采样到 8 次通道 1 的电平,如果都是高电平,则说
明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话) 。这样可以滤除那些高电平
脉宽低于 8 个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所以设
置 IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。
TIMx的CHx 在输入捕获模式下就变味TIx了...
刚刚看了一下手册,好像有点了解,不知道对不对。
CCx是指4各通道当中的一个通道,而ICx对应的这个输入通道,TI1、TI2、TI3、TI4,是指定时器的4个通道的引脚(比如:TIM4_REMAP=0,没有重映像(TIM4_CH1/PB6,TIM4_CH2/PB7,TIM4_CH3/PB8,TIM4_CH4/PB9))。
//=========================================================================
定时器从0开始计数,计数到TIM_Period后,重新归零再计数。
捕获只是把发生捕获时刻的计数器当前值拷贝下来,你的IC2Value就是这个数值。
如果配置了 发生捕获时复位计数器,则计数器没有计数到TIM_Period时也被归零并重新计数。
如果你配置比较小的TIM_Period数值,则可能在还没有发生捕获时,计数器就被归零,所以你要在软件中记录下计数器被归零的次数。
当计数器的时钟频率为F时,计数器每计数一次表示1/F的时间,因此从捕获寄存器中读出的数值表示了一个时间。如果配置了发生捕获时复位计数器,则这个时间恰好就是要捕获信号的周期。
从上面原理可以看出,TIM_Period的数值与捕获的精度没有关系。
发生捕获时复位计数器》》》》这个如何实现?通过TI1-->TI1F_ED
//=======================================================================================================
TIM通用定时器(二):输出比较——翻转模式
一、基本概念理解
1. 输出比较:打开一个TIMx计数器,再打开TIMx的一路或几路输出比较器(共4路),都配置好以后,计数器开始计数,当计数器里的值和
比较寄存器里的值相等时,产生输出比较中断,在中断中将计数器中的值读出,与翻转周期相加再写道比较寄存器中,使得和
下一个事件有相同的翻转周期。
2. 举例说明:例如TIM时钟频率设置为12MHZ,输出比较寄存器中的自装载值为600(高电平或低电平计数值),则输出的PWM频率为
frequency = 12MHZ/(600*2)=10KHZ。
二、程序设计与分析
1. TIM计数器和输出比较器的配置
uint16_t capture = 0;
extern __IO uint16_t CCR1_Val;
extern __IO uint16_t CCR2_Val;
extern __IO uint16_t CCR3_Val;
extern __IO uint16_t CCR4_Val;
void TIM_Configuration(void)
{
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535; //这里必须是65535
TIM_TimeBaseStructure.TIM_Prescaler = 2; //3分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化TIM3
/* Output Compare Toggle Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; //输出比较翻转模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能通道1
TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //待装入输出比较寄存器中的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出为正逻辑
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //写入配置
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable); //使能或者失能TIMx在CCR1上的预装载寄存器
/* Output Compare Toggle Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel4 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE); //开启计数器
/* TIM IT enable */ //TIM1中断源设置,开启相应通道的捕捉比较中断
TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
}
2. 时钟配置
3. GPIO配置
4. 中断服务程序
void TIM3_IRQHandler(void)
{
/* TIM3_CH1 toggling with frequency = 183.1 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) //检查指定的TIM中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 ); //清除TIMx的中断待处理位
capture = TIM_GetCapture1(TIM3); //获得输入捕获值,即计数器中的计数值
TIM_SetCompare1(TIM3, capture + CCR1_Val ); //将计数值加上翻转的脉冲值写入输出比较寄存器中,以保证下一个TIM事
//件也是相同的脉冲数
}
/* TIM3_CH2 toggling with frequency = 366.2 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
capture = TIM_GetCapture2(TIM3);
TIM_SetCompare2(TIM3, capture + CCR2_Val);
}
/* TIM3_CH3 toggling with frequency = 732.4 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
capture = TIM_GetCapture3(TIM3);
TIM_SetCompare3(TIM3, capture + CCR3_Val);
}
/* TIM3_CH4 toggling with frequency = 1464.8 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
capture = TIM_GetCapture4(TIM3);
TIM_SetCompare4(TIM3, capture + CCR4_Val);
}
}
//=================================================================================================================
TIM_OCMode_Timing是在比较成功后不在对应输出管脚上产生输出。
TIM_OCMode_Toggle是在比较成功后翻转对应输出管脚上的电平。
TIM_OCMode_Timing是在比较成功后不在对应输出管脚上产生输出,但是产生中断啊,
//=================================================================================
试STM32的定时器好几天了,也算是对STM32的定时器有了点清楚的认识了。我需要测量4路信号的频率然后通过DMA将信号的频率传输到存储器区域,手册说的很明白每个定时器有4个独立通道。然后我就想能不能将这4路信号都连接到一个定时器的4个通道上去。理论上应该是行的通的。刚开始俺使用的是TIM2的1 2 3通道,TIM4的2通道来进行频率的测量。由于没有频率发生器,所以我用tim3作为信号源,用TIM2,TIM4来进行测量就ok了。
请看一开始的程序,以TIM2的1,3通道为例子:
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择通道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(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3; //选择通道3
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(TIM2, &TIM_ICInitStructure);
这个是输入捕获配置
还需要做的工作就是(参考stm32参考手册的TIM的结构框图):
/* Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); //参考TIM结构图选择滤波后的TI1输入作为触发源,触发下面程序的复位
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
/* Enable the Master/Slave Mode */
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
//主从模式选择
这样我们就可以很轻松的就得到了 连接在TIM2的通道1上的信号的频率,但是3通道的频率的值永远都是跳动的不准,测试了半天也没有找到根本原因,请看TIM的结构框图的一部分
红色箭头所指,这才找到原因,触发的信号源只有这四种,而通道3上的计数器的值不可能在接受到信号的上升沿时候,有复位这个动作,找到原因了。这就是3通道上的数据不停跳动的原因,要想的到信号的频率也是有办法的,可以取连续两次捕捉的值之差,这个值就是信号的周期,自己根据实际情况去算频率吧。
有以上可以得到:
stm32的TIM的四个通道可以同时配置成输入捕捉模式,但是计算CH3,CH4信号的频率步骤有点繁琐(取前后捕捉的差值),但是他的CH1,和CH2可以轻松得到:
通道1
/* Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); //参考TIM结构图选择滤波后的TI1输入作为触发源,触发下面程序的复位
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
TIMx->CRR1的值即为信号的周期
通道2:
TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //参考TIM结构图选择滤波后的TI1输入作为触发源,触发下面程序的复位
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
TIMx->CRR2的值即为信号的周期
//======================================================
TIM设置为输入捕获模式的出现的问题 | 发布时间:2008-10-14 19:14:10 |
技术类别:ARM | |
在应用的时候将将TIM2的CH1,Ch2两个通道设置为输入捕获模式,为了测量路信号的频率
关于TIM2的配置如下
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择通道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(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //选择通道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(TIM2, &TIM_ICInitStructure);
/* Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2,TIM_TS_TI1FP1); //参考TIM结构图选择滤波后的TI2输入 寄存器SMCR
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
/* Enable the Master/Slave Mode */
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable); //主从模式选择
这里注意了 TIM_SelectInputTrigger(TIM2,TIM_TS_TI1FP1); //参考TIM结构图选择滤波后的TI2输入 寄存器SMCR
如果这么设置的输入触发源为TI1FP1 我只是要捕捉CH1的信号频率 ,但是CH2不接信号读出来的数据跟CH1一模一样,如果设置为
TIM_SelectInputTrigger(TIM2,TIM_TS_TI2FP2); 设置的输入触发源为TI2FP2,那么就能准确的读回来
CH2上的信号频率,此时CH1没有接信号度回来全部是0
为什么设置了前者TIM_SelectInputTrigger(TIM2,TIM_TS_TI1FP1); 两个通道CH1,CH2都有数据呢 纳闷中
希望这个不会是STM32的bug,正在寻找解决的办法。。。。
答案:你应该选择pwmi模式才能两个联起来,一个测频率,一个测占空比 正解
、、=====================================================================================================
调试的过程中,总能遇到一些问题,很庆幸能遇到那么多的问题,也许这就是最好的学习过程:
继续我的笔记:
在main函数中,文件名:main.c
对TIM2的CH1,CH2配置如下:
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择通道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(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICMode = TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //选择通道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(TIM2, &TIM_ICInitStructure);
TIM1->PSC = 10; //由于要测量的信号频率为200-1000HZ 采取10倍的预分频
TIM2->PSC = 10; //如果不分频最小的频率为1100hz,分频后可以测量的频率为110HZ,为了达到最佳捕捉效果,且满足要求建议分频系数设为6;
/* Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2,TIM_TS_TI1FP1); //参考TIM结构图选择滤波后的TI2输入 寄存器SMCR
/* Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
/* Enable the Master/Slave Mode */
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable); //主从模式选择
说说我要测量两路信号频率的思路吧:
我想把两路信号跟别加到TIM2的CH1跟CH2上面去,然后通过TI1FP1跟TI2FP2轮流触发 ,TIM2->CCR1与TIM2->CCR2记录下来的数据就是信号的周期,接着根据具体的情况计算出信号的频率。那么,究竟是怎么实现TI1FP1跟TI2FP2轮流触发呢?这就是DMA的问题了,当信号的周期被TIM2->CRRx收到,DMA就会将这个数据送到一个存储器区。DMA传输完数据后就会发生中断,我在DMA传输中断函数中修改触发信号源。
比如说DMA-CH5传输到是TIM2->CRR1
他的中断函数如下:
void DMAChannel5_IRQHandler(void)
{
if (DMA_GetITStatus(DMA_IT_TC5) != RESET)
{
DMA_ClearITPendingBit(DMA_IT_TC5);
/* Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2,TIM_TS_TI2FP2);
上句就是修改触发源轮流触发
}
}
但是实验的过程中发现 两个通道所测量得到信号的周期 怎么着都只有一组是正确
例如:
最后通过高人的指点才发现:
原来这个定时器只有一个COUNT(计数器)
当我选择执行 TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Reset);这个函数,问题就来了,当一路有信号触发,count复位,那么另一路永远也得不到正确的值。。所以测量信号频率还有待改良。。。。
2008年10.14 晚九点
实验室看门的又来催了 ,记录到此
、、=============================================================================
调试TIM输入比较模式笔记 | 发布时间:2008-10-13 19:12:51 |
技术类别:ARM |
【原创】调试stm32的TIM输入捕获模式
应用平台为英贝特EMSTM32V1开发板
下载例程:http://space.ednchina.com/Upload/2008/10/13/7a65b455-187b-4847-87a7-767f4a60a546.rar
这个程序的要完成的任务是:通过定时器的输入捕获模式,测量4路信号的频率(频率的范围200—1000HZ),并且通过相应的DMA通道将测量的值传送到存储器部分一个数据缓冲区。
由于STM32的定时器一般都有4个独立的通道,可以考虑就用一个定时器的4个通道测量信号的频率,参见下图
我们不难发现只有TIM1,TIM2为其4个通道配备了DMA通道,因此只能选择TIM1,TIM2来应用。这里我实验选择了TIM2,但是ch2与ch4共用了一个DMA通道,所以我又开启了TIM4,利用了TIM4_CH2.
测量信号的4个TIM通道为:
TIM2_CH1
(PA0) DMA_CH5
TIM2_CH2
(PA1) DMA_CH7
TIM2_CH3
(PA2) DMA_CH1
TIM4_CH2
(PB7) DMA_CH4
以下为各个部分的配置步骤,全部以TIM2_CH1的配置作示例:
1:Stm32的TIM的输入模式的配置:
TIM_ICInitStructure.TIM_ICMode =
TIM_ICMode_ICAP; //配置为输入捕获模式
TIM_ICInitStructure.TIM_Channel =
TIM_Channel_1; //选择通道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(TIM2, &TIM_ICInitStructure);
注意点:《一》:TIM_ICMode只有两种模式供选择1. TIM_ICMode_ICAP 输入捕获模式
2. TIM_ICMode_PWMI PWM输入模式。此模式是输入捕获模式的一种特殊应用,可以方便的计算出占空比,笔者只在IC1,IC2上试验成功,在IC3,IC4上没有测试
《二》:TIM_ICSelection 是为设置通道的方向以及ICx的映射,可以参考CCMRx这个寄存器的低两位以及8.9两位
2:
/*
Select the TIM2 Input Trigger: TI2FP2 【输入触发源选择】*/
TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //参考TIM结构图选择滤波后的TI2输入 寄存器SMCR
/*
Select the slave Mode: Reset Mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //复位模式-选中的触发输入(TRGI)的上升沿初始化计数器,并且产生一个更新线号
/*
Enable the Master/Slave Mode */
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //主从模式选择
拿TIM2作为例子,要想把TIM2的一个channel配置为输入模式这三个函数是必要的
3:预分频的设置
由于stm32的输入捕获模式下,在没有分频的情况下可以捕获的信号的频率最小为72MHZ/65536约为1100HZ;所以为了满足测量200-1000HZ的要求,需要配置预分频系数:
TIM2->PSC
= 10; 在实际应用中为了满足要求,并且提高精度可以选取6分频。
4:DMA通道的初始化设置
这里只拿channel5作为示例
/* DMA channel5 configuration
----------------------------------------------*/
DMA_DeInit(DMA_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr =
TIM2_CRR1_Address ;
DMA_InitStructure.DMA_MemoryBaseAddr =
(u32)&TIM2_Capture_value;
DMA_InitStructure.DMA_DIR =
DMA_DIR_PeripheralSRC;
//设置外设为源地址
DMA_InitStructure.DMA_BufferSize = 16;
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable; //外设地址不要递增
DMA_InitStructure.DMA_MemoryInc =
DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize =
DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode =
DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority =
DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //不是存储器到存储器
DMA_Init(DMA_Channel5,
&DMA_InitStructure);
这里要注意的是:
1.DMA_InitStructure.DMA_PeripheralBaseAddr
= TIM2_CRR1_Address ;
前面有定义 #define TIM2_CRR1_Address ((u32)0x40000034)
在调试的时候,TIM2的三个通道全部调试通过,但是只有TIM_CH2收不到信号,这里就是地址没有搞对,因为TIM4的地址要在TIM2的基址上加0x0800;所以折腾了大半天也没有收到信号,这里要注意一下
2.DMA_InitStructure.DMA_Mode
= DMA_Mode_Circular;
这里我们的数据是连续不断的采集的所以需要一个循环缓冲区,只好设置为DMA_Mode_Circular;
5:关于中断与DMA
在调试程序的过程中,我遇到这样的情况:
设置了TIM2的中断使能信号
/* Enable the CC1 Interrupt Request */
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
/* Enable the CC2 Interrupt Request */
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);
同时我需要通过DMA_Channel_7讲采集信号的频率发送到存储器区域,于是有以下设置:
/*配置TIM2—ch1的MDA 并开启DMA【ch7】*/
TIM_DMAConfig(TIM2,TIM_DMABase_CCR1,TIM1_DMABurstLength_2Bytes);
TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);
这样中断与DMA通道的设置就同时被初始化,结果程序运行的时候,当发生TIM_IT_CC2中断请求的时候,程序并不能进入中断服务程序,只响应了相应的DMA请求,当我去掉以上DMA的设置程序的时候,程序很顺利就响应了中断请求进入了中断服务程序。
所以得到了以下结论:
中断与DMA的使能不必同时开启,及时都开启了,程序在接收到相应的请求信号时候,只会去响应dma请求。
6:GPIO的配置
/*
GPIOA Configuration 【设置TIM2的 ch1,ch2,ch3】*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//浮点输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
7:注意打开相应外设的时钟
RCC_APB1PeriphClock: TIM2,TIM4
RCC_APB2PeriphClock: GPIOx
RCC_AHBPeriphClock:
DMA
8:关于调试过程中的MDK的使用
A:打开箭头指向的两项
B:在Address里面分别输入
0x40000038,0x4000003c,0x40000040以及0x40000838
就可以看到TIM2_CRR1,TIM2_CRR2,TIM2_CRR3以及TIM4_CRR2中的值也就是各个通道捕捉的值value
因为捕获的数十经过PSC10分频的所以用72MHZ/10*value就可以得到测量信号的频率
C:我们可以在这个框里点F2输入我们定义的缓冲区的名字
TIM2_Capture_value
TIM2_Capture_value1
TIM2_Capture_value2
TIM4_Capture_value
就可以看到DMA的各个通道传输到存储器区的数据,这个数据是跟步骤B里面的数据是一致的。缓冲区的大小为16,信号的频率在一段时间内是不变的那么这16个数就是一样的
同样用72MHZ/10*value就可以得到测量信号的频率
你几个问题点没有把握好,
一个是映射,
一个是主输入,
一个是从输入,
只有从输入才有第一个跳变复位,第二个跳变存在CCR2或者CCR1中,
主输入可以先CCR1,也可能选择CCR2,这里要从输入配合,他们不能同用一个CCR.
这几个问题你能搞清楚了,就很清楚了。
2008.10.13 15:05
刚刚开始做定时器输入捕获的时候在这个论坛上找了好久,都没有人分享,也有很多人问,无奈自己动手写了。花了一天的时间,这个东西真的不好写了,对比了库的例子,仿真,等。可以说付出了很多。现在完成了和大家分享。希望大家多多支持。这个程序是在定时器输入捕获的基础上看手册完成的,
程序说明:1、程序中定时器4的PB6用于输出频率为1K,占空比为50%的PWM信号。
2、定时器2的PA0用于输入捕获,当程序下到板子上,只有两个脚连在一起才会发生捕获。
3、串口用于发送捕获的值到PC机上。
4、定时器2的CCR1存PWM信号的频率,CCR2存高电平时间。
这里声明一下,如果你要捕获的PWM信号不在ARR,PSC计算的范围内,请自己先计算再使用本程序。
#include "stm32f10x_lib.h"
#include "sys.h"
#include "delay.h" //延时子函数
#include "usart.h"
u16 IC1Value;
u16 IC2Value;
u16 DutyCycle;
u16 Frequency;
void PWM_Init(u16 arr,u16 psc);
void Capture_Init(u16 arr,u16 psc);
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72);//延时函数初始化
uart_init(72,9600);
PWM_Init(1000,72-1);
//不分频。PWM频率=72000/1440=5Khz
Capture_Init(2000,72-1);
while(1)
{
Frequency = 1000000/IC1Value;
DutyCycle = (IC2Value*100)/IC1Value;//占空比=(IC2Value/IC1Value)*100;
printf("Frequency = %d\r\n",Frequency);
printf("DutyCycle = %d\r\n",DutyCycle);
printf("suqingxiao\r\n");
}
}
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void PWM_Init(u16 arr,u16 psc)
{
//此部分需手动修改 IO口设置
RCC->APB2ENR|=1<<0; //
RCC->APB1ENR|=1<<2; //TIM4 时钟使能
RCC->APB2ENR|=1<<3; //使能PORTB时钟
GPIOB->CRL&=0XF0FFFFFF;//PB6 输出
GPIOB->CRL|=0X0B000000;//复用功能输出
GPIOB->ODR|=1<<6;//PB6 上拉
TIM4->ARR=arr;//设定计数器自动重装值
TIM4->PSC=psc;//预分频器不分频
TIM4->CCMR1|=7<<4; //CH1 PWM2模式
TIM4->CCMR1|=1<<3; //CH1 预装载使能
TIM4->CCER|=1<<0; //OC1 输出使能
TIM4->CR1=0x0080; //ARPE使能
TIM4->CR1|=0x01; //使能定时器 3
TIM4->CCR1 = 500;
//占空比初值 = 1440*50% = 720
}
void Capture_Init(u16 arr,u16 psc)
{
//此部分需手动修改 IO口设置
RCC->APB2ENR|=1<<0; //
RCC->APB1ENR|=1<<0; //TIM2 时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
TIM2->ARR=arr; //设定计数器自动重装值//刚好1ms
TIM2->PSC=psc; //预分频器,
GPIOA->CRL&=0XFFFFFFF0;//PA0 输出
GPIOA->CRL|=0X00000004;//复用功能输出
GPIOA->ODR|=1<<0;//PA0 上拉
TIM2->SMCR|=0x00D4;
//TIM2->SMCR|= 1<<5; //MSM=1 主/从模式
//TIM2->SMCR|= 5<<4; //TS=101 触发选择
//TIM2->SMCR|= 4<<0; //SMS=100 复位模式
TIM2->CCMR1|=1<<0;//CC1S=01 选择输入端
TIM2->CCMR1|=3<<4; //IC1F=0011配置输入滤波器
TIM2->CCER|=0<<1; //CC1P=0
选择有效转换边沿 上升沿有效
TIM2->CCMR1|=0<<2; //IC1PS=00 配置输入分频
TIM2->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中
TIM2->CCMR1|=2<<8;//CC2S=10 选择输入端
TIM2->CCER|=1<<5; //CC2P=1
选择有交转换边沿 下降沿有效
TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->DIER|=1<<1; //允许更新捕获中断
TIM2->CR1|=0x01; //使能定时器2
MY_NVIC_Init(1,3,TIM2_IRQChannel,2);//抢占1,子优先级3,组2
}
//定时器2中断服务程序
void TIM2_IRQHandler(void)
{
IC1Value = TIM2->CCR1;//读取CCR1也可以清CC1IF标志位
IC2Value = TIM2->CCR2;//读取CCR1也可以清CC2IF标志位
TIM2->SR&=~(1<<1);//清除中断标志位
}
//===============================================================
还有上面Capture_Init(2000,72-1); 里面的参数与PWM_Init(1000,72-1); 里面的参数有什么具体关系呢? 谢谢!
关系是:Capture_Init(2000,72-1)中的2000是用于计数的,就是触发后到下一次触发的计数,如果捕获的PWM信号周期太长20000不够会有溢出的。PWM_Init(1000,72-1); 中的1000是重载,当设置72-1时,分频为1M,重载1000,就是1M/1000=1K。
如果Capture_Init(2000,72-1)设置72-1,说明分频为1M,1M的频率在计数1K的信号那计数器要计数1000次,这个1000次要在Capture_Init(2000,72-1)中的2000内,
如果1000次大于Capture_Init(2000,72-1)中的2000,那就会溢出了。
所以,如果PWM_Init(1000,72-1); 中的1000这个数很大,那Capture_Init(2000,72-1)中的2000要比它大就得了。这样就不会溢出了。
不稳定不确定说明你重载太少了,已经溢出了。你可以改大一点Capture_Init(65536,72-1); 这样可以测几十HZ.
A:这里是定时器输入口1,我是IC1映射到TI1, 所以是从TI1PF1触发的,具体请看PWM输入图表。
B:通道选择请看手册,CC1通道被配置为输入,IC1映射到TI1上
C:输入滤波器请看手册
D:从输入选择了CCR2,通道TI2.
PWM输入模式 库函数例程位置 : STM32F10x_StdPeriph_Lib_V3.3.0\Project\STM32F10x_StdPeriph_Examples\TIM\PWM_Input 该模式是输入捕获模式的一个特例 例如,你需要测量输入到TI1上的PWM信号的长度(TIMx_CCR1寄存器)和占空比(TIMx_CCR2寄存器),具体步骤如下(取决于CK_INT的频率和预分频器的值)
● 选择TIMx_CCR1的有效输入:置TIMx_CCMR1寄存器的CC1S=01(选择TI1)。
● 选择TI1FP1的有效极性(用来捕获数据到TIMx_CCR1中和清除计数器):置CC1P=0(上升沿有效)。
● 选择TIMx_CCR2的有效输入:置TIMx_CCMR1寄存器的CC2S=10(选择TI1)。
● 选择TI1FP2的有效极性(捕获数据到TIMx_CCR2):置CC2P=1(下降沿有效)。
● 选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS=101(选择TI1FP1)。
● 配置从模式控制器为复位模式:置TIMx_SMCR中的SMS=100。
● 使能捕获:置TIMx_CCER寄存器中CC1E=1且CC2E=1。
STM32 TImer几种模式_通用定时器