本章要实现的功能是:
通过 TIM5 的通道 1(PA0)捕获外界输入信号的高电平脉宽,通过 printf 打印
捕获的高电平时间,DS0 指示灯不断闪烁表示系统正常运行。
开发步骤
(1)使能定时器及端口时钟,并设置引脚模式等 因为输入捕获也是通用定时器的一个功能,所以需要使能相应定时器时钟。 由于输入捕获通道是对应着 STM32F1 芯片的 IO 口,所以需要使能对应的端口时 钟,并将对应 IO 口设置为输入模式功能。例如本章输入捕获实验,我们是使用 TIM5 的 CH1 通道来测量输入信号的脉宽,因此需要使能 TIM5 时钟,调用的库函 数如下: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能 TIM5 时 钟 而 TIM5 的 CH1 通道对应的管脚是 PA0,在 STM32F1 芯片数据手册引脚功能上 都有介绍如下: 所以需要使能 GPIOA 端口时钟。因为我们使用 PA0 来测量输入信号的高电平 时间,因此需要将 PA0 配置为下拉输入模式。GPIO 初始化如下: 1. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//管脚设置 2. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//设置下拉输入模式 3. GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化结构体 (2)初始化定时器参数,包含自动重装值,分频系数,计数方式等 要使用定时器功能,必须对定时器内相关参数初始化,其库函数如下: voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); 这个在定时器中断章节就已经介绍。 (3)设置通用定时器的输入捕获参数,开启输入捕获功能 初始化定时器后,需要设置对应输入捕获通道参数,比如输入通道、滤波、 分频系数、映射关系、捕获极性等。输入捕获参数初始化函数如下: void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); 函数的第一个参数相信大家一看就清楚,是用来选择定时器的。第二个参数 是一个结构体指针变量,同样我们看下这个结构体 TIM_ICInitTypeDef 成员变 量: 1. typedef struct 2. { 3. uint16_t TIM_Channel; //通道 4. uint16_t TIM_ICPolarity; //捕获极性 5. uint16_t TIM_ICSelection;//映射 6. uint16_t TIM_ICPrescaler;//分频系数 7. uint16_t TIM_ICFilter; //滤波器长度 8. } TIM_ICInitTypeDef; TIM_Channel:输入捕获通道设置,通用定时器每个多达 4 个通道,假如我 们使用 TIM5 的通道 1,参数为 TIM_Channel_1。 TIM_ICPolarity:输入信号的有效捕获极性设置,假如我们需要对输入信号 上升沿开始捕获,参数为 TIM_ICPolarity_Rising,如果是下降沿捕获,参数为 TIM_ICPolarity_Rising。库函数内还提供了单独设置通道捕获极性的函数,假 如要修改 TIM5 的通道 1 捕获极性为下降沿,函数如下: TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); 此函数可以理解为通用函数 TIM_OCxPolarityConfig(),函数中的 x 表示通 道,所以如果是对通道 2 捕获极性操作,可以调用 TIM_OC2PolarityConfig 函数。 TIM_ICSelection:映射设置,在“定时器中断实验”章节中,我们分析了 通用定时器结构框图,知道 ICx 可以映射到 2 个 TIx 上,比如 IC1 可以直接映射 到 TI1 上,也可以间接映射到 TI2 上,但是不能映射到 TI3 和 TI4 上。假如我们 直接映射在 TI1 上,参数为 TIM_ICSelection_DirectTI。 TIM_ICPrescaler:分频系数设置,分频系数可以为 TIM_ICPSC_DIV1、 TIM_ICPSC_DIV2、TIM_ICPSC_DIV4、TIM_ICPSC_DIV8,假如我们不分频,参数为 TIM_ICPSC_DIV1。 TIM_ICFilter:滤波长度设置,假如我们不使用滤波器,参数为 0。 如果我们需要配置 TIM5 的通道 1 为输入捕获功能,并且为上升沿捕获、不 分频、直接映射到 TI,可以如下配置: 1. TIM_ICInitTypeDef TIM_ICInitStructure; 2. TIM_ICInitStructure.TIM_Channel=TIM_Channel_1; //通道 1 3. TIM_ICInitStructure.TIM_ICFilter=0x00; //滤波 4. TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//捕获极性 5. TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //分频系数 6. TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//直接映射到 TI1 7. TIM_ICInit(TIM5,&TIM_ICInitStructure); (4)开启捕获和定时器溢出(更新)中断 假如我们需要检测输入信号的高电平脉宽,就需要在第一次上升沿到来时捕 获一次,然后设置为下降沿捕获,等到下降沿到来时又捕获一次。如果输入信号 的高电平脉宽比较长,那么定时器就可能溢出,所以需要对定时器溢出进行处理, 否则计算的高电平时间将不准。所以需要开启定时器溢出中断。 对定时器中断类型和使能设置的函数如下: void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); 假如我们要开启 TIM5 的捕获中断和定时器溢出中断,调用函数如下: TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE); (5)设置定时器中断优先级,使能定时器中断通道 在上一步我们已经使能了定时器的捕获和更新中断,只要使用到中断,就必 需对 NVIC 初始化,NVIC 初始化库函数是 NVIC_Init(),这个在前面讲解 STM32 中断时就已经介绍过,不清楚的可以回过头看下。 (6)编写定时器中断服务函数 最后我们还需要编写一个定时器中断服务函数,通过中断函数处理定时器产 生的相关中断。定时器中断服务函数名在 STM32F1 启动文件内就有,TIM5 中断 函数名如下: TIM5_IRQHandler 因为定时器的中断类型有很多,所以进入中断后,我们需要在中断服务函数 开头处判断是哪种类型,根据不同中断类型完成相应功能,比如我们需要在捕获 中断内完成捕获成功记录,更新中断内完成溢出次数的累计。在中断结束时要清 除中断标志,这些在前面定时器中断实验中都有介绍。 库函数中用来读取定时器中断状态标志位的函数如下: ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT); 清除中断标志位的函数如下: void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT); 当进入第一次捕获中断,我们会先清空计数器内值,例如对 TIM5 计数器值 清空,函数如下: TIM_SetCounter(TIM5,0); //定时器初值为 0 (7)使能定时器 前面几个步骤已经将定时器输入捕获配置好,但还不能正常使用,只有开启 定时器了才能让它正常工作,开启定时器的库函数如下: void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); 例如我们要开启 TIM5,那么调用此函数如下: TIM_Cmd(TIM5,ENABLE); //开启定时器 将以上几步全部配置好后,我们就可以正常使用定时器输入捕获功能了
input.h+input.c
#ifndef _input_H
#define _input_H
#include "system.h"
extern u8 TIM5_CH1_CAPTURE_STA;//输入捕获状态
extern u16 TIM5_CH1_CAPTURE_VAL;//输入捕获值
void TIM5_CH1_Input_Init(u16 arr, u16 psc);
#endif
#include "input.h"
void TIM5_CH1_Input_Init(u16 arr, u16 psc)//自动重装载值,预分频值
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period=arr;//自动重装载值(与预分频值决定计时时间)
TIM_TimeBaseInitStructure.TIM_Prescaler=psc;//预分频值
//72000k/预分频值+1=Nk,1k=0.5ms,Nk=1/N ms,再乘周期值等于计数值
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频因子
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数模式
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure );
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占式优先级,按自己需求配置
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//响应式优先级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//通道1
TIM_ICInitStructure.TIM_ICFilter=0x00;//ICFILTER <= 0xF//滤波
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);
TIM_ITConfig(TIM5, TIM_IT_Update | TIM_IT_CC1, ENABLE);//两个中断,溢出和捕获中断
TIM_Cmd( TIM5, ENABLE);
}
u8 TIM5_CH1_CAPTURE_STA;//输入捕获状态,是个8位变量,主要用到最高位和次高位,后六位表示溢出次数
//最高位用来判断是否成功捕获1,次高位用来判断是上升沿1还是下降沿0
u16 TIM5_CH1_CAPTURE_VAL;//输入捕获值
void TIM5_IRQHandler()
{
if((TIM5_CH1_CAPTURE_STA&0x80)==0)//限制捕获一次,如果要继续捕获,在主函数中清零标志位
{
//产生溢出中断
if(TIM_GetITStatus( TIM5, TIM_IT_Update))
{
if(TIM5_CH1_CAPTURE_STA&0x40)//捕获到上升沿
{
if((TIM5_CH1_CAPTURE_STA&0x3f)==0x3f)//与上后六位,表示溢出次数超限
{
TIM5_CH1_CAPTURE_STA |= 0x80;//表示强制成功捕获
TIM5_CH1_CAPTURE_VAL=0xffff;//表示一个错误值,因为没有捕获下降沿
}
else
{
TIM5_CH1_CAPTURE_STA++;
}
}
}
//产生捕获中断
if(TIM_GetITStatus( TIM5, TIM_IT_CC1))
{
if(TIM5_CH1_CAPTURE_STA&0x40)
{
TIM5_CH1_CAPTURE_STA |= 0x80;//表示成功捕获一次
TIM5_CH1_CAPTURE_VAL =TIM_GetCapture1( TIM5);//上升沿到下降沿的时间,不包括n次溢出时间
TIM_OC1PolarityConfig( TIM5, TIM_ICPolarity_Rising);//上升沿极性,为了循环使用
}
else
{
//解释:产生了捕获中断,然后先进入else语句,表明产生了上升沿捕获
//然后开始计数,然后再进行if语句,捕获下降沿
TIM5_CH1_CAPTURE_STA=0;
TIM5_CH1_CAPTURE_VAL=0;//捕获值清零,准备计数
TIM5_CH1_CAPTURE_STA |=0x40;
TIM_Cmd( TIM5, DISABLE);
TIM_SetCounter( TIM5, 0);
TIM_OC1PolarityConfig( TIM5, TIM_ICPolarity_Falling);//下降沿极性
TIM_Cmd( TIM5, ENABLE);
}
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1 | TIM_IT_Update);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "system.h"
#include "SysTick.h"
#include "beep.h"
#include "key.h"
#include "exti.h"
#include "time.h"
#include "pwm.h"
#include "usart.h"
#include "stdio.h"
#include "iwdg.h"
#include "wwdg.h"
#include "input.h"
int main()
{
u8 i=0;
u32 indata=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组
SysTick_Init(72);
LED_Init();
USART1_Init(115200);//波特率115200
TIM5_CH1_Input_Init(0xffff, 72-1);//计数一次为1us
while(1)
{
if(TIM5_CH1_CAPTURE_STA&0x80)
{
indata=TIM5_CH1_CAPTURE_STA&0x3f;
indata *=0xffff;//溢出次数乘溢出一次的数值得到总溢出数
indata+=TIM5_CH1_CAPTURE_VAL;
printf("高电平持续时间: %d us\r\n",indata);
TIM5_CH1_CAPTURE_STA=0;
}
i++;
if(i%20==0)LED1=!LED1;
delay_ms(10);
}
}