UART协议——FPGA代码篇

一.串口(UART)协议简介

        UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用UART 串口通信的端口,这些参数必须匹配,否则通

图片

  • 起始位:表示数据传输的开始,电平逻辑为“0” 。

  • 数据位:可能值有5、6、7、8、9,表示传输这几个bit 位数据。一般取值为8,因为一个ASCII 字符值为8 位。

  • 奇偶校验位:用于接收方对接收到的数据进行校验,校验“1” 的位数为偶数(偶校验) 或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。

  • 停止位:表示一帧数据的结束。电平逻辑为“1”。

  • 波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit) 数来表示,其单位为每秒比特数bit/s(bps)。常见的波特率值有4800、9600、14400、38400、115200 等,数值越大数据传输的越快,波特率为115200 表示每秒钟传输115200 位数据。

 

二.串口发送端设计 

  (1)流程设计 

         当使能信号有效后拉高发送标志信号,标志模块进入发送过程;当发送完10个bit后,拉低发送标志信号,标志发送过程结束。使能信号有效时将要发送的数据寄存。

  (2)verilog代码

// ** 功能 : 1、基于FPGA的串口发送驱动模块;
//        2、可设置波特率BPS、主时钟CLK_FRE;
//        3、起始位1bit,数据位8bit,停止位1bit,无奇偶校验;                                                                                                                                             
//        4、每发送1个字节后拉高uart_tx_done一个周期,可用于后续发送多字节模块。                                                                                                                                             
// *******************************************************************************************************  
 
module uart_tx
#(
  parameter  integer  BPS    = 9_600    ,  //发送波特率
  parameter   integer  CLK_FRE  = 50_000_000  //主时钟频率
)
(
//系统接口
  input       sys_clk      ,      //系统时钟
  input       sys_rst_n    ,      //系统复位,低电平有效
//用户接口  
  input  [7:0]   uart_tx_data  ,      //需要通过UART发送的数据,在uart_tx_en为高电平时有效
  input      uart_tx_en    ,      //发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送  
  output  reg    uart_tx_done  ,      //成功发送1BYTE数据后拉高一个周期
  output   reg    uart_txd          //UART发送数据线tx
);
 
 
//当发送使能信号到达时,寄存待发送的数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    uart_tx_data_reg <=8'd0;
  else if(uart_tx_en)              //要发送有效的数据
    uart_tx_data_reg <= uart_tx_data;    //寄存需要发送的数据      
  else 
    uart_tx_data_reg <= uart_tx_data_reg;
end    
//当发送使能信号到达时,进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    tx_state <=1'b0;  
  else if(uart_tx_en)                        
    tx_state <= 1'b1;            //发送信号有效则进入发送过程
  //发送完了最后一个数据则退出发送过程    
  else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))    
    tx_state <= 1'b0;                                              
  else 
    tx_state <= tx_state;  
end
 
//发送数据完毕后拉高发送完毕信号一个周期,指示一个字节发送完毕
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    uart_tx_done <=1'b0;
  //发送数据完毕后拉高发送完毕信号一个周期     
  else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))                                             
    uart_tx_done <=1'b1;                    
  else 
    uart_tx_done <=1'b0;
