蓝桥杯嵌入式基础板模块之单个定时器的多路捕获和多路输出

本文详细介绍了STM32单个定时器实现多路捕获、测量频率与占空比、多路输出频率及占空比调节的编程技巧。通过实例展示了如何配置定时器参数,实现精确的信号捕获与输出,特别关注了ARR寄存器设置、通道初始化及中断处理。同时,文章探讨了使用TIM_OCMode_Toggle与TIM_OCMode_Timing模式的区别,以及如何避免占空比输出错误。
摘要由CSDN通过智能技术生成

关于蓝桥杯嵌入式的第六届的模拟题,碰到了一个问题就是关于输入捕获与PWM输出的一些问题,经过一天半的总结终于实现。

单个定时器实现两个频率的捕获

这里实现的是 PA1与PA2的输入捕获,他们分别属于Timer2的通道2和通道3,这里实现了多路捕获

void Timer2_PwmIn(void){
	TIM_ICInitTypeDef  TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;	
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  GPIO_InitStructure.GPIO_Pin = 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);
	
  TIM_TimeBaseStructure.TIM_Period = 0xffff;
  TIM_TimeBaseStructure.TIM_Prescaler = 35;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	
  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(TIM2, &TIM_ICInitStructure);
	
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_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);
	
**//  TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);**
  TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
  TIM_Cmd(TIM2, ENABLE);
  TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_CC3, ENABLE);
	
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void)
{
	__IO uint16_t temp;
	static __IO uint16_t IC1Value_temp,IC2Value_temp;
	if(TIM_GetITStatus(TIM2, TIM_IT_CC2)!= RESET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
		IC2Value = TIM_GetCapture2(TIM2);
		temp = IC2Value - IC1Value_temp;
		if(temp==0){
			Frequency_1=0;
		}
		else{
			Frequency_1 = 2000000/temp; //此处的2_000_000 与上面的72M/36相关 
		}
		IC1Value_temp = IC2Value;
	}
	if(TIM_GetITStatus(TIM2, TIM_IT_CC3)!= RESET)
	{
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC3);
		IC3Value = TIM_GetCapture3(TIM2);
		temp = IC3Value - IC2Value_temp;
		if(temp==0){
			Frequency_2=0;
		}
		else{

			Frequency_2 = 2000000/temp;
		}
		IC2Value_temp = IC3Value;
	}
}

注意事项

  1. 首先是ARR寄存器的设置一定要是0XFFFF。
  2. 关于分频系数要考虑采的频率的大小,参考下表所示。
PSC定时器计数频率最高输出方波频率最低输出方波频率
07200000072MHZ1098.65
13600000036MHZ549.32
31800000018MHZ274.66
51200000012MHZ183.11
790000009MHZ137.33
1160000006MHZ91.55
2330000003MHZ45.78
3520000002MHZ30.52
7110000001MHZ15.26
7191000000.1MHZ1.53
  1. 每设置一个通道一定要初始化一次TIM_PWMIConfig(TIM2,&TIM_ICInitStructure);,上次就因为忘了初始化通道3,导致浪费了一天的时间!
  2. TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);这个要关掉,如果打开的话会导致通道的没办法测量相应的频率。

单个定时器实现测量占空比和频率

我这里是对PA6进行输入捕获。
Timer.c

	#include "timer.h"

	__IO uint16_t IC2Value = 0;
	__IO uint16_t DutyCycle = 0;
	__IO uint32_t Frequency = 0;

	void Timer3_InputInit(void){
		TIM_ICInitTypeDef  TIM_ICInitStructure;
		GPIO_InitTypeDef GPIO_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

		NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);

		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);

		TIM_TimeBaseStructure.TIM_Period = 65535;
		TIM_TimeBaseStructure.TIM_Prescaler =  71;
		TIM_TimeBaseStructure.TIM_ClockDivision = 0;
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
		TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
		//如果说PA7这里改成TIM_Channel_2
		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_PWMIConfig(TIM3, &TIM_ICInitStructure);
		//如果说PA7这里改成TIM_TS_TI2FP2
		TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
		TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
		TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
		TIM_Cmd(TIM3, ENABLE);
		//如果说PA7这里改成TIM_IT_CC2
		TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
	}

	void TIM3_IRQHandler(void)
	{
		//如果说PA7这里改成TIM_IT_CC2
		TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
		//如果说PA7这里改成TIM_GetCapture2
		IC2Value = TIM_GetCapture1(TIM3);
		if (IC2Value != 0)
		{
		   //如果说PA7这里改成TIM_GetCapture1
			DutyCycle = (TIM_GetCapture2(TIM3) * 100) / IC2Value;
			Frequency = SystemCoreClock/72/IC2Value;
		}
		else
		{
			DutyCycle = 0;
			Frequency = 0;
		}
	}



