STM32 F103 输入捕获实验

实验目的

STM32的通用定时器和高级定时器拥有输入捕获功能,本章我们将利用TIM3的CH3通道产生PWM输出波形连接到TIM5的CH2输入捕捉通道,测量PWM输出波形频率。数据手册请参看第14章中的相关内容

实验简介

输入捕获模式可以用来测量脉冲宽度和频率。所谓的捕获,就是检测定时器的TIMx_CHx输入捕获引脚,检测到信号跳变时,将当前的定时器的计数值(TIMx_CNT)保存到捕获寄存器(TIMx_CCRx),完成一次捕获,当使能捕获中断时,会产生中断。
本实验是利用捕获功能来测量脉冲频率,脉冲由PWM模块产生,那我们是怎么样测量的呢?原理是这样的:连续捕获2次上升沿/下降沿信号跳变,分别记录下2次的定时器计数值,这2个计数值的差值就是脉冲宽度,且计数器的计数频率我们是知道的,这样我们就可以计算出周期和频率。

电路设计

本实验,是将上一章产生的PWM波形,通过跳帽连接到TIM5的CH2(PA1)进行捕捉,USB转串口打印频率信息
在这里插入图片描述

有关寄存器

因为使用的是TIM5_CH2通道

APB1 外设时钟使能寄存器(RCC_APB1ENR)
在这里插入图片描述
APB2 外设时钟使能寄存器(RCC_APB2ENR)

在这里插入图片描述
TIM1 和TIM8 捕获/比较模式寄存器 1(TIMx_CCMR1)
在这里插入图片描述
这里配置为10 IC2映射到TI1上
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意: 这里主要靠介绍,我们使用的是2 这里是1
TIM1 和TIM8 捕获/比较使能寄存器(TIMx_CCER) 在这里插入图片描述
在这里插入图片描述
**TIM1 和TIM8 DMA/中断使能寄存器(TIMx_DIER) **
在这里插入图片描述
在这里插入图片描述

寄存器代码

test.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"

extern u8 TIM5CH2_CAPTURE_STA; 
//输入捕获状态
extern u8 TIM5CH2_CAPTURE_VAL; 
//输入捕获值

int main(void)
{
	u32 temp = 0;
	Stm32_Clock_Init(9);
	uart_init(72,115200);
	delay_init(72);
	LED_Init();
	TIM3_PWM_Init(899,0);
	//不分频 ,PWM频率为72000/(899+1)=80Khz
	TIM5_Cap_Init(0xffff,72-1);
	//以1Mhz的频率计数
	while(1)
	{
		delay_ms(10);
		LED3_PWM_VAL++;
		if(LED3_PWM_VAL ==300)
			LED3_PWM_VAL=0;
		if(TIM5CH2_CAPTURE_STA & 0x80)
		//成功捕获道了一次高电平
		{
			temp = TIM5CH2_CAPTURE_STA & 0x3f;
			//一般情况下定时器不会溢出,前六位做溢出时间
			temp *= 65536;
			//1次 65536 
			temp += TIM5CH2_CAPTURE_VAL;
			//得到高电平时间时间
			printf("HIGH: %d us\r\n",temp);
			//打印高电平
			TIM5CH2_CAPTURE_STA = 0;
			//开启下次捕获
			
		}
		
	}
}

timer.h

#ifndef __TIMER_H
#define __TIMER_H

#include "sys.h"

#define LED3_PWM_VAL  TIM3->CCR3

void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
void TIM5_Cap_Init(u16 arr,u16 psc);

#endif

timer.c

#include "timer.h"
#include "led.h"



//捕捉前的前章有,不再赘述
void TIM3_IRQHandler(void)
{
	if(TIM3->SR & 0x0001)
	{
		LED2 = !LED2;
	}
	TIM3->SR &= ~(1<<0);
}

void TIM3_Int_Init(u16 arr,u16 psc)
{
	RCC->APB1ENR |= 1<<1; 
	TIM3->ARR = arr; 
	TIM3->PSC = psc;  
	TIM3->DIER |= 1<<0;  
	TIM3->CR1  |= 0x01;   
	
	MY_NVIC_Init(1,3,TIM3_IRQn,2);×é 2
}




void TIM3_PWM_Init(u16 arr,u16 psc)
{
	RCC->APB1ENR |= 1<<1;
	RCC->APB2ENR |= 1<<3;
	
	GPIOB->CRL &= 0xFFFFFFF0;
	GPIOB->CRL |= 0x0000000B;
  
	RCC->APB2ENR |= 1<<0; 
	
	AFIO->MAPR &= 0xFFFFF3FF;
  AFIO->MAPR |= 1<<11;  
	
	TIM3->ARR = arr;
	TIM3->PSC = psc;
	
	TIM3->CCMR2 |= 7<<4;
	TIM3->CCMR2 |= 1<<3;
	TIM3->CCER |= 1<<8;
	TIM3->CR1 = 0x0080;
	TIM3->CR1 |= 0x01;
}