end
//进入发送过程后,启动时钟计数器与发送个数bit计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    clk_cnt <= 32'd0;
    bit_cnt <= 4'd0;
  end
  else if(tx_state) begin                    //在发送状态
    if(clk_cnt < BPS_CNT - 1'd1)begin            //一个bit数据没有发送完
      clk_cnt <= clk_cnt + 1'b1;              //时钟计数器+1
      bit_cnt <= bit_cnt;                  //bit计数器不变
    end          
    else begin                        //一个bit数据发送完了  
      clk_cnt <= 32'd0;                  //清空时钟计数器,重新开始计时
      bit_cnt <= bit_cnt+1'b1;              //bit计数器+1,表示发送完了一个bit的数据
    end          
  end          
  else begin                          //不在发送状态
    clk_cnt <= 32'd0;                             //清零
    bit_cnt <= 4'd0;                              //清零
  end
end
endmodule

   (3)前仿真代码

`timescale 1ns/1ns  //定义时间刻度
 
module tb_uart_tx();
 
reg       sys_clk      ;      
reg       sys_rst_n    ;      
reg [7:0]    uart_tx_data  ;
reg       uart_tx_en    ;
      
wire        uart_txd    ;
 
parameter  integer  BPS   = 'd230400    ;      //波特率
parameter  integer  CLK_FRE = 'd50_000_000  ;      //系统频率50M
 
 
localparam  integer  BIT_TIME = 'd1000_000_000 / BPS ;  //计算出传输每个bit所需要的时间
 
initial begin  
  sys_clk <=1'b0;  
  sys_rst_n <=1'b0;    
  uart_tx_en <=1'b0;
  uart_tx_data <=8'd0;        
  #80                     //系统开始工作
    sys_rst_n <=1'b1;
    
  #200
    @(posedge sys_clk);
    uart_tx_en <=1'b1;  
    uart_tx_data <= ({$random} % 256);    //发送8位随机数据
  #20  
    uart_tx_en <=1'b0;
  
  #(BIT_TIME * 10)              //发送1个BYTE需要10个bit
  #200 $finish;                //结束仿真
end
 
always #10 sys_clk=~sys_clk;          //定义主时钟,周期20ns,频率50M
 
//例化发送驱动模块
uart_tx #(
  .BPS      (BPS      ),    
  .CLK_FRE    (CLK_FRE    )    
)  
uart_tx_inst(  
  .sys_clk    (sys_clk    ),      
  .sys_rst_n    (sys_rst_n    ),
  
  .uart_tx_data  (uart_tx_data  ),      
  .uart_tx_en    (uart_tx_en    ),    
  .uart_tx_done  (uart_tx_done  ),    
  .uart_txd    (uart_txd    )  
);
 
endmodule

   (3)结果

         

三.接收端设计

  (1)流程设计 

        串口的传输是以起始位开始的,而起始位是将数据线拉低 ,所以我们需要捕捉数据线的下降沿,将接收数据线打拍3次,捕捉其下降沿。当捕捉到接收数据线的下降沿,拉高接收标志信号,标志模块进入接收过程;当接收完10个bit后,拉低接收标志信号,标志接收过程结束。

  (2)verilog代码

module uart_rx
#(
  parameter  integer  BPS    = 9_600    ,    //发送波特率
  parameter   integer  CLK_FRE  = 50_000_000    //输入时钟频率
)  
(  
//系统接口
  input         sys_clk      ,      //50M系统时钟
  input         sys_rst_n    ,      //系统复位
//UART接收线  
  input         uart_rxd    ,      //接收数据线
//用户接口  
  output reg       uart_rx_done  ,      //数据接收完成标志,当其为高电平时,代表接收数据有效
  output reg [7:0]  uart_rx_data        //接收到的数据,在uart_rx_done为高电平时有效
);
 
assign  neg_uart_rxd = uart_rx_d3 & (~uart_rx_d2);  //捕获数据线的下降沿,用来标志数据传输开始
 
//将数据线打3拍,作用1:同步不同时钟域信号,防止亚稳态;作用2:捕获下降沿
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    uart_rx_d1 <= 1'b0;
    uart_rx_d2 <= 1'b0;
    uart_rx_d3 <= 1'b0;
  end
  else begin
    uart_rx_d1 <= uart_rxd;
    uart_rx_d2 <= uart_rx_d1;
    uart_rx_d3 <= uart_rx_d2;
  end    
end
//捕获到数据下降沿(起始位0)后,拉高传输开始标志位,并在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    rx_en <= 1'b0;
  else begin 
    if(neg_uart_rxd )                
      rx_en <= 1'b1;
    //接收完第9个数据(终止位)将传输开始标志位拉低,标志传输结束,判断高电平
    else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'b1) && (uart_rx_d3 == 1'b1) )
      rx_en <= 1'b0;
    else 
      rx_en <= rx_en;      
  end
end
//当数据传输到终止位时,拉高传输完成标志位,并将数据输出
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    uart_rx_done <= 1'b0;
    uart_rx_data <= 8'd0;
  end  
  //结束接收后,将接收到的数据输出
  else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'd1) && (uart_rx_d3 == 1'b1))begin    
    uart_rx_done <= 1'b1;                  //仅仅拉高一个时钟周期
    uart_rx_data <= uart_rx_data_reg;  
  end              
  else begin          
    uart_rx_done <= 1'b0;                  //仅仅拉高一个时钟周期
    uart_rx_data <= uart_rx_data;
  end
end
 
//时钟每计数一个BPS_CNT(传输一位数据所需要的时钟个数),即将数据计数器加1,并清零时钟计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    bit_cnt <= 4'd0;
    clk_cnt <= 32'd0;
  end
  else if(rx_en)begin                            //在接收状态
    if(clk_cnt < BPS_CNT - 1'b1)begin                 //一个bit数据没有接收完
      clk_cnt <= clk_cnt + 1'b1;                    //时钟计数器+1
      bit_cnt <= bit_cnt;                           //bit计数器不变
    end                                               
    else begin                                        //一个bit数据接收完了  
      clk_cnt <= 32'd0;                             //清空时钟计数器,重新开始计时
      bit_cnt <= bit_cnt + 1'b1;                    //bit计数器+1,表示接收完了一个bit的数据
    end                                               
  end                                                   
    else begin                                        //不在接收状态
      bit_cnt <= 4'd0;                              //清零
      clk_cnt <= 32'd0;                             //清零
    end    
end
 
endmodule

   (3)前仿真代码

// ** 功能 : 1、对基于FPGA的串口接收驱动模块的测试testbench
//        2、通过构建一个task来模拟上位机时序发送数据给串口接收驱动,观察该模块能否成功接收数据。
//        3、依次发送4个随机的8bit数据                                                                                                                                             
// *******************************************************************************************************      
 
`timescale 1ns/1ns  //定义时间刻度
 
//模块、接口定义
module tb_uart_rx();
 
reg       sys_clk      ;      
reg       sys_rst_n    ;      
reg       uart_rxd    ;
 
wire       uart_rx_done  ;    
wire  [7:0]  uart_rx_data  ;
 
localparam  integer  BPS   = 'd230400        ;  //波特率
localparam  integer  CLK_FRE = 'd50_000_000      ;  //系统频率50M
localparam  integer  CNT     = 1000_000_000 / BPS  ;  //计算出传输每个bit所需要的时间,单位:ns
 
 
//初始时刻定义
initial begin  
  $timeformat(-9, 0, " ns", 10);  //定义时间显示格式  
  sys_clk  =1'b0;  
  sys_rst_n <=1'b0;    
  uart_rxd <=1'b1;
  
  #20 //系统开始工作
  sys_rst_n <=1'b1;
  
  #3000
  rx_byte({$random} % 256);    //生成8位随机数1
  rx_byte({$random} % 256);    //生成8位随机数2
  rx_byte({$random} % 256);       //生成8位随机数3
  rx_byte({$random} % 256);       //生成8位随机数4  
  #60  $finish();
end
 
//每当成功接收一个BYTE的数据,就在测试端窗口打印出来
always @(posedge sys_clk)begin
  if(uart_rx_done)begin
    $display("@time%t", $time);  
    $display("rx : 0x%h",uart_rx_data);
  end
end
 
//定义任务,每次发送的数据10 位(起始位1+数据位8+停止位1)
task rx_byte(
  input [7:0] data
);
  integer i; //定义一个常量
  //用 for 循环产生一帧数据,for 括号中最后执行的内容只能写 i=i+1
  for(i=0; i<10; i=i+1) begin
    case(i)
    0: uart_rxd <= 1'b0;    //起始位
    1: uart_rxd <= data[0];    //LSB
    2: uart_rxd <= data[1];
    3: uart_rxd <= data[2];
    4: uart_rxd <= data[3];
    5: uart_rxd <= data[4];
    6: uart_rxd <= data[5];
    7: uart_rxd <= data[6];
    8: uart_rxd <= data[7];    //MSB
    9: uart_rxd <= 1'b1;    //停止位
    endcase
    #CNT;             //每发送 1 位数据延时
  end    
endtask               //任务结束
 
//设置主时钟
always #10 sys_clk <= ~sys_clk;    //时钟20ns,50M
 
//例化被测试的串口接收驱动
uart_rx
#(
  .BPS      (BPS      ),    
  .CLK_FRE    (CLK_FRE    )      
)
uart_rx_inst(
  .sys_clk    (sys_clk    ),      
  .sys_rst_n    (sys_rst_n    ),      
  .uart_rxd    (uart_rxd    ),      
  .uart_rx_done  (uart_rx_done  ),    
  .uart_rx_data  (uart_rx_data  )  
);
 
