第29章 通用定时器-测量脉冲宽度

第二十九章 通用定时器-测量脉冲宽度

1. 硬件设计

无需硬件设计

2. 软件设计

  • 定时器相关参数宏定义
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 我们这里默认使用TIM5
#define GENERAL_TIM                TIM5                   // 通用定时器选择-TIM5
#define GENERAL_TIM_APBxClock_FUN  RCC_APB1PeriphClockCmd // 使能时钟
#define GENERAL_TIM_CLK            RCC_APB1Periph_TIM5    // 时钟选择
#define GENERAL_TIM_PERIOD         0XFFFF                 // 自动重装载值
#define GENERAL_TIM_PSC            (72-1)                 // 时钟预分频系数

// TIM 输入捕获通道GPIO相关宏定义
#define GENERAL_TIM_CH1_GPIO_CLK   RCC_APB2Periph_GPIOA   // 通道1 GPIO时钟
#define GENERAL_TIM_CH1_PORT       GPIOA                  // 通道1 GPIO端口
#define GENERAL_TIM_CH1_PIN        GPIO_Pin_0             // 通道1 GPIO引脚
#define GENERAL_TIM_CHANNEL_x      TIM_Channel_1          // 通道选择

// 中断相关宏定义
#define GENERAL_TIM_IT_CCx         TIM_IT_CC1             // 输入捕获中断
#define GENERAL_TIM_IRQ            TIM5_IRQn              // 通用定时器中断
#define GENERAL_TIM_INT_FUN        TIM5_IRQHandler        // 通用定时器中断服务函数

// 获取捕获寄存器值函数宏定义
#define GENERAL_TIM_GetCapturex_FUN        TIM_GetCapture1
// 捕获信号极性函数宏定义
#define GENERAL_TIM_OCxPolarityConfig_FUN  TIM_OC1PolarityConfig

// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity       TIM_ICPolarity_Rising
// 测量的结束边沿
#define GENERAL_TIM_END_ICPolarity         TIM_ICPolarity_Falling

有四个宏定义,我们是第一次见,需要简单解释一下:

  1. GENERAL_TIM_GetCapturex_FUN:指定用于获取捕获寄存器值的定时器函数。例如,TIM_GetCapture1 用于读取定时器 TIM 的捕获寄存器 1 的值。这个宏定义可以根据不同的定时器配置,方便地替换不同的函数调用。

  2. GENERAL_TIM_OCxPolarityConfig_FUN:指定用于配置输出比较通道极性的函数。例如,TIM_OC1PolarityConfig 用于设置定时器 TIM 的通道 1 的输出比较极性。这个宏定义使得在不同的定时器和通道配置下,可以统一修改极性配置的函数。

  3. GENERAL_TIM_STRAT_ICPolarity:定义测量信号的起始边沿。TIM_ICPolarity_Rising 表示测量开始于上升沿。这个宏用于配置输入捕获信号的初始边沿,以进行测量或时间间隔计算。

  4. GENERAL_TIM_END_ICPolarity:定义测量信号的结束边沿。TIM_ICPolarity_Falling 表示测量结束于下降沿。这个宏用于配置输入捕获信号的结束边沿。

  • 中断优先级配置
// 中断优先级配置
static void GENERAL_TIM_NVIC_Config(void)
{
    // 1.定义一个NVIC_InitTypeDef类型的结构体变量,用于配置NVIC
    NVIC_InitTypeDef NVIC_InitStructure; 
    // 2.设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);        
    // 3.设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ ;    
    // 4.设置主优先级为 0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;     
    // 5.设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    // 6.使能中断    
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); // 7.根据配置结构体初始化NVIC
}
  • 输入捕获通道 GPIO配置
// 输入捕获通道 GPIO 配置
static void GENERAL_TIM_GPIO_Config(void) 
{
    // 输入捕获通道 GPIO 端口初始化
    GPIO_InitTypeDef GPIO_InitStructure;

    // 输入捕获通道 GPIO 初始化
    RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE); // 使能GPIO时钟
    GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH1_PIN;       // 选择GPIO引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;     // 设置为浮空输入模式
    GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);     // 初始化GPIO
}
  • 定时器模式配置
// 定时器模式配置
static void GENERAL_TIM_Mode_Config(void)
{
    // 开启定时器时钟,即内部时钟CK_INT=72M
    GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);

/*--------------------时基结构体初始化-------------------------*/    
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Period = GENERAL_TIM_PERIOD;    
    // 驱动CNT计数器的时钟 = Fck_int/(psc+1)
    TIM_TimeBaseStructure.TIM_Prescaler = GENERAL_TIM_PSC;    
    // 时钟分频因子 ,配置死区时间时需要用到
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        
    // 计数器计数模式,设置为向上计数
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        
    // 重复计数器的值,没用到不用管
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;    
    // 初始化定时器
    TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);

    /*--------------------输入捕获结构体初始化-------------------*/    
    TIM_ICInitTypeDef TIM_ICInitStructure;
    // 配置输入捕获的通道,需要根据具体的GPIO来配置
    TIM_ICInitStructure.TIM_Channel = GENERAL_TIM_CHANNEL_x; // 这里我们宏定义的是通道1
    // 输入捕获信号的极性配置
    TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity; // 这里我们宏定义的是输出比较极性
    // 输入通道和捕获通道的映射关系,有直连和非直连两种
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 这里我们选择直连
    // 输入的需要被捕获的信号的分频系数
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    // 输入的需要被捕获的信号的滤波系数
    TIM_ICInitStructure.TIM_ICFilter = 0;
    // 定时器输入捕获初始化
    TIM_ICInit(GENERAL_TIM, &TIM_ICInitStructure);

    // 清除更新和捕获中断标志位
    TIM_ClearFlag(GENERAL_TIM, TIM_FLAG_Update|GENERAL_TIM_IT_CCx);    
    // 开启更新和捕获中断  
    TIM_ITConfig(GENERAL_TIM, TIM_IT_Update | GENERAL_TIM_IT_CCx, ENABLE);
    // 使能计数器
    TIM_Cmd(GENERAL_TIM, ENABLE);
}
  • 主函数