void TIM5_Cap_Init(u16 arr,u16 psc)
{
	RCC->APB1ENR |= 1<<3; 
	//TIM5使能
	RCC->APB2ENR |= 1<<2; 
	//GPIOA 也就是PORTA时钟使能
	//因为使用的是PB1 
	GPIOA->CRL &= 0xFFFFFF0F;
	GPIOA->CRL |= 0x00000080;
	//PA1 引脚 下拉/上拉输入模式
	GPIOA->ODR |= 0<<1; 
	//先让PA1为低电平,然后上升沿捕捉
	
	TIM5->ARR = arr; 
	//设定计时器自动重装值
	TIM5->PSC = psc; 
	//预分频器
	
	TIM5->CCMR1 |= 1<<9; 
	//CC2S = 10 
	TIM5->CCMR1 |= 0<<12;
	 //IC2F = 0000 ,配置输入滤波器,不滤波
	TIM5->CCMR1 |= 0<<10;
	//配置输入分频,不分频
	
	TIM5->CCER |= 0<<5;  
	 //CC2P = 0 上升沿捕捉
	TIM5->CCER |= 1<<4;   
	 //允许捕捉计数器值到捕获寄存器中
	
	TIM5->DIER |= 1<<2;
	 //允许2捕获中断
  TIM5->DIER |= 1<<0; 
  //允许更新中断
	TIM5->CR1 |=0x01;  
	 //使能定时器
	MY_NVIC_Init(2,0,TIM5_IRQn,2);
}


u8 TIM5CH2_CAPTURE_STA = 0;
//输入捕获状态
u16 TIM5CH2_CAPTURE_VAL ; 
 //输入捕获值

void TIM5_IRQHandler(void)
{
	u16 tsr;
	tsr = TIM5->SR;
	//状态寄存器
	if((TIM5CH2_CAPTURE_STA&0x80) == 0) 
	//还未捕捉成功
	{
		if(tsr&0x01)
		//溢出
		{
			if(TIM5CH2_CAPTURE_STA&0x40)
			//已经捕获到了高电平
			{
				if((TIM5CH2_CAPTURE_STA&0x3F) == 0x3F)
				//高电平太长了
				{
					TIM5CH2_CAPTURE_STA |= 0x80;
					//标志成功捕获了一次
					TIM5CH2_CAPTURE_VAL = 0xFFFF;
				}
				else
					TIM5CH2_CAPTURE_STA++;
			}
		}
		if(tsr&0x04)
		//捕获2发生捕获事件
		{
			if(TIM5CH2_CAPTURE_STA&0x40)
			//捕获到一个下降沿
			{
				TIM5CH2_CAPTURE_STA|=0x80;
				//标记成功捕获到一次高电平脉宽
				TIM5CH2_CAPTURE_VAL = TIM5->CCR1;
				//读取当前的捕获值
				TIM5->CCER &= ~(1<<5); 
				
			}
			else
			{
				TIM5CH2_CAPTURE_STA = 0;
				//清空
				TIM5CH2_CAPTURE_VAL = 0;
				//
				TIM5CH2_CAPTURE_STA |= 0x40;
				//标记捕捉上升沿
				TIM5->CNT = 0;
				//计数器清零
				TIM5->CCER |= 1<<5;
				//CC1p = 1 设置为下降沿捕捉
		 }
		}
	 }
	TIM5->SR = 0;
	//清除中断标志位
}




HAL库代码

main.c

#include "MyIncludes.h"
uint32_t uwIC2Value1 = 0;
//第一次捕捉值
uint32_t uwIC2Value2 = 0;
//第二次捕捉值
uint32_t uwDiffCapture = 0;
//两次捕捉差值
uint16_t uhCaptureIndex = 0;
//捕捉索引
uint32_t uwFrequency = 0;
//捕捉到的波形频率

uint8_t Cap_Over = 0;

void Cap_Process(void)
{
   if(TimCapHandle.Channel == HAL_TIM_ACTIVE_CHANNEL_2)
 //如果有源通道  为2 
 // TIM5 的 CH2 输入捕捉通道
   {
     if(uhCaptureIndex == 0)
     //第一次捕捉
     {
       uwIC2Value1 = HAL_TIM_ReadCapturedValue(&TimCapHandle,TIM_CHANNEL_2);
       //第一次捕捉值
       uhCaptureIndex = 1;
     }
     else if(uhCaptureIndex == 1)
     //第二次捕捉
     {
      uwIC2Value2 = HAL_TIM_ReadCapturedValue(&TimCapHandle,TIM_CHANNEL_2);
      //第二次捕捉值
      if(uwIC2Value2 > uwIC2Value1)
      {
       uwDiffCapture = (uwIC2Value2 - uwIC2Value1);
      }
      else if(uwIC2Value2 < uwIC2Value1)
      {
        uwDiffCapture = ((0xffff - uwIC2Value1) + uwIC2Value2) + 1;
        //如果第二次小于第一次说明已经计满一次
      }
      uwFrequency = HAL_RCC_GetPCLK1Freq()/uwDiffCapture;
      uhCaptureIndex = 0;
      //捕捉到的波形频率=定时器PCK频率/2次捕捉的差值
      Cap_Over = 1;
      //捕捉完成
     }
   }
}

