STM32F429第二十三篇之捕获实验详解

文章目录

  • 前言
  • 结构体
    • TIM_IC_InitTypeDef
      • ICPolarity(极性选择)
      • ICSelection(输入选择)
      • ICPrescaler(预分频)
      • ICFilter(滤波器)
  • 源程序
    • 主程序
    • 配置
      • TIM5_CH1_Cap_Init
      • HAL_TIM_IC_Init
      • HAL_TIM_IC_MspInit
      • TIM_Base_SetConfig
      • HAL_TIM_IC_ConfigChannel
      • TIM_TI1_SetConfig
      • HAL_TIM_IC_Start_IT
      • TIM_CCxChannelCmd
    • 中断响应
      • TIM5_IRQHandler
      • HAL_TIM_IRQHandler
      • HAL_TIM_IC_CaptureCallback
      • HAL_TIM_ReadCapturedValue
      • HAL_TIM_PeriodElapsedCallback

前言

本博客主要介绍F429时钟功能中的捕获功能,其程序源代码以正点原子的实验代码为依据,主要涉及以下两个方面:

  • 重点结构体的介绍,其与寄存器的关系。
  • 介绍源代码的架构以及实现方式。

本文只介绍捕获相关的内容,其余的内容在之前的博客中已经介绍,本文不能详细介绍。

由于定时器基本不涉及硬件电路,本文不再介绍硬件相关设计。

HAL库版本:

STM32Cube_FW_F4_V1.25.0

本实验的主要功能为:
通过TIM3通道4产生PWM波,且通过TIM5通道1捕获PWM波的高电平持续时间。高电平持续时间(PWM占空比)在不断的变化中。若不连接PWM波输出与捕获输入,同样,可以用来你测量按键KEY_UP按下时间。

本文重新实现的代码下载以及下载链接,见博客

结构体

TIM_IC_InitTypeDef

/**
  * @brief  TIM Input Capture Configuration Structure definition
  */