endmodule

  (3)结果

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在Eclipse中安装WindowBuilder插件,详解过程

第一步&#xff1a;找到自己安装eclipse的版本&#xff0c;在Help-关于eclipse里面&#xff0c;即Version 第二步&#xff1a;去下面这个网站找到对应的 link&#xff08;Update Site&#xff09;&#xff0c;这一步很重要&#xff0c;不然版本下载错了之后还得删除WindowBuil…

【计算机网络】TCP协议——3. 可靠性策略效率策略

前言 TCP是一种可靠的协议&#xff0c;提供了多种策略来确保数据的可靠性传输。 可靠并不是保证每次发送的数据&#xff0c;对方都一定收到&#xff1b;而是尽最大可能让数据送达目的主机&#xff0c;即使丢包也可以知道丢包。 目录 一. 确认应答和捎带应答机制 二. 超时重…

什么是关键词排名蚂蚁SEO

关键词排名是指通过搜索引擎优化&#xff08;SEO&#xff09;技术&#xff0c;将特定的关键词与网站相关联&#xff0c;从而提高网站在搜索引擎中的排名。关键词排名对于网站的流量和用户转化率具有至关重要的影响&#xff0c;因此它是SEO工作中最核心的部分之一。 如何联系蚂…

机器学习 | SVM支持向量机

