FPGA开发——UART回环实现之发送模块的设计和数据回环整体实现

news2024/9/21 12:40:34

一、简介


        在上一篇文章当中我们实现了UART接收模块的相关设计和功能实现,在今天的文章中我们继续实现剩下的发送模块的相关设计和完成完整的串口数据回环的实验。

在文章的最后我会给出完整的工程,给小伙伴们参考。

二、接收模块的基本设计


        在接收模块设计的时候我们就采用了状态机的实现方式,所以在本次的发送模块的设计中我们也采用状态机进行实现。本将状态机划分为四个,第一个就是空闲状态,表示发送模块没有接收到来自接收模块的信号和数据,第二个是开始状态,和接受模块一样也是表示设备检测到起始位,第三个发送数据过程状态,用于表示设备发送数据的过程,最后一个就是停止位,表示设备发送数据完成。

84fb58dcea744f928d012776893bf7ef.png

三、接收模块的波形图绘制


        根据上面的状态机,我们可以据此展开波形图的绘制,分别就是对于来自接收模块转换的数据以及发送信号标志位,状态机转移、相关计数器、busy等信号的表示。

        当发送模块接收到来自接收模块的标志信号时,状态机就从IDLE进入到START,然后利用波特率计数器计数1bit的起始位,来到DATA,利用波特率计数器和bit计数器用于接收数据,接收完数据之后进入STOP,最后利用波特率计数器计数1bit的停止位,状态又回到初始的IDLE状态。

eb445838cadd4df0bc52c963362cb1d3.png

        

四、发送模块代码编写

1、设计代码的编写

在上一篇文章中的基础之上新建一个uart_tx.v文件,如下:在代码中可能会看到中间部分会和屏蔽了一样,这里应该是verilog使用其他语言格式造成的,这里不影响,直接使用代码就行,哪些代码时没有屏蔽的。

//发送模块
module uart_tx (
    input               clk         ,
    input               rst_n       ,
    input   [7:0]       tx_din      ,//进入发送模块准备发送的数据
    input               tx_din_vld  ,//要发送数据的有效信号
    output  reg         tx_dout     ,//串行发送出去的数据
    output              busy      //发送一字节完成信号
);

//参数定义
    localparam  IDLE  = 4'b0001,//空闲状态
                START = 4'b0010,//发送起始位
                DATA  = 4'b0100,//发送数据
                STOP  = 4'b1000;//发送停止位

    parameter   CLOCK_FRQ   = 50_000_000,//50M时钟
                BAUD_9600   = 9600      ,//波特率设置
                BAUD_14400  = 14400     ,
                BAUD_19200  = 19200     ,
                BAUD_38400  = 38400     ,
                BAUD_115200 = 115200    ;

//中间信号定义 
    reg  [12:0]     cnt_bps     ;//波特率计数器,最大值是50M/9600 13位宽
    wire            add_cnt_bps ;
    wire            end_cnt_bps ;

    reg  [3:0]      cnt_bit     ;//bit计数器,只记数据位 8位
    wire            add_cnt_bit ;
    wire            end_cnt_bit ;

    reg  [7:0]      tx_din_r    ;//寄存一次数据,否则第一个数据会被丢弃

    reg  [3:0]      state_c     ;//现态
    reg  [3:0]      state_n     ;//次态

    wire            idle2start  ;//状态机转移条件
    wire            start2data  ;
    wire            data2stop   ;
    wire            stop2idle   ;

//状态机
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
       state_c <= IDLE;
   else
       state_c <= state_n;
end
 
always @(*)begin
    case (state_c)
        IDLE  : begin
            if(idle2start)begin
                state_n = START;
            end
            else begin
                state_n = state_c;
            end
        end
        START : begin
            if(start2data)begin
                state_n = DATA;
            end
            else begin
                state_n = state_c;
            end
        end
        DATA  : begin
            if(data2stop)begin
                state_n = STOP;
            end
            else begin
                state_n = state_c;
            end
        end
        STOP  : begin
            if(stop2idle)begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end        
        default: state_n = IDLE; 
    endcase       
end

assign idle2start = state_c == IDLE  && (tx_din_vld );//数据来临有效即可以发
assign start2data = state_c == START && (end_cnt_bps);//发完1bit
assign data2stop  = state_c == DATA  && (end_cnt_bit );//发完8bit
assign stop2idle  = state_c == STOP  && (end_cnt_bps);//发完1bit

//tx_din_r
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        tx_din_r <= 'd0;
    end 
    else if(tx_din_vld)begin 
        tx_din_r <= tx_din;
    end 
end
//cnt_bps
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_bps <= 0;
    end 
    else if(add_cnt_bps)begin 
        if(end_cnt_bps)begin 
            cnt_bps <= 0;
        end
        else begin 
            cnt_bps <= cnt_bps + 1;
        end 
    end
    else begin
        cnt_bps <= cnt_bps;
    end
