数码管显示的频率计 – 设计与实现

数码管显示的频率计 – 设计与实现

数码管显示频率计系统,包含硬件电路、软件程序、测量算法和显示控制。

一、系统总体设计

1.1 系统架构

输入信号 → 信号调理 → 微控制器 → 数码管显示
           (放大整形)   (测量计算)  (动态扫描)

1.2 技术指标

二、硬件电路设计

2.1 核心元件清单

元件 型号/规格 数量 用途
主控MCU STM32F103C8T6 1 核心处理器
数码管 0.56寸共阴8位 1 频率显示
驱动芯片 74HC595 2 串行转并行
运放 LM358 1 信号放大
比较器 LM393 1 信号整形
晶振 8MHz 1 系统时钟
按键 轻触开关 4 功能控制
三极管 S8050 8 位选驱动

2.2 电路原理图

2.2.1 信号调理电路

/* 信号调理电路设计 */
// 输入保护 → 放大电路 → 施密特整形 → 方波输出

// 输入保护电路
#define INPUT_PROTECTION
// 1. 100k电阻串联限流
// 2. 双向TVS管保护
// 3. 0.1uF耦合电容

// 放大电路(LM358)
// 第一级:10倍同相放大
// 第二级:可调增益(1-100倍)

// 施密特整形(LM393)
// 回差电压:0.5V
// 输出:0-3.3V方波

2.2.2 数码管驱动电路

/* 8位数码管驱动电路 */
// 段选:2片74HC595级联
// 位选:8个S8050 NPN三极管
// 扫描频率:200Hz(每位数码管点亮2.5ms)

// 74HC595连接方式:
// DS   -> MCU_PA0 (数据)
// SHCP -> MCU_PA1 (时钟)
// STCP -> MCU_PA2 (锁存)

// 位选三极管基极连接:
// DIG1 -> MCU_PB0
// DIG2 -> MCU_PB1
// ...
// DIG8 -> MCU_PB7

三、软件程序设计

3.1 主程序框架

#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <math.h>

// 系统定义
#define SYS_CLOCK       72000000  // 72MHz
#define MEASURE_TIMER   TIM2
#define DISPLAY_TIMER   TIM3
#define INPUT_PIN       GPIO_PIN_0
#define INPUT_PORT      GPIOA

// 数码管显示缓冲区
uint8_t display_buffer[8] = {0};
uint8_t dp_position = 0;  // 小数点位置

// 测量变量
volatile uint32_t pulse_count = 0;
volatile uint32_t gate_time_ms = 1000;  // 默认1秒闸门
volatile uint8_t measurement_ready = 0;
uint32_t frequency_hz = 0;

// 数码管段码表(共阴极)
const uint8_t segment_table[16] = {
    0x3F, // 0: 0b00111111
    0x06, // 1: 0b00000110
    0x5B, // 2: 0b01011011
    0x4F, // 3: 0b01001111
    0x66, // 4: 0b01100110
    0x6D, // 5: 0b01101101
    0x7D, // 6: 0b01111101
    0x07, // 7: 0b00000111
    0x7F, // 8: 0b01111111
    0x6F, // 9: 0b01101111
    0x77, // A: 0b01110111
    0x7C, // b: 0b01111100
    0x39, // C: 0b00111001
    0x5E, // d: 0b01011110
    0x79, // E: 0b01111001
    0x71  // F: 0b01110001
};

3.2 测量算法实现

3.2.1 直接测频法

