FPGA实现通过写一个串口接收模块实现上位机发送数据然后再数码管上显示数字

1. 串口通信基础

串口通信(UART)是一种异步串行通信协议,广泛应用于嵌入式系统和FPGA设计中。它使用简单的两根线(TX和RX)实现全双工通信,是连接上位机和下位机的常用接口。

1.1 串口通信格式

UART通信以帧为单位传输数据,每帧包含以下部分:

  • 起始位:1位低电平,标志数据传输开始
  • 数据位:5-9位,通常为8位,表示实际传输的数据
  • 校验位:可选,用于错误检测(奇校验或偶校验)
  • 停止位:1-2位高电平,标志数据传输结束

1.2 波特率与时钟计算

波特率(Baud Rate)表示每秒传输的比特数。常见的波特率有9600、115200等。在FPGA设计中,需要根据系统时钟频率计算每个比特的采样点数:

// 计算每个比特的时钟周期数
parameter CLK_FREQ = 50_000_000;  // 系统时钟50MHz
parameter BAUD_RATE = 115200;     // 波特率115200
parameter rx_1bit = CLK_FREQ / BAUD_RATE;  // 每个比特的时钟周期数

2. 串口接收模块设计(不带校验位)

下面是一个完整的串口接收模块(UART RX)实现,用于接收上位机发送的16进制数据。

module rx(
    input wire clk,        // 系统时钟输入,50MHz
    input wire rst_n,      // 异步复位信号,低电平有效
    input wire rx,         // 串口接收数据线

    output wire [7:0] data // 接收到的8位数据输出

);
    // 系统参数定义
    parameter CLK = 50000000;      // 系统时钟频率:50MHz
    parameter baudrate = 115200;   // 串口波特率:115200 bps
    parameter rx_1bit = CLK / baudrate;  // 每个比特位所需的时钟周期数
    
    // 同步器:用于消除亚稳态,对异步输入信号rx进行两级同步
    reg rx_z1, rx_z2;  // 两级同步寄存器
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin  // 复位时初始化
            rx_z1 <= 1;   // 默认高电平(空闲状态)
            rx_z2 <= 1;
        end
        else begin
            rx_z1 <= rx;   // 第一级同步
            rx_z2 <= rx_z1; // 第二级同步
        end
    end
    
    // 下降沿检测:检测起始位(从高电平跳变到低电平)
    wire rx_negedge;
    assign rx_negedge = !rx_z1 && rx_z2;  // 当前为低电平,上一拍为高电平
    
    // 比特计数器:用于计数每个比特的采样位置
    reg [12:0] cnt;  // 13位计数器,足够计数10个比特位
    reg en;          // 计数器使能信号,高电平有效
    
    // 使能控制逻辑:控制接收过程的开始和结束
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            en <= 0;  // 复位时禁用计数器
        end
        else if(rx_negedge && en == 0)  // 检测到起始位且当前未在接收
            en <= 1;  // 开始接收一帧数据
        else if(cnt == 10 * rx_1bit - 1) // 10个比特位接收完成(1起始+8数据+1停止)
            en <= 0;   // 停止接收
    end

    // 计数器逻辑:在使能期间计数时钟周期
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cnt <= 0;  // 复位时清零
        else begin
            if(en) begin  // 只有在使能期间才计数
                if(cnt == 10 * rx_1bit - 1)  // 达到一帧的最大计数值
                    cnt <= 0;  // 归零,准备下一帧
                else
                    cnt <= cnt + 1;  // 正常计数
            end
        end
    end

    // 数据采样寄存器:临时存储接收到的8位数据
    reg [7:0] data_out;
    
    // 数据采样逻辑:在每个数据位的中间位置采样
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            data_out <= 0;  // 复位时清零
        // 在比特位的中间位置采样,提高抗干扰能力
        else if(cnt == rx_1bit + (rx_1bit >> 1))  // 第1个数据位中间(起始位后1.5个周期)
            data_out[0] <= rx;  // 采样最低位(LSB first)
        else if(cnt == rx_1bit * 2 + (rx_1bit >> 1))  // 第2个数据位中间
            data_out[1] <= rx;
        else if(cnt == rx_1bit * 3 + (rx_1bit >> 1))  // 第3个数据位中间
            data_out[2] <= rx;
        else if(cnt == rx_1bit * 4 + (rx_1bit >> 1))  // 第4个数据位中间
            data_out[3] <= rx;
        else if(cnt == rx_1bit * 5 + (rx_1bit >> 1))  // 第5个数据位中间
            data_out[4] <= rx;
        else if(cnt == rx_1bit * 6 + (rx_1bit >> 1))  // 第6个数据位中间
            data_out[5] <= rx;
        else if(cnt == rx_1bit * 7 + (rx_1bit >> 1))  // 第7个数据位中间
            data_out[6] <= rx;
        else if(cnt == rx_1bit * 8 + (rx_1bit >> 1))  // 第8个数据位中间
            data_out[7] <= rx;  // 采样最高位
    end
    
    // 数据输出寄存器:在停止位开始时锁存数据
    reg [7:0] data_reg;
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            data_reg <= 0;  // 复位时清零
        else if(cnt == rx_1bit * 9)  // 第9个比特位(停止位)开始位置
            data_reg <= data_out;  // 锁存接收到的完整数据
        else 
            data_reg <= data_reg;  // 保持当前值
    end
    
    assign data = data_reg;  // 输出最终接收到的数据
    
