Thiết kế máy đo tần số số sử dụng chức năng Input Capture trên STM32

Nguyên lý đo và lựa chọn phương pháp

Hai phương pháp đo cơ bản

Phương phápƯu điểmNhược điểmDải tần phù hợp
Đo chu kỳĐộ chính xác cao ở tần số thấpSai số lớn ở tần số cao1Hz - 100KHz
Đo tần sốĐộ chính xác cao ở tần số caoCần cổng thời gian định trước100KHz - 10MHz

Giải pháp đề xuất: Tự động chuyển đổi giữa đo chu kỳ và đo tần số

Kết nối phần cứng

Tín hiệuChân STM32Lưu ý
Tín hiệu đoPA0 (TIM2_CH1)Chấp nhận đầu vào 3.3V
Điện trở phân áp1kΩ + 9kΩBảo vệ đầu vào
Mạch định dạngBộ kích SchmittTùy chọn

Bảo vệ đầu vào: Bắt buộc thêm điện trở 1kΩ nối tiếp + diode kẹp 3.3V

Cấu hình bộ định thời STM32

1. TIM2 (Phương pháp đo chu kỳ - Chế độ Capture)

  • Kênh: CH1
  • Chế độ: Input Capture
  • Kích cạnh: Cạnh lên
  • Bộ chia: 72-1 → Tần số đếm 1MHz
  • Ngắt: Ngắt capture + ngắt tràn

2. TIM3 (Phương pháp đo tần số - Chế độ đếm ngoài)

  • Kênh: ETR
  • Chế độ: External Clock Mode 1
  • Kích cạnh: Cạnh lên
  • Bộ chia: 1-1
  • Ngắt: Ngắt cập nhật

Cấu trúc dữ liệu chính

typedef struct {
    uint32_t freq_value;    // Tần số cuối cùng
    uint8_t method_type;    // 0=đo chu kỳ 1=đo tần số
    float duty_ratio;      // Hệ số nhiệm vụ
} FrequencyData_t;

FrequencyData_t freq_data = {0};

Triển khai phương pháp đo chu kỳ (TIM2 Capture)

1. Mã khởi tạo

#include "stm32f1xx_hal.h"

TIM_HandleTypeDef timer2;
volatile uint32_t cap_value1 = 0, cap_value2 = 0;
volatile uint8_t capture_status = 0;
volatile uint16_t overflow_counter = 0;

void TIM2_Configuration(void)
{
    TIM_ClockConfigTypeDef clock_config = {0};
    TIM_MasterConfigTypeDef master_config = {0};
    TIM_IC_InitTypeDef ic_config = {0};

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

    clock_config.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&timer2, &clock_config);

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

    master_config.MasterOutputTrigger = TIM_TRGO_RESET;
    master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&timer2, &master_config);

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

2. Xử lý ngắt (Phần quan trọng)

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
        if (capture_status == 0) {
            cap_value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            capture_status = 1;
        } else {
            cap_value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            
            // Tính chu kỳ (xét trường hợp tràn)
            uint32_t period_count = 0;
            if (cap_value2 >= cap_value1) {
                period_count = cap_value2 - cap_value1;
            } else {
                period_count = 0xFFFFFFFF - cap_value1 + cap_value2;
            }
            
            // Tính tần số
            freq_data.freq_value = 1000000 / period_count;  // Đồng hồ 1MHz
            
            capture_status = 0;
            overflow_counter = 0;
        }
    }
}

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

Triển khai phương pháp đo tần số (TIM3 đếm ngoài)

1. Mã khởi tạo

TIM_HandleTypeDef timer3;
volatile uint32_t count_result = 0;

void TIM3_Configuration(void)
{
    TIM_SlaveConfigTypeDef slave_config = {0};
    TIM_MasterConfigTypeDef master_config = {0};

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

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

    master_config.MasterOutputTrigger = TIM_TRGO_RESET;
    master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&timer3, &master_config);
}

2. Đo định kỳ (Cổng 1 giây)

void Frequency_Measurement(void)
{
    static uint32_t previous_count = 0;
    uint32_t current_count = __HAL_TIM_GET_COUNTER(&timer3);
    
    // Ngắt định thời 1 giây kích hoạt
    freq_data.freq_value = current_count - previous_count;
    previous_count = current_count;
}

Logic tự động chuyển dải

void Auto_Range_Selection(void)
{
    static uint32_t previous_frequency = 0;
    static uint8_t stability_counter = 0;
    
    if (freq_data.freq_value > 100000) {  // >100KHz
        freq_data.method_type = 1;  // Phương pháp đo tần số
    } else {
        freq_data.method_type = 0;  // Phương pháp đo chu kỳ
    }
    
    // Chống nhiễu
    if (abs(freq_data.freq_value - previous_frequency) < 10) {
        stability_counter++;
        if (stability_counter > 5) {
            stability_counter = 0;
            previous_frequency = freq_data.freq_value;
        }
    } else {
        stability_counter = 0;
    }
}

Đo hệ số nhiệm vụ (Capture hai cạnh)

float Duty_Cycle_Measurement(void)
{
    uint32_t high_duration, low_duration, total_period;
    
    // Cấu hình capture cạnh lên và cạnh xuống
    // ... mã khởi tạo
    
    // Tính hệ số nhiệm vụ
    total_period = high_duration + low_duration;
    if (total_period > 0) {
        freq_data.duty_ratio = (float)high_duration / total_period * 100.0f;
    }
    
    return freq_data.duty_ratio;
}

Ví dụ hàm chính

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    
    GPIO_Initialize();
    TIM2_Configuration();
    TIM3_Configuration();
    
    HAL_TIM_IC_Start_IT(&timer2, TIM_CHANNEL_1);
    HAL_TIM_Base_Start_IT(&timer2);
    HAL_TIM_Base_Start_IT(&timer3);
    
    while (1) {
        Auto_Range_Selection();
        
        printf("Tần số: %lu Hz, Phương pháp: %s, Hệ số nhiệm vụ: %.1f%%\r\n",
               freq_data.freq_value,
               freq_data.method_type ? "Đo tần số" : "Đo chu kỳ",
               freq_data.duty_ratio);
        
        HAL_Delay(500);
    }
}

Tối ưu hóa hiệu suất

1. Trung bình đồng bộ nhiều chu kỳ

#define SAMPLE_COUNT 10
uint32_t frequency_samples[SAMPLE_COUNT];

2. Điều chỉnh tín hiệu đầu vào

  • Bộ kích Schmitt
  • Điều khiển khuếch đại tự động
  • Bộ lọc thông dải

3. Tự động hiệu chỉnh điểm 0

void Auto_Zero_Adjustment(void)
{
    uint32_t average = 0;
    for (int i=0; i<100; i++) {
        average += ADC_ReadValue();
    }
    zero_calibration = average / 100;
}

Dải đo và độ chính xác

Dải tầnPhương phápĐộ phân giải
1Hz - 100HzĐo chu kỳ0.1Hz
100Hz - 10KHzĐo chu kỳ1Hz
10KHz - 100KHzĐo chu kỳ10Hz
100KHz - 1MHzĐo tần số100Hz
1MHz - 10MHzĐo tần số1KHz

Độ chính xác cao nhất: 0.1Hz @ 1Hz
Sai số lớn nhất: < 0.1%

Thẻ: STM32 timer InputCapture FrequencyCounter microcontroller

Đăng vào ngày 11 tháng 9 lúc 11:21