FPGA开发实验(8)- UART串口实验

一、UART简介与实验任务

1.1 UART简介

UART即通用异步收发器,串口是串行接口的简称,两者组合起来就是通用异步串行通信接口,它包括了 RS232、RS499、RS423、RS422 和 RS485等接口标准规范和总线标准规范,因此串口广泛应用于嵌入式、工业控制等领域。
1)数据通信的概念
通信方式在日常的应用中一般分为串行通信并行通信并行通信:并行通信是指多比特数据同时通过并行线进行传送,一般以字或字节为单位并行进行传输,这种传输方式用的通信线多、成本高,故不宜进行远距离通信,因此并行通信一般用于近距离的通信,通常传输距离小于 30 米;串行通信是指数据在一条数据线上,一比特接一比特地按顺序传送的方式,串行通信的的特点:一是节省传输线,大大降低了使用成本,二是数据传送速度慢,这一点在大位宽的数据传输上尤为明显。
串行通信一般有 2 种通信方式:同步串行通信异步串行通信。同步串行通信需要通信双方在同一时钟的控制下同步传输数据;异步串行通信是指具有不规则数据段传送特性的串行数据传输。在常见的通信总线协议中,I2C,SPI 属于同步通信而 UART 属于异步通信。

1.2 UART通信协议介绍

UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位。如下图所示:
在这里插入图片描述
**起始位:**当不传输数据时,UART 数据传输线通常保持高电压电平。若要开始数据传输,发送 UART会将传输线从高电平拉到低电平并保持 1 个波特率周期。
数据帧:数据帧包含所传输的实际数据。如果使用奇偶校验位,数据帧长度可以是 5 位到 8 位。如果不使用奇偶校验位,数据帧长度可以是 9 位。在大多数情况下,数据以最低有效位优先方式发送。
奇偶校验:奇偶性描述数字是偶数还是奇数。通过奇偶校验位,接收 UART 判断传输期间是否有数据发生改变
停止位:为了表示数据包结束,发送 UART 将数据传输线从低电压驱动到高电压并保持 1 到 2 位时间。
数据位可选择为 5、6、7、8 位,其中 8 位数据位是最常用的,在实际应用中一般都选择 8 位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择 1 位(默认),1.5 或 2 位。串口通信的速率用
波特率
表示,它表示每秒传输二进制数据的位数,单位是 bps(位/秒),常用的波特率有 9600、19200、38400、57600 以及 115200 等。“波特率”,常用符号“Baud”表示,其单位为“波特每(Bps)。 故若115200 的波特率,串口发送或者接收 1bit 数据的时间为一个波特,即 1/115200s。

1.3 实验任务

本节实验任务是上位机通过串口调试助手发送数据给 FPGA,FPGA 通过 USB 串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。

二、程序设计

2.1 串口接收模块设计

串口接收模块我们的输入信号主要有系统时钟信号、系统复位信号与串口接收端口。我们需要知道接受完成以及将接受到的数据存储起来,所以输出为接收完成标志和串口接收数据信号。模块接口框图如下所示:
在这里插入图片描述
本次实验波特率采用115200bps,在50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = 50_000_000/115200bit ≈ 434 个系统时钟周期。为了消除亚稳态,将进来的串口接收数据打拍
波形图如下:
在这里插入图片描述
RTL代码uart_rx如下:

module uart_loopback(
    input sys_clk,
    input sys_rst_n,
    
    input uart_rxd,
    output uart_txd
);

parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200;

wire    uart_rx_done;
wire [7:0] uart_rx_data;

uart_rx u_uart_rx #(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
    )
    (
    .clk         (sys_clk),
    .rst_n       (sys_rst_n), 
    .uart_rxd    (uart_rxd),   
    .uart_rx_done(uart_rx_done),
    .uart_rx_data(uart_rx_data)
    );

uart_tx u_uart_tx#(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
    )
    (
    .clk         (sys_clk),
    .rst_n       (sys_rst_n),
    .uart_tx_en  (uart_rx_done),
    .uart_tx_data(uart_rx_data),

    .uart_tx_busy(),
    .uart_txd    (uart_txd)
    );

endmodule

2.2 串口发送模块设计

串口发送模块我们的输入信号主要有系统时钟信号、系统复位信号以及发送使能信号和待发送数据,输出信号主要有发送忙状态标志和串口发送端口。模块接口框图如下所示:
在这里插入图片描述
波形图如下:
在这里插入图片描述
RTL代码如下:

module uart_tx(
    input clk,
    input rst_n,
    input uart_tx_en,
    input [7:0] uart_tx_data,
    
    output reg uart_tx_busy,
    output reg uart_txd
);

parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200;
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;