typedef struct
{
     
  uint32_t  ICPolarity;  /*!< Specifies the active edge of the input signal.
                              This parameter can be a value of @ref TIM_Input_Capture_Polarity */

  uint32_t ICSelection;  /*!< Specifies the input.
                              This parameter can be a value of @ref TIM_Input_Capture_Selection */

  uint32_t ICPrescaler;  /*!< Specifies the Input Capture Prescaler.
                              This parameter can be a value of @ref TIM_Input_Capture_Prescaler */

  uint32_t ICFilter;     /*!< Specifies the input capture filter.
                              This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
} TIM_IC_InitTypeDef;

ICPolarity(极性选择)

/** @defgroup TIM_Input_Capture_Polarity TIM Input Capture Polarity
  * @{
  */
#define  TIM_ICPOLARITY_RISING             TIM_INPUTCHANNELPOLARITY_RISING      /*!< Capture triggered by rising edge on timer input                  */
#define  TIM_ICPOLARITY_FALLING            TIM_INPUTCHANNELPOLARITY_FALLING     /*!< Capture triggered by falling edge on timer input                 */
#define  TIM_ICPOLARITY_BOTHEDGE           TIM_INPUTCHANNELPOLARITY_BOTHEDGE    /*!< Capture triggered by both rising and falling edges on timer input*/
/**
  * @}
  */



/** @defgroup TIM_Input_Channel_Polarity TIM Input Channel polarity
  * @{
  */
#define  TIM_INPUTCHANNELPOLARITY_RISING      0x00000000U                       /*!< Polarity for TIx source */
#define  TIM_INPUTCHANNELPOLARITY_FALLING     TIM_CCER_CC1P                     /*!< Polarity for TIx source */
#define  TIM_INPUTCHANNELPOLARITY_BOTHEDGE    (TIM_CCER_CC1P | TIM_CCER_CC1NP)  /*!< Polarity for TIx source */
/**
  * @}
  */

STM32F429第二十三篇之捕获实验详解_第1张图片

ICSelection(输入选择)

/** @defgroup TIM_Input_Capture_Selection TIM Input Capture Selection
  * @{
  */
#define TIM_ICSELECTION_DIRECTTI           TIM_CCMR1_CC1S_0                     /*!< TIM Input 1, 2, 3 or 4 is selected to be
                                                                                     connected to IC1, IC2, IC3 or IC4, respectively */
#define TIM_ICSELECTION_INDIRECTTI         TIM_CCMR1_CC1S_1                     /*!< TIM Input 1, 2, 3 or 4 is selected to be
                                                                                     connected to IC2, IC1, IC4 or IC3, respectively */
#define TIM_ICSELECTION_TRC                TIM_CCMR1_CC1S                       /*!< TIM Input 1, 2, 3 or 4 is selected to be connected to TRC */
/**
  * @}
  */

STM32F429第二十三篇之捕获实验详解_第2张图片

ICPrescaler(预分频)

/** @defgroup TIM_Input_Capture_Prescaler TIM Input Capture Prescaler
  * @{
  */
#define TIM_ICPSC_DIV1                     0x00000000U                          /*!< Capture performed each time an edge is detected on the capture input */
#define TIM_ICPSC_DIV2                     TIM_CCMR1_IC1PSC_0                   /*!< Capture performed once every 2 events                                */
#define TIM_ICPSC_DIV4                     TIM_CCMR1_IC1PSC_1                   /*!< Capture performed once every 4 events                                */
#define TIM_ICPSC_DIV8                     TIM_CCMR1_IC1PSC                     /*!< Capture performed once every 8 events                                */
/**
  * @}
  */

STM32F429第二十三篇之捕获实验详解_第3张图片

ICFilter(滤波器)

取值范围为:[0x0-0xF]。
STM32F429第二十三篇之捕获实验详解_第4张图片

注意:
f D T S f_{DTS} fDTS的配置方式通过TIMx_CR1寄存器实现,在HAL库中,通过基本时钟配置实现。
STM32F429第二十三篇之捕获实验详解_第5张图片

源程序

程序的总体架构如下:

  • main
    • TIM5_CH1_Cap_Init
      • HAL_TIM_IC_Init
        • HAL_TIM_IC_MspInit
        • TIM_Base_SetConfig
    • HAL_TIM_IC_ConfigChannel
      • TIM_TI1_SetConfig
    • HAL_TIM_IC_Start_IT
      • TIM_CCxChannelCmd

主程序

int main(void)
{
     
    long long temp = 0;
    HAL_Init();                      //初始化HAL库
    Stm32_Clock_Init(360, 25, 2, 8); //设置时钟,180Mhz
    delay_init(180);                 //初始化延时函数
    uart_init(115200);               //初始化USART
    LED_Init();                      //初始化LED

    TIM3_PWM_Init(500 - 1, 90 - 1);        //90M/90=1M的计数频率,自动重装载为500,那么PWM频率为1M/500=2kHZ
    TIM5_CH1_Cap_Init(0XFFFFFFFF, 90 - 1); //以1MHZ的频率计数

    while (1)
    {
     
        delay_ms(10);
        TIM_SetTIM3Compare4(TIM_GetTIM3Capture4() + 1);

        if (TIM_GetTIM3Capture4() == 300)
            TIM_SetTIM3Compare4(0);

        if (TIM5CH1_CAPTURE_STA & 0X80) //成功捕获到了一次高电平
        {
     
            temp = TIM5CH1_CAPTURE_STA & 0X3F; //获得溢出次数
            temp *= 0XFFFFFFFF;                //溢出时间总和
            temp += TIM5CH1_CAPTURE_VAL;       //得到总的高电平时间
            printf("HIGH:%lld us\r\n", temp);  //打印总的高点平时间
            TIM5CH1_CAPTURE_STA = 0;           //开启下一次捕获
        }
    }
}

在主函数中,大致分成两个部分:

  1. 外设配置
  2. while循环

在外设配置中,本文只关注TIM5_CH1_Cap_Init的外设,其余的外设前文已经介绍过。

在while循环中,实现本实验的主要功能:

  • 每次循环,改变PWM波的占空比,高电平由0增加到300,往复循环。
  • 通过标志位,得知完成一次PWM高电平捕获。计算高电平持续的时间,并通过串口输出。

配置

TIM5_CH1_Cap_Init

/** 
 * @brief 时钟5初始化
 * @note  TIM2和TIM5是32位的
 * @param {u32} arr 自动重载值
 * @param {u16} psc 预分频
 * @retval 无
 */
void TIM5_CH1_Cap_Init(u32 arr, u16 psc)
{
     
    /* 1.RCC时钟使能 */
    __HAL_RCC_TIM5_CLK_ENABLE(); //使能TIM5时钟

    /* 2.通用时钟初始化 */
    TIM5_Handler.Instance = TIM5;                             //通用定时器5
    TIM5_Handler.Init.Prescaler = psc;                        //时钟预分频
    TIM5_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;       //向上计数器
    TIM5_Handler.Init.Period = arr;                           //自动装载值
    TIM5_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //时钟分频因子
    HAL_TIM_IC_Init(&TIM5_Handler);                           //初始化输入捕获时基参数

    /* 3.输入通道配置 */
    TIM_IC_InitTypeDef TIM5_CH1Config;
    TIM5_CH1Config.ICPolarity = TIM_ICPOLARITY_RISING;                       //上升沿捕获
    TIM5_CH1Config.ICSelection = TIM_ICSELECTION_DIRECTTI;                   //映射到TI1上
    TIM5_CH1Config.ICPrescaler = TIM_ICPSC_DIV1;                             //配置输入分频,不分频
    TIM5_CH1Config.ICFilter = 0;                                             //配置输入滤波器,不滤波
    HAL_TIM_IC_ConfigChannel(&TIM5_Handler, &TIM5_CH1Config, TIM_CHANNEL_1); //配置TIM5通道1

    /* 4.启动定时器 */
    HAL_TIM_IC_Start_IT(&TIM5_Handler, TIM_CHANNEL_1); //开启TIM5的捕获通道1,并且开启捕获中断
    __HAL_TIM_ENABLE_IT(&TIM5_Handler, TIM_IT_UPDATE); //使能更新中断
}

该程序大致分成四个部分:

  1. RCC时钟使能
  2. 时钟基本单元初始化
  3. 输入通道初始化
  4. 启动定时器与中断

该函数与PWM配置的流程基本一致,可以参考博客

其中涉及到两个结构体:

  • 定时器句柄结构体 TIM_HandleTypeDef
  • 定时器输入通道初始化结构体 TIM_IC_InitTypeDef

其中,第一个结构体在之前博客中已经详细介绍,本文不再详细介绍,更多详细内容参考

第二个结构体可以参考本文结构体部分:TIM_IC_InitTypeDef。

最后的宏定义为:

/** @brief  Enable the specified TIM interrupt.
  * @param  __HANDLE__ specifies the TIM Handle.
  * @param  __INTERRUPT__ specifies the TIM interrupt source to enable.
  *          This parameter can be one of the following values:
  *            @arg TIM_IT_UPDATE: Update interrupt
  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
  *            @arg TIM_IT_COM:   Commutation interrupt
  *            @arg TIM_IT_TRIGGER: Trigger interrupt
  *            @arg TIM_IT_BREAK: Break interrupt
  * @retval None
  */
#define __HAL_TIM_ENABLE_IT(__HANDLE__, __INTERRUPT__)    ((__HANDLE__)->Instance->DIER |= (__INTERRUPT__))

通过将DIER寄存器对应位置1,使能对应的中断。

HAL_TIM_IC_Init

/**
  * @brief  Initializes the TIM Input Capture Time base according to the specified
  *         parameters in the TIM_HandleTypeDef and initializes the associated handle.
  * @note   Switching from Center Aligned counter mode to Edge counter mode (or reverse)
  *         requires a timer reset to avoid unexpected direction
  *         due to DIR bit readonly in center aligned mode.
  *         Ex: call @ref HAL_TIM_IC_DeInit() before HAL_TIM_IC_Init()
  * @param  htim TIM Input Capture handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
{
     
    /* 1.检测参数 */
    /* Check the TIM handle allocation */
    if (htim == NULL)
    {
     
        return HAL_ERROR;
    }

    /* Check the parameters */
    assert_param(IS_TIM_INSTANCE(htim->Instance));
    assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
    assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
    assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));

    /* 2.底层初始化 */
    if (htim->State == HAL_TIM_STATE_RESET)
    {
     
        /* Allocate lock resource and initialize it */
        htim->Lock = HAL_UNLOCKED;

#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
        /* Reset interrupt callbacks to legacy weak callbacks */
        TIM_ResetCallback(htim);

        if (htim->IC_MspInitCallback == NULL)
        {
     
            htim->IC_MspInitCallback = HAL_TIM_IC_MspInit;
        }
        /* Init the low level hardware : GPIO, CLOCK, NVIC */
        htim->IC_MspInitCallback(htim);
#else
        /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
        HAL_TIM_IC_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
    }

    /* 3.时钟的基本配置 */
    /* Set the TIM state */
    htim->State = HAL_TIM_STATE_BUSY;

    /* Init the base time for the input capture */
    TIM_Base_SetConfig(htim->Instance, &htim->Init);

    /* Initialize the TIM state*/
    htim->State = HAL_TIM_STATE_READY;

    return HAL_OK;
}