end 

assign add_cnt_bps = (state_c != IDLE);
assign end_cnt_bps = add_cnt_bps && (cnt_bps == (CLOCK_FRQ/BAUD_115200 - 1));

//cnt_bit  
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt_bit <= 0;
    end 
    else if(add_cnt_bit)begin 
        if(end_cnt_bit)begin 
            cnt_bit <= 0;
        end
        else begin 
            cnt_bit <= cnt_bit + 1;
        end 
    end
    else begin
        cnt_bit <= cnt_bit;
    end
end 

assign add_cnt_bit = (state_c == DATA && end_cnt_bps);//每发完1bit才会加1
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 8 - 1);

//tx_dout
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        tx_dout <= 1'b1;
    end 
    else begin 
        case (state_c)
            IDLE : tx_dout <= 1'b1;
            START: tx_dout <= 1'b0;
            DATA : tx_dout <= tx_din_r[cnt_bit];//LSB发送数据
            STOP : tx_dout <= 1'b1;
            default: tx_dout <= 1'b1;
        endcase
    end 
end

//busy
assign busy = state_c !=IDLE;

endmodule

2、测试文件的编写

新建一个uart_tx_tb.v文件,如下:先对编写的发送模块进行一个设计再进行整个回环实验进行一个实现。这里直接对输入数据进行一个8位赋值,观察发送模块发送出的数据。