注意事项

  1. 这里对IC捕获的结构体初始化的是TIM_PWMIConfig前面的是TIM_ICInit!!!
  2. TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);这个要使能。
  3. 关于初始化的时候一定要清晰对于TimerX哪个系列。

单个定时器实现多路输出频率与占空比都可调

在这里我使用的是PA6与PA7,他们对应的通道分别是Timer3的通道1与通道2

void Timer3_PwmOut(u16 arr,u16 psc){
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	/* Enable the TIM2 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 0xffff;
  TIM_TimeBaseStructure.TIM_Prescaler = 35;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  /* PWM1 Mode configuration: Channel1 */
  TIM3CH1_set(0); 			//这个函数的实现在后面
  /* PWM1 Mode configuration: Channel2 */
  TIM3CH2_set(0);
  
  TIM_Cmd(TIM3, ENABLE);  /* TIM3 enable counter */
  //TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);//通道1、通道2中断使能	
}

uint16_t capture = 0;
_Bool pa6_state=0,pa7_state=0;
void TIM3_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
    capture = TIM_GetCapture1(TIM3);
		if(pa6_state==0){
			TIM_SetCompare1(TIM3,(u16)(capture + CCR1_Val*CCR1_ZKB));
			pa6_state=1;
		}
		else if(pa6_state==1){
			TIM_SetCompare1(TIM3,(u16)(capture + CCR1_Val*(1-CCR1_ZKB)));
			pa6_state=0;
		}
  }
  if (TIM_GetITStatus(TIM3,TIM_IT_CC2) != RESET)
  {
		TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
		capture = TIM_GetCapture2(TIM3);
		if(pa7_state==0){
			TIM_SetCompare2(TIM3, (u16)(capture+(u16)CCR2_Val*CCR2_ZKB));
			pa7_state=1;
		}
		else if(pa7_state==1){
			TIM_SetCompare2(TIM3, (u16)(capture +(u16)CCR2_Val*(1-CCR2_ZKB)));
			pa7_state=0;
		}
  }
}
void TIM3CH1_set(u8 status)
{
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
	if(status)
	{
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
		TIM_ITConfig(TIM3, TIM_IT_CC1 , ENABLE);
	}
	else
	{
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
		TIM_ITConfig(TIM3, TIM_IT_CC1 , DISABLE);
	}
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
}

void TIM3CH2_set(u8 status)
{
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  if(status)
  {
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
  }
  else
  {
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
	TIM_ITConfig(TIM3, TIM_IT_CC2, DISABLE);
  }
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
}

注意事项

  1. ARR寄存器一定要设置为0xFFFF,很重要!
  2. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; 这个设置要注意。因为STM32的官方库给的模式是TIM_OCMode_Timing,但是用TIM_OCMode_Toggle这个模式最好的。
  3. pa6_state与pa7_state这两个变量千万别忘了哦!!
  4. 最重要的一点!!!就是GPIO的模式一定要设置为复用输出GPIO_Mode_AF_PP!!!STM32官方库给的是GPIO_Mode_Out_PP,最好改一下。

缺点

  1. 多路输出的很明显的一个缺点就是输出的频率与占空比不准确,但是我没有很好的解决这个问题。如果有其他其他好的办法,希望大哥们能够指出来。




补充:单个定时器实现多路输出频率固定占空比可调