该函数分成三个部分:

  1. 检测参数
  2. 底层初始化
  3. 时钟的基本配置

该函数与PWM波部分基本一致,可以参考博客

HAL_TIM_IC_MspInit

/** 
 * @brief 输入通道底层初始化
 * @note 该函数在HAL_TIM_IC_Init函数中被调用
 * @param {TIM_HandleTypeDef} *htim 时钟句柄
 * @retval 无
 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
     
    /* 1.RCC时钟使能 */
    __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟

    /* 2.GPIO配置 */
    GPIO_InitTypeDef GPIO_Initure;
    GPIO_Initure.Pin = GPIO_PIN_0;          //PIN0
    GPIO_Initure.Mode = GPIO_MODE_AF_PP;    //复用推挽输出
    GPIO_Initure.Pull = GPIO_PULLDOWN;      //下拉
    GPIO_Initure.Speed = GPIO_SPEED_HIGH;   //高速
    GPIO_Initure.Alternate = GPIO_AF2_TIM5; //复用为TIM5
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);    //初始化PA0

    /* 3.中断配置 */
    HAL_NVIC_SetPriority(TIM5_IRQn, 2, 0); //设置中断优先级,抢占优先级2,子优先级0
    HAL_NVIC_EnableIRQ(TIM5_IRQn);         //开启ITM5中断通道
}

输入通道的底层配置,底层配置分成三个部分:

  1. RCC时钟初始化
  2. GPIO配置
  3. 中断配置

在底层配置中,最常见的就是GPIO的配置与中断的配置。

TIM_Base_SetConfig

/**
  * @brief  Time Base configuration
  * @param  TIMx TIM peripheral
  * @param  Structure TIM Base configuration structure
  * @retval None
  */
