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)beginif(!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)beginif(!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)beginif(!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)beginif(!sys_rst_n)beginclk_cnt <= 32'd0;bit_cnt <= 4'd0;endelse if(tx_state) begin                    //在发送状态if(clk_cnt < BPS_CNT - 1'd1)begin            //一个bit数据没有发送完clk_cnt <= clk_cnt + 1'b1;              //时钟计数器+1bit_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  ;      //系统频率50Mlocalparam  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;                //结束仿真
endalways #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)beginif(!sys_rst_n)beginuart_rx_d1 <= 1'b0;uart_rx_d2 <= 1'b0;uart_rx_d3 <= 1'b0;endelse beginuart_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)beginif(!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)beginif(!sys_rst_n)beginuart_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)beginif(!sys_rst_n)beginbit_cnt <= 4'd0;clk_cnt <= 32'd0;endelse if(rx_en)begin                            //在接收状态if(clk_cnt < BPS_CNT - 1'b1)begin                 //一个bit数据没有接收完clk_cnt <= clk_cnt + 1'b1;                    //时钟计数器+1bit_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    
endendmodule

   (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;#3000rx_byte({$random} % 256);    //生成8位随机数1rx_byte({$random} % 256);    //生成8位随机数2rx_byte({$random} % 256);       //生成8位随机数3rx_byte({$random} % 256);       //生成8位随机数4  #60  $finish();
end//每当成功接收一个BYTE的数据,就在测试端窗口打印出来
always @(posedge sys_clk)beginif(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+1for(i=0; i<10; i=i+1) begincase(i)0: uart_rxd <= 1'b0;    //起始位1: uart_rxd <= data[0];    //LSB2: 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];    //MSB9: 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.rhkb.cn/news/219946.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据机器学习与深度学习—— 生成对抗网络(GAN)

GAN概述 在讲GAN之前&#xff0c;先讲一个小趣事&#xff0c;你知道GAN是怎么被发明的吗&#xff1f;据Ian Goodfellow自己说&#xff1a; 之前他一直在研究生成模型&#xff0c;可能是一时兴起&#xff0c;有一天他在酒吧喝酒时&#xff0c;在酒吧里跟朋友讨论起生成模型。然…

【网络安全】-Linux操作系统—CentOS安装、配置

文章目录 准备工作下载CentOS创建启动盘确保硬件兼容 安装CentOS启动安装程序分区硬盘网络和主机名设置开始安装完成安装 初次登录和配置更新系统安装额外的软件仓库安装网络工具配置防火墙设置SELinux安装文本编辑器配置SSH服务 总结 CentOS是一个基于Red Hat Enterprise Linu…

和鲸科技CEO范向伟受邀出席港航数据要素流通与生态合作研讨会,谈数据资产入表的战略机会

近日&#xff0c;由上海虹口数字航运创新中心、龙船&#xff08;北京&#xff09;科技有限公司&#xff08;下简称“龙船科技”&#xff09;、华东江苏大数据交易中心联合举办的“港航数据要素流通与生态合作研讨会”圆满落幕&#xff0c;来自港航领域的近百名企业代表共同参与…

Spring 原理(一)

Spring 原理 它是一个全面的、企业应用开发一站式的解决方案&#xff0c;贯穿表现层、业务层、持久层。但是 Spring仍然可以和其他的框架无缝整合。 Spring 特点 轻量级控制反转面向切面容器框架集合 Spring 核心组件 Spring 常用模块 Spring 主要包 Spring 常用注解 bean …

软件测试职业规划

软件测试人员的发展误区【4】 公司开发的产品专业性较强&#xff0c;软件测试人员需要有很强的专业知识&#xff0c;现在软件测试人员发展出现了一种测试管理者不愿意看到的景象&#xff1a; 1、开发技术较强的软件测试人员转向了软件开发(非测试工具开发)&#xff1b; 2、业务…

【Hadoop】执行start-dfs.sh启动hadoop集群时,datenode没有启动怎么办

执行start-dfs.sh后&#xff0c;datenode没有启动&#xff0c;很大一部分原因是因为在第一次格式化dfs后又重新执行了格式化命令&#xff08;hdfs namenode -format)&#xff0c;这时主节点namenode的clusterID会重新生成&#xff0c;而从节点datanode的clusterID 保持不变。 在…

ES查询流程

在ES中查询分为两类&#xff1a;1.基于文档ID查询&#xff0c;2.按照非文档ID查询。 基于文档id查询 1.基于文档ID查询 当执行如下查询时&#xff1a; GET /megacorp/employee/1ES在执行上述查询的具体过程如下&#xff1a; 1、客户端向 Node 1 发送获取请求&#xff0c;此…

mybatisplus使用雪花id通过swagger返回ID时精度丢失问题

在使用mybatisplus自带雪花的时候会发现返回的ID是19位的长度&#xff0c;因此在通过swagger页面展示的时候会发现后端返回的和页面展示的ID不一致问题。是因为精度丢失的问题。因此需要更改雪花ID的长度跟踪进去&#xff1a;发现是DefaultIdentifierGenerator类实现了Identifi…

css3实现动态心电图折线

css3实现动态心电图折线 M&#xff08;moveto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff0c;移动到的点的x轴和y轴的坐标L&#xff08;lineto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff09;&#xff0c;它会在当前位置…

汽车充电协议OpenV2G的平替cbexigen!!

纵所周知&#xff0c;开源欧规协议 CCS 的 OpenV2G 协议不支持 ISO15118-20:2022 协议&#xff0c;并且软件维护者已经明确不在进行该软件的维护。 前几天在 Github 上冲浪发现了一个宝藏开源项目&#xff0c;完美的实现的 OpenV2G 的 Exidizer 工具的功能&#xff1a;cbexigen…

云仓酒庄的品牌雷盛红酒LEESON分享香槟为什么是“酸”的?

云仓酒庄致力成为红酒爱好者的首选供应商。云仓酒庄品牌雷盛红酒多系列、多国家、多价位区间的多品种供货&#xff0c;使得酒品丰富而多样&#xff0c;既可以整箱方式销售&#xff0c;也可以单瓶模式购买&#xff0c;全管道使成本更低&#xff0c;降低中间仓储环节、支线物流仓…

HarmonyOS开发实战:如何实现一个运动排名榜页面

HarmonyOS开发实战&#xff1a;如何实现一个运动排名榜页面 代码仓库&#xff1a; 运动排名榜页面 项目介绍 本项目使用声明式语法和组件化基础知识&#xff0c;搭建一个可刷新的排行榜页面。在排行榜页面中&#xff0c;使用循环渲染控制语法来实现列表数据渲染&#xff0c;…

【Unity自动寻路】使用Navigation系统实现物体自动寻路绕开障碍物

知识点流程图 自动导航Navigation系统 我们在游戏场景中经常会有一些障碍物、墙壁、树木等等&#xff0c;如果我想要让角色或者怪物去墙的另一边&#xff0c;我直接在墙另一边点击左键&#xff0c;我希望角色自动跑过去&#xff0c;但是他不能直接穿透墙&#xff0c;他需要“智…

安卓开发学习---kotlin版---笔记(二)

UI学习 UI分类 安卓的UI分为两大类&#xff1a;一类叫做View视图&#xff0c;一类叫做ViewGroup容器 View视图&#xff1a;TextView,Button,ImageView都是常见的视图ViewGroup容器&#xff1a;内部尅承载、放置、添加View视图的容器 布局方式 安卓布局主要有&#xff1a;线…

【智能算法】11种混沌映射算法+2种智能算法示范【鲸鱼WOA、灰狼GWO算法】

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 混沌映射算法是我们在智能算法改进中常用到的方法&#xff0c;本程序充分考虑改进算法应用的便捷性&#xff0c;集成了11种混合映射算法&#xff0c;包括Singer、tent、Logistic、Cubic、chebyshev、Piecewise…

原生html + vue3 获取引用元素refs - elementUI如何在setup中进行表单校验设置

背景&#xff1a; 原生Html 引入elementPlust vue3 &#xff0c;需要在vue3 setup里做表单校验&#xff0c;通过el-form refform 的refs元素执行校验。 解决方案1&#xff1a; 保存vue挂载之后实例vm为一个常量&#xff0c;由实例来获取&#xff1a;vm.$refs.form.validate(…

iClient3D 图元操作

1. S3MTilesLayer&#xff0c;S3M(Spatial 3D Model)图层类 S3MTilesLayer&#xff0c;S3M(Spatial 3D Model)图层类&#xff0c;通过该图层实现加载三维切片缓存&#xff0c;包括倾斜摄影模型、BIM模型、点云数据、精细模型、矢量数据、符号等。 那S3MTilesLayer中针对图元的…

AI性能再提升12.5%,ZStack Cube 超融合一体机基于第五代英特尔®至强®可扩展处理器解决方案发布

12月15日&#xff0c;以“Al无处不在&#xff0c;创芯无所不及”为主题的2023英特尔新品发布会暨AI技术创新派对上&#xff0c;云轴科技ZStack与英特尔联合发布基于第五代英特尔 至强 可扩展处理器的 ZStack Cube 超融合一体机解决方案白皮书&#xff08;简称解决方案&#xff…

二进制枚举算法

二进制 : 也就是只有0和1的进制表示 ; 二进制枚举算法 一个二进制数 x 可以表示 S 的一个子集&#xff0c;某个二进制位i上为0表示没有选i元素&#xff0c;为1表示选了该元素放入子集,比如13为1101就表示选了0,2,3号元素;对于一个长度为N的序列(也就是包含N个元素)有2^N个子…

C++之程序生成

一、C的发展史 截止到2023年12月&#xff0c;C已经更新了很多版本&#xff0c;并在每个版本中修复了bug和添加了新的特性&#xff0c;ISO C委员会每三年会对C进行一次更新&#xff1a; C98&#xff1a;于1998年发布&#xff0c;是最早的国际标准化版本。它包含了面向对象编程…