`timescale 1ns/1ns
 
module uart_tx_tb();
//激励信号定义 
reg          clk          ;
reg          rst_n        ;
reg          tx_din_vld   ;
wire         tx_dout      ;
wire         tx_done   ;
//时钟周期参数定义 
parameter CYCLE = 20;

uart_tx uart_tx_inst(
    /*input          */ .clk       (clk       )  ,
    /*input          */ .rst_n     (rst_n     )  ,
    /*input   [7:0]  */ .tx_din    (8'b10100101    )  ,//进入发送模块准备发送的数据
    /*input          */ .tx_din_vld(tx_din_vld && ~busy)  ,//要发送数据的有效信号
    /*output  reg    */ .tx_dout   (tx_dout   )  ,//串行发送出去的数据
    /*output  reg    */ .busy      (busy   )   //发送一字节完成信号
);
//产生时钟 
initial  clk = 1'b0; 
always #(CYCLE/2) clk = ~clk; 
 
//产生激励 
initial  begin 
    rst_n = 1'b0; 
    #(CYCLE*10+3); 
    rst_n = 1'b1; 
    repeat (10)begin
        //起始位
        tx_din_vld = 1;
        #20;
        #(434*CYCLE);
        
        #(434*CYCLE*8);
        //停止位
        #(434*CYCLE);

        #(CYCLE*100);
        $stop;
    end
end 
endmodule

3、波形仿真

通过波形图我们可以看到发送模块的发送端发出的数据和我们设定的值一样,发送模块设计没有问题。

a159276f9c7247ffb08386084f484631.png

五、UART回环实验的实现

1、编写顶层模块

编写一个顶层模块,将接收模块和发送模块两者结合起来,从而实现数据回环,新建一个top.v文件,如下:

 

module top (
    input       clk     ,
    input       rst_n   ,
    input       rx      ,
    output      tx          
);
wire  [7:0] rx_data;
wire        rx_dout_vld;
wire            busy;
//不加控制模块
uart_rx uart_rx_inst(  
    /*input		   */	  .clk		   (clk   ),
    /*input		   */	  .rst_n	   (rst_n ),
    /*input		   */	  .din_rx      (rx    ),
    /*output [7:0]*/      .dout_data   (rx_data  ),
    /*output	   */     .dout_flag   (rx_dout_vld  )	
);	
uart_tx uart_tx_inst(
    /*input        */ .clk        (clk      ) ,
    /*input        */ .rst_n      (rst_n    ) ,
    /*input   [7:0]*/ .tx_din     (rx_data     ) ,//进入发送模块准备发送的数据
    /*input        */ .tx_din_vld (rx_dout_vld && ~busy    ) ,//要发送数据的有效信号
    /*output  reg  */ .tx_dout    (tx       ) ,//串行发送出去的数据
    /*output  reg  */ .busy    (busy   )  //发送一字节完成信号
);

endmodule

2、顶层测试文件的编写

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

//激励信号定义 
    reg				tb_clk  	;
    reg				tb_rst_n	;
    reg				rx		;

//输出信号定义	 
    wire            tx     ;
//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    top tb_top( 
    /*input				  */.clk		(tb_clk     ),
    /*input				  */.rst_n	    (tb_rst_n   ),
    /*input               */.rx         (rx     ),
    /*output              */.tx         (tx    )
);	

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;

//产生激励
    integer i;
    initial  begin 
        tb_rst_n = 1'b1;
        rx = 1;//空闲为高电平
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;
        #1002;

        //模拟UART接收模块的串行输入
        repeat(20)begin
            //起始位
            rx = 0;
            #(434*CLOCK_CYCLE);
            //数据位:
            for (i=0;i<8;i=i+1) begin
                rx = $random;
                #(434*CLOCK_CYCLE);
            end
            //停止位
            rx = 1;
            #(434*CLOCK_CYCLE);
            //两次发送的间隔时间
            #(CLOCK_CYCLE*1000);
        end
        
        // #(CLOCK_CYCLE*434*11);
        // $stop;
    end

endmodule 

3、波形仿真

从最后的仿真图中我们可以看到接收端和发送端最后的数据都是一样,说明我们的串口回环实验设计成功。

4ea5b71b32ab436eaf773efd4fd817e4.png

4、下板验证

经过我们最后的下板验证之后,发现数据能够正确的进行回环,如图所示:

d03ad8a49460441481a1455ebcbc5b9b.png

 到这里UART回环就设计完成了。

六、完整工程

 

https://pan.baidu.com/s/12IrXc6A00a_zLP3SHXlriA?pwd=7f9i 
提取码:7f9i

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何下载老版本 的mysql

方案1&#xff1a;打开地址&#xff0c;即可 MySQL :: Download MySQL Community Server (Archived Versions) 输入地址 https://downloads.mysql.com/archives/community/ 方案2&#xff1a; MySQL :: Download MySQL Installer https://dev.mysql.com/downloads/windows…

推荐使用阿贝云免费云服务器、免费虚拟主机

官网地址&#xff1a;https://www.abeiyun.com 阿贝云免费云服务器&#xff0c;性价比之选&#xff01; 不得不说&#xff0c;阿贝云的免费云服务器真的太棒了&#xff01;不仅免费&#xff0c;还能提供如此优质的服务。服务器的配置虽然不算高端&#xff0c;但对于一般的应用…

【MySQL】数据库约束

系列文章目录 第一章 数据库基础 第二章 数据库基本操作 文章目录 系列文章目录前言约束关键字一览NOT NULLUNIQUEDEFAULTPRIMARY KEY自增主键 FOREIGN KEY总结 前言 在学习了数据库的增删改查操作之后&#xff0c;接下来就需要进阶的学习关键字来完善SQL语句的条件。学习数据…

宝塔部署Django项目(华为云)

1、登录华为云: 2、点击远程登录: 3、打开宝塔网址(华为云选的是centos) 4、在华为终端复制指令点击运行: 会显示安装完成,出现一个页面记录一下,方便以后登录: 5、复制外网面板地址到浏览器地址栏,输入账号,密码登录,在这里进行配置: 一、Django项目的设置以及压…

Gafgyt僵尸网络针对云原生环境,SSH弱密码成GPU挖矿新目标

近日&#xff0c;网络安全研究人员发现了 Gafgyt 僵尸网络的一个新变种&#xff0c;它以 SSH 密码较弱的机器为目标&#xff0c;最终利用其 GPU 计算能力在被攻击的实例上挖掘加密货币。 Aqua Security 研究员 Assaf Morag 在周三的一份分析报告中说&#xff1a;"这表明&…

虎牙的商业化畅想

2024年8月13日&#xff0c;虎牙公司公布了2024年第二季度财报。财报显示&#xff0c;2024年第二季度&#xff0c;虎牙公司总收入达15.4亿元。其中&#xff0c;来自游戏相关服务、广告和其他业务的收入同比增长152.7%至3.1亿元。在非美国通用会计准则下&#xff0c;该季度归属于…

KDP数据平台:以实战案例验证技术领先力

本文由智领云 LeetTools 工具自动生成 申请试用&#xff1a; https://www.leettools.com/feedback/ 在当今快速发展的技术环境中&#xff0c;数据平台的选择对企业的数字化转型和业务发展至关重要。智领云开源KDP&#xff08;Kubernetes Data Platform&#xff09;在数据处理和…

状态dp或滑动窗口

前言&#xff1a;这个题目可以用状态dp来做&#xff0c;其实还有一个思路&#xff0c;类似滑动窗口&#xff0c;如果有遇到第二个0&#xff0c;左指针加一 class Solution { public:int longestSubarray(vector<int>& nums) {int n nums.size();vector<vector<…

Mac 自定义键盘快捷键打开系统应用

在 macOS 中&#xff0c;可以通过自定义键盘快捷键来打开系统应用程序。这可以更快捷地访问常用的应用程序&#xff0c;提高工作效率。下面以创建活动监视器快捷键为例介绍“Mac 自定义键盘快捷键打开系统应用”的一般步骤&#xff1a; 创建活动监视器快捷键 键入command空格打…

DVWA | Brute Force通关指南

目录 Low Medium High Impossible Brute force主要是通过爆破达到渗透目的&#xff1a; Low 查看源代码&#xff1a; <?phpif( isset( $_GET[ Login ] ) ) {// Get username$user $_GET[ username ];// Get password$pass $_GET[ password ];$pass md5( $pass );//…

软件工程概述(上)

1、软件的概念、特点和分类 要了解软件工程&#xff0c;首先让我们重新认识一下软件。如今可以说是一个软件定义一切的时代&#xff0c;虽然人工智能发展的如火如荼&#xff0c;但究其本质&#xff0c;核心还是软件。那么&#xff0c;如何给软件下一个定义呢&#xff1f;软件又…

再造行业旗舰爆款,书客L2 Pro重塑医护级”护眼更养眼”,被誉为最强旗舰级标杆!

近日&#xff0c;作为照明领域黑马的SUKER书客正式发布护眼台灯L2 Pro&#xff0c;在各项标准都远超国AA的基础上&#xff0c;推出了RRT2.0红光养眼技术、第三代SDIT自适应调光技术以及全新一代紫光激发全光谱技术等一系列首创护眼黑科技&#xff0c;从强悍的护眼效果到惊艳的光…

《python语言程序设计》2018版第7章第04题Fan类,设计一个名为Fan的类表示一个风扇

8点下课到家也9点多。眼睛抬不起来 明天到周二要为好几十个兄弟姐妹们&#xff0c;完成日志和反馈表&#xff0c;还要保证低错误。明天还要完成单位的一些工作。 哦对了&#xff0c;还有这些兄弟姐妹们的视频。 好先看一下Fan类的代码&#xff0c;它继续存在exCode07 class Fa…

【uni-app】使用天气API做一个天气APP(全过程)- 实况、逐小时、40日

头一次使用uni-app写代码, 现学现卖, 写的不好的地方见谅, 申请个appid就可以运行 切换城市界面比较简单, 城市名称需要符合天气api参数规则, 录入的城市不要带市区县; 格式如: 青岛、铁西、海淀、沛县 APP效果 功能说明 实况天气逐小时预报未来7日天气未来40日天气空气质量详…

【Qt】QWidget的toolTip属性

QWidget的toolTip属性 如果一个GUI程序&#xff0c;界面比较复杂&#xff0c;按钮比较多&#xff0c;使用toolTip可以设置当鼠标悬停在控件上的时候&#xff0c;可以弹出一个提示。 API说明 setToolTip 设置 toolTip. ⿏标悬停在该 widget 上时会有提⽰说明. setToolTipDur…

10 ARM 体系

10 ARM 体系 ARM体系1、基本概念1.1 常见的处理器1.2 ARM7三级指令流水线1.3 初识PC寄存器 2、 ARM核的七种工作模式3、ARM核七种异常 ARM体系 1、基本概念 1.1 常见的处理器 PowerPC处理器&#xff1a;飞思卡尔MPC系列 DSP:TI达芬奇系列 FPGA&#xff1a;Xilinx赛灵思的ZYN…

python小游戏之摇骰子猜大小

最近学习Python的随机数&#xff0c;逻辑判断&#xff0c;循环的用法&#xff0c;就想找一些练习题&#xff0c;比如小游戏猜大小&#xff0c;程序思路如下&#xff1a; 附上源代码如下&#xff1a; 摇骰子的函数&#xff0c;这个函数其实并不需要传任何参数&#xff0c;调用后…

理性看待、正确理解 AI 中的 Scaling “laws”

编者按&#xff1a;LLMs 规模和性能的不断提升&#xff0c;让人们不禁产生疑问&#xff1a;这种趋势是否能一直持续下去&#xff1f;我们是否能通过不断扩大模型规模最终实现通用人工智能&#xff08;AGI&#xff09;&#xff1f;回答这些问题对于理解 AI 的未来发展轨迹至关重…

独立开发者,技术只是其一

刚开始做独立开发者时&#xff0c;总是想着追究技术的深度。我的软件用了特别牛的技术&#xff0c;我的软件这个功能技术花了很多个日日夜夜才实现&#xff01;真的好有成就感啊&#xff01; “额&#xff0c;请问一下&#xff0c;你技术这么牛&#xff0c;卖出去多少钱啦&…

linux如何配置主机间免密ssh连接

1. 生成key 其实就是配置从本地节点到远程主机之间基于key&#xff08;无密码的方式&#xff09;的SSH连接&#xff1a; # 生成ssh keyssh-keygen2. 拷贝key到远端主机 # 拷贝ssh key到远程主机&#xff0c;ssh的时候就不需要输入密码了# ssh-copy-id remoteuserremoteserve…