/* 直接测频法 - 闸门时间内计数 */
void frequency_measure_direct(void) {
    static uint32_t last_count = 0;
    static uint32_t overflow_count = 0;
    static uint8_t measuring = 0;
    
    if (!measuring) {
        // 开始测量
        pulse_count = 0;
        overflow_count = 0;
        
        // 配置定时器为外部计数模式
        TIM_HandleTypeDef htim2 = {
            .Instance = TIM2,
            .Init = {
                .Prescaler = 0,
                .CounterMode = TIM_COUNTERMODE_UP,
                .Period = 0xFFFFFFFF,
                .ClockDivision = TIM_CLOCKDIVISION_DIV1
            }
        };
        
        HAL_TIM_Base_Start(&htim2);
        measuring = 1;
        return;
    }
    
    // 检查闸门时间是否到达
    if (HAL_GetTick() - measurement_start_time >= gate_time_ms) {
        // 停止计数
        measuring = 0;
        
        // 读取计数值
        uint32_t current_count = __HAL_TIM_GET_COUNTER(&htim2);
        
        // 计算总脉冲数
        uint64_t total_pulses = (uint64_t)overflow_count * 0xFFFFFFFF + current_count;
        
        // 计算频率
        frequency_hz = (uint32_t)(total_pulses * 1000 / gate_time_ms);
        
        // 标记测量完成
        measurement_ready = 1;
        
        // 重启测量
        frequency_measure_direct();
    }
}

3.2.2 周期测量法

/* 周期测量法 - 测量单个周期时间 */
float measure_period(void) {
    static uint32_t last_capture = 0;
    static uint32_t periods[10] = {0};
    static uint8_t period_index = 0;
    
    // 配置输入捕获
    TIM_HandleTypeDef htim4 = {
        .Instance = TIM4,
        .Init = {
            .Prescaler = 72 - 1,  // 1MHz计数频率
            .CounterMode = TIM_COUNTERMODE_UP,
            .Period = 0xFFFFFFFF,
            .ClockDivision = TIM_CLOCKDIVISION_DIV1
        }
    };
    
    // 等待上升沿
    while (HAL_GPIO_ReadPin(INPUT_PORT, INPUT_PIN) == 0);
    while (HAL_GPIO_ReadPin(INPUT_PORT, INPUT_PIN) == 1);
    
    uint32_t start_time = __HAL_TIM_GET_COUNTER(&htim4);
    
    // 等待下一个上升沿
    while (HAL_GPIO_ReadPin(INPUT_PORT, INPUT_PIN) == 0);
    while (HAL_GPIO_ReadPin(INPUT_PORT, INPUT_PIN) == 1);
    
    uint32_t end_time = __HAL_TIM_GET_COUNTER(&htim4);
    
    // 计算周期(微秒)
    uint32_t period_ticks = end_time - start_time;
    float period_us = (float)period_ticks;  // 1 tick = 1us
    
    // 存储到数组
    periods[period_index] = period_ticks;
    period_index = (period_index + 1) % 10;
    
    // 计算平均值
    uint32_t sum = 0;
    uint8_t count = 0;
    for (uint8_t i = 0; i < 10; i++) {
        if (periods[i] > 0) {
            sum += periods[i];
            count++;
        }
    }
    
    return (count > 0) ? (float)sum / count : 0;
}

3.2.3 自动量程选择

/* 自动选择测量方法和闸门时间 */
void auto_range_selection(void) {
    // 先用周期法测量大概频率
    float period_us = measure_period();
    
    if (period_us > 0) {
        float estimated_freq = 1000000.0 / period_us;
        
        if (estimated_freq < 100) {
            // 低频:使用周期法,长闸门
            gate_time_ms = 10000;  // 10秒
        } else if (estimated_freq < 1000) {
            // 中低频:使用直接测频,1秒闸门
            gate_time_ms = 1000;
        } else if (estimated_freq < 10000) {
            // 中频:使用直接测频,0.1秒闸门
            gate_time_ms = 100;
        } else {
            // 高频:使用直接测频,0.01秒闸门
            gate_time_ms = 10;
        }
        
        // 更新显示单位
        update_display_unit(estimated_freq);
    }
}

