基于STM32F407单一定时器捕获四路PWM波并测量占空比

最近有需求捕获天地飞ET07遥控器接收端的PWM波并测量占空比以获取当前摇杆位置(PS:本版本天地飞遥控器有7路PWM波2路PPM/W.BUS协议,本文只捕获其中4路PWM,后续更新W.BUS协议版本更加方便实用)

程序思路为在直连模式下先将通道设置为上升沿捕获,在进入中断后将其改为下降沿捕获,在上升沿和下降沿中断时分别获取当前定时器CNT计数器值据此统计高电平的持续时间并计算一个周期的时间长度(PS:还要统计定时器更新中断次数,并在中断内分别保存当前更新中断次数才可以计算出以上数据)

程序现象如下:(PS:遥控器摇杆占空比为5.5-7.6-9.7,代表摇杆在最下方在最中间和最上方)

天地飞遥控器PWM捕获

1、首先将接收器信号输出端与开发板选好的定时器引脚连接
在这里插入图片描述

 本例选择的是定时器3,F407引脚复用图如下:

在这里插入图片描述

2、配置GPIO及TIM定时器
PS:若不在程序最上方定义结构体时要注意是否开启了C99代码支持,否则会编译报错
在这里插入图片描述

//配置相应的GPIO并复用引脚
void GPIO_Config(void)
{
	RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOB, ENABLE); //GPIOB使能
	
	//设置引脚复用,PS:引脚复用方法一次只能复用一个引脚
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_TIM4);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource9,GPIO_AF_TIM4);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//开启引脚复用模式
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	//设置为推挽输出
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	//选择定时器4引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
	//将引脚下拉,引脚尽量不设置浮空,否则会出现不稳定电平
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
	//设置GPIO引脚速度	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//手动拉低所有引脚电平,可不写
	GPIO_ResetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);
}

3、配置定时器参数
PS:配置时要注意自己开发板定时器所在的总线及时钟频率,定时器参数根据要捕获的PWM波频率调整,本文捕获的波形为50Hz

//配置对应定时器
void TIM_Config(void)
{
	//开启对应定时器的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	//设置时钟分割
	TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//设置定时器为向上计数模式
	TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	//设置自动重装载值为65536
	TIM_InitStructure.TIM_Period = 65536-1;
	//设置定时器分频值为84,f407定时器默认频率为168MHZ设置为84方便计算
	TIM_InitStructure.TIM_Prescaler = 84-1;
	//设置为每次重装载数据时都产生中断 PS:设置为N时N+1次重装载产生一次中断
	TIM_InitStructure.TIM_RepetitionCounter = 0;
	//初始化定时器
	TIM_TimeBaseInit(TIM4,&TIM_InitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitStructure;
	//选择定时器通道1
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
	//设置滤波大小
	TIM_ICInitStructure.TIM_ICFilter = 0xFF;
	//设置触发模式为上升沿触发
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
	//设置时钟分割
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	//选择连接模式为直连
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM4,&TIM_ICInitStructure);
	//同理设置通道2-4
	//选择定时器通道2
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
	TIM_ICInit(TIM4,&TIM_ICInitStructure);
	//选择定时器通道3
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
	TIM_ICInit(TIM4,&TIM_ICInitStructure);
	//选择定时器通道4
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
	TIM_ICInit(TIM4,&TIM_ICInitStructure);
	
	//设置定时器中断触发使能
	TIM_ITConfig(TIM4,TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4,ENABLE);
	//清除因初始化产生的中断
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4);
	
	//使能定时器4
	TIM_Cmd(TIM4,ENABLE);
}

4、配置NVIC中断控制器

//配置NVIC中断使能
void TIM_NVIC_Config(void)
{
	//设置NVIC中断分组,PS:一个程序中只能设置一次分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	
	
	NVIC_InitTypeDef NVIC_InitStructure;
	//配置需要中断响应的定时器
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	//设置中断抢占优先级分组
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	//设置中断响应优先级分组
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	
	NVIC_Init(&NVIC_InitStructure);
}

5、封装代码调用方法

//开始定时器捕获方法
void StartTIM_Capture(void){
	//开启NVIC
	TIM_NVIC_Config();
	//配置定时器
	TIM_Config();
	//配置GPIO
	GPIO_Config();
}

