AX7020 学习笔记(三)UART 持续更新中

三.常用外设接口

1.UART

串口介绍

串口大家都已经非常熟悉了这里简单带大家复习一下,以及补充一些我们在用单片机上使用串口与在FPGA上的区别

1.串口分为同步串口(USRT)与异步串口(UART),区别在于是否需要时钟线。

2.数据帧:一帧数据帧有十位起始低电平,停止高电平中间八位数据位,空闲状态为高电平。

3.波特率与比特率:波特率即每秒传输的码元数符号为Baud单位Bps波特每秒,由于串口是1bit进行传输,一个码元就是一个二进制数。每秒传输的信息量为传输速率简称比特率,单位为bps。 比特率 = 波特率 * 单个调制状态对应的二进制位数

老式的电脑配备有RS232或者RS485接口,RS232单端输入输出只是将电平转换,而RS485是单端转差分,如今大多是USB接口,将USB的差分信号与串口的TTL信号互相转化。

由于AX7020的USB转串口在PS端而我有懒得去找USB转TTL模块故本次只有仿真没有上板。

实验任务

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

程序设计

顶层模块设计
在这里插入图片描述

module uart_loopback(
    input       sys_clk     ,
    input       sys_rst_n   ,
    
    //UART端口
    input       uart_rxd    ,
    output      uart_txd    
);

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

//wire define
wire       uart_rx_done;
wire [7:0] uart_rx_data;

//**************************************
//**        main code
//**************************************

//串口接收模块
uart_rx #(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
)
    u_uart_rx(
        .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 #(
    .CLK_FREQ (CLK_FREQ),
    .UART_BPS (UART_BPS)
    )
    u_uart_tx(
        .clk         (sys_clk     ),
        .rst_n       (sys_rst_n   ),
        .uart_tx_en  (uart_rx_done),
        .uart_tx_data(uart_rx_data),
        .uart_txd    (uart_txd    ),
        .uart_tx_busy(            )
    );
    
endmodule

串口接收模块

在这里插入图片描述

module uart_rx(
    input            clk         ,
    input            rst_n       ,
    
    input            uart_rxd    ,
    output reg       uart_rx_done,
    output reg [7:0] uart_rx_data
);

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

//reg define 
reg         uart_rxd_d0;
reg         uart_rxd_d1;
reg         uart_rxd_d2;
reg         rx_flag    ;
reg [3:0 ]  rx_cnt     ;
reg [15:0]  baud_cnt   ;
reg [7:0 ]  rx_data_t  ;

//wire define
wire        start_en   ;

//*********************************************
//**            main code
//*********************************************
//捕获接收端口的下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);

//针对异步信号处理
always @(posedge clk or negedge rst_n)begin
    if (!rst_n) begin
        uart_rxd_d0 <= 1'b0;
        uart_rxd_d0 <= 1'b0;
        uart_rxd_d0 <= 1'b0;
    end
    else begin
        uart_rxd_d0 <= uart_rxd;
        uart_rxd_d1 <= uart_rxd_d0;
        uart_rxd_d2 <= uart_rxd_d1;
    end
end

//给接收标志位
always @(posedge clk or negedge rst_n)begin
    if (!rst_n)
        rx_flag <= 1'b0;
    else if (start_en)
        rx_flag <= 1'b1;
    else if ((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1))
        rx_flag <= 1'b0;
    else
        rx_flag <= rx_flag;
end