void TIM_Base_SetConfig(TIM_TypeDef *TIMx, TIM_Base_InitTypeDef *Structure)
{
     
    /*********************1.设置CR1寄存器********************************/
    uint32_t tmpcr1; //CR1临时值
    tmpcr1 = TIMx->CR1;

    /* 1.1 设置计数模式 */
    /* Set TIM Time Base Unit parameters ---------------------------------------*/
    if (IS_TIM_COUNTER_MODE_SELECT_INSTANCE(TIMx)) //1,2,3,4,5,8:可以选择计数器的方向
    {
     
        /* Select the Counter Mode */
        tmpcr1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS); //清零
        tmpcr1 |= Structure->CounterMode;       //设置模式,对齐方式以及计数器增减方向
    }

    /* 1.2 设置死区发生器与采样时钟之间的分频比 */
    if (IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx)) //除了6,7:可以设置时钟分频
    {
     
        /* Set the clock division */
        tmpcr1 &= ~TIM_CR1_CKD;
        tmpcr1 |= (uint32_t)Structure->ClockDivision; //设置死区发生器与采样时钟之间的分频比
    }

    /* 1.3 使能自动加载寄存器的影子寄存器 */
    /* Set the auto-reload preload */
    MODIFY_REG(tmpcr1, TIM_CR1_ARPE, Structure->AutoReloadPreload); //使能自动加载寄存器的影子寄存器

    TIMx->CR1 = tmpcr1;

    /*********************2.设置ARR寄存器********************************/
    /* Set the Autoreload value */
    TIMx->ARR = (uint32_t)Structure->Period; //设置时钟的周期

    /*********************3.设置PSC寄存器********************************/
    /* Set the Prescaler value */
    TIMx->PSC = Structure->Prescaler; //设置时钟的预分频

    /*********************4.设置RCR寄存器********************************/
    if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx)) //1,8:支持重复计数器,高级时钟
    {
     
        /* Set the Repetition Counter value */
        TIMx->RCR = Structure->RepetitionCounter; //设置比较寄存器的更新频率
    }

    /*********************5.设置EGR  寄存器********************************/
    /* Generate an update event to reload the Prescaler
     and the repetition counter (only for advanced timer) value immediately */
    TIMx->EGR = TIM_EGR_UG;
}

该部分程序已经多次介绍,此处不再详细展开。

HAL_TIM_IC_ConfigChannel

/**
  * @brief  Initializes the TIM Input Capture Channels according to the specified
  *         parameters in the TIM_IC_InitTypeDef.
  * @param  htim TIM IC handle
  * @param  sConfig TIM Input Capture configuration structure
  * @param  Channel TIM Channel to configure
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef *sConfig, uint32_t Channel)
{
     
    /* 1.预处理:参数检测,上锁,状态变化 */
    /* Check the parameters */
    assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
    assert_param(IS_TIM_IC_POLARITY(sConfig->ICPolarity));
    assert_param(IS_TIM_IC_SELECTION(sConfig->ICSelection));
    assert_param(IS_TIM_IC_PRESCALER(sConfig->ICPrescaler));
    assert_param(IS_TIM_IC_FILTER(sConfig->ICFilter));

    /* Process Locked */
    __HAL_LOCK(htim);

    htim->State = HAL_TIM_STATE_BUSY;

    /* 2.判断通道 */
    if (Channel == TIM_CHANNEL_1)
    {
     
        /* 3.设置相关参数 */
        /* TI1 Configuration */
        TIM_TI1_SetConfig(htim->Instance,
                          sConfig->ICPolarity,
                          sConfig->ICSelection,
                          sConfig->ICFilter);

        /* 4.设置预分频 */
        /* Reset the IC1PSC Bits */
        htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;

        /* Set the IC1PSC value */
        htim->Instance->CCMR1 |= sConfig->ICPrescaler;
    }
    else if (Channel == TIM_CHANNEL_2)
    {
     
        /* TI2 Configuration */
        assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));

        TIM_TI2_SetConfig(htim->Instance,
                          sConfig->ICPolarity,
                          sConfig->ICSelection,
                          sConfig->ICFilter);

        /* Reset the IC2PSC Bits */
        htim->Instance->CCMR1 &= ~TIM_CCMR1_IC2PSC;

        /* Set the IC2PSC value */
        htim->Instance->CCMR1 |= (sConfig->ICPrescaler << 8U);
    }
    else if (Channel == TIM_CHANNEL_3)
    {
     
        /* TI3 Configuration */
        assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));

        TIM_TI3_SetConfig(htim->Instance,
                          sConfig->ICPolarity,
                          sConfig->ICSelection,
                          sConfig->ICFilter);

        /* Reset the IC3PSC Bits */
        htim->Instance->CCMR2 &= ~TIM_CCMR2_IC3PSC;

        /* Set the IC3PSC value */
        htim->Instance->CCMR2 |= sConfig->ICPrescaler;
    }
    else
    {
     
        /* TI4 Configuration */
        assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));

        TIM_TI4_SetConfig(htim->Instance,
                          sConfig->ICPolarity,
                          sConfig->ICSelection,
                          sConfig->ICFilter);

        /* Reset the IC4PSC Bits */
        htim->Instance->CCMR2 &= ~TIM_CCMR2_IC4PSC;

        /* Set the IC4PSC value */
        htim->Instance->CCMR2 |= (sConfig->ICPrescaler << 8U);
    }

    htim->State = HAL_TIM_STATE_READY;

    __HAL_UNLOCK(htim);

    return HAL_OK;
}