6、配置数据保存数组及占空比保存结构体

//标志位数组	,数据为0表示捕获上升沿,数据为1表示捕获下降沿  
uint8_t  TIMCH_FLAG[4] = {0};	
//统计不同通道上升沿时计时器值
float	TIMCH_UP[4] = {0};
//统计不同通道下降沿时计时器值
float	TIMCH_DOWN[4] = {0};
//统计计数器溢出次数
uint32_t TIMCH_IT_UPDATE_COUNT[1] = {0};
//统计不同通道上升沿CNT计数器溢出次数
uint32_t TIMCH_IT_UP_COUNT[4] = {0};
//统计不同通道下降沿CNT计数器溢出次数
uint32_t TIMCH_IT_DOWN_COUNT[4] = {0};
//保存不同通道一个周期的计数值大小
float TIMCH_IT_NUM[4] = {0};
//保存不同通道高电平持续时间
float TIMCH_HIGH_TIME[4]={0};
//存放捕获到的PWM占空比,尽量放在h文件中,此处为方便讲解
typedef  struct PWM_CH {
	float PWM_0;
	float PWM_1;
	float PWM_2;
	float PWM_3;
} P;
//用于保存不同通道的占空比
P test ={0,0,0,0};

7、在中断方法中捕获当前数据并保存到上列数组中计算当前占空比

//处理中断信息
void TIM4_IRQHandler(void)
{
	//处理定时器产生的更新中断
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET){
		TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
		//C定时器4NT计数累加
		TIMCH_IT_UPDATE_COUNT[0]+=1;
	}
	
	//处理不同通道产生的上升沿或下降沿中断
	if(TIM_GetITStatus(TIM4,TIM_IT_CC1)==SET){
		TIM_ClearITPendingBit(TIM4,TIM_IT_CC1);
		if(TIMCH_FLAG[0] == 0){
			//调用封装方法获取当前PWM占空比
			(&test)->PWM_0 = rtPwmValue(0,TIM_GetCapture1(TIM4));
			
			//将定时器设置为下降沿捕获
			TIM_OC1PolarityConfig(TIM4,TIM_ICPolarity_Falling); 
			TIMCH_FLAG[0] = 1;
		}
		else
		{
			TIMCH_DOWN[0] = TIM_GetCapture1(TIM4);
			//计算下降沿时的高电平时间并更新此时的更新次数
			setDownValue(0,TIMCH_IT_UPDATE_COUNT[0]);
			//将定时器设置为上升沿捕获
			TIM_OC1PolarityConfig(TIM4,TIM_ICPolarity_Rising); 
			TIMCH_FLAG[0] = 0;
		}
	}
	
	//处理不同通道产生的上升沿或下降沿中断
	if(TIM_GetITStatus(TIM4,TIM_IT_CC2)==SET){
		TIM_ClearITPendingBit(TIM4,TIM_IT_CC2);
		if(TIMCH_FLAG[1] == 0){
			//调用封装方法获取当前PWM占空比
			(&test)->PWM_1 = rtPwmValue(1,TIM_GetCapture2(TIM4));
			//将定时器设置为下降沿捕获
			TIM_OC2PolarityConfig(TIM4,TIM_ICPolarity_Falling); 
			TIMCH_FLAG[1] = 1;
		}
		else
		{
			TIMCH_DOWN[1] = TIM_GetCapture2(TIM4);
			//计算下降沿时的高电平时间并更新此时的更新次数
			setDownValue(1,TIMCH_IT_UPDATE_COUNT[0]);
			//将定时器设置为上升沿捕获
			TIM_OC2PolarityConfig(TIM4,TIM_ICPolarity_Rising); 
			TIMCH_FLAG[1] = 0;
		}
	}
	
	//处理不同通道产生的上升沿或下降沿中断
	if(TIM_GetITStatus(TIM4,TIM_IT_CC3)==SET){
		TIM_ClearITPendingBit(TIM4,TIM_IT_CC3);
		if(TIMCH_FLAG[2] == 0){
			//调用封装方法获取当前PWM占空比
			(&test)->PWM_2 = rtPwmValue(2,TIM_GetCapture3(TIM4));
			//将定时器设置为下降沿捕获
			TIM_OC3PolarityConfig(TIM4,TIM_ICPolarity_Falling); 
			TIMCH_FLAG[2] = 1;
		}
		else
		{
			TIMCH_DOWN[2] = TIM_GetCapture3(TIM4);
			//计算下降沿时的高电平时间并更新此时的更新次数
			setDownValue(2,TIMCH_IT_UPDATE_COUNT[0]);
			//将定时器设置为上升沿捕获
			TIM_OC3PolarityConfig(TIM4,TIM_ICPolarity_Rising); 
			TIMCH_FLAG[2] = 0;
		}
	}
	
	//处理不同通道产生的上升沿或下降沿中断
	if(TIM_GetITStatus(TIM4,TIM_IT_CC4)==SET){
		TIM_ClearITPendingBit(TIM4,TIM_IT_CC4);
		if(TIMCH_FLAG[3] == 0){
			//调用封装方法获取当前PWM占空比
			(&test)->PWM_3 = rtPwmValue(3,TIM_GetCapture4(TIM4));
			//将定时器设置为下降沿捕获
			TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling); 
			TIMCH_FLAG[3] = 1;
		}
		else
		{
			TIMCH_DOWN[3] = TIM_GetCapture4(TIM4);
			//计算下降沿时的高电平时间并更新此时的更新次数
			setDownValue(3,TIMCH_IT_UPDATE_COUNT[0]);
			//将定时器设置为上升沿捕获
			TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising); 
			TIMCH_FLAG[3] = 0;
		}
	}
}

