基于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
六、工程注意事项
-
信号调理电路
外部信号 → 电压跟随器 → 施密特触发器 → 定时器输入引脚- 使用运放(如LM358)做阻抗匹配
- 采用74HC14做边沿整形
-
PCB布局要点 定时器输入引脚就近连接滤波电容(0.1μF) 信号走线长度≤5cm,避免直角转弯 电源层分割隔离数字/模拟地
-
抗干扰措施
// 软件数字滤波 #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; }
七、扩展功能实现
-
频率范围自适应
void Auto_Range_Detect() { if(Get_Frequency() > 100000) Set_High_Speed_Mode(); // 启用高速计数模式 else Set_Low_Speed_Mode(); // 启用精密计数模式 } -
触发阈值设置
// 仅当信号频率在设定范围内时测量 if((freq > MIN_FREQ) && (freq < MAX_FREQ)) { valid_measurement = 1; } -
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)); }
浙公网安备 33010602011771号