其实前面也可以实现这个功能,但是会出现一个Bug,就是如果说通过按键,来回的去开关定时器的通道,然后就会出现占空比相反的问题,就比如说你想让它输出的是10%占空比,但是他给你输出的是90%的占空比。

针对于这个问题我想了一下,最后还是想到了一个解决的方法,那就是使用TC_MODE中的TIM_OCMode_Timing。那么它与TIM_OCMode_Toggle模式不同在于当TIM->CNT与TIM->CRR的相同的时候,如果选TIM_OCMode_Toggle,相应的引脚会相应的反转,而TIM_OCMode_Timing什么都不做。那么重头戏就来了,如果我选择TIM_OCMode_Timing就意味着我可以人工去控制什么时候输出高电平什么,什么时候输出低电平了。

PS:可能也有人会说,我选择TIM_OCMode_Toggle模式,但是我在中断里,先让它反转,然后在选择人工去控制它输出相应的引脚不可以吗?答案是不可以的。。。本人已经测试过,还是会出现那个现象。。具体原因我也不是很清楚,希望有大神能够解释一下。

__IO uint16_t CCR2_Val = 200;  		//2M/2000 = 1Khz
__IO uint16_t CCR3_Val = 200;			//2M/2000 = 1KHz
__IO double ZKB_C2=0.1,ZKB_C3=0.1;

void Timer2_Init(void){
	NVIC_InitTypeDef NVIC_InitStructure;	
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  //注意当选择是TIM_OCMode_Timing,这里为GPIO_Mode_Out_PP。
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = 35;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	
  /* Output TIM_OCMode_Toggle Mode configuration: Channel2 */
	Set_Tim2Ch2(1);

  /* Output Compare Timing Mode configuration: Channel3 */
	Set_Tim2Ch3(1);
	
  /* TIM2 enable counter */
  TIM_Cmd(TIM2, ENABLE);
	GPIO_SetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2);
}
_Bool PA1_State=0,PA2_State=0;
void TIM2_IRQHandler(void)
{
	uint16_t capture = 0; 
	if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
		capture = TIM_GetCapture2(TIM2);
		if(PA1_State==0){
			PA1_On(); //这里我用的是一个宏定义GPIO_SetBits(GPIOA,GPIO_Pin_1) 下同
			PA1_State=1;
			TIM_SetCompare2(TIM2,capture + (u16)CCR2_Val*ZKB_C2);
		}
		else{
			PA1_Off();
			PA1_State=0;
			TIM_SetCompare2(TIM2,capture + (u16)CCR2_Val*(1.0-ZKB_C2));
		}
  }
  if(TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    capture = TIM_GetCapture3(TIM2);
		if(PA2_State==0){
			PA2_On();
			PA2_State=1;
			TIM_SetCompare3(TIM2,(u16)(capture + (u16)(CCR3_Val*ZKB_C3)));
		}
		else{
			PA2_Off();
			PA2_State=0;
			TIM_SetCompare3(TIM2,(u16)(capture + (u16)(CCR3_Val*(1.0-ZKB_C3))));
		}
  }
}

void Set_Tim2Ch2(u8 mode){
	TIM_OCInitTypeDef  TIM_OCInitStructure;
		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
	if(mode==1){
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
		TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
	}
	else if(mode==0){
		PA1_Off();
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
		TIM_ITConfig(TIM2,TIM_IT_CC2,DISABLE);
	}
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OC2Init(TIM2,&TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Disable);
}
void Set_Tim2Ch3(u8 mode){
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
	if(mode==1){
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
		TIM_ITConfig(TIM2,TIM_IT_CC3,ENABLE);
	}
	else if(mode==0){
		PA2_Off();
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
		TIM_ITConfig(TIM2,TIM_IT_CC3,DISABLE);
	}
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OC3Init(TIM2, &TIM_OCInitStructure);
  TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Disable);
}

这样操作之后,确实再也没有出现占空比输出相反的情况了。。。具体原因我也不是很清楚,等本人开学之后,请教老师之后再补充原理吧。。。。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值