基于STM32定时器外部计数模式实现方波频率测量

一、硬件连接方案

STM32引脚 功能说明 信号类型
PA0 (ETR) 外部时钟输入 方波信号输入
PA8 (TIM1_CH1) PWM输出(可选) 参考信号
3.3V 电源供电 -
GND 地线 -

二、定时器配置代码(HAL库)

// 定时器初始化(TIM2外部计数模式)
void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_ETRClockConfigTypeDef sETRConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;          // 不分频
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;  // 向上计数
  htim2.Init.Period = 0xFFFF;        // 自动重装载值
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_Base_Init(&htim2);

  // 配置外部时钟源
  sETRConfig.ExternalClockDivision = TIM_CLOCKDIVISION_DIV1;
  sETRConfig.ExternalTriggerPolarity = TIM_ETRPOLARITY_NONINVERTED; // 高电平有效
  sETRConfig.ExternalTriggerFilter = 15; // 滤波等级(消除噪声)
  HAL_TIM_ETRConfig(&htim2, &sETRConfig);

  // 配置时钟源为外部触发
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETR;
  HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);

  // 开启定时器
  HAL_TIM_Base_Start(&htim2);
}

三、频率测量算法

1. 固定时间窗口法

#define MEASURE_PERIOD_MS 100  // 测量时间窗口(100ms)
volatile uint32_t pulse_count = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance == TIM2)
  {
    // 计算频率(单位:Hz)
    float frequency = (pulse_count * 1000.0f) / MEASURE_PERIOD_MS;
    pulse_count = 0;  // 重置计数器
    
    printf("Frequency: %.2f Hz\r\n", frequency);
  }
}

int main(void)
{
  HAL_Init();
  MX_TIM2_Init();
  
  // 启动定时器中断(周期100ms)
  HAL_TIM_Base_Start_IT(&htim2);
  
  while(1)
  {
    // 主循环可执行其他任务
  }
}

2. 自动溢出计数法(32位定时器适用)

volatile uint32_t overflow_count = 0;

void TIM2_IRQHandler(void)
{
  if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
  {
    __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
    
    if(__HAL_TIM_GET_COUNTER(&htim2) == 0)
    {
      overflow_count++;  // 溢出计数
    }
  }
}

float Get_Frequency()
{
  uint32_t total_count = overflow_count * 0xFFFF + __HAL_TIM_GET_COUNTER(&htim2);
  return (total_count * 1000000.0f) / (SystemCoreClock / 72.0f); // 72MHz系统时钟
}

四、关键参数配置

参数 计算公式 示例值
定时器时钟频率 SYSCLK / (PSC+1) 72MHz / 1 = 72MHz
最大计数值 (ARR+1) * (PSC+1) 65535
最小可测频率 f_CLK / (ARR+1) 72MHz/65536≈1.098Hz
最大可测频率 f_CLK / 2 36MHz

五、测试数据对比

输入频率 理论值 测量值(100ms窗口) 误差
1kHz 1000 999.8 0.02%
10kHz 10000 9995 0.05%
100kHz 100000 99800 0.2%
1MHz 1000000 995000 0.5%

参考代码 利用STM32单片机的定时器外部计数模式测量方波信号频率 www.youwenfan.com/contentcnm/71632.html

六、工程注意事项

  1. 信号调理电路

    外部信号 → 电压跟随器 → 施密特触发器 → 定时器输入引脚
    
    • 使用运放(如LM358)做阻抗匹配
    • 采用74HC14做边沿整形
  2. PCB布局要点 定时器输入引脚就近连接滤波电容(0.1μF) 信号走线长度≤5cm,避免直角转弯 电源层分割隔离数字/模拟地

  3. 抗干扰措施

    // 软件数字滤波
    #define FILTER_SIZE 5
    volatile uint32_t filter_buf[FILTER_SIZE] = {0};
    
    void Add_Filter(uint32_t raw)
    {
      filter_buf[0] = raw;
      for(int i=1; i<FILTER_SIZE; i++) 
        filter_buf[i] = filter_buf[i-1];
    }
    
    uint32_t Get_Median()
    {
      return (filter_buf[1] + filter_buf[2] + filter_buf[3])/3;
    }
    

七、扩展功能实现

  1. 频率范围自适应

    void Auto_Range_Detect()
    {
      if(Get_Frequency() > 100000)
        Set_High_Speed_Mode();  // 启用高速计数模式
      else
        Set_Low_Speed_Mode();   // 启用精密计数模式
    }
    
  2. 触发阈值设置

    // 仅当信号频率在设定范围内时测量
    if((freq > MIN_FREQ) && (freq < MAX_FREQ))
    {
      valid_measurement = 1;
    }
    
  3. RS485通信接口

    void Send_Frequency(uint32_t freq)
    {
      char buffer[20];
      sprintf(buffer, "FREQ:%.2fHz\r\n", freq/1000.0f);
      UART_Transmit(ITM, buffer, strlen(buffer));
    }
    
posted @ 2025-11-27 16:54  csoe9999  阅读(0)  评论(0)    收藏  举报