通过上面源程序,可以知道,该函数大致可以分成四个部分:

  1. 函数预处理,包括参数判断等。。
  2. 判断通道
  3. 设置参数
  4. 设置预分频信息

需要注意状态的变化为:

  1. 当参数判断完成后,对句柄进行上锁,且将状态变为BUSY。
  2. 接着 ,对参数进行设置。
  3. 当参数设置完成后,状态变成READY,接着解锁。

在函数的第三步骤设置参数中,该函数调用了函数TIM_TI1_SetConfig,该函数详细定义如下:

TIM_TI1_SetConfig

/**
  * @brief  Configure the TI1 as Input.
  * @param  TIMx to select the TIM peripheral.
  * @param  TIM_ICPolarity The Input Polarity.
  *          This parameter can be one of the following values:
  *            @arg TIM_ICPOLARITY_RISING
  *            @arg TIM_ICPOLARITY_FALLING
  *            @arg TIM_ICPOLARITY_BOTHEDGE
  * @param  TIM_ICSelection specifies the input to be used.
  *          This parameter can be one of the following values:
  *            @arg TIM_ICSELECTION_DIRECTTI: TIM Input 1 is selected to be connected to IC1.
  *            @arg TIM_ICSELECTION_INDIRECTTI: TIM Input 1 is selected to be connected to IC2.
  *            @arg TIM_ICSELECTION_TRC: TIM Input 1 is selected to be connected to TRC.
  * @param  TIM_ICFilter Specifies the Input Capture Filter.
  *          This parameter must be a value between 0x00 and 0x0F.
  * @retval None
  * @note TIM_ICFilter and TIM_ICPolarity are not used in INDIRECT mode as TI2FP1
  *       (on channel2 path) is used as the input signal. Therefore CCMR1 must be
  *        protected against un-initialized filter and polarity values.
  */
void TIM_TI1_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
                       uint32_t TIM_ICFilter)
{
     
    uint32_t tmpccmr1;
    uint32_t tmpccer;

    /* 1.关闭通道1捕获使能 */
    /* Disable the Channel 1: Reset the CC1E Bit */
    TIMx->CCER &= ~TIM_CCER_CC1E;
    tmpccmr1 = TIMx->CCMR1;
    tmpccer = TIMx->CCER;

    /* 2.设置通道的输入选择 */
    /* Select the Input */
    if (IS_TIM_CC2_INSTANCE(TIMx) != RESET) //至少包含2通道:1,2,3,4,5,8,9,12
    {
     
        tmpccmr1 &= ~TIM_CCMR1_CC1S;
        tmpccmr1 |= TIM_ICSelection;
    }
    else
    {
     
        tmpccmr1 |= TIM_CCMR1_CC1S_0;
    }

    /* 3.设置滤波器 */
    /* Set the filter */
    tmpccmr1 &= ~TIM_CCMR1_IC1F;
    tmpccmr1 |= ((TIM_ICFilter << 4U) & TIM_CCMR1_IC1F);

    /* 4.设置极性 */
    /* Select the Polarity and set the CC1E Bit */
    tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
    tmpccer |= (TIM_ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP));

    /* 5.将临时变量写入 */
    /* Write to TIMx CCMR1 and CCER registers */
    TIMx->CCMR1 = tmpccmr1;
    TIMx->CCER = tmpccer;
}

此函数大致分成5个步骤:

  1. 禁用通道1的捕获使能
  2. 设置通道的输入选择
  3. 设置滤波器
  4. 设置极性
  5. 将临时变量写入

此处需要注意两点:

  1. 禁用的使能,在该函数中并未重新打开。
  2. 预分频信息没有在该函数中设置,而是在上一个函数中设置。

HAL_TIM_IC_Start_IT

/**
  * @brief  Starts the TIM Input Capture measurement in interrupt mode.
  * @param  htim TIM Input Capture handle
  * @param  Channel TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
{
     
    uint32_t tmpsmcr;

    /* 1.检测参数 */
    /* Check the parameters */
    assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));


    /* 2.使能通道中断 */
    switch (Channel)
    {
     
    case TIM_CHANNEL_1:
    {
     
        /* Enable the TIM Capture/Compare 1 interrupt */
        __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
        break;
    }

    case TIM_CHANNEL_2:
    {
     
        /* Enable the TIM Capture/Compare 2 interrupt */
        __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
        break;
    }

    case TIM_CHANNEL_3:
    {
     
        /* Enable the TIM Capture/Compare 3 interrupt */
        __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
        break;
    }

    case TIM_CHANNEL_4:
    {
     
        /* Enable the TIM Capture/Compare 4 interrupt */
        __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);
        break;
    }

    default:
        break;
    }
    /* 3.使能通道,通过CCER寄存器 */
    /* Enable the Input Capture channel */
    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

    /* 4.若非触发模式,则使能时钟 */
    /* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */
    tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
    if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))//判断其为非触发模式
    {
     
        __HAL_TIM_ENABLE(htim);
    }

    /* Return function status */
    return HAL_OK;
}

