目录
STM32输入捕获工作过程
输入捕获滤波器
设置输入捕获极性
设置捕获映射通道
设置输入捕获分频器
捕获到有效信号可以开启中断
频率计算:
输入捕获结构体详解
输入捕获相关函数
输入捕获的一般配置步骤:
实验代码:
STM32输入捕获工作过程(通道1为例)
一句话总结工作过程:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx里面),完成一次捕获。
输入捕获1滤波器ICIF[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,fck_INT是定时器的输入频率(TIMxCLK),一般为72Mhz,而fDTS则是根据TIMx_CRI的CKD[1:0]的设置来确定的,如果CKD[1:0]设置为00,那么fors=fck_INT。N值就是滤波长度。
如果CC1S设置为00通道1就被设置成输出.01就是CC1通道被配置位输入,映射到TI1上。
一般情况下我们都是通道1就是映射到IC1上,通道2就是映射到IC2上。
简单说明:STM32的输入捕获,简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。先设置输入捕获为上升沿检测,记录发生上升沿的时候 TIMx_CNT 的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的 TIMx_CNT 值。这样,前后两次 TIMx_CNT 之差,就是高电平的脉宽,同时 TIMx 的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。
将捕获的模式设置为下降沿/上升沿捕获,当第一次捕获到上升沿/下降沿,则会触发中断,在中断中,把当前的计数值记录下来Value1;第二次捕获到上升沿/下降沿,再一次触发中断,在中断中我们把当前的计数值记录下来Value2。所以第二次捕获和第一次捕获之间的时间就是波的周期了。假设定时器的时钟被设置为72MHz,那么得到的波的频率应该为72MHz/(Value2 - Value1)。
typedef struct
{
uint16_t TIM_Channel; //捕获通道1-4
uint16_t TIM_ICPolarity; //捕获极性
uint16_t TIM_ICSelection; //映射关系
uint16_t TIM_ICPrescaler; //分频系数
uint16_t TIM_ICFilter; //滤波器
}TIM_ICInitTypeDef;
输入捕获通道初始化函数
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
通道极性设置独立函数
void TIM_OCxPofarityConfig(TIM_TypeDef* TIMx, uint16_1 TIM_OCPolarity);
获取通道捕获值
uint32_t TIM GetCapture 1(TIM_ TypeDef* TIMx):
1.初始化定时器和通道对应IO的时钟。
2.初始化IO口,模式位输入:
GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
3.初始化定时器ARR,PSC
TIM_TimeBaselnit();
4.初始化输入捕获通道
TIM ICInit();
5.如果要开启捕获中断
TIM_ITConfig();
NVIC Init();
6.使能定时器
TIM_Cmd();
7.编写中断服务函数
TIMx_IRQHandler();
检测GPIOA.0引脚的高电平时间
/**
******************************************************************************
* @file : user_rcc_config.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_rcc_config.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief 定时器初始化
\param[in] none
\param[in] none
\retval none
*/
void Rcc_config(void)
{
/*使能GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*使能UART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/*使能定时器5时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_gpio.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_gpio.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief GPIO初始化函数
\param[in] none
\param[out] none
\retval none
*/
void Gpio_Init(void)
{
/*GPIO结构体*/
GPIO_InitTypeDef GPIO_InitTypeDefstruct;
/*UART1发送引脚配置*/
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_AF_PP;//推挽复用输出
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
/*写入结构体到GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
/*UART1接收引脚配置*/
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
/*写入结构体到GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
/*初始化PA0*/
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IPD;//下拉输入
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_0;
/*写入结构体到GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
/*设置为下拉输入*/
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_uart.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
extern uint16_t USART_RX_STA;
extern uint8_t USART_RX_BUF[200];
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
#if 1
#pragma import(__use_no_semihosting)
/*实现Printf代码*/
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
/*!
\brief UART1初始化
\param[in] none
\param[out] none
\retval none
*/
void Uart1_Init(u32 bound)
{
/*UART结构体*/
USART_InitTypeDef USART_InitTypeDefstruct;
/*UART结构体配置*/
USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
/*写入USART1*/
USART_Init(USART1,&USART_InitTypeDefstruct);
/*使能串口1*/
USART_Cmd(USART1,ENABLE);
}
/*!
\brief UART1中断服务函数
\param[in] none
\param[out] none
\retval none
*/
void USART1_IRQHandler(void)
{
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_timer.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_timer.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/*
因为捕获的高电平不知道是多久
有可能超过了定时器的时间还是
高电平所以弄了一个字节来记录
捕获完成、捕获到高电平、和定时器溢出次数
这样可以算出我们捕获的时间是多少
TIM5CH1_CAPTURE_STA
bit7 捕获完成标志
bit6 捕获到高电平标志
bit5~0 捕获高电平定时器溢出的次数
*/
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM5CH1_CAPTURE_VAL; //输入捕获值
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief 定时器初始化
\param[in] 装载值
\param[in] 分频系数
\retval none
*/
void Tim5_Input_Init(uint16_t arr, uint16_t psc)
{
/*定时器结构体*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeDefstruct;
/*定时器5输入捕获结构体*/
TIM_ICInitTypeDef TIM5_ICInitStructure;
/*中断控制器结构体*/
NVIC_InitTypeDef NVIC_InitStructure;
/*定时器初始化配置*/
TIM_TimeBaseInitTypeDefstruct.TIM_Period = arr; //自动装载值
TIM_TimeBaseInitTypeDefstruct.TIM_Prescaler = psc; //分频系数
TIM_TimeBaseInitTypeDefstruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitTypeDefstruct.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
/*写入定时器5*/
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitTypeDefstruct);
/*初始化定时器5输入捕获参数*/
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//配置输入分频,不分频就是有上升沿捕获,如果是分频2的话就是2次上升沿才开始捕获
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波,不采集
/*写入定时器5输入捕获通道里面*/
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
/*中断分组初始化*/
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先0级
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE; //使能输入捕获
/*写入中断控制器中*/
NVIC_Init(&NVIC_InitStructure);
/*使能更新、允许CCC1IE捕获中断*/
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
/*使能定时器*/
TIM_Cmd(TIM5,ENABLE);
}
/*!
\brief TIM5_IRQHandler
\param[in] none
\param[in] none
\retval none
*/
void TIM5_IRQHandler(void)
{
/*判断是不是捕获到了一个高电平,是0的时候我们才开始有效计数,否则退出中断*/
if((TIM5CH1_CAPTURE_STA & 0x80) == 0) //还未捕获完成
{ /*判断是不是定时器到*/
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{ /*已经捕获到高电平了*/
if(TIM5CH1_CAPTURE_STA&0X40)
{ /*高电平太长了,计数次数已经满了,直接强制标志成*/
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)
{
TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}
else
{ /*计数器更新事件加1,用于计算高电平时间
TIM5CH1_CAPTURE_STA*定时器设定+TIM5CH1_CAPTURE_VAL =高平时间
*/
TIM5CH1_CAPTURE_STA++;
}
}
}
}
/*判断是不是捕获发生*/
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
{ /*判断上一次是不是捕获到了高电平,如果上一次
是高电平则这次就是捕获到了低电平
*/
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); //把捕获比较寄存器的值保存起来用于计算时间
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}else
{
/*捕获到高电平*/
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM5,0); //设置定时器计数为0开始记录高电平时间
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
}
}
/*清除中断标志位*/
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "user_timer.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
//最多一次接收200个字节
uint8_t USART_RX_BUF[200];
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
uint16_t USART_RX_STA=0; //接收状态标记
extern u8 TIM5CH1_CAPTURE_STA; //输入捕获状态
extern u16 TIM5CH1_CAPTURE_VAL; //输入捕获值
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
int main(void)
{
u32 temp=0;
/*配置系统中断分组为2位抢占2位响应*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*定时器5初始化*/
Tim5_Input_Init(0xFFFF,72-1); //以1Mhz的频率计数 计数器从0计数到FFFF才会溢出 一个时钟周期就是1us
/*死循环*/
while(1){
delay_ms(10);
/*成功捕获到了完成*/
if(TIM5CH1_CAPTURE_STA&0X80)
{ /*取溢出多少次*/
temp=TIM5CH1_CAPTURE_STA&0X3F;
/*因为每次计数器时间到都是FFFF换算成10进制就是65535*/
temp*=65536;//溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL;//最后一次捕获到的+溢出时间总和 = 得到总的高电平时间
printf("HIGH:%d ms\r\n",temp/1000);//打印总的高点平时间
TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
}
}
}
/************************************************************** END OF FILE ****/