基于FPGA的频率计与串口通信系统设计与实现

一、系统概述

基于FPGA(Field-Programmable Gate Array) 实现数字频率计与UART串口通信功能,核心通过测频法测量外部输入信号的频率,并将结果以ASCII字符串形式通过串口发送至PC端(如串口助手)。系统采用模块化设计,包含时钟分频、信号整形、计数控制、UART发送四大核心模块,支持1Hz~10MHz频率测量(可扩展),精度达±1Hz(1秒闸门时间)。

二、硬件设计

2.1 核心组件

模块 型号/参数 功能说明
FPGA Altera Cyclone IV EP4CE6F17C8 主控制器,实现频率测量与串口通信逻辑
时钟源 50MHz有源晶振(FPGA外部输入) 提供系统基准时钟(用于分频、计数、UART)
信号输入 SMA接口(外接信号发生器/待测信号) 输入待测频率信号(TTL电平,0~3.3V)
串口输出 USB-TTL模块(CH340芯片) FPGA的UART_TX引脚→PC的USB接口(虚拟COM口)
电源 5V/2A DC电源 为FPGA开发板、信号源、USB-TTL供电

2.2 硬件连接

模块 FPGA引脚(EP4CE6F17C8) 说明
50MHz时钟 PIN_23(CLK_50M) 系统主时钟输入
待测信号 PIN_45(SIG_IN) 外部信号输入(TTL电平)
UART_TX PIN_67(UART_TXD) 串口发送引脚(接CH340 RX)
复位信号 PIN_12(RST_N) 低电平复位(接按键)

三、软件设计(Verilog HDL)

3.1 系统架构

50MHz主时钟
时钟分频模块
1Hz门控信号
UART波特率时钟
待测信号SIG_IN
信号整形模块
计数模块
控制逻辑
数据转换模块
UART发送模块
UART_TXD输出至PC

3.2 核心模块代码实现

3.2.1 时钟分频模块(生成1Hz门控与UART波特率时钟)
复制代码
module clk_divider(
    input        clk_50m,    // 50MHz主时钟
    input        rst_n,      // 复位(低有效)
    output reg   gate_1hz,   // 1Hz门控信号(高电平1秒)
    output reg   uart_clk    // UART波特率时钟(9600bps,5200分频)
);