该函数用于启动捕获通道,共分成4个步骤:

  1. 检测参数
  2. 使能通道中断
  3. 使能通道,通过CCER寄存器
  4. 使能时钟。

其中使能通道这一步是该函数的关键,通过函数TIM_CCxChannelCmd实现,该函数具体定义如下:

TIM_CCxChannelCmd

/**
  * @brief  Enables or disables the TIM Capture Compare Channel x.
  * @param  TIMx to select the TIM peripheral
  * @param  Channel specifies the TIM Channel
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1
  *            @arg TIM_CHANNEL_2: TIM Channel 2
  *            @arg TIM_CHANNEL_3: TIM Channel 3
  *            @arg TIM_CHANNEL_4: TIM Channel 4
  * @param  ChannelState specifies the TIM Channel CCxE bit new state.
  *          This parameter can be: TIM_CCx_ENABLE or TIM_CCx_DISABLE.
  * @retval None
  */
void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState)
{
     
    uint32_t tmp;

    /* 1.参数检查 */
    /* Check the parameters */
    assert_param(IS_TIM_CC1_INSTANCE(TIMx));
    assert_param(IS_TIM_CHANNELS(Channel));

    /* 2.通过位移得到通道对应位数 */
    tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */

    /* 3.清除对应的位 */
    /* Reset the CCxE Bit */
    TIMx->CCER &= ~tmp;

    /* 4.使能通道对应的位 */
    /* Set or reset the CCxE Bit */
    TIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
}

该函数在博客中已经介绍。

大致分成四个步骤:

  1. 参数检测
  2. 将CCER对应位置1,使能通道。

中断响应

在本实验中,关于捕获功能部分,共开了两个中断:

  1. 更新中断(Update)
  2. 捕获/比较1中断(Capture/Compare 1 interrupt)

首先,看一下中断响应函数:

TIM5_IRQHandler

/** 
 * @brief TIM5中断响应函数
 * @note 该函数响应TIM5的中断
 * @param {*}无
 * @retval 无
 */
void TIM5_IRQHandler(void)
{
     
    HAL_TIM_IRQHandler(&TIM5_Handler); //定时器中断处理函数
}

该函数为TIM5的中断响应函数,当本实验中的两个中断发生时,都共同调用此函数。在此函数中,调用了HAL库中的定时器中断处理函数HAL_TIM_IRQHandler。该函数的作用就是用于区分是控制器发生哪个中断。该函数的具体定义如下:

HAL_TIM_IRQHandler

/**
  * @brief  This function handles TIM interrupts requests.
  * @param  htim TIM  handle
  * @retval None
  */
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
     
    /* Capture compare 1 event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET)
        {
     
            {
     
                __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
                htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;

                /* Input capture event */
                if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U)
                {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                    htim->IC_CaptureCallback(htim);
#else
                    HAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
                }
                /* Output compare event */
                else
                {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                    htim->OC_DelayElapsedCallback(htim);
                    htim->PWM_PulseFinishedCallback(htim);
#else
                    HAL_TIM_OC_DelayElapsedCallback(htim);
                    HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
                }
                htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
            }
        }
    }
    /* Capture compare 2 event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
            /* Input capture event */
            if ((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00U)
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->IC_CaptureCallback(htim);
#else
                HAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            /* Output compare event */
            else
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->OC_DelayElapsedCallback(htim);
                htim->PWM_PulseFinishedCallback(htim);
#else
                HAL_TIM_OC_DelayElapsedCallback(htim);
                HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
        }
    }
    /* Capture compare 3 event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3);
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
            /* Input capture event */
            if ((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00U)
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->IC_CaptureCallback(htim);
#else
                HAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            /* Output compare event */
            else
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->OC_DelayElapsedCallback(htim);
                htim->PWM_PulseFinishedCallback(htim);
#else
                HAL_TIM_OC_DelayElapsedCallback(htim);
                HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
        }
    }
    /* Capture compare 4 event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4);
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
            /* Input capture event */
            if ((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00U)
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->IC_CaptureCallback(htim);
#else
                HAL_TIM_IC_CaptureCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            /* Output compare event */
            else
            {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                htim->OC_DelayElapsedCallback(htim);
                htim->PWM_PulseFinishedCallback(htim);
#else
                HAL_TIM_OC_DelayElapsedCallback(htim);
                HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
            }
            htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
        }
    }
    /* TIM Update event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
            htim->PeriodElapsedCallback(htim);
#else
            HAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
        }
    }
    /* TIM Break input event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
            htim->BreakCallback(htim);
#else
            HAL_TIMEx_BreakCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
        }
    }
    /* TIM Trigger detection event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
            htim->TriggerCallback(htim);
#else
            HAL_TIM_TriggerCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
        }
    }
    /* TIM commutation event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET)
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) != RESET)
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_FLAG_COM);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
            htim->CommutationCallback(htim);
#else
            HAL_TIMEx_CommutCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
        }
    }
}

该函数通过一系列的判断语句,来确定发生的中断。由于语句含义都基本类似,本文只是详细介绍与本文相关的更新中断与捕获/比较中断。

与更新中断相关语句如下:

    /* TIM Update event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) //判断更新中断标志位是否置位
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)//判断更新中断是否使能
        {
     
            __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);//清除中断标志
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
            htim->PeriodElapsedCallback(htim);
#else
            HAL_TIM_PeriodElapsedCallback(htim);//调用中断回调函数
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
        }
    }

捕获/比较中断中断相关语句如下:

    /* Capture compare 1 event */
    if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET) //通道中断标志是否置位
    {
     
        if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET) //通过中断是否使能
        {
     
            {
     
                __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);     //清除中断标志位
                htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1; //设置句柄活跃通道

                /* Input capture event */
                if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U) //通道若为输入通道
                {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                    htim->IC_CaptureCallback(htim);
#else
                    HAL_TIM_IC_CaptureCallback(htim); //调用回调函数
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
                }
                /* Output compare event */
                else //通道为输出通道
                {
     
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
                    htim->OC_DelayElapsedCallback(htim);
                    htim->PWM_PulseFinishedCallback(htim);
#else
                    HAL_TIM_OC_DelayElapsedCallback(htim);
                    HAL_TIM_PWM_PulseFinishedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
                }
                htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; //清除句柄活跃通道
            }
        }
    }