欲穷千里目&#xff0c;更上一层楼。 一个空间的混乱在更高维度的空间往往意味着秩序。 Machine-Learning: 《机器学习必修课&#xff1a;经典算法与Python实战》配套代码 - Gitee.com 1、核心思想及原理 针对线性模型中分类两类点的直线如何确定。这是一个ill-posed problem。…

【工作流Activiti】Activiti的使用

1、数据库支持 Activiti 运行必须要有数据库的支持&#xff0c;支持的数据库有&#xff1a;mysql、oracle、postgres、mssql、db2、h2 2、Activiti环境 我们直接在当前项目&#xff1a;guigu-oa-parent做Activiti入门讲解 2.1、引入依赖 <!--引入activiti的springboot启动…

苹果个人开发者如何实现应用下载安装

作为苹果个人开发者&#xff0c;你可以为iOS设备用户提供应用程序&#xff0c;而用户将能够通过下载和安装这些应用来丰富他们的设备体验。本文将详细介绍个人开发者实现应用下载安装的步骤&#xff0c;包括开发和上架应用程序到App Store。 图片来源&#xff1a;苹果个人开发者…

P2P应用

目录 一.P2P的简介 二.P2P的工作方式 1.具有集中目录服务器的P2P工作方式 2.具有全分布式结构的P2P文件共享程序 一.P2P的简介 P2P(对等连接)&#xff0c;是指两台主机在通信时&#xff0c;并不区分哪一个是服务请求方和哪一个是服务提供方。只要两台主机都运行了对等连接…

【06】GeoScene海图或者电子航道图数据自动化质检

1 S-58错误管理器验证产品 在你编辑数据时进行快速的质量检查可以使用S-58错误管理器&#xff0c;S-58错误管理器工具允许您使用IHO S-58验证标准来验证海事数据库中的产品。你可以验证整个产品&#xff0c;或验证产品的当前范围。 1.1验证产品 使用S-58错误管理器工具完成以…

Android vs. iOS:移动操作系统的对决

导言 Android和iOS作为两大主流移动操作系统&#xff0c;影响着数十亿用户的数字生活。Android和iOS&#xff0c;作为移动操作系统的巅峰代表&#xff0c;它们的竞争塑造了全球数十亿用户的数字化生活。本文将深入探讨这两个系统的起源、特点以及它们在用户体验、开发者生态和市…

Linux 特殊符号

