STM32输入捕获功能,同时捕获8路波形。

STM32输入捕获功能,使用TIM2和TIM3同时捕获8路波形。

工作上的一个需求,要采集两组控制步进电机的信号。就是采集8个通道的方波,测量频率和计脉冲个数。看起来简简单单的东西,磕磕碰碰搞了两天,走了一些弯路,这里写出来记录一下。顺便理解一下输入捕获个多种用法。

1. 硬件设计

首先看一下用到那些IO口。在STM32手册里找这样TIMx_CHx的就是输入捕获的口了。
这里用了TIM2的4个口和TIM3的4个口。

定义端口
TIM2_CH1PA0
TIM2_CH2PA1
TIM2_CH3PA2
TIM2_CH4PA3
TIM3_CH1PA6
TIM3_CH2PA7
TIM3_CH3PB0
TIM3_CH3PB1

在这里插入图片描述

2. 定时器和输入捕获配置(初始化)

比较长,大概分为,初始化时钟,初始化端口,初始化定时器,配置中断优先级,设置上升沿,使能中断和定时器。

主程序里的调用

		TIM2_Cap_Init(0XFFFF,72-1);		//1MHz的频率计数

Q:定时器的时间如何计算呢?
A:72000000/72=1000000

TIM_ICInitTypeDef  TIM_ICInitStructure;
void TIM2_Cap_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);	//使能定时器2时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //使能GPIOA时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //使能GPIOB时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;  //
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成输入 并 全部拉低
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);						 
	GPIO_ResetBits(GPIOA,GPIO_Pin_2);						 
	GPIO_ResetBits(GPIOA,GPIO_Pin_3);						 
	GPIO_ResetBits(GPIOA,GPIO_Pin_6);						 
	GPIO_ResetBits(GPIOA,GPIO_Pin_7);						 
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0 | GPIO_Pin_1;  //
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成输入 并 全部拉低
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);						 
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);						 
	
	//初始化定时器2
	TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler = psc; 	//设置与分配器
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //设置向上计数
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //配置TIM2
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //配置TIM3
  
	//初始化输入捕获参数
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端口1:CH1
  	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
  	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置分配为不分配
  	TIM_ICInitStructure.TIM_ICFilter = 0x00;//配置输入滤波为不滤波
  	TIM_ICInit(TIM2,&TIM_ICInitStructure);
  	TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	//下面的都一样,就不一一说明了。
	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 = 0x00;
  	TIM_ICInit(TIM2,&TIM_ICInitStructure);
  	TIM_ICInit(TIM3,&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 = 0x00;
  	TIM_ICInit(TIM2,&TIM_ICInitStructure);
  	TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; 
  	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 = 0x00;
  	TIM_ICInit(TIM2,&TIM_ICInitStructure);
  	TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	//中断分组优先级初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //配置寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  //先占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //配置寄存器
	
	//8个通道都设置成,上升沿捕获
	TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
	TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
	TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
	TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
	
	TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Rising);
	TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Rising);
	TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);
	TIM_OC4PolarityConfig(TIM3,TIM_ICPolarity_Rising);

	//允许更新中断,允许CCxIE捕获中断
	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);//
	TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);//
	//使能定时器
 	TIM_Cmd(TIM2,ENABLE); 	
 	TIM_Cmd(TIM3,ENABLE);
}

3. 中断函数

这里举一个例子,TIM2_CH1的,其他的复制粘贴改一下就行。
捕获方波的思路就是,捕获两个上升沿,两个上升沿的时间差就是周期。周期取倒数就是频率呗。

u8  TIMCH_STA[8] = {0};	//标志位数组	    				
u16	TIMCH_VAL[8] = {0};	//计时器数组
void TIM2_IRQHandler(void)
{
		if((TIMCH_STA[0]&0X80)==0)//还未捕获到一个完整的周期
		{
				if(TIM_GetITStatus(TIM2,TIM_IT_CC1) != RESET)//捕获1发生捕获事件
				{
						if(TIMCH_STA[0]&0X40)		//捕获到第二个上升沿
						{
								TIMCH_STA[0]|=0X80;		//标记捕获到一个完整的周期,给主程序处理
								TIMCH_VAL[0]=TIM_GetCapture1(TIM2);//获取事件
						}
						else  								//捕获到第一个上升沿
						{
								TIMCH_STA[0]=0;				 //清空标志位
								TIMCH_VAL[0]=0;				 //清空计时器
								TIM_SetCounter(TIM2,0);		 //清空定时器
								TIMCH_STA[0]|=0X40;		 //标记捕获到第一个上升沿
						}
				}
		}
}

主程序里就很简单了

for(i=0;i<8;i++)//扫描8个通道的标志位
{
	if(TIMCH_STA[i]&0X80)//成功捕获到脉冲
	{
		temp = TIMCH_VAL[i];	//获取周期时间
		printf("HIGH[%d]:%d us\r\n",i,temp);	//打印输出
		TIMCH_STA[i]=0;			//复位标志位,开启下一次捕获
	}
}

4. 注意事项和扩展说明

1. 采样精度
输入一个1KHz的频率,采到的数是999us。也就是1.001KHz,四舍五入就是1KHz。由于电路也没滤波有误差也情有可原。
2. 采样范围
因为定时器的频率是1M,所以10KHz一下的采的才比较准。测试时候给了一个50KHz,采样的数据是19纳秒,也就是52.6KHz,就不太准了。而且计数器的数组也只是U16型,就是说如果频率小于15.25HZ,就溢出了。
所以这个程序的采样范围就是50Hz~5KHz,如果要大或者要小,就得调整程序。就像示波器不也是要调整频率的吗。
3. 如果要采占空比
第一个上升沿和第二个上升沿的时间是周期,那第一个上升沿和第一个下降沿的时间就是高电平的时间,算一下就是占空比了。
在采到上升沿的时候,把捕获改成捕获下降沿,采到下降沿的时候,改成捕获上升沿。就可以算出占空比了。但是实际测试中精度下检了,大概是因为,在中断里面改配置捕获会占用时间。

TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);//捕获下降沿。
  • 5
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值