通过比较以上两段代码,可以发现中断处理函数基本一致:

  1. 判断是哪个中断标志位已经置位以及该中断是否使能,从而确定控制器被激活的中断。
  2. 清除激活中断的标志位。
  3. 调用中断回调函数。

个人认为,HAL库此处处理时有一定问题的,这是因为中断处理函数与中断回调函数加起来比较长。若程序中开的中断比较多,那么这里有点类似于递归函数,此处对于栈的开销比较大。

下面看中断回调函数:

HAL_TIM_IC_CaptureCallback

/** 
 * @brief 通道回调函数
 * @note 在HAL_TIM_IRQHandler中自动调用,捕获中断发生时响应
 * @param {TIM_HandleTypeDef} *htim 句柄
 * @retval 无
 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
     
    if ((TIM5CH1_CAPTURE_STA & 0X80) == 0) //还未成功捕获
    {
     
        if (TIM5CH1_CAPTURE_STA & 0X40) //捕获到一个下降沿
        {
     
            
            TIM5CH1_CAPTURE_STA |= 0X80;                                                   //标记成功捕获到一次高电平脉宽
            TIM5CH1_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&TIM5_Handler, TIM_CHANNEL_1); //获取当前的捕获值.
            
            TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler, TIM_CHANNEL_1);                       //清除极性:将对应位清零
            TIM_SET_CAPTUREPOLARITY(&TIM5_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);  //配置TIM5通道1上升沿捕获

        }
        else //还未开始,第一次捕获上升沿
        {
     
            TIM5CH1_CAPTURE_STA = 0;                                                       //清空标记位
            TIM5CH1_CAPTURE_VAL = 0;                                                       //清空数据位
            TIM5CH1_CAPTURE_STA |= 0X40;                                                   //标记捕获到了上升沿
            
            __HAL_TIM_DISABLE(&TIM5_Handler);                                              //关闭定时器5
            __HAL_TIM_SET_COUNTER(&TIM5_Handler, 0);                                       //将计数器值清零

            TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler, TIM_CHANNEL_1);                       //清除极性:将对应位清零
            TIM_SET_CAPTUREPOLARITY(&TIM5_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //定时器5通道1设置为下降沿捕获
            
            __HAL_TIM_ENABLE(&TIM5_Handler);                                               //使能定时器5
        }
    }
}

通道中断回调函数。当发生捕获事件时,会调用该函数。该函数主要功能为:测试PWM波的高电平时间。所以,首先需要在上升沿处重新启动定时器,再在下降沿处获得捕获事件。

一次完整的捕获过程如下所示:

  • 首先TIM5CH1_CAPTURE_STA为0,表明捕获还没有开始,置0工作在主函数中。
  • 捕获到上升沿,将TIM5CH1_CAPTURE_STA[6]置1,表示捕获已经开始,且捕获到上升沿。
  • 捕获到下降沿,将TIM5CH1_CAPTURE_STA[7]置1,表示已经捕获到下降沿,一次捕获结束。
  • 在主函数中,计算高电平时间,且将TIM5CH1_CAPTURE_STA设置为0。

此处需要注意几个宏的定义:

/**
  * @brief  Disable the TIM peripheral.
  * @param  __HANDLE__ TIM handle
  * @retval None
  */
#define __HAL_TIM_DISABLE(__HANDLE__) \
  do { \
    if (((__HANDLE__)->Instance->CCER & TIM_CCER_CCxE_MASK) == 0UL) \
    { \
      if(((__HANDLE__)->Instance->CCER & TIM_CCER_CCxNE_MASK) == 0UL) \
      { \
        (__HANDLE__)->Instance->CR1 &= ~(TIM_CR1_CEN); \
      } \
    } \
  } while(0)

