9-STM32F1-输入捕获

STM32F1-输入捕获

以下部分内容来自正点原子的书籍

输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。 STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)
存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。
	本章我们用到 TIM5_CH1 来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,当发生上升沿中断时记录发生上升沿的时候 TIM5_CNT 的值。然后配置捕获信号为下降沿捕获中断,当下降沿到来时,发生捕获,并记录此时的 TIM5_CNT 值。这样,前后两次 TIM5_CNT 之差,就是高电平的脉宽,同时 TIM5 的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。 当然这只是大概原理,实际应用还是需要注意一些细节的。
TIM5的计数器是有长度限制的,如果捕获脉冲的长度超过计数器的长度那就不准了,所以我们用定时器的溢出来辅助计时。具体步骤如下;
1,把定时器计数器溢出时间设置为一个定值,为了计算方便,本实验设置为1ms,另外设置一个ms_count的计数变量,这里就用ms_count吧
2,当上升沿来到时把TIM5的计数器清零,ms_count清零,把输入中断改为下降沿触发
3,在高电平期间,TIM5的计数器溢出一次ms_count就+1,把输入中断改为上升沿触发,以便下次捕获
4,当下降沿到来时,取当前计数器的值和ms_count变量的计数值,所以高电平的时间为t=ms_count+TIM5计数器的值TIM5_CNT。这里需要注意的是ms_count的单位是毫秒,但是TIM5计数器的值TIM5_CNT却不是毫秒,要把TIM5_CNT转为毫秒再相加。

TIM5只有一个中断入口函数,但是TIM5却有更新、上升沿捕获、下降沿捕获等中断,所以要在中断函数里区分。更新中断还好,更坑的是上升沿中断和下降沿中断都是用同一个标志位,所以要定义一个变量记录当前状态,也就是当前是否在捕获计时中,以区分下一个捕获中断来到时是开始捕获还是捕获结束。
输入捕获需要配置几个部分;
1,定时器分频、重载值、计数方向等
2,输入捕获通道、滤波、分频等
3,定时器的中断配置,要配置两个中断,一个是定时器溢出中断,另一个是捕获中断。

具体配置讲解;
1,定时器分频、重载值、计数方向等
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//APB1时钟1分频=36m
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Down;//计数器向下计数
	TIM_TimeBaseInitStruct.TIM_Period=1000-1;//1000;//1ms溢出一次
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//外部时钟分频作为TIM5时钟
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//意思是计数器溢出多少次才会产生一次中断
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct );
关于这部分有一个很郁闷的问题,按理说TIM5是挂接在APB1上,APB1的时钟最大为36m,把外部时钟36分频作为TIM5的时钟然后重装值设为1000,这个配置计算得到的溢出时间是1ms,但是事实上这个配置得到的溢出时间是0.5ms,挺郁闷的。现在把外部36m的APB1时钟经过72分频后作为TIM5的时钟,重装值为1000时溢出时间才是1ms,这不科学!但这是事实。

2,输入捕获通道、滤波、分频等
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;//要捕获的是通道1,在PA0上
	TIM_ICInitStruct.TIM_ICFilter=0;//输入滤波0个系统时钟
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿触发中断开始捕获
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//对输入的频率进行分频
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;//把输入通道映射到TI1,也就是TIM5的通道1
	TIM_ICInit(TIM5,&TIM_ICInitStruct);
其中第一个参数是设置要捕获的通道,TIM5的通道1在PA0上(32的数据手册有),
第二个参数是输入滤波,这里设置为0的意思是不需要滤波,如果捕获到的电平变化直接触发跳变,这个滤波是用系统时钟(一般为72m)而不是经过分频后的TIM5时钟。
第三个参数是设置捕获的模式,因为按键是高电平有效,所以这里设置为上升沿捕获
第四个参数是对输入脉冲的分频,就是产生多少个跳变后才会产生一个捕获中断
第五个参数是吧TIM5的捕获通道映射到通道1上
这里有必要区分下输入滤波和输入分频。输入滤波顾名思义就是滤除时间太短的干扰脉冲,比如把输入滤波设置为72(好像是不能设置这个值的,但为了方便计算,举个栗子还是可以的),72个系统时钟的时间是1us,就是说小于1us的高电平都不能触发定时器的跳变,这就是数字滤波。对应输入分频,就是对定时器跳变次数的统计,需要跳变多少次才能触发一次捕获中断。比如输入8个高脉冲,同时把分频值设为8,那么第八个脉冲输入时才会产生一个捕获中断,而不是一共产生8个捕获中断,当然每个脉冲还是能够产生一次跳变的。如果还不懂的话就看图吧