目录 1. # 注释 2. &#xff1b;命令分隔符 3. .. 上级目录 4. . 当前目录 5. " " 换行&#xff0c;解析变量 6. 换行&#xff0c;不解析变量 7. \ 和 / 8. &#xff01;历史命令调用&#xff0c;取反 9. * 通配符 10. $ 调用变量 11. | 管道 12. || …

云原生之深入解析减少Docker镜像大小的优化技巧

一、什么是 Docker&#xff1f; Docker 是一种容器引擎&#xff0c;可以在容器内运行一段代码&#xff0c;Docker 镜像是在任何地方运行应用程序而无需担心应用程序依赖性的方式。要构建镜像&#xff0c;docker 使用一个名为 Dockerfile 的文件&#xff0c;Dockerfile 是一个包…

【二分查找】【z型搜索】LeetCode240:搜索二维矩阵

LeetCoe240搜索矩阵 作者推荐 【贪心算法】【中位贪心】.执行操作使频率分数最大 本文涉及的基础知识点 二分查找算法合集 题目 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的…

Vue 项目中使用 debugger 在 chrome 谷歌浏览器中失效以及 console.log 指向去了 vue.js 代码

问题 今天在代码里面输出 console.log 信息直接指向了 vue.js&#xff0c;并且代码里面写了 debgger 也不生效 解决 f12 找到浏览器的这个设置图标 找到这个 ignore list 的 custom exclusion rules 取消掉 /node_modules/|/bower_components/ 这样就正常了

【图神经网络】社区检测

社区检测 社区是许多网络的特性&#xff0c;一个特定的网络可能有多个社区&#xff0c;社区内的节点之间连接紧密。 网络节点在社区内部形成紧密的连接&#xff0c;而在社区之间则形成松散连接。 社区检测技术对于社交媒体算法来说非常有用&#xff0c;可以发现具有共同兴趣…

TrustZone之顶层软件架构

在处理器中的TrustZone和系统架构中,我们探讨了硬件中的TrustZone支持,包括Arm处理器和更广泛的内存系统。本主题关注TrustZone系统中发现的软件架构。 一、顶层软件架构 下图显示了启用TrustZone的系统的典型软件栈: 【注意】:为简单起见,该图不包括管理程序,尽管它们可…

sqlserver dba日常操作

查询慢sql的方法 1.whoisactive 安装方法 http://whoisactive.com/downloads/下载地址 将下载好的zip包放到sqlserver服务器中 文件-打开-文件-下载好的zip包-在查询窗口点击执行 新建一个查询窗口&#xff0c;输入sp_whoisactive&#xff0c;获取当前运行的所有sql语句 使用…

【计算机网络】TCP协议——2.连接管理(三次握手,四次挥手)

目录 前言 一. 建立连接——三次握手 1. 三次握手过程描述 2. TCP连接建立相关问题 二. 释放连接——四次挥手 1. 四次挥手过程描述 2. TCP连接释放相关问题 三. TCP状态转换 结束语 前言 TCP——传输控制协议(Transmission Control Protocol)。是一种面向连接的传…

Android 性能优化一篇解决

前言 使用java编写的源代码编译后生成了对于的class文件&#xff0c;但是class文件是一个非常标准的文件&#xff0c;市面上很多软件都可以对class文件进行反编译&#xff0c;为了我们app的安全性&#xff0c;就需要使用到Android代码混淆这一功能。针对 Java 的混淆&#xff…

不同版本QT使用qmake时创建QML项目的区别

不同版本QT使用qmake时创建QML项目的区别 文章目录 不同版本QT使用qmake时创建QML项目的区别一、QT5新建QML项目1.1 目录结构1.2 .pro 文件内容1.3 main.cpp1.4 main.qml 二、QT6新建QML项目2.1 目录结构2.2 .pro文件内容2.3 main.cpp2.4 main.qml 三、两个版本使用资源文件的区…

DSO在Euroc上运行经验贴,关于时间戳为0的结局方法

网上DSO基本上都是在TUM数据集上跑得&#xff0c;教程也比较多&#xff0c;写论文需要&#xff0c;使用DSO跑了一下Euroc数据集&#xff0c;踩了很多坑&#xff0c;花了一天的时间才调通&#xff0c;记录一下。 本机运行环境&#xff1a;Ubuntu16.04 其它环境只要安装过ORB-SA…
最新文章