由于功能添加且板子已经定型的原因,在原来没有定时器输入捕获的引脚上添加频率检测,便使用了STM32的外部中断与定时器的配合来做简单测量。
1、初始化引脚的外部中断,我这里用了PA11和PA12两个引脚,void EXTI_PA11_PA12_Config(void),需要注意的是频率检测只使用上升沿触发即可,如果测量占空比,则采用双边沿检测;
2、初始化两个定时器,TIM4和TIM6, GENERAL_TIM4_Mode_Config(); BASIC_TIM6_Mode_Config();
这里需要注意的是:
(1)定时器的最小采样时间是根据时钟的分频系数相关的,比如时钟现在是72000000HZ,定时器分频系数是72-1,则分频后的定时器时钟为1000000HZ(即1us);
(2)最大采样间隔(即超过这个时间还没有采样到一个脉冲)是跟定时器的中断间隔相关的,定时器产生溢出中断后计数值CNT会自动清0,定时器的中断间隔由分频系数Prescaler和自动重装载寄存器Period决定,分频系数前面已经确定,那最大采样间隔只需要考虑自动重装载寄存器Period的设置,比如频分析系数72-1,自动重装寄存器值10000-1,则 中断间隔=72000000/72/10000=100HZ,即最大采样间隔10ms,如果10ms内没有检测到一个脉冲,则这么设定间隔是不合理的,必须想办法牺牲最小的采样时间1us(扩大分频系数)或者扩大自动重装寄存器值(16位,<65535)来增加定时器中断间隔,也可以编写自己的应用函数来计算溢出的定时时间。
3.编写外部中断函数void EXTI15_10_IRQHandler(void);
来一次脉冲上升沿则会触发一次外部中断,定时器的时钟一直运行着,CNT计数值++,计算两次的CNT差值然后根据定时器时钟即可算出频率;
4.编写定时器中断函数,这部分主要是应用,根据项目的要求,在没有接收到脉冲后1s需要清零计数值,这里清零的地方正好放在定时器中断中处理。
/**
******************************************************************************
* @file TimeInput.c
* @author davidysw
* @version V1.0
* @date 2019-12-xx
* @brief 定时器输入捕获
* @brief
******************************************************************************
**/
#include "TimeInput.h"
float FrequencyPA11; //输入脉冲的频率HZ
float FrequencyPA12; //输入脉冲的频率HZ
uint16_t PA11_Counter1=0;
uint16_t PA12_Counter1=0;
uint16_t ClearPA11Count=0;//用来计数,2s内不在有脉冲进入则清零 测速值
uint16_t ClearPA12Count=0;//用来计数,2s内不在有脉冲进入则清零 测速值
/***************************************************
//中断优先级配置
*****************************************************/
static void GENERAL_TIM4_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM4_IRQ ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
// 设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/***************************************************
//定时器配置
*****************************************************/
static void GENERAL_TIM4_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
GENERAL_TIM4_NVIC_Config();
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM4_APBxClock_FUN(GENERAL_TIM4_CLK, ENABLE);
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM4_Period;
// 时钟预分频数
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM4_Prescaler;
// 时钟分频因子 ,没用到不用管
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM4, &TIM_TimeBaseStructure);
// 清除计数器中断标志位
TIM_ClearFlag(GENERAL_TIM4, TIM_FLAG_Update);
// 开启计数器中断
TIM_ITConfig(GENERAL_TIM4,TIM_IT_Update,ENABLE);
// 使能计数器
TIM_Cmd(GENERAL_TIM4, ENABLE);
}
/***************************************************
//中断优先级配置
*****************************************************/
static void BASIC_TIM6_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM6_IRQ ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/***************************************************
//定时器配置
*****************************************************/
static void BASIC_TIM6_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
BASIC_TIM6_NVIC_Config();
// 开启定时器时钟,即内部时钟CK_INT=72M
BASIC_TIM6_APBxClock_FUN(BASIC_TIM6_CLK, ENABLE);
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period = BASIC_TIM6_Period;
// 时钟预分频数为
TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM6_Prescaler;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(BASIC_TIM6, &TIM_TimeBaseStructure);
// 清除计数器中断标志位
TIM_ClearFlag(BASIC_TIM6, TIM_FLAG_Update);
// 开启计数器中断
TIM_ITConfig(BASIC_TIM6,TIM_IT_Update,ENABLE);
// 使能计数器
TIM_Cmd(BASIC_TIM6, ENABLE);
}
/***************************************************
//引脚输入初始化
*****************************************************/
void GPIO_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置NVIC为优先级组1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* 配置中断源:PA11、PA12 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
/* 配置抢占优先级 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 配置 IO为EXTI中断口,并设置中断优先级
* @param 无
* @retval 无
*/
void EXTI_PA11_PA12_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/*开启GPIO口的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
/* 配置 NVIC 中断*/
GPIO_NVIC_Configuration();
/*--------------------------配置-----------------------------*/
/* 选择按键用到的GPIO */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
/* 配置为浮空输入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 选择EXTI的信号源 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource11);
EXTI_InitStructure.EXTI_Line = EXTI_Line11;
/* EXTI为中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*--------------------------配置-----------------------------*/
/* 选择按键用到的GPIO */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
/* 配置为浮空输入 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 选择EXTI的信号源 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource12);
EXTI_InitStructure.EXTI_Line = EXTI_Line12;
/* EXTI为中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 下降沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
/***************************************************
//定时器初始化,定时10us
*****************************************************/
void TIME_INPUT_Init(void)
{
GENERAL_TIM4_Mode_Config();
BASIC_TIM6_Mode_Config();
EXTI_PA11_PA12_Config();
}
/***************************************************
//PA11、PA12外部中断,上升沿检测,计数脉冲精度1us,与定时器分频有关,最大脉冲间隔与定时器计数值有关,10000即10ms
*****************************************************/
void EXTI15_10_IRQHandler(void)
{
//确保是否产生了EXTI Line中断
if(EXTI_GetITStatus(EXTI_Line11) != RESET)
{
FrequencyPA11=1000000/(TIM4->CNT-PA11_Counter1);
TIM4->CNT=0;
PA11_Counter1=TIM4->CNT;
ClearPA11Count=0;
EXTI_ClearITPendingBit(EXTI_Line11); //清除中断标志位
}
if(EXTI_GetITStatus(EXTI_Line12) != RESET)
{
FrequencyPA12=1000000/(TIM6->CNT-PA12_Counter1);
TIM6->CNT=0;
PA12_Counter1=TIM6->CNT;
ClearPA12Count=0;
EXTI_ClearITPendingBit(EXTI_Line12);//清除中断标志位
}
}
/***************************************************
//定时器中断
*****************************************************/
void TIM4_IRQHandler (void)
{
if ( TIM_GetITStatus( GENERAL_TIM4, TIM_IT_Update) != RESET )
{
ClearPA11Count++;
if(ClearPA11Count>100)//1s
{
ClearPA11Count=0;
PA11_Counter1=0;
FrequencyPA11=0;
}
TIM_ClearITPendingBit(GENERAL_TIM4 , TIM_FLAG_Update);
}
}
void TIM6_IRQHandler (void)
{
if ( TIM_GetITStatus( BASIC_TIM6, TIM_IT_Update) != RESET )
{
ClearPA12Count++;
if(ClearPA12Count>100) //1s
{
ClearPA12Count=0;
PA12_Counter1=0;
FrequencyPA12=0;
}
TIM_ClearITPendingBit(BASIC_TIM6 , TIM_FLAG_Update);
}
}
#ifndef _TIMEINPUT_H
#define _TIMEINPUT_H
#include "stm32f10x.h"
#define GENERAL_TIM4 TIM4
#define GENERAL_TIM4_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM4_CLK RCC_APB1Periph_TIM4
#define GENERAL_TIM4_Period (10000-1)
#define GENERAL_TIM4_Prescaler 72-1
#define GENERAL_TIM4_IRQ TIM4_IRQn
#define GENERAL_TIM4_IRQHandler TIM4_IRQHandler
#define BASIC_TIM6 TIM6
#define BASIC_TIM6_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BASIC_TIM6_CLK RCC_APB1Periph_TIM6
#define BASIC_TIM6_Period 10000-1
#define BASIC_TIM6_Prescaler 71
#define BASIC_TIM6_IRQ TIM6_IRQn
#define BASIC_TIM6_IRQHandler TIM6_IRQHandler
extern void TIME_INPUT_Init(void);
#endif