int main(void)
{
    uint32_t time; // 存储高电平脉宽时间

    // TIM 计数器的驱动时钟
    uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);
    // 串口初始化
    USART_Config();
    // 定时器初始化
    GENERAL_TIM_Init();
    printf ( "\r\n野火 STM32 输入捕获实验\r\n" );
    printf ( "\r\n按下K1,测试K1按下的时间\r\n" );

    while(1)
    {
        if(TIM_ICUserValueStructure.Capture_FinishFlag == 1)
        {
            // 计算高电平时间的计数器的值
            time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) + 
                   (TIM_ICUserValueStructure.Capture_CcrValue+1);

            // 打印高电平脉宽时间
            printf ("\r\n测得高电平脉宽时间:%d.%d s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );

            TIM_ICUserValueStructure.Capture_FinishFlag = 0;            
        }        
    }
}

这个实验实际上就是输入捕获实验,使用定时器来测量输入信号的高电平脉宽,并通过串口输出结果,我们重点分析一下主循环:

while(1)
{
    if(TIM_ICUserValueStructure.Capture_FinishFlag == 1)
    {
        // 计算高电平时间的计数器的值
        time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) + 
               (TIM_ICUserValueStructure.Capture_CcrValue+1);

        // 打印高电平脉宽时间
        printf ("\r\n测得高电平脉宽时间:%d.%d s\r\n",time/TIM_PscCLK, time%TIM_PscCLK);

        TIM_ICUserValueStructure.Capture_FinishFlag = 0;            
    }        
}
  • 在主循环中,程序检查 TIM_ICUserValueStructure.Capture_FinishFlag 标志位,判断是否捕获完成。
  • 如果捕获完成,计算高电平脉宽时间。TIM_ICUserValueStructure.Capture_Period 是捕获周期值,GENERAL_TIM_PERIOD 是定时器的计数周期。TIM_ICUserValueStructure.Capture_CcrValue 是捕获值。
  • 计算的时间值 time 是通过周期和捕获值结合起来得出的高电平脉宽时间。
  • 将计算出的时间以秒为单位打印出来,格式为 秒.微秒
  • 重新设置 Capture_FinishFlag 为 0,以便等待下一个捕获事件。

3. 小结

本章实质就是实验通用定时器的输入捕获功能,我们就如何使用输入捕获的基本流程展开讨论:

1. 硬件连接

假设我们将输入信号连接到TIM2的通道1(即TIM2_CH1),通常这个通道是通过PA0引脚连接的。

2. 配置代码

下面是配置TIM2为输入捕获模式的完整代码示例,分为两个主要部分:初始化代码和输入捕获中断处理函数。

#include "stm32f10x.h"

// TIM2 输入捕获初始化函数
void TIM2_InputCapture_Init(void)
{
    TIM_ICInitTypeDef TIM_ICInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能GPIOA和TIM2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    // 使能TIM2的时钟

    // 配置PA0为输入捕获功能
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;                // 选择PA0引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;     // 设置为浮空输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         // 设置GPIO速度为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    // 初始化GPIOA引脚

    // 配置TIM2的基本参数
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;                // 设置计数器周期为0xFFFF
    TIM_TimeBaseStructure.TIM_Prescaler = 0;                   // 不使用预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    // 时钟分频因子为1
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数器向上计数
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);            // 初始化TIM2

    // 配置TIM2输入捕获
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;            // 选择通道1
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 选择上升沿触发
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 选择直接输入模式
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        // 输入分频器设置为1
    TIM_ICInitStructure.TIM_ICFilter = 0;                        // 输入滤波器设置为0
    TIM_ICInit(TIM2, &TIM_ICInitStructure);                      // 初始化TIM2输入捕获

    TIM_Cmd(TIM2, ENABLE); // 启动TIM2计数器

    // 配置TIM2输入捕获中断
    TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能通道1捕获中断
    
    // 配置NVIC中断
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             // 选择TIM2中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   // 设置抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          // 设置子优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              // 使能TIM2中断
    NVIC_Init(&NVIC_InitStructure);                              // 初始化NVIC中断设置
}

// TIM2中断处理函数
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) // 检查是否是通道1捕获中断
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除通道1捕获中断挂起位

        // 获取捕获值
        uint32_t capture = TIM_GetCapture1(TIM2); // 从通道1读取捕获值

        // 处理捕获值
        // printf("Captured Value: %lu\n", capture); // 打印捕获值(需要有相应的串口配置)
    }
}


2024.9.13 第一次修订,后期不再维护

posted @ 2024-09-13 15:17  hazy1k  阅读(9)  评论(0编辑  收藏  举报