通用定时器-输入捕获实验

知识回顾

  前置技能 : 通用定时器原理

 我们将通用定时器分为四个部分:
      1,选择时钟
      2,时基电路
      3,输入捕获
      4,输出比较

本节定时器PWM输出主要涉及到定时器框图左下方部分,即输入捕获部分

通用定时器-输入捕获实验_第1张图片


输入捕获工作过程(以通道1为例)

一个通道的框图:

通用定时器-输入捕获实验_第2张图片

作用:捕获TIMx_CH1通道信号的跳变,并触发捕获中断


我们将此过程拆分为4个部分

1,设置输入捕获滤波器

通用定时器-输入捕获实验_第3张图片

fDTS是根据TIMx_CR1-CKD[1:0]的设置确定的

通用定时器-输入捕获实验_第4张图片

如果TIMx_CR1-CKD[1:0]设置为00,则fTDS=fCK_INT = 72Mhz
如果TIMx_CR1-CKD[1:0]设置为10,则fTDS=fCK_INT / 4 = 18Mhz

TIMx_CCMR1-ICF[3:0]输入捕获1滤波器,设置输入采样频率和数字滤波长度

通用定时器-输入捕获实验_第5张图片

如果TIMx_CCMR1-IC1F[3:0]=0011,采样频率fSAMPLING=fCK_INT , 数字滤波长度为8

举例:
    如果IC1映射到通道1,且设置为上升沿捕获
    当出现上升沿后,以fCK_INT频率连续采集8次通道1的电平,如果都是高电平,说明是一次有效触发
    如果此时开启了输入捕获中断,会进入输入捕获中断
    这样会过滤掉小于8个采样周期的脉冲信号,达到滤波目的

2,设置输入捕获极性(上升沿还是下降沿捕获)

通用定时器-输入捕获实验_第6张图片

经过滤波器的信号(步骤1),再进入边沿检测器,通过TIMx_CCER-CC1P设置上升沿检测还是下降沿检测

通用定时器-输入捕获实验_第7张图片

TIMx_CCER-CC1P[0]设置边沿检测器上升沿检测还是下降沿检测
    0:上升沿捕获
    1:下降沿捕获

3,设置输入捕获映射通道(通道1为例)

通用定时器-输入捕获实验_第8张图片

如图,已将干扰部分隐去
通过设置TIMx_CCMR1-CC1S[1:0],设置通道方向(输入/输出),及输入脚的选择

通用定时器-输入捕获实验_第9张图片

 当CC1S=00时,为输出比较
 当CC1S=01时,为输入捕获,IC1映射在TI1上
 当CC1S=10时,为输入捕获,IC1映射在TI2上

解释一下输入脚:

 回到定时器框图,本节输入捕获部分

通用定时器-输入捕获实验_第10张图片

如图:
    以IC1为例,是可以映射到TI1或TI2的
    TI1捕获的信号可以连到IC1也可以连接到IC2
    也就是TI1和TI2的IC1和IC2是可以彼此交叉的

4,设置捕获分频器(通道1为例)

通用定时器-输入捕获实验_第11张图片

通过设置TIMx_CCMR1-ICPS[1:0]位,设置捕获分频系数,达到分频目的

通用定时器-输入捕获实验_第12张图片

例如:
当设置为上升沿触发,TIMx_CCMR1-ICPS[1:0]=01时,每两个上升沿捕获一次,相当于2分频

5,捕获到有效信号开启中断

通用定时器-输入捕获实验_第13张图片

例如:
    配置TIMx_DIER-CC1IE[1]=1,开启捕获比较通道1中断
    若设置为上升沿捕获,当捕获到上升沿时,会触发捕获/比较中断

定时器通道对应引脚(以TIM5为例)

通用定时器-输入捕获实验_第14张图片


输入捕获相关库函数

// 1,输入捕获通道初始化函数
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

// TIM_ICInitTypeDef结构体
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;

// 2,通道极性设置函数
void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

// 3,获取通道捕获值
//     每触发一次捕获,都会将计数器值CNT加载到捕获比较寄存器中,可以读取
uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx);

输入捕获的配置步骤

// 1,初始化定时器和通道对应IO的时钟。

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// 2,初始化IO口,模式为输入:GPIO_Init();

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入

// 3,初始化定时器,设置自动装在职ARR,预分频系数PSC,确定每一个时钟周期时间

    TIM_TimeBaseInit();

// 4,初始化输入捕获通道,设置哪个沿捕获,是否分频等

    TIM_ICInit();

// 5,设置开启捕获中断

    TIM_ITConfig();
    NVIC_Init();(需在主函数配置中断优先级分钟组)