/* 更新显示单位 */
void update_display_unit(float freq_hz) {
    if (freq_hz >= 1000000) {
        // MHz显示
        dp_position = 2;  // 小数点在第3位后
    } else if (freq_hz >= 1000) {
        // kHz显示
        dp_position = 5;  // 小数点在第6位后
    } else {
        // Hz显示
        dp_position = 7;  // 小数点在第8位后
    }
}

3.3 数码管显示驱动

3.3.1 动态扫描显示

/* 数码管动态扫描 */
void display_scan(void) {
    static uint8_t current_digit = 0;
    
    // 关闭所有位选
    for (uint8_t i = 0; i < 8; i++) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 << i, GPIO_PIN_SET);
    }
    
    // 获取当前位的段码
    uint8_t segment_data = segment_table[display_buffer[current_digit]];
    
    // 添加小数点
    if (current_digit == dp_position) {
        segment_data |= 0x80;  // 点亮小数点
    }
    
    // 发送段码到74HC595
    send_to_595(segment_data);
    
    // 开启当前位数码管
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 << current_digit, GPIO_PIN_RESET);
    
    // 移动到下一位
    current_digit = (current_digit + 1) % 8;
}

/* 74HC595发送数据 */
void send_to_595(uint8_t data) {
    // 拉低锁存
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
    
    // 逐位发送
    for (int8_t i = 7; i >= 0; i--) {
        // 设置数据
        if (data & (1 << i)) {
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
        } else {
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
        }
        
        // 时钟上升沿
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_Delay(1);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    }
    
    // 锁存上升沿,更新输出
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
}

3.3.2 频率格式化显示

/* 格式化频率到显示缓冲区 */
void format_frequency(uint32_t freq_hz) {
    char buffer[9] = "        ";
    
    if (freq_hz >= 10000000) {
        // 8位数显示
        sprintf(buffer, "%8lu", freq_hz);
    } else if (freq_hz >= 1000000) {
        // MHz显示,保留3位小数
        float freq_mhz = freq_hz / 1000000.0;
        sprintf(buffer, "%7.3f", freq_mhz);
        dp_position = 4;  // 小数点在第5位
    } else if (freq_hz >= 1000) {
        // kHz显示,保留3位小数
        float freq_khz = freq_hz / 1000.0;
        sprintf(buffer, "%7.3f", freq_khz);
        dp_position = 4;  // 小数点在第5位
    } else {
        // Hz显示
        sprintf(buffer, "%8lu", freq_hz);
        dp_position = 7;  // 无小数点
    }
    
    // 转换到显示缓冲区
    for (uint8_t i = 0; i < 8; i++) {
        if (buffer[i] >= '0' && buffer[i] <= '9') {
            display_buffer[i] = buffer[i] - '0';
        } else if (buffer[i] == '.') {
            // 小数点已处理
        } else {
            display_buffer[i] = 0;  // 空格
        }
    }
    
    // 添加单位指示
    if (freq_hz >= 1000000) {
        display_buffer[7] = 0x37;  // M
    } else if (freq_hz >= 1000) {
        display_buffer[7] = 0x76;  // H (k的替代)
    } else {
        display_buffer[7] = 0x76;  // H
    }
}

3.4 中断服务程序

/* 定时器中断 - 用于数码管扫描 */
void TIM3_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE) != RESET) {
        __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
        
        // 数码管扫描
        display_scan();
    }
}

/* 外部中断 - 脉冲计数 */
void EXTI0_IRQHandler(void) {
    if (__HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_0) != RESET) {
        __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_0);
        
        pulse_count++;
        
        // 处理计数器溢出
        if (pulse_count == 0xFFFFFFFF) {
            pulse_count = 0;
        }
    }
}

3.5 按键处理

/* 按键功能定义 */
#define KEY_MODE      GPIO_PIN_8   // 模式切换
#define KEY_GATE      GPIO_PIN_9   // 闸门时间
#define KEY_HOLD      GPIO_PIN_10  // 保持/释放
#define KEY_RESET     GPIO_PIN_11  // 复位

