数码管显示的频率计 – 设计与实现
数码管显示频率计系统,包含硬件电路、软件程序、测量算法和显示控制。
一、系统总体设计
1.1 系统架构
输入信号 → 信号调理 → 微控制器 → 数码管显示
(放大整形) (测量计算) (动态扫描)
1.2 技术指标
- 测量范围:1Hz ~ 10MHz
- 测量精度:±0.1% ±1个字
- 显示位数:8位LED数码管
- 测量方式:直接测频法、周期测量法
- 闸门时间:0.1s, 1s, 10s可选
- 输入灵敏度:100mV RMS
二、硬件电路设计
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]);
}
}
}
十、项目总结
这个数码管显示的频率计具有以下特点:
优点:
- 宽测量范围:1Hz-10MHz覆盖
- 高精度:±0.1%测量精度
- 直观显示:8位数码管清晰显示
- 多功能:支持频率、周期、占空比测量
- 易用性:4个按键实现所有功能
- 低成本:使用常见元件,成本可控
应用场景:
- 电子实验室频率测量
- 通信设备调试
- 教学演示
- 生产线测试
- 维修检测
改进方向:
- 增加USB接口
- 添加无线传输
- 实现自动量程
- 增加波形显示
- 支持更高频率