// 1Hz门控信号(50MHz→1Hz,分频系数=50,000,000-1=49,999,999)
reg [25:0] cnt_1hz;  // 26位计数器(2^26=67,108,864 > 50M)
always @(posedge clk_50m or negedge rst_n) begin
    if (!rst_n) begin
        cnt_1hz <= 26'd0;
        gate_1hz <= 1'b0;
    end else begin
        if (cnt_1hz == 26'd49_999_999) begin  // 1秒计数
            cnt_1hz <= 26'd0;
            gate_1hz <= ~gate_1hz;  // 翻转生成1Hz方波(高电平1秒,低电平1秒)
        end else begin
            cnt_1hz <= cnt_1hz + 1'b1;
        end
    end
end

// UART波特率时钟(50MHz→9600bps,分频系数=50,000,000/9600≈5208.33→取5200)
reg [12:0] cnt_uart;  // 13位计数器(2^13=8192 > 5200)
always @(posedge clk_50m or negedge rst_n) begin
    if (!rst_n) begin
        cnt_uart <= 13'd0;
        uart_clk <= 1'b0;
    end else begin
        if (cnt_uart == 13'd5199) begin  // 5200分频(0~5199共5200个周期)
            cnt_uart <= 13'd0;
            uart_clk <= ~uart_clk;
        end else begin
            cnt_uart <= cnt_uart + 1'b1;
        end
    end
end

endmodule
3.2.2 信号整形与计数模块(测频法核心)
复制代码
module freq_counter(
    input        clk_50m,    // 50MHz时钟
    input        rst_n,      // 复位
    input        gate_1hz,   // 1Hz门控信号(高电平计数)
    input        sig_in,     // 待测信号(TTL电平)
    output reg [23:0] cnt_out  // 24位计数值(最大16,777,216,支持10MHz@1秒)
);

// 信号边沿检测(生成单周期脉冲)
reg sig_d1, sig_d2;
wire sig_pos = sig_d1 & ~sig_d2;  // 上升沿检测
always @(posedge clk_50m or negedge rst_n) begin
    if (!rst_n) begin
        sig_d1 <= 1'b0;
        sig_d2 <= 1'b0;
    end else begin
        sig_d1 <= sig_in;
        sig_d2 <= sig_d1;
    end
end

// 计数逻辑(门控高电平时计数)
always @(posedge clk_50m or negedge rst_n) begin
    if (!rst_n) begin
        cnt_out <= 24'd0;
    end else if (gate_1hz) begin  // 门控高电平期间计数
        if (sig_pos) begin  // 检测到上升沿计数
            cnt_out <= cnt_out + 1'b1;
        end
    end else begin  // 门控低电平期间清零
        cnt_out <= 24'd0;
    end
end

endmodule
3.2.3 UART发送模块(按协议发送频率数据)
复制代码
module uart_tx(
    input        clk_50m,    // 50MHz时钟
    input        rst_n,      // 复位
    input        uart_clk,   // 9600bps波特率时钟
    input [23:0] data_in,   // 24位计数值(频率=计数值/1秒)
    output reg   txd         // UART发送引脚
);

// 数据转换:计数值→ASCII字符串(格式:"FREQ:XXXXXX Hz\r\n")
reg [7:0] tx_data[0:15];  // 发送缓冲区(16字节)
reg [3:0] tx_cnt;         // 发送字节计数(0~15)
reg [3:0] bit_cnt;        // 位计数(0~9:起始位+8数据位+停止位)
reg [7:0] current_byte;   // 当前发送字节

// 计数值转ASCII(示例:计数值1234→"1234")
task convert_data;
    input [23:0] data;
    integer i;
    begin
        tx_data[0] = "F"; tx_data[1] = "R"; tx_data[2] = "E"; tx_data[3] = "Q";  // "FREQ:"
        tx_data[4] = ":"; tx_data[5] = " ";
        // 千位(假设最大6位,补空格)
        tx_data[6] = (data / 100000) % 10 + "0";  // 十万位
        tx_data[7] = (data / 10000) % 10 + "0";   // 万位
        tx_data[8] = (data / 1000) % 10 + "0";    // 千位
        tx_data[9] = (data / 100) % 10 + "0";     // 百位
        tx_data[10] = (data / 10) % 10 + "0";    // 十位
        tx_data[11] = data % 10 + "0";            // 个位
        tx_data[12] = " "; tx_data[13] = "H"; tx_data[14] = "z"; tx_data[15] = "\r\n";  // " Hz\r\n"
    end
endtask

// UART发送状态机(空闲→发送起始位→发送8数据位→发送停止位)
always @(posedge uart_clk or negedge rst_n) begin
    if (!rst_n) begin
        txd <= 1'b1;  // 空闲状态高电平
        tx_cnt <= 4'd0;
        bit_cnt <= 4'd0;
        current_byte <= 8'd0;
    end else begin
        if (tx_cnt < 4'd16) begin  // 16字节待发送
            if (bit_cnt == 4'd0) begin  // 起始位(低电平)
                txd <= 1'b0;
                current_byte <= tx_data[tx_cnt];  // 加载当前字节
                bit_cnt <= bit_cnt + 1'b1;
            end else if (bit_cnt >= 4'd1 && bit_cnt <= 4'd8) begin  // 数据位(LSB先发)
                txd <= current_byte[bit_cnt-1];
                bit_cnt <= bit_cnt + 1'b1;
            end else if (bit_cnt == 4'd9) begin  // 停止位(高电平)
                txd <= 1'b1;
                bit_cnt <= 4'd0;
                tx_cnt <= tx_cnt + 1'b1;  // 下一个字节
            end
        end else begin  // 发送完成,空闲
            txd <= 1'b1;
            tx_cnt <= 4'd0;
        end
    end
end

// 顶层调用数据转换(每次计数完成后触发)
always @(posedge clk_50m) begin
    if (gate_1hz == 1'b0 && cnt_out != 24'd0) begin  // 门控结束且有计数
        convert_data(cnt_out);  // 转换计数值为ASCII
        tx_cnt <= 4'd0;  // 重置发送计数
    end
end

endmodule
3.2.4 顶层模块(模块互联)
复制代码
module top_freq_meter(
    input        clk_50m,    // 50MHz时钟
    input        rst_n,      // 复位(低有效)
    input        sig_in,     // 待测信号输入
    output       txd         // UART发送引脚
);

// 内部信号
wire gate_1hz;
wire uart_clk;
wire [23:0] cnt_out;

// 实例化时钟分频模块
clk_divider u_clk_divider(
    .clk_50m(clk_50m),
    .rst_n(rst_n),
    .gate_1hz(gate_1hz),
    .uart_clk(uart_clk)
);

// 实例化频率计数模块
freq_counter u_freq_counter(
    .clk_50m(clk_50m),
    .rst_n(rst_n),
    .gate_1hz(gate_1hz),
    .sig_in(sig_in),
    .cnt_out(cnt_out)
);

// 实例化UART发送模块
uart_tx u_uart_tx(
    .clk_50m(clk_50m),
    .rst_n(rst_n),
    .uart_clk(uart_clk),
    .data_in(cnt_out),
    .txd(txd)
);

endmodule

参考代码 基于FPGA的频率计与串口通信 www.youwenfan.com/contentcss/161291.html

四、关键问题与解决方案

4.1 频率测量精度

  • 问题:门控信号非精确1秒导致计数误差。

  • 解决:用50MHz高精度时钟分频生成1Hz门控(误差<1μs),并通过FPGA内部PLL校准时钟。

4.2 信号抖动干扰

  • 问题:待测信号含毛刺导致误计数。

  • 解决 :在信号输入端添加施密特触发器 (如74HC14),或在FPGA中用两级寄存器同步+边沿检测滤除抖动。

4.3 UART通信丢包

  • 问题:波特率时钟分频误差导致数据错位。

  • 解决:精确计算分频系数(50MHz/9600=5208.33,取5200或5208,实测调整后取5200误差最小)。

五、测试与验证

5.1 硬件测试

  1. 信号输入:用信号发生器输出1Hz、100Hz、1kHz、1MHz方波,接入FPGA的SIG_IN引脚;

  2. 串口连接:FPGA的UART_TX通过USB-TTL模块连接PC,打开串口助手(波特率9600,8N1);

  3. 预期结果:串口助手接收字符串如"FREQ: 01000000 Hz"(对应1MHz信号,计数值10,000,000)。

5.2 精度验证

  • 输入10MHz信号,计数值应为10,000,000±1(误差<0.0001%),符合设计要求。

六、总结

基于FPGA实现了频率计与串口通信系统,核心是测频法计数与UART协议发送。通过模块化Verilog代码,完成了从信号输入、整形计数到数据转换与串口输出的全流程。系统扩展性强,可通过增加测周法模块(低频信号)、LCD显示模块或以太网传输进一步提升功能。

相关推荐
FPGA_ADDA2 小时前
国产复旦微FPGA+DSP 6U VPX处理板
fpga开发·全国产·复旦微690t·ft6678dsp·6u vpx板卡·jfm7vx690t36
fengfuyao9854 小时前
基于FPGA的简易电子密码锁设计(Verilog实现)
fpga开发
hoiii1875 小时前
104键PS2接口标准键盘C语言驱动程序
c语言·fpga开发·计算机外设
jllllyuz15 小时前
基于FPGA的通信信号源设计
fpga开发
Saniffer_SH19 小时前
【每日一题】一台可编程的PCIe 6.0主机 + 一套自动化CTS验证平台 + 一个轻量级链路分析系统
运维·服务器·测试工具·fpga开发·自动化·计算机外设·硬件架构
952361 天前
计算机组成原理 - 主存储器
单片机·嵌入式硬件·学习·fpga开发
简简单单做算法1 天前
【第2章>第1节】基于FPGA的图像放大和插值处理概述
计算机视觉·fpga开发·双线性插值·线性插值·图像放大·均值插值·最邻近插值
木心术11 天前
OpenClaw FPGA资源利用率优化深度指南
人工智能·fpga开发
发光的沙子1 天前
FPGA----zynq 7000与zynqMP内存区域保留方法
fpga开发