该宏用于关闭时钟,通过直接写入CR1寄存器的CEN位。但是需要注意的是,有两个判断语句作为前提条件。换言之,必须与该时钟相关的通道都处于禁用状态,才可以关闭时钟。

此处,正点原子的代码我个人认为,该宏使用是有问题的。

/**
  * @brief  Set the TIM Counter Register value on runtime.
  * @param  __HANDLE__ TIM handle.
  * @param  __COUNTER__ specifies the Counter register new value.
  * @retval None
  */
#define __HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)  ((__HANDLE__)->Instance->CNT = (__COUNTER__))
/**
  * @brief  Enable the TIM peripheral.
  * @param  __HANDLE__ TIM handle
  * @retval None
  */
#define __HAL_TIM_ENABLE(__HANDLE__)                 ((__HANDLE__)->Instance->CR1|=(TIM_CR1_CEN))

以上两个宏作用分别为写入计数器值,以及使能计数器。两个宏没有任何条件,且直接操作寄存器,所以不再详细解释。

#define TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__) \
  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER |= (__POLARITY__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER |= ((__POLARITY__) << 4U)) :\
   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER |= ((__POLARITY__) << 8U)) :\
   ((__HANDLE__)->Instance->CCER |= (((__POLARITY__) << 12U))))

#define TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__) \
  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP)) :\
   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP)) :\
   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC3P | TIM_CCER_CC3NP)) :\
   ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC4P | TIM_CCER_CC4NP)))

以上两个宏定义是关于捕获极性操作的。
首先,关于宏TIM_SET_CAPTUREPOLARITY 其定义是存在问题的。并不能实现随意改变捕获极性为__POLARITY__。其设置方式是位或操作,也就是说,当参数为1的时候,设置结果肯定为1;若参数为0的时候,设置结果不一定为0。所以正点原子的代码强调在宏TIM_SET_CAPTUREPOLARITY使用之前必须先使用宏TIM_RESET_CAPTUREPOLARITY

HAL_TIM_ReadCapturedValue

/**
  * @brief  Read the captured value from Capture Compare unit
  * @param  htim TIM handle.
  * @param  Channel TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval Captured value
  */
uint32_t HAL_TIM_ReadCapturedValue(TIM_HandleTypeDef *htim, uint32_t Channel)
{
     
    uint32_t tmpreg = 0U;

    switch (Channel)
    {
     
    case TIM_CHANNEL_1:
    {
     
        /* Check the parameters */
        assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));

        /* Return the capture 1 value */
        tmpreg = htim->Instance->CCR1;

        break;
    }
    case TIM_CHANNEL_2:
    {
     
        /* Check the parameters */
        assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));

        /* Return the capture 2 value */
        tmpreg = htim->Instance->CCR2;

        break;
    }

    case TIM_CHANNEL_3:
    {
     
        /* Check the parameters */
        assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));

        /* Return the capture 3 value */
        tmpreg = htim->Instance->CCR3;

        break;
    }

    case TIM_CHANNEL_4:
    {
     
        /* Check the parameters */
        assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));

        /* Return the capture 4 value */
        tmpreg = htim->Instance->CCR4;

        break;
    }

    default:
        break;
    }

    return tmpreg;
}

该函数比较简单,即通过通道辨别,直接获取计数器值,通过直接读取寄存器获得。

HAL_TIM_PeriodElapsedCallback

/** 
 * @brief 更新中断回调函数
 * @note 在HAL_TIM_IRQHandler中自动调用,更新中断发生时
 * @param {TIM_HandleTypeDef} *htim 句柄
 * @retval 无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     

    if ((TIM5CH1_CAPTURE_STA & 0X80) == 0) //还未成功捕获
    {
     
        if (TIM5CH1_CAPTURE_STA & 0X40) //已经捕获到高电平了
        {
     
            if ((TIM5CH1_CAPTURE_STA & 0X3F) == 0X3F) //高电平太长了
            {
     
                TIM5CH1_CAPTURE_STA |= 0X80; //标记成功捕获了一次
                TIM5CH1_CAPTURE_VAL = 0XFFFFFFFF;
            }
            else
                TIM5CH1_CAPTURE_STA++;
        }
    }
}

该中断回调函数在每次自动装载新值的时候触发。所以,若触发该中断,说明一次装载值记录的时间不够高电平的时间。为了准确记录时间,应该记录自动装载的此处,且将这部分时间计算进去。此中断回调函数的作用就是记录计数器自动装载的次数。

在此处,正点原子定义了两个全局变量:TIM5CH1_CAPTURE_STATIM5CH1_CAPTURE_VAL。其中,第二个变量比较简单,用于存储计数器的捕获数值。第一个变量可以分成三个部分:

  • TIM5CH1_CAPTURE_STA & 0X80:用于标记一次捕获过程,即从上升沿开始到下降沿结束,一个完整的捕获过程完成,则将该值置位,表示一次捕获结束。
  • TIM5CH1_CAPTURE_STA & 0X40:用于标记捕获过程开始,即上升沿已经被捕获到。
  • TIM5CH1_CAPTURE_STA & 0X3F:计数器自动装载次数。

你可能感兴趣的:(ARM,STM32F429,ARM,HAL库,捕获,时钟)