char buff[50];
int main(void)
{
 u8 dir = 0;
 u16 duty = 50;
 u32 Set_Freq = 10000;
 //设置频率
  
 System_Init();
 SysTick_Init(NULL);
 USART1_Init(115200,NULL,NULL);
 PWM_Init(Set_Freq,duty);
 // 默认10kHz  
 //PWM初始化(频率,占空比)
 Tim5_CapInit(FREQ_COUNTER,Cap_Process);
//Tim5输入捕捉初始化(计数器频率,函数)
 
 while(1)
 {
   if(Cap_Over == 1)
   //显示捕获结果
   {
     Cap_Over = 0;
     sprintf(buff,"Cap Freq: %dHz\z\r\n",uwFrequency);
     printf(buff);
     //打印信息
     if(dir == 0)
     //递增
     {
       if(Set_Freq < 20000)
       Set_Freq+=100;
       else dir = 1;
     }
     else if(dir == 1)
     {
       if(Set_Freq > 10000)
       Set_Freq -= 100;
       else dir = 0;
     }
     PWM_Init(Set_Freq,duty);
     delay_ms(500);
   }
 }
}

InCap.h

#ifndef __INCAP_H_
#define __INCAP_H_

#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"

#define FREQ_COUNTER 1000000
//定义计数频率,要和初始化中一致

typedef struct
{
  void (*T5_CapISR)(void);
  //定时器5中断中运行的函数
}_TIMERCAP_ISR;

extern TIM_HandleTypeDef TimCapHandle;

void Tim5_CapInit(u32 Cnt_Freq,void(*ISR)(void));
//TImER5输入捕捉初始化

#endif

InCap.c

#include "InCap.h"

TIM_HandleTypeDef TimCapHandle;
//TIM基本句柄结构变量声明
TIM_IC_InitTypeDef sICConfig;
//TIM输入捕获配置结构变量声明

_TIMERCAP_ISR TIM5_CAP_ISR;

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef  *htim)
//HAL_TIM_IC_Init中调用
{
  GPIO_InitTypeDef GPIO_InitStruct;
  //GPIO基本配置结构
  __HAL_RCC_TIM5_CLK_ENABLE();
  //使能TIM5时钟
  __GPIOA_CLK_ENABLE();
  //TIM5_CH2管脚PA1
  //使能GPIOA时钟
  
  //配置输入捕捉管脚 
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
  //交替功能输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
  //配置NVIC中断
  HAL_NVIC_SetPriority(TIM5_IRQn,0,1);
  //设置中断
  HAL_NVIC_EnableIRQ(TIM5_IRQn);
  //设置中断源
}

void Tim5_CapInit(u32 Cnt_Freq,void (*ISR)(void))
//TIMER5输入捕捉初始化
{
  TIM5_CAP_ISR.T5_CapISR = ISR;
   //回调函数
  TimCapHandle.Instance = TIM5;
  //设置定时器
  TimCapHandle.Init.Period = 0xffffffff;
  //自动重装值,这里设为最大
  TimCapHandle.Init.Prescaler = 0;
  //设置1us的计数频率
  TimCapHandle.Init.ClockDivision = 0;
  //时钟不分频,来自RCC
  TimCapHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
 // 递增计数
  TimCapHandle.Init.RepetitionCounter = 0;
  //指定重复计数器值。
  HAL_TIM_IC_Init(&TimCapHandle);
   //定时器输入捕获初始化
  sICConfig.ICPolarity = TIM_ICPOLARITY_RISING;
  //上升沿捕获
  sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
  //管脚为捕获输入,映射到TI2
  sICConfig.ICPrescaler = TIM_ICPSC_DIV1;
  //输入捕获预分频,为 0 禁止预分频
  sICConfig.ICFilter =0x00;
//输入捕获滤波器,这是一个递减计数器,这里不滤波 
 HAL_TIM_IC_ConfigChannel(&TimCapHandle,&sICConfig,TIM_CHANNEL_2);
 //定时器捕获配置通道
 
 HAL_TIM_IC_Start_IT(&TimCapHandle,TIM_CHANNEL_2);
 //启动输入捕捉
}

void TIM5_IRQHandler(void)
//TIMER5中断服务函数
{
  HAL_TIM_IRQHandler(&TimCapHandle);
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
//用户回调函数,在HAL_TIM_IRQHandler中调用
{
  if(TIM5_CAP_ISR.T5_CapISR != NULL)
  TIM5_CAP_ISR.T5_CapISR();
}

实验现象

注意:要将P8 跳帽连接。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值