//波特率的计数器赋值
always @(posedge clk or negedge rst_n)begin
    if (!rst_n)
        baud_cnt <= 16'd0;
    else if (rx_flag)begin  
        if (baud_cnt < BAUD_CNT_MAX - 1'b1)
            baud_cnt <= baud_cnt + 16'b1;
        else
            baud_cnt <= 16'd0;
    end
    else
        baud_cnt <= 16'd0;
end

//对于接收数据计数器(rx_cnt)进行赋值
always @(posedge clk or negedge rst_n)begin
    if (!rst_n)
        rx_cnt <= 4'd0;
    else if (rx_flag)begin
        if (baud_cnt == BAUD_CNT_MAX - 1'b1)
            rx_cnt <= rx_cnt +1'b1;
        else
            rx_cnt = rx_cnt;
    end
    else 
    rx_cnt <= 4'd0;
end

//根据rx_cnt来寄存rxd端口的数据
always@(posedge clk or negedge rst_n)begin
    if (!rst_n)
        rx_data_t <= 8'b0;
    else if (rx_flag)begin
        if (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1)begin
            case (rx_cnt)
                4'd1:rx_data_t[0] <= uart_rxd_d2;
                4'd2:rx_data_t[1] <= uart_rxd_d2;
                4'd3:rx_data_t[2] <= uart_rxd_d2;
                4'd4:rx_data_t[3] <= uart_rxd_d2;
                4'd5:rx_data_t[4] <= uart_rxd_d2;
                4'd6:rx_data_t[5] <= uart_rxd_d2;
                4'd7:rx_data_t[6] <= uart_rxd_d2;
                4'd8:rx_data_t[7] <= uart_rxd_d2;
                default:;
            endcase
        end
        else 
            rx_data_t <= rx_data_t;
    end
    else
        rx_data_t <= 8'b0;
end

//给接收完成信号核接收到的数据赋值
always @(posedge clk or negedge rst_n)begin
    if (!rst_n)begin
        uart_rx_done <= 1'b0;
        uart_rx_data <= 8'b0;
    end
    else if (rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 2 - 1'b1)begin
        uart_rx_done <= 1'b1     ;
        uart_rx_data <= rx_data_t;
    end
    else begin
        uart_rx_done <= 1'b0;
        uart_rx_data <= uart_rx_data;
    end
end

endmodule

串口发送模块设计
在这里插入图片描述

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

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

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

//******************************************
//**        main code
//******************************************

//当uart_tx_en为高时,寄存器输出并行数据,并拉高BUSY信号
always @(posedge clk or negedge rst_n)begin
    if (!rst_n) begin
        tx_data_t <= 8'b0;
        uart_tx_busy <= 1'b0;
    end
    //发送使能时,寄存要发送的数据,并拉高BUSY信号
    else if (uart_tx_en)begin
        tx_data_t <= uart_tx_data;
        uart_tx_busy <= 1'b1;
    end
    //当计数到停止位结束时,停止发送过程
    else if (tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 16)begin
        tx_data_t <= 8'b0;
        uart_tx_busy <= 1'b0;
    end
    else begin
        tx_data_t <= tx_data_t;
        uart_tx_busy <= uart_tx_busy;
    end
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 + 16'b1;
        else
            baud_cnt <= 16'd0;
    end
    else
        baud_cnt <= 16'd0;
end

//tx_cnt进行赋值
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

//根据tx_cnt来给uart发送端口赋值
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[0];
            4'd2:uart_txd <= tx_data_t[1];
            4'd3:uart_txd <= tx_data_t[2];
            4'd4:uart_txd <= tx_data_t[3];
            4'd5:uart_txd <= tx_data_t[4];
            4'd6:uart_txd <= tx_data_t[5];
            4'd7:uart_txd <= tx_data_t[6];
            4'd8:uart_txd <= tx_data_t[7];
            4'd9:uart_txd <= 1'b1        ;
            default:uart_txd <= 1'b1;
        endcase
    end
    else
        uart_txd <= 1'b1;
end

endmodule

tb文件

`timescale 1ns/1ns
module tb_uart_loopback();

//parameter define
parameter CLK_PERIOD = 20;

//reg define 
reg         sys_clk     ;
reg         sys_rst_n   ;
reg         uart_rxd    ;

//wire define   
wire        uart_txd    ;

//*************************************
//              main code
//*************************************

//发送8'h55 8'b0101_0101
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
仿真验证

在这里插入图片描述
关于HDMI的超详细教程等我学会了马上就发(:

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值