基于STM32F103ZET6实现输入捕获

定时器可以使能位PWM模式,当然也可以使能为PWM波的输入捕获!按键可以输入脉冲,正好对应的是定时器TIM5的通道1,输入捕获模式可以用来测量脉冲宽度或者测量频率

一句话概括

**通过检测定时器某个通道上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。**同时还可以配置捕获时是否触发中断/DMA 等。

目的
用 TIM5 的通道 1(PA0)来做输入捕获,捕获 PA0 上高电平的脉宽(用 WK_UP 按键输入高电平),通过串口打印高电平脉宽时间
在这里插入图片描述
算法流程
我们用到 TIM5_CH1 来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,记录发生上升沿的时候 TIM5_CNT 的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的 TIM5_CNT 值。这样,前后两次 TIM5_CNT 之差,就是高电平的脉宽,同时 TIM5 的计数周期已知,从而可以计算出高电平脉宽的准确持续时间。
捕获/比较模式寄存器 1:TIMx_CCMR1
在这里插入图片描述
TIMx_CCMR1寄存器可以配置通道1和2,低八位[7:0]用于捕获/比较通道 1 的控制。
0-1:位控制输入输出的方向及输入脚的选择,我们配置值为01,为输入模式,IC1映射到TI1上,由下图可知,当然还有一个选择就是IC2映射到TI1上。平时就默认IC1映射到TI1上。
我们用到的是IC1
2-3:输入捕获预分频器
值00:不分频,每次边沿都触发捕获
值01:每两个边沿出现触发捕获
值10:每四个边沿出现触发捕获
值11:每八个边沿出现触发捕获
我们用到的是:00
4-7:输入捕获滤波器
0000:无虑波,每次采样触发一次都会产生一个输出的跳变
0001:每两次采样触发一次都会产生一个输出的跳变
等等;
我们用到的是:0000
在这里插入图片描述
捕获/比较使能寄存器:TIMx_CCER
我们要用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。
在这里插入图片描述
所以,要使能输入捕获,必须设置 CC1E=0,而 CC1P 则根据自己的需要来配置。

初始化寄存器流程:

(1):使能GPIOA,定时器5时钟
(2):初始化定时器,得出溢出时间,GPIO及中断,
(3):初始化定时器为输入捕获模式,映射通道1到IC1
(4):开启更新中断和捕获中断,编写中断服务函数
如果捕获的是高电平信号的脉宽,那第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理。这两件事,我们都在中断里面做,所以必须开启捕获中断更新中断
(5):开启定时器

输入捕获头文件timepwm.h

#ifndef TIMEPWM_H
#define TIMEPWM_H
#include "sys.h"
void timepwm_init(u16 arr,u16 prer);
#endif

输入捕获源文件timepwm.c

#include "timepwm.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
void timepwm_init(u16 arr,u16 prer)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	
	TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_InitStructure.TIM_Period=arr;
	TIM_InitStructure.TIM_Prescaler=prer;
	TIM_TimeBaseInit(TIM5, &TIM_InitStructure);
	
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter=0x00;
	TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM5, &TIM_ICInitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel= TIM5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
	TIM_Cmd(TIM5,ENABLE );
}
unsigned char status;
short int value;
void TIM5_IRQHandler(void)
{
	if((status&0x80)==0)
	{
		if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
		{
			if(status&0x40)
			{
				if((status&0x3f)==0x3f)
				{
					status|=0x80;
					value=0xffff;
				}
				else
						status++;
			}
		}
		if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != 0)
		{
			if(status&0x40)
			{
				status|=0x80;
				value=TIM_GetCapture1(TIM5);
				TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
			}
			else
			{
				status=0;
				value=0;
				TIM_SetCounter(TIM5,0);
				status|=0x40;
				TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
			}
		}
	}
		TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
}

主函数main.c

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "stdio.h"
#include "timepwm.h"
extern unsigned char status;
extern short int value;

**#if 1//添加printf输出到串口
#pragma import(__use_no_semihosting)
struct __FILE 
{ 
int handle;
	}; 
FILE __stdout; 
	
_sys_exit(int x) 
{ 
x = x; 
}
int fputc(int ch, FILE *f)
{ 
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); 
 USART_SendData(USART1,(uint8_t)ch); 
return ch;
}
#endif**
int main(void)
{
	u32  temp;
	delay_init();
	LED_Init();
	usart_init(115200);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	timepwm_init(0xFFFF,71);
	 while(1)
	 {
		 delay_ms(10);
		 if(status&0x80)
 {
			 temp=(status&0x3f);
			 temp*=65536;
	     temp+=value;
			 printf("temp:%d",temp);
			 status=0;
		 }
	 }
}

这段引入 printf 函数支持的代码在 usart.h 头文件的最上方,这段代码加入之后便可以通过printf 函数向串口发送我们需要的内容,方便开发过程中查看代码执行情况以及一些变量值。这段代码不需要修改,引入到 usart.h 即可。

计算

总时间计算:
首先我们设置定时器的频率为72MHz/prer:72M/72=1MHz,周期为1/f=1us,定时器的位数为16位,最大溢出时计数2的16次方为65535,次数为65536,溢出时间为65536us,根据当前计数值,加上溢出次数的总和,可算出总时间值;
中断函数思路
定义一个八位的状态变量,定义一个十六位的定时器变量存储定时器值
在这里插入图片描述
更新中断和捕获中断都是定时器中断,我们要设置优先级及中断分组,一旦进入中断函数,有可能是捕获中断也可能是更新中断,所以进入中断函数先判断中断类型。

void TIM5_IRQHandler(void)//**程序文字化,进入中断函数**
{
if(脉冲接收完成)
{
			if(如果是更新中断)
			{
			     if(bit6为1)//也就是说接收到了上升沿
			     {
					     if(溢出次数达到上限0x3f)
					     {
					             bit7标志位赋值为1;
					             计数值赋值为最大0xffff;
					     }
					     else
					          次数加1}
	     }
		 if(如果是捕获中断)
	    {
						  if(判断bit6为1)//这次来的应该是下降沿
						  {
						  记下当前定时器值;
						  bit7赋值为1,表示捕获结束;
						  改变为下次上升沿捕获;
						  }
						  else//这次来的是上升沿,第一次捕获
						  {
						        定时器置零,方便后面时间计算;
						        十六位变量值清零;
						        bit6赋值为1;
						        改变捕极性为下降沿;
						  }
			  }
}
清除中断标志位;
}

希望我的理解可以为读者解决问题,也请大家有什么好的方法或者算法可以在留言下方给出,谢谢,相互学习!

发布了10 篇原创文章 · 获赞 14 · 访问量 4084

猜你喜欢

转载自blog.csdn.net/weixin_42271802/article/details/104617090