一、测量原理与方案选择
两种经典方法
| 方法 | 优点 | 缺点 | 适用频段 |
|---|---|---|---|
| 测周法 | 低频精度高 | 高频误差大 | 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%