endmodule

 

 

3.数码管显示模块(在我之前的博客当中也有提到,这里就不再详细介绍)使用数码管设计秒表:Verilog实现与原理详解-CSDN博客

1.数码管显示模块

module seven_seg (
    input wire clk,
    input wire rst_n,
    input wire [11:0] data,
    output wire [5:0] sel,
    output wire [7:0] seg
);
    parameter cnt_qiehuan_max=5000;
    reg [12:0]cnt_qiehuan;
    reg flag;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            cnt_qiehuan<=0;
            flag<=0;
        end
        else if(cnt_qiehuan==cnt_qiehuan_max-1)begin
            cnt_qiehuan<=0;
            flag<=1;
        end

        else
            begin
                cnt_qiehuan<=cnt_qiehuan+1;
                flag<=0;
            end
    end 

    reg [2:0]state;
    parameter [2:0] STATE0=0,
                    STATE1=1,
                    STATE2=2,
                    STATE3=3,
                    STATE4=4,
                    STATE5=5;    
    reg [5:0] sel_out;
    reg [3:0]data_out;
//位选    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            state=STATE0;
            sel_out=6'b111111;
            data_out=4'b0000;
        end
        else if(flag)begin
            case(state)
            STATE0:begin
                sel_out=6'b111110;
                state=STATE1;
                data_out=data[3:0];
            end
            STATE1:begin
                sel_out=6'b111101;
                state=STATE2;
                data_out=data[7:4];
            end
            STATE2:begin
                sel_out=6'b111011;
                state=STATE0;
                data_out=data[11:8];
            end
            // STATE3:begin
            //     sel_out=6'b110111;
            //     state=STATE4;
            //     data_out=data[15:12];
            // end
            // STATE4:begin
            //     sel_out=6'b101111;
            //     state=STATE5;
            //     data_out=data[19:16];
            // end
            // STATE5:begin
            //     sel_out=6'b011111;
            //     state=STATE0;
            //     data_out=data[23:20];
            // end
            default:;
            endcase
        end 
    end
//段选    
    reg [7:0] seg_out;
    always@(*)begin
        case(data_out)
            0:seg_out=8'b11000000;
            1:seg_out=8'b11111001;
            2:seg_out=8'b10100100;
            3:seg_out=8'b10110000;
            4:seg_out=8'b10011001;
            5:seg_out=8'b10010010;
            6:seg_out=8'b10000010;
            7:seg_out=8'b11111000;
            8:seg_out=8'b10000000;
            9:seg_out=8'b10010000;
            default: seg_out = 8'b11111111;  
        endcase
    end
    assign sel=sel_out;
    assign seg=seg_out;
endmodule

2.BCD模块:

module BCD (
    input [7:0] data,
    output[11:0] data_out
);
    reg [19:0] temp;
    integer i;
    always @(*) begin
        temp={8'b0,data};
        for(i=0;i<8;i=i+1)begin
            if(temp[11:8]>=5)begin
                temp[11:8]=temp[11:8]+3;
            end
             if(temp[15:12]>=5)begin
                temp[15:12]=temp[15:12]+3;
            end
             if(temp[19:16]>=5)begin
                temp[19:16]=temp[19:16]+3;
            end
            
            temp=temp<<1;
        end
    end
    assign data_out=temp[19:8];
endmodule

 

4. 顶层模块与系统集成

4.1 顶层模块设计(只需要将我的子模块例化一遍即可)

module top #(
    parameter CLK=50000000,
    parameter baudrate=115200,
    parameter rx_1bit=CLK/baudrate
) (
    input wire clk,
    input wire rst_n,
    input wire rx,
    output wire [5:0]sel,
    output wire [7:0]seg
);
    wire [7:0] data;
    wire [11:0] data_out;
    BCD bcd_tb(
        .data(data),
        .data_out(data_out)
    );
    seven_seg seven_seg_tb(
        .clk(clk),
        .rst_n(rst_n),
        .sel(sel),
        .seg(seg),
        .data(data_out)
    );

    rx rx_test(
    .clk(clk),
    .rst_n(rst_n),
    .rx(rx),
    .data(data)
 )  ;
endmodule

5. 总结与注意事项

5.1 关键设计要点

  • 波特率匹配确保FPGA内部时钟分频与上位机波特率一致(115200)
  • 采样点选择:在比特中间采样可以提高抗干扰能力
  • 同步处理:使用两级寄存器同步异步信号,避免亚稳态
  • 帧完整性:完整接收10个比特(1起始+8数据+1停止)

5.2 常见问题与调试

  1. 数据接收错误:检查波特率计算是否正确
  2. 数码管不显示:验证段码表和位选信号
  3. 时序问题:使用仿真工具验证时序逻辑
  4. 硬件连接:确认串口线连接正确(TX接RX,RX接TX)

5.3 扩展功能建议

  • 添加奇偶校验功能
  • 实现多字节数据接收
  • 添加接收缓冲区
  • 支持多种波特率可配置
  • 添加错误检测和重传机制

通过本文介绍的串口接收模块和数码管显示模块,可以实现上位机发送16进制数据并在FPGA开发板的数码管上显示的功能。读者可以根据实际需求修改波特率、数据位数等参数,并添加更多功能模块。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值