封装上升沿计算函数

/*
 * 函数名:rtPwmValue
 * 描述  :根据传入参数计算不同通道的占空比
 * 输入  :ch:通道号,cnt:当前计数器值
 * 输出  : PWM占空比
 * 调用  :外部调用
 */	 
float rtPwmValue(uint8_t ch, uint16_t Cnt)
{
	float PWM = 0;
	//计算一个周期的计数值
	TIMCH_IT_NUM[ch]=(float)Cnt-TIMCH_UP[ch]+(TIMCH_IT_UPDATE_COUNT[0]-TIMCH_IT_UP_COUNT[ch])*65536;
	//当产生上升沿时更新上升沿计数器溢出次数
	TIMCH_IT_UP_COUNT[ch] = TIMCH_IT_UPDATE_COUNT[0];
	TIMCH_UP[ch]=Cnt;
	if(TIMCH_IT_UP_COUNT[ch]>=TIMCH_IT_DOWN_COUNT[ch]){
		//将当前计数值相减并计算占空比
		PWM = (float)TIMCH_HIGH_TIME[ch]/TIMCH_IT_NUM[ch];
	}
	return PWM;
}

封装下降沿计算函数

/*
 * 函数名:setDownValue
 * 描述  :根据传入通道信息计算下降沿时高电平时间
 * 输入  :ch:通道号,updateCount:当前更新中断次数
 * 输出  : 无
 * 调用  :外部调用
 */	 
void setDownValue(uint8_t ch, uint32_t updateCount)
{
	if(TIMCH_DOWN[0]-TIMCH_UP[0]>0){
		//计算高电平持续时间并乘以100方便计算百分比
		TIMCH_HIGH_TIME[ch]=(float)(TIMCH_DOWN[ch]-TIMCH_UP[ch])*100;
	}else{
		//计算高电平持续时间
		TIMCH_HIGH_TIME[ch]=(float)(TIMCH_DOWN[ch]-TIMCH_UP[ch]+(updateCount-TIMCH_IT_DOWN_COUNT[ch])*65536)*100;
	}
	//当产生下降沿时更新下降沿计数器溢出次数
	TIMCH_IT_DOWN_COUNT[ch] = updateCount;
}

8、在主函数中调用捕获方法

int main(void)
{
  StartTIM_Capture();
	while(1)                            
	{	   

	}
}

9、烧录验证
在这里插入图片描述

9、小结
定时器捕获四路PWM波并计算占空比程序到此就结束了,有问题可以在评论区反馈或者私信也可

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值