reg [7:0] tx_data_t;
reg [15:0] baud_cnt;
reg [3:0] tx_cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        tx_data_t <= 8'd0;
    else if(uart_tx_en)
        tx_data_t <= uart_tx_data;
    else
        tx_data_t <= tx_data_t;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        uart_tx_busy <= 1'b0;
    else if(uart_tx_en)
        uart_tx_busy <= 1'b1;
    else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - 1'b1)
        uart_tx_busy <= 1'b0;
    else
        uart_tx_busy <= uart_tx_busy;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        baud_cnt <= 16'd0;
    else if(uart_tx_busy) begin
        if(baud_cnt < BAUD_CNT_MAX - 1'b1)
            baud_cnt <= baud_cnt + 1'b1;
        else
            baud_cnt <= 16'd0;
    end
    else
        baud_cnt <= 16'd0;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        tx_cnt <= 4'd0;
    else if(uart_tx_busy) begin
        if(baud_cnt < BAUD_CNT_MAX - 1'b1)
            tx_cnt <= tx_cnt + 1'b1;
        else
            tx_cnt <= tx_cnt;
    end
    else
        tx_cnt <= 4'd0;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        uart_txd <= 1'b1;
    else if(uart_tx_busy) begin
        case(tx_cnt)
            4'd0:uart_txd <= 1'b0;
            4'd1:uart_txd <= tx_data_t[1];
            4'd2:uart_txd <= tx_data_t[2];
            4'd3:uart_txd <= tx_data_t[3];
            4'd4:uart_txd <= tx_data_t[4];
            4'd5:uart_txd <= tx_data_t[5];
            4'd6:uart_txd <= tx_data_t[6];
            4'd7:uart_txd <= tx_data_t[7];
            4'd8:uart_txd <= tx_data_t[8];
            4'd9:uart_txd <= 1'b1;
            default:uart_txd <= 1'b1;
        endcase
    end
    else
        uart_txd <= 1'b1;
end

endmodule

2.3 顶层模块

RTL代码如下:

module uart_loopback(
    input sys_clk,
    input sys_rst_n,
    
    input uart_rxd,
    output uart_txd
);

parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200;

wire    uart_rx_done;
wire [7:0] uart_rx_data;

uart_rx u_uart_rx #(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
    )
    (
    .clk         (sys_clk),
    .rst_n       (sys_rst_n), 
    .uart_rxd    (uart_rxd),   
    .uart_rx_done(uart_rx_done),
    .uart_rx_data(uart_rx_data)
    );

uart_tx u_uart_tx#(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
    )
    (
    .clk         (sys_clk),
    .rst_n       (sys_rst_n),
    .uart_tx_en  (uart_rx_done),
    .uart_tx_data(uart_rx_data),

    .uart_tx_busy(),
    .uart_txd    (uart_txd)
    );

endmodule

三、仿真

仿真代码:

`timescale 1ns/1ns

module tb_uart_loopback();

parameter CLK_PERIOD = 20;

reg sys_clk; //周期20ns
reg sys_rst_n;
reg uart_rxd;
wire uart_txd;

initial begin
    sys_clk <= 1'b0;
    sys_rst_n <= 1'b0;
    uart_rxd <= 1'b1;
    #200 sys_rst_n <= 1'b1;  //空闲
    #1000 uart_rxd <= 1'b0;  //起始位
    #8680 uart_rxd <= 1'b1;  //D0
    #8680 uart_rxd <= 1'b0;  //D1
    #8680 uart_rxd <= 1'b1;  //D2
    #8680 uart_rxd <= 1'b0;  //D3
    #8680 uart_rxd <= 1'b1;  //D4
    #8680 uart_rxd <= 1'b0;  //D5
    #8680 uart_rxd <= 1'b1;  //D6
    #8680 uart_rxd <= 1'b0;  //D7
    #8680 uart_rxd <= 1'b1;  //停止位
    #8680 uart_rxd <= 1'b1;  //空闲位
end

always #(CLK_PERIOD/2) sys_clk = ~sys_clk;


uart_loopback u_uart_loopback(
    .sys_clk   (sys_clk),
    .sys_rst_n (sys_rst_n),
    .uart_rxd  (uart_rxd),
    .uart_txd  (uart_txd)
);

endmodule

仿真结果:

在这里插入图片描述

四、下载验证

4.1 引脚约束

#时序约束
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
#IO 引脚约束
#----------------------系统时钟---------------------------
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS33} [get_ports sys_clk]
#----------------------系统复位---------------------------
set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
#----------------------USB UART---------------------------
set_property -dict {PACKAGE_PIN U5 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN T6 IOSTANDARD LVCMOS33} [get_ports uart_txd]

4.2 上版验证

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值