// 6,使能定时器:

    TIM_Cmd();

// 7,编写中断服务函数:

    TIMx_IRQHandler();

输入捕获实验-测量信号脉冲宽度

PA0引脚有一个Weak_UP按键,按下为高电平,抬起为低电平,请参考 : 按键输入-GPIO输入

使用TIM5_CH1捕获PA0高电平持续时间

通用定时器-输入捕获实验_第15张图片

方法:
    先设置为上升沿捕获,捕获后将定时器值设置为0
    然后设置为下降沿捕获,捕获到下降沿时,将计数器值记录下来
    上升沿持续时间 = ( A-0 ) *  1 / f

代码实现

timer.h

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM5_Cap_Init(u16 arr,u16 psc);

#endif

timer.c

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

// 定时器5通道1输入捕获配置
TIM_ICInitTypeDef TIM5_ICInitStructure;

// arr 重装载值
// psc 预分频系数
void TIM5_Cap_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);    // 使能定时器5时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   // 使能GPIOA时钟

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;   // PA0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;// PA0 输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOA,GPIO_Pin_0);            // PA0 下拉(默认低电平,按下为高电平)

    //初始化 TIM5
    TIM_TimeBaseStructure.TIM_Period = arr;         // 重装载值
    TIM_TimeBaseStructure.TIM_Prescaler =psc;       // 预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    // 时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;// 向上计数模式
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

    //初始化ITM5输入捕获参数
    TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; // CC1S=01 选择输入端 IC1映射到TI1上
    TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
    TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;// 映射到TI1
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 配置输入分频-不分频
    TIM5_ICInitStructure.TIM_ICFilter = 0x00;// IC1F=0000 配置输入滤波器-不滤波
    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;     // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);

    TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE); // 使能更新中断,使能CC1IE捕获中断

    TIM_Cmd(TIM5,ENABLE );     // 使能定时器5

}

u8  TIM5CH1_CAPTURE_STA=0;     // 输入捕获状态
u16 TIM5CH1_CAPTURE_VAL;       // 记录输入比较值

// 定时器5中断服务函数
void TIM5_IRQHandler(void)
{

     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) // 捕获的时间超长了(2的6次方减1)
                {
                    TIM5CH1_CAPTURE_STA|=0X80;  // 标记成功捕获
                    TIM5CH1_CAPTURE_VAL=0XFFFF; // 赋一个最大值
                }else TIM5CH1_CAPTURE_STA++;    // 溢出次数+1
            }
        }
    if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)  // 捕获中断
        {
            if(TIM5CH1_CAPTURE_STA&0X40)  // STA位6是否为1,若为1说明前一次为上升沿,本次为下降沿
            {
                TIM5CH1_CAPTURE_STA|=0X80;        //本次捕获完成,标记捕获完成
                TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); //将捕获比较寄存器的值记录到Val
                TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获,为下一次捕获做准备
            }else   // STA位6不为1,说明上一次未捕获上升沿,本次为上升沿,开始一次计数
            {
                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); // 清除中断标志位

}

main.c

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

extern u8  TIM5CH1_CAPTURE_STA;
extern u16 TIM5CH1_CAPTURE_VAL;
 int main(void)
 {
     u32 temp=0;
     delay_init();        // 延时函数初始化
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组
     LED_Init();          // LED初始化
     uart_init(115200);   // 配置串口 115200
     TIM3_PWM_Init(899,0);       // PWM频率=72000/(899+1)=80Khz
     TIM5_Cap_Init(0XFFFF,72-1); // TIM5为16为定时器,ARR最大0xFFFF,预分频系数72-1,通用定时器时钟72M,一个计数器周期为1ms
     while(1)
    {
        delay_ms(10);
        TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);

        if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);

        if(TIM5CH1_CAPTURE_STA&0X80)// 检测最高位是否为1,如果是1说明已经捕获到了上升沿
        {
            temp=TIM5CH1_CAPTURE_STA&0X3F;// 取低六位,看计数器溢出了多少次
            temp*=65536;// 到0xFFFF才溢出,所以事件总和(微秒) = 溢出次数 * 2的16次方(65536)
            temp+=TIM5CH1_CAPTURE_VAL;// 再加上没有溢出的这段计时,得到总时间(微秒)

            printf("HIGH:%d us\r\n",temp);// 打印总时间
            TIM5CH1_CAPTURE_STA=0;// 开启下一次捕获:捕获中断函数才会进入判断
        }
    }
 }

实验结果

使用串口调试助手XCOM,输出按键按下时间,以微秒为单位

你可能感兴趣的:(STM32,STM32学习笔记)