STM32F103输入捕获--按键

STM32F103输入捕获–按键

一、输入捕获

输入捕获是定时器的一个应用,用于

  1. 波形高低电平的时间的计算
  2. 波形频率的计算

输入捕获的步骤为

  1. GPIO初始化(GPIO时钟使能)
  2. TIMx初始化(TIMx时钟使能)
  3. TIM_IC初始化
  4. 中断初始化
  5. 中断函数的处理

其中最为重要的就是中断函数的处理。

二、定义初始化所需结构体

  • GPIO结构体
  • TIMx结构体
  • TIM_IC结构体(捕获的结构体)
  • NVIC中断结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_ICInitTypeDef TIM5_ICInitStructure;

三、时钟使能

GPIOAPB2
TIMxAPB1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);

四、GPIO初始化

	// GPIOA0初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,&GPIO_InitStructure);

此处使用下拉输入是因为此实验用于测试高电平的时间

那为什么IO口是PA0呢?

4.1 IO与TIMx

可根据芯片的数据去查询,由于本次实验使用TIM5,所以以下为IO与TIM5的关系

TxCx(某定时器的某一频道)默认IO重映射IO
T5C1PA0None
T5C2PA1None
T5C3PA2None
T5C4PA3None

五、定时器初始化

// TIM5初始化
	TIM_TimeBaseInitStructure.TIM_Period = 0xFFFF;       // 定时器最大数字  
	TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1);    // 时基
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;     // 分频系数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 计数方式
	
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);

难点为TIM_Period(arr)与TIM_Prescaler(psc)和定时器时间的关系
T = ( a r r + 1 ) × ( p s c + 1 ) 72 × 1 0 6 ( s ) T = \frac{(arr+1)\times(psc+1)}{72 \times 10^6}(s) T=72×106(arr+1)×(psc+1)(s)

T b a s e = ( p s c + 1 ) 72 × 1 0 6 ( s ) T_{base} =\frac{(psc+1)}{72 \times 10^6}(s) Tbase=72×106(psc+1)(s)

如假设 p s c = ( 72 − 1 ) psc=(72-1) psc=721

T b a s e = 1 0 − 6 s T_{base} = 10^-6s Tbase=106s 1 u s 1us 1us

定时器里的数字为多少即此时的时间为多少us。(后续在超声波的应用中会详细讲解,此文主要讲解输入捕获部分的内容)

六、定时器捕获初始化

	// 输入捕获初始化
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1;                 // 频道1 -- PA0
	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;      // 上升沿捕获
	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  // 映射到TI1
	TIM5_ICInitStructure.TIM_ICPrescaler = 0;                         // 不分频
	TIM5_ICInitStructure.TIM_ICFilter = 0;                            // 不滤波
	
	TIM_ICInit(TIM5,&TIM5_ICInitStructure);

在4.1的表格中明确写明了IO口与channel之间的关系,由于GPIO定义的是PA0,所以相对应的,捕获通道要定义为Channel1。

七、中断初始化

	
	// 中断初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;              // TIM5中断函数
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    // 先占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;           // 从优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);

	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);    // TIM5中断允许
	TIM_Cmd(TIM5,ENABLE);   // 启动定时器

八、中断函数

难点在于如何在中断函数里去获取高电平的时间

8.1 进入中断的条件

首先明确进入中断的条件为:

  • 定时器溢出中断
  • 捕获发生中断

以上两个条件满足其一均可以进入中断函数,因此需要在中断函数里对其进行区分。

8.2 高电平捕获的流程

1、捕获到高电平,进入中断,将捕获条件改为下降沿。

2、捕获到低电平,一次高电平捕获完成,将捕获条件改为上升沿。

由此可见,就是在高低电平之间进行一个来回的交替

8.3 时间的计算

1、捕获到高电平后开始计时

  • 若是溢出,则溢出次数+1
  • 若是超出最大值,则为最大值,不再变化

2、捕获到低电平时计时结束

8.4 伪代码

需要定义的变量

变量含义
f_up=1发生上升沿捕获(1:发生;0:未发生)
f_cap=0发生捕获,开始计时(1:开始计时;0:不开始计时)
finall=0是否完成一次捕获(1:完成;0:未完成)
num=0溢出次数
val=0捕获完成后定时器的值

中断函数

如果:finall == 1

不执行任何代码,直接返回,代表完成捕获

如果捕获标志位!=RESET ;(即代表发生捕获并进入中断)