/* 按键处理函数 */
void process_keys(void) {
    static uint32_t last_press_time[4] = {0};
    
    // 模式切换键
    if (HAL_GPIO_ReadPin(GPIOC, KEY_MODE) == GPIO_PIN_RESET) {
        if (HAL_GetTick() - last_press_time[0] > 200) {
            // 切换测量模式
            // 实现模式切换逻辑
            last_press_time[0] = HAL_GetTick();
        }
    }
    
    // 闸门时间键
    if (HAL_GPIO_ReadPin(GPIOC, KEY_GATE) == GPIO_PIN_RESET) {
        if (HAL_GetTick() - last_press_time[1] > 200) {
            // 切换闸门时间
            switch (gate_time_ms) {
                case 10: gate_time_ms = 100; break;
                case 100: gate_time_ms = 1000; break;
                case 1000: gate_time_ms = 10000; break;
                default: gate_time_ms = 10; break;
            }
            last_press_time[1] = HAL_GetTick();
        }
    }
    
    // 保持键
    if (HAL_GPIO_ReadPin(GPIOC, KEY_HOLD) == GPIO_PIN_RESET) {
        if (HAL_GetTick() - last_press_time[2] > 200) {
            measurement_ready = 0;  // 暂停测量
            last_press_time[2] = HAL_GetTick();
        }
    }
    
    // 复位键
    if (HAL_GPIO_ReadPin(GPIOC, KEY_RESET) == GPIO_PIN_RESET) {
        if (HAL_GetTick() - last_press_time[3] > 200) {
            // 复位测量
            pulse_count = 0;
            frequency_hz = 0;
            measurement_ready = 1;
            last_press_time[3] = HAL_GetTick();
        }
    }
}

四、完整主程序

/* 频率计主程序 */
int main(void) {
    // HAL库初始化
    HAL_Init();
    
    // 系统时钟配置
    SystemClock_Config();
    
    // GPIO初始化
    MX_GPIO_Init();
    
    // 定时器初始化
    MX_TIM2_Init();  // 测量定时器
    MX_TIM3_Init();  // 显示扫描定时器
    MX_TIM4_Init();  // 周期测量定时器
    
    // 中断配置
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
    
    // 启动显示扫描定时器
    HAL_TIM_Base_Start_IT(&htim3);
    
    // 启动测量定时器
    HAL_TIM_Base_Start(&htim2);
    
    // 显示开机画面
    display_boot();
    HAL_Delay(1000);
    
    // 主循环
    while (1) {
        // 处理按键
        process_keys();
        
        // 检查测量是否完成
        if (measurement_ready) {
            // 格式化并显示频率
            format_frequency(frequency_hz);
            
            // 清除标志
            measurement_ready = 0;
        }
        
        // 自动量程选择
        static uint32_t last_auto_range = 0;
        if (HAL_GetTick() - last_auto_range > 5000) {  // 每5秒自动量程
            auto_range_selection();
            last_auto_range = HAL_GetTick();
        }
        
        // 短暂延时
        HAL_Delay(10);
    }
}

/* 系统时钟配置 */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
    // 配置HSE
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
    
    // 配置时钟
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

/* GPIO初始化 */
void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIO时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    
    // 74HC595控制引脚
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 数码管位选引脚
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                         |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    // 按键引脚
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
    // 输入信号引脚
    GPIO_InitStruct.Pin = INPUT_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(INPUT_PORT, &GPIO_InitStruct);
    
    // 配置外部中断
    HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

/* 显示开机画面 */
void display_boot(void) {
    // 显示"FREQ TEST"
    uint8_t boot_display[8] = {0x71, 0x50, 0x79, 0x76, 0x00, 0x78, 0x79, 0x78};
    
    for (uint8_t i = 0; i < 8; i++) {
        display_buffer[i] = boot_display[i];
    }
    
    // 更新显示
    for (uint8_t i = 0; i < 100; i++) {
        display_scan();
        HAL_Delay(1);
    }
}

