Nuru_Banmian
Nuru_Banmian
Published on 2025-07-07 / 54 Visits
0
0

通用定时器_输入捕获功能_测量PWM_寄存器&HAL库实现

测量PWM周期原理

  1. 假设对计数器时钟72分频(为了方便计算),则计数器频率为1MHz,计数器累加一次的事件为1us

  2. 设置定时器自动重装载寄存器的值为65535。把这个值设置为最大,尽量避免溢出

    假设测量的信号周期小于65535us,即频率大于16Hz

    两种情况

  • 假设测量信号周期小于65535us,即频率大于16Hz

    1. 在一个周期内,计数器不会溢出

    2. 当第一个上升沿到来时重置计数器的值(让计数器从0开始计数)

    3. 当第二个上升沿到来时,计数器的值会自动copy到捕获寄存器,读出捕获寄存器的值,这个值就表示信号周期。单位us

通用定时器-输入捕获功能-测量PWM寄存器-情况一图例.png

  • 如果测量频率超过1MHz(超过计数器的时钟频率)

    1. 第二个上升沿到了之后,计数器还没有完成一次累加,则无法测量

    2. 可以考虑测试第一个上升沿和第n个上升沿的间隔。这种一般用来测量高频信号,频率超过了计数器的时钟频率

通用定时器-输入捕获功能-测量PWM寄存器-情况二图例.png

代码

基本步骤

  • 输入通道滤波器的设置

  • 输入通道信号边缘检测和捕获使能

  • 捕获比较中断

寄存器实现

Driver_TIM4.h

#ifndef __DRIVER_TIM4_H
#define __DRIVER_TIM4_H
#include "stm32f10x.h"

void TIM4_Init(void);
void TIM4_Start(void);
double TIM4_GetPWMCycle(void);
double TIM4_GetPWMFreq(void);
#endif

Driver_TIM4.c

#include "tim4.h"
void TIM4_Init(void)
{
  // 1. 开启时钟
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
  RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;// 2. GPIO工作模式,PB6:浮空输入 CNF-01,MODE-00
  GPIOB->CRL &= ~GPIO_CRL_MODE6;
  GPIOB->CRL &= ~GPIO_CRL_CNF6_1;
  GPIOB->CRL |= GPIO_CRL_CNF6_0;

  // 定时器配置
  // 3. 时基部分
  // 3.1 预分频值 71,得到1MHz
  TIM4->PSC = 71;

  // 3.2 重装载值,65535,尽量在信号一个周期内不要产生溢出
  TIM4->ARR = 65535;

  // 3.3 计数方向
  TIM4->CR1 &= ~TIM_CR1_DIR;

  // 4. 输入通道部分
  // 4.1 TI1的输入选择
  TIM4->CR2 &= ~TIM_CR2_TI1S;

  // 4.2 输入滤波器
  TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;

  // 4.3 配置极性:上升沿触发
  TIM4->CCER &= ~TIM_CCER_CC1P;

  // 4.4 选择通道1的输入映射为 TI1:CC1S - 01
  TIM4->CCMR1 &= ~TIM_CCMR1_CC1S_1;
  TIM4->CCMR1 |= TIM_CCMR1_CC1S_0;

  // 4.5 预分频器
  TIM4->CCMR1 &= ~TIM_CCMR1_IC1PSC;
  
  // 4.6 通道1输入捕获使能
  TIM4->CCER |= TIM_CCER_CC1E;
  
  // 4.7 开启捕获中断使能
  TIM4->DIER |= TIM_DIER_CC1IE;

  // 5. NVIC配置
  NVIC_SetPriorityGrouping(3);
  NVIC_SetPriority(TIM4_IRQn, 3);
  NVIC_EnableIRQ(TIM4_IRQn);
}

// 将定时器的开关包装成函数
void TIM4_Start(void)
{
  TIM4->CR1 |= TIM_CR1_CEN;
}

void TIM4_Stop(void)
{
  TIM4->CR1 &= ~TIM_CR1_CEN;
}

void TIM4_IRQHandler(void)
{
  if (TIM4->SR & TIM_SR_CC1IF)
  {
    // 清除中断标志位
    TIM4->SR &= ~TIM_SR_CC1IF;

    // 直接清零计数器
    TIM4->CNT = 0;
  }
}

// cycle单位为us,返回周期单位为ms
double TIM4_GetPWMCycle(void)
{
  return TIM4->CCR1 / 1000.0;
}

double TIM4_GetPWMFreq(void)
{
return 1000000.0 / TIM4->CCR1;
}

main.c

#include "usart.h"
#include "tim5.h"
#include "tim4.h"
#include "delay.h"
int main(void)
{
  // 初始化
  USART_Init();
  TIM4_Init();
  TIM5_Init();printf("Hello, world!\n");

  // 开启定时器
  TIM5_Start();
  TIM4_Start();
  
  while (1)
  {
    printf("T = %.2f ms, f = %.2f Hz\n", TIM4_GetPWMCycle(), TIM4_GetPWMFreq());
	Delay_ms(1000);
  }
}

HAL库实现

基本设置

通用定时器-输入捕获功能-HAL库设置-1.png

通用定时器-输入捕获功能-HAL库设置-2.png

通用定时器-输入捕获功能-HAL库设置-3.png

在tim.c添加代码

/* USER CODE BEGIN 1 */
/* 记录上升沿的个数 */
uint8_t raiseEdgeCount = 0;
/* 存储信号的周期 */
uint16_t t = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM4)
    {
        if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
        {
            /* 上升沿个数 */
            raiseEdgeCount++;
            /* 如果是第1个上升沿,则清零计数器,让计数器从0开始计数 */
            if (raiseEdgeCount == 1)
            {
                //TIM4->CNT = 0; /* 计数器清零 */
                __HAL_TIM_SetCounter(htim, 0);
            }
            else if (raiseEdgeCount == 2)
            {
                /* 读取捕获寄存器的值,就是周期 单位是us*/
                //t = TIM4->CCR1;
                t = __HAL_TIM_GetCompare(htim, TIM_CHANNEL_1);
                /* 上升沿的计数从0重新计数 */
                raiseEdgeCount = 0;
            }
        }
    }
}
/* 返回PWM的周期 ms*/
double Driver_TIM4_GetPWMCycle(void)
{
    return t / 1000.0;
}

/* 返回PWM的频率 */
double Driver_TIM4_GetPWMFreq(void)
{
    return 1000000 / t;
}

/* USER CODE END 1 */

在main.c添加代码

int main(void)
{
 
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM4_Init();
  MX_TIM5_Init();
  MX_USART1_UART_Init();
 
  HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2); // 让tim5产生pwm

  HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);;

  double t, f;
  while (1)
  {
      t = Driver_TIM4_GetPWMCycle();
      f = Driver_TIM4_GetPWMFreq();

      printf("t=%.4fms, f=%.4fHz\r\n", t, f);

      HAL_Delay(1000);
  }
}


Comment