基于 STM32 定时器输入捕获功能的数字频率计方案

一、测量原理与方案选择

两种经典方法

方法 优点 缺点 适用频段
测周法 低频精度高 高频误差大 1Hz - 100KHz
测频法 高频精度高 需要定时闸门 100KHz - 10MHz

推荐方案测周法 + 测频法自动切换


二、硬件连接

信号 STM32 引脚 注意
被测信号 PA0 (TIM2_CH1) 可接 3.3V 输入
分压电阻 1kΩ + 9kΩ 保护输入
整形电路 施密特触发器 可选

输入保护 :必须加 1kΩ 串联电阻 + 3.3V 钳位二极管


三、STM32 定时器配置

1、TIM2(测周法 - 捕获模式)

  • 通道:CH1
  • 模式:Input Capture
  • 触发:上升沿
  • 分频:72-1 → 计数频率 1MHz
  • 中断:捕获中断 + 溢出中断

2、TIM3(测频法 - 外部计数模式)

  • 通道:ETR
  • 模式:External Clock Mode 1
  • 触发:上升沿
  • 分频:1-1
  • 中断:更新中断

四、核心数据结构

c 复制代码
typedef struct {
    uint32_t frequency;     // 最终频率
    uint8_t mode;          // 0=测周 1=测频
    float duty;           // 占空比
} FreqMeter_t;

FreqMeter_t meter = {0};

五、测周法实现(TIM2 捕获)

1、初始化代码

c 复制代码
#include "stm32f1xx_hal.h"

TIM_HandleTypeDef htim2;
volatile uint32_t capture1 = 0, capture2 = 0;
volatile uint8_t capture_flag = 0;
volatile uint16_t overflow_count = 0;

void TIM2_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_IC_InitTypeDef sConfigIC = {0};

    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 72-1;  // 72MHz/72 = 1MHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 0xFFFFFFFF;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&htim2);

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);

    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 0;
    HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);

    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    HAL_TIM_Base_Start_IT(&htim2);
}

2、中断处理(核心)

c 复制代码
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
        if (capture_flag == 0) {
            capture1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            capture_flag = 1;
        } else {
            capture2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            
            // 计算周期(考虑溢出)
            uint32_t period_ticks = 0;
            if (capture2 >= capture1) {
                period_ticks = capture2 - capture1;
            } else {
                period_ticks = 0xFFFFFFFF - capture1 + capture2;
            }
            
            // 计算频率
            meter.frequency = 1000000 / period_ticks;  // 1MHz 时钟
            
            capture_flag = 0;
            overflow_count = 0;
        }
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2) {
        overflow_count++;
    }
}

六、测频法实现(TIM3 外部计数)

1、初始化代码

c 复制代码
TIM_HandleTypeDef htim3;
volatile uint32_t count_value = 0;

void TIM3_Init(void)
{
    TIM_SlaveConfigTypeDef sSlaveConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 0;
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 0xFFFF;
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim3);

    sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
    sSlaveConfig.InputTrigger = TIM_TS_ETRF;
    sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    sSlaveConfig.TriggerFilter = 0;
    HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig);

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
}

2、定时测量(1秒闸门)

c 复制代码
void Measure_Frequency(void)
{
    static uint32_t last_count = 0;
    uint32_t current_count = __HAL_TIM_GET_COUNTER(&htim3);
    
    // 1秒定时中断触发
    meter.frequency = current_count - last_count;
    last_count = current_count;
}

七、自动量程切换逻辑

c 复制代码
void Auto_Range_Switch(void)
{
    static uint32_t last_freq = 0;
    static uint8_t stable_count = 0;
    
    if (meter.frequency > 100000) {  // >100KHz
        meter.mode = 1;  // 测频法
    } else {
        meter.mode = 0;  // 测周法
    }
    
    // 防抖动
    if (abs(meter.frequency - last_freq) < 10) {
        stable_count++;
        if (stable_count > 5) {
            stable_count = 0;
            last_freq = meter.frequency;
        }
    } else {
        stable_count = 0;
    }
}

八、占空比测量(双沿捕获)

c 复制代码
float Measure_Duty_Cycle(void)
{
    uint32_t high_time, low_time, period;
    
    // 配置上升沿和下降沿捕获
    // ... 初始化代码
    
    // 计算占空比
    period = high_time + low_time;
    if (period > 0) {
        meter.duty = (float)high_time / period * 100.0f;
    }
    
    return meter.duty;
}

九、主函数示例

c 复制代码
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    
    MX_GPIO_Init();
    MX_TIM2_Init();
    MX_TIM3_Init();
    
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    HAL_TIM_Base_Start_IT(&htim2);
    HAL_TIM_Base_Start_IT(&htim3);
    
    while (1) {
        Auto_Range_Switch();
        
        printf("频率: %lu Hz, 模式: %s, 占空比: %.1f%%\r\n",
               meter.frequency,
               meter.mode ? "测频" : "测周",
               meter.duty);
        
        HAL_Delay(500);
    }
}

参考代码 数字频率计,基于stm32的中断捕获模式对输入信号进行计数 www.youwenfan.com/contentcsu/69881.html

十、性能优化

1、多周期同步平均

c 复制代码
#define AVG_COUNT 10
uint32_t freq_buffer[AVG_COUNT];

2、输入信号调理

  • 施密特触发器
  • 自动增益控制
  • 带通滤波器

3、自动校零

c 复制代码
void Auto_Zero_Calibration(void)
{
    uint32_t avg = 0;
    for (int i=0; i<100; i++) {
        avg += Read_ADC();
    }
    zero_offset = avg / 100;
}

十一、测量范围与精度

频段 方法 分辨率
1Hz - 100Hz 测周法 0.1Hz
100Hz - 10KHz 测周法 1Hz
10KHz - 100KHz 测周法 10Hz
100KHz - 1MHz 测频法 100Hz
1MHz - 10MHz 测频法 1KHz

最高精度 :0.1Hz @ 1Hz
最大误差:< 0.1%

相关推荐
踏着七彩祥云的小丑1 小时前
嵌入式学习第 11 天:温湿度、红外、光电传感器原理
单片机·嵌入式硬件
齐齐大魔王2 小时前
关于 安装串口CH340、CH341驱动预安装成功,但是不显示端口问题
stm32·单片机·嵌入式硬件
LingLong_roar2 小时前
普冉单片机PY32F002AF15P6TU + 0.96寸TFT ST7735s 80*160显示屏,使用软件SPI进行颜色填充
单片机·嵌入式硬件
楼兰公子2 小时前
SoC嵌入式硬件设计:原理图搭建与PCB画板系统教学(KiCad 10.0版)
嵌入式硬件·kicad
LCG元2 小时前
STM32实战:基于STM32F103的智能充电器(电压电流检测+PWM)
stm32·单片机·嵌入式硬件
feifeigo1232 小时前
汽车CAN J1939协议完整编程源码和STM32移植指南
stm32·嵌入式硬件·汽车
LCG元3 小时前
STM32实战:基于OpenMV与STM32的智能视觉追踪小车(颜色识别+舵机控制)
stm32·单片机·嵌入式硬件
星夜夏空993 小时前
STM32单片机学习(13) —— 串口通信协议
stm32·单片机·学习
崇山峻岭之间3 小时前
单片机时钟配置:HSE改为HSI
单片机·嵌入式硬件