默认如果f_up==1; 即发生上升沿捕获
定时器数字=0,num=0,val=0
开始计时,f_cap=0(允许计时)
同时改为下降沿捕获(f_up=0,定时器捕获配置)

否则(表示捕获到了下降沿)

val = 定时器内的数字

finall = 1;完成一次捕获

f_cap=0;关闭计时

同时改为下降沿捕获(f_up=1,定时器捕获配置)

如果 定时器溢出标志位!=RESET;(代表发生了定时器溢出中断)

如果 cap==1;允许计时

如果 num超出最大值

num = 最大值

否则

num++ ;溢出次数+1

​ 清除溢出与捕获更新标志;

主函数:

如果finall==1;完成了一次捕获

t i m e = n u m × a r r + v a l time = num\times arr + val time=num×arr+val (us)

finall = 0;开启下一次捕获

8.5 可执行代码

#include "sys.h"
#include "delay.h"
#include "usart.h"

u8 f_up = 1;    // 判断上升捕获
u8 f_cap = 0;   // 是否发生捕获
u8 finish = 0;  // 捕获完成

u16 num = 0;     // 定时器溢出次数
u16 val = 0;    // 定时器值

void caputer_init()
{
	GPIO_InitTypeDef GPIO_Cap_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_ICInitTypeDef TIM5_ICInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
	
	// GPIOA0初始化
	GPIO_Cap_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_Cap_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_Cap_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,&GPIO_Cap_InitStructure);
	
	// TIM5初始化
	TIM_TimeBaseInitStructure.TIM_Period = 0xFFFF;
	TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1);
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
	
	// 中断初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	
	// 输入捕获初始化
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1;
	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM5_ICInitStructure.TIM_ICPrescaler = 0;
	TIM5_ICInitStructure.TIM_ICFilter = 0;
	
	TIM_ICInit(TIM5,&TIM5_ICInitStructure);
	
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
	TIM_Cmd(TIM5,ENABLE); // 开定时器
}


void TIM5_IRQHandler(void)
{
	if(finish)   // 完成一次捕获,不进入任何中断处理,返回主函数处理数据
		return;
	
	// 如果发生捕获
	if(TIM_GetITStatus(TIM5,TIM_IT_CC1)!=RESET)
	{	
		printf("cap\r\n");
		// 是否为上升捕获
		if(f_up)   // 上升沿
		{
			printf("up\r\n");
			val = 0;
			num = 0;
			
			TIM_SetCounter(TIM5,0);   // 定时器数字为0开始计算高电平时间
			
			f_cap = 1; // 打开进入计时中断
			
			// 将捕获切换为下降沿
			f_up =0;
			TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
			
			
		}
		
		else // 下降沿
		{
			printf("down\r\n");		
			// 完成了一次捕获
			finish = 1;
			// 获取时间
			val = TIM_GetCapture1(TIM5);
			// 关闭溢出允许
			f_cap = 0;
			
			// 将捕获切换为上升沿
			f_up = 1;
			TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
		}
		
		

	}
	
	// 如果发生计时溢出中断
	if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)
	{
		if(f_cap) // 开始计时已打开
		{	
			// 超出最大值
			if(num == 0xFFFF)
			{
				num = 0xFFFF;
			}
			else
				// 记录溢出次数
				num++;
			printf("num = %d\r\n",num);
		}
	}

	TIM_ClearITPendingBit(TIM5,TIM_IT_Update|TIM_IT_CC1); // 清除中断更新标志
}



int main(void)
{	
	
	float num_f;   // 定义变量,必须放在最前面
	u32 time=0;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	  
	delay_init();  //
	
	uart_init(115200);
	delay_ms(30);
	

	caputer_init(); // 捕获初始化

	
	// 主循环函数
	while(1)
	{	
		if(finish) // 一次捕获完成
		{
			time = num * 65536;
			time +=  val;		
			printf("HIGH_time = %d us\r\n",time);		
			
			finish = 0;  // 捕获完成
		}
		
	}
	
}

九、结果展示

在这里插入图片描述

十、存在的问题

我是用按键实现的输入捕获,但是执行若干次后程序会崩溃,必须要reset重启才可以用。

(注:若是无法输出的朋友,按一下复位按键即可。)

在这里插入图片描述

若是那位大神知道错误在哪儿,还请不吝赐教!!!我也还在秃头思考ing。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值