3,定时器的中断配置,要配置两个中断,一个是定时器溢出中断,另一个是捕获中断。
这个很简单,但是要记得同时打开更新和捕获这两个中断
	NVIC_InitStruct.NVIC_IRQChannel=TIM5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//要打开两个中断,一个是捕获通道的使能,另一个是定时器的更新中断

本实验的功能;测量按键按下的时间,在按键松开时把时间通过串口输出到电脑上,其中按键接在PA0上,也就是TIM5的通道1,正是本次实验要捕获的通道。

代码如下;
#include "sys.h"
#include "delay.h"
#include "stdio.h"

/************************************************************
功能;捕获按键按下的时间,并利用串口输出到电脑上,单位为ms
其中按键接在PA0
输入捕获用的是TIM5的通道1
开发板用的是正点原子的精英板

****************************************************************/

void init__uart1()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	//	串口IO配置,PA9,PA10
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//IO时钟打开
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//IO方式具体看《中文手册》8.1.11章节
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;//IO方式具体看《中文手册》8.1.11章节
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//配置串口1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打开串口时钟
	
	USART_InitStruct.USART_BaudRate=115200;	//波特率115200
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;	//无硬件流
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;	//接收和发送都使能
	USART_InitStruct.USART_Parity=USART_Parity_No;						//无奇偶校验
	USART_InitStruct.USART_StopBits=USART_StopBits_1;					//停止位1位
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;		//数据长度8位
	USART_Init(USART1,&USART_InitStruct);

	USART_Cmd(USART1,ENABLE);	//配置完成后一定要记得使能串口
}
void init_key()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void init_TIM5()
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//APB1时钟1分频=36m
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Down;//计数器向下计数
	TIM_TimeBaseInitStruct.TIM_Period=1000-1;//1000;//1ms溢出一次
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//计数器时钟为1mhz
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//意思是计数器溢出多少次才会产生一次中断
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct );
	
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;//要捕获的是通道1,在PA0上
	TIM_ICInitStruct.TIM_ICFilter=0;//输入滤波0个系统时钟
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿触发中断开始捕获
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//对输入的频率进行分频
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;//把输入通道映射到TI1,也就是TIM5的通道1
	TIM_ICInit(TIM5,&TIM_ICInitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel=TIM5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//要打开两个中断,一个是捕获通道的使能,另一个是定时器的更新中断
	TIM_Cmd(TIM5,ENABLE);
}

uint32_t key_on_time=0;//捕获到的时间,单位是ms
uint8_t IC_statue=0;//捕获状态,1=捕获完成,0=捕获中
 int main(void)
 {	 
	delay_init();	//延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	init__uart1();//串口1初始化
	init_key();//按键初始化
	init_TIM5();//这里是利用TIM5的通道1捕获的 
	while(1)
	{	}//等待输入信号
 }
 
 //中断处理函数
 void TIM5_IRQHandler()
 {
	 if(TIM_GetITStatus(TIM5,TIM_IT_Update)==1)//定时器计时1ms溢出
	 {
		 TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
			key_on_time++;//输入捕获的时间ms++
	 }
	 if(TIM_GetITStatus(TIM5,TIM_IT_CC1)==1)//捕获通道中断
	 {
		 TIM_ClearITPendingBit(TIM5, TIM_IT_CC1); //清除中断标志位
		 if(IC_statue==1)//当前为非捕获状态,开始捕获
		 {
			 IC_statue=0;//设置当前捕获状态为;捕获中
			 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//把下次触发捕获的条件改位上升沿触发
			 TIM_SetCounter(TIM5,0);//重置计数器,从0开始计数器
			 key_on_time=0;//毫秒计时值当然也要清零
		 } 
		 else//捕获完成
		 {
			 IC_statue=1;//设置当前状态为;捕获完成
			 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);把下次触发捕获的条件改位下降沿触发
			 //key_on_time=key_on_time+TIM_GetCapture1(TIM5)/1000;//捕获的时间要加上计数器的时间,
			 //但是注意计数器的时间单位和溢出中断的计数单位不是一样的,这里为了省事就没有把计数器时间加上
			 
			 printf("key_on_time=%8d ms\r\n",key_on_time);//把本次捕获到的时间通过串口打印到电脑上
		 }
	 }
 }



//重定义fputc函数 ,想要使用printf函数得添加这个函数
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值