参考资料 数码管显示的频率计 www.youwenfan.com/contentcnt/133788.html

五、PCB设计要点

5.1 布局建议

PCB布局分层:
顶层:数码管、按键、接口
中层:电源走线
底层:MCU、驱动芯片、信号调理

关键信号线:
1. 输入信号线:最短路径,屏蔽处理
2. 数码管扫描线:足够宽度,降低压降
3. 时钟线:远离模拟信号线

5.2 电源设计

/* 电源电路设计 */
// 输入:9V DC
// 稳压1:7805 → 5V(数字部分)
// 稳压2:AMS1117-3.3 → 3.3V(MCU核心)

// 去耦电容配置:
// 每个IC的VCC-GND:0.1uF陶瓷电容
// 电源输入输出:10uF电解 + 0.1uF陶瓷
// 模拟部分:增加LC滤波

六、测试与校准

6.1 测试程序

/* 频率计自检程序 */
void self_test(void) {
    printf("频率计自检开始...\n");
    
    // 1. 数码管测试
    printf("测试数码管...\n");
    for (uint8_t i = 0; i < 16; i++) {
        for (uint8_t j = 0; j < 8; j++) {
            display_buffer[j] = i;
        }
        HAL_Delay(200);
    }
    
    // 2. 按键测试
    printf("测试按键...\n");
    // 等待按键按下
    
    // 3. 测量功能测试
    printf("测试测量功能...\n");
    
    // 使用内部时钟产生测试信号
    // 测试1kHz信号
    generate_test_signal(1000);
    HAL_Delay(1100);
    
    uint32_t measured = frequency_hz;
    float error = fabs((float)measured - 1000.0) / 1000.0 * 100.0;
    
    printf("测试频率: 1000Hz\n");
    printf("测量结果: %luHz\n", measured);
    printf("误差: %.2f%%\n", error);
    
    if (error < 1.0) {
        printf("自检通过!\n");
    } else {
        printf("自检失败!\n");
    }
}

6.2 校准程序

/* 频率计校准 */
void calibration(void) {
    // 需要标准频率源(如10MHz OCXO)
    float reference_freq = 10000000.0;  // 10MHz
    
    // 测量10次取平均
    uint64_t total = 0;
    for (uint8_t i = 0; i < 10; i++) {
        // 执行测量
        frequency_measure_direct();
        while (!measurement_ready);
        total += frequency_hz;
    }
    
    float average = total / 10.0;
    float error = (average - reference_freq) / reference_freq;
    
    // 计算校准系数
    float cal_factor = 1.0 / (1.0 + error);
    
    printf("校准结果:\n");
    printf("参考频率: %.0fHz\n", reference_freq);
    printf("平均测量: %.2fHz\n", average);
    printf("误差: %.4f%%\n", error * 100);
    printf("校准系数: %.6f\n", cal_factor);
    
    // 保存到EEPROM
    save_calibration(cal_factor);
}

七、性能优化

7.1 提高测量精度

/* 高精度测量技术 */
// 1. 多次测量取平均
// 2. 数字滤波
// 3. 温度补偿
// 4. 时钟校准

// 移动平均滤波
#define FILTER_SIZE 16
uint32_t filter_buffer[FILTER_SIZE] = {0};
uint8_t filter_index = 0;

uint32_t moving_average(uint32_t new_value) {
    filter_buffer[filter_index] = new_value;
    filter_index = (filter_index + 1) % FILTER_SIZE;
    
    uint64_t sum = 0;
    uint8_t count = 0;
    for (uint8_t i = 0; i < FILTER_SIZE; i++) {
        if (filter_buffer[i] > 0) {
            sum += filter_buffer[i];
            count++;
        }
    }
    
    return (count > 0) ? (uint32_t)(sum / count) : 0;
}

7.2 提高测量速度

/* 快速测量算法 */
// 使用更高的时钟频率
// 优化中断处理
// 使用DMA传输

// 高频测量优化
void high_freq_measure(void) {
    // 使用更短的闸门时间
    if (frequency_hz > 100000) {
        gate_time_ms = 10;  // 10ms闸门
    } else if (frequency_hz > 10000) {
        gate_time_ms = 100; // 100ms闸门
    }
    
    // 使用预分频
    if (frequency_hz > 1000000) {
        // 使用硬件分频
        setup_prescaler(10);
    }
}

八、常见问题解决

8.1 问题诊断表

问题现象 可能原因 解决方法
显示不亮 电源问题 检查电源电压和连接
显示乱码 段码错误 检查段码表和连接
测量不准 时钟误差 校准系统时钟
高频不响应 带宽不足 检查信号调理电路
按键不灵 上拉电阻 检查按键电路
显示闪烁 扫描过快 调整扫描频率

8.2 调试技巧

/* 调试功能 */
void debug_mode(void) {
    // 显示原始计数值
    printf("脉冲计数: %lu\n", pulse_count);
    printf("闸门时间: %lums\n", gate_time_ms);
    printf("计算频率: %luHz\n", frequency_hz);
    
    // 显示各引脚状态
    printf("输入引脚: %d\n", HAL_GPIO_ReadPin(INPUT_PORT, INPUT_PIN));
    printf("按键状态: %d %d %d %d\n",
           HAL_GPIO_ReadPin(GPIOC, KEY_MODE),
           HAL_GPIO_ReadPin(GPIOC, KEY_GATE),
           HAL_GPIO_ReadPin(GPIOC, KEY_HOLD),
           HAL_GPIO_ReadPin(GPIOC, KEY_RESET));
}

九、扩展功能

9.1 添加串口通信

/* 串口通信功能 */
void uart_init(void) {
    // 配置串口1,波特率115200
    USART_HandleTypeDef huart1 = {
        .Instance = USART1,
        .Init = {
            .BaudRate = 115200,
            .WordLength = UART_WORDLENGTH_8B,
            .StopBits = UART_STOPBITS_1,
            .Parity = UART_PARITY_NONE,
            .Mode = UART_MODE_TX_RX,
            .HwFlowCtl = UART_HWCONTROL_NONE,
            .OverSampling = UART_OVERSAMPLING_16
        }
    };
    
    HAL_UART_Init(&huart1);
}

/* 发送测量数据到PC */
void send_to_pc(void) {
    char buffer[50];
    sprintf(buffer, "Freq: %luHz\r\n", frequency_hz);
    HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 1000);
}

9.2 添加数据存储

/* 数据存储功能 */
#define MAX_RECORDS 100
uint32_t freq_records[MAX_RECORDS] = {0};
uint8_t record_index = 0;

void save_record(uint32_t freq) {
    freq_records[record_index] = freq;
    record_index = (record_index + 1) % MAX_RECORDS;
}

void display_history(void) {
    for (uint8_t i = 0; i < MAX_RECORDS; i++) {
        if (freq_records[i] > 0) {
            printf("记录%d: %luHz\n", i, freq_records[i]);
        }
    }
}

十、项目总结

这个数码管显示的频率计具有以下特点:

优点:

  1. 宽测量范围:1Hz-10MHz覆盖
  2. 高精度:±0.1%测量精度
  3. 直观显示:8位数码管清晰显示
  4. 多功能:支持频率、周期、占空比测量
  5. 易用性:4个按键实现所有功能
  6. 低成本:使用常见元件,成本可控

应用场景:

  1. 电子实验室频率测量
  2. 通信设备调试
  3. 教学演示
  4. 生产线测试
  5. 维修检测

改进方向:

  1. 增加USB接口
  2. 添加无线传输
  3. 实现自动量程
  4. 增加波形显示
  5. 支持更高频率

 

专注于matlab/simulink,电子电路,编程