热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于FPGA的UART全双工数据控制器

引言:UART串行通讯协议是一种经典通讯协议,尽管在当前,它的通讯传输速度已经不能满足高数据量传输场景,但在传统的工业应用

引言:

UART串行通讯协议是一种经典通讯协议,尽管在当前,它的通讯传输速度已经不能满足高数据量传输场景,但在传统的工业应用中还十分普遍广泛。在网上,一般所见到的可应用于FPGA设计的UART接口都遗留有编程语言的设计痕迹,无法实现FPGA上的多数据时序控制传输,因此本文提出了一种带有多标志位的UART控制器,可以满足数据流的双向全双工传递。但由于本人在设计上经验的缺失,本文采用的接收状态机与发送状态机的设置与控制存在有严重不足,希望本文的设计能够引起大家的思考。

1.UART协议

UART协议通过两根数据线实现上下位机数据的双向传递,在数据通讯开始之时,通讯双方需要约定通信所使用的波特率值。由于UART不使用时钟线,因此只有约定了波特率值的前提下,有效数据才可以被识别采集。在约定的波特率下,系统按照指定间隔对数据线上的数据进行采样,读取出有效数据位;而发送方按照指定间隔将数据的每位保持指定的时间,方便接收方进行等间隔采样。

在数据发送中,发送方先将TX线由高拉至低,然后按照从最低位到最高位的顺序将数据放置于TX线上。有效数据一般为6-10位,这需要通讯双方提前约定。当有效数据传输完毕后,可以选择添加一位的奇偶校检位,也可以选择不添加。当一帧数据被传送出去后,发送方将TX总线拉高,等待下一次数据发送。

在数据接收中,接收方检测到RX总线上的电平从高变为低时,就明确发送方开始发送数据。当这个低电平位结束后,数据按从最低位到最高位传送过来,接收方可以根据波特率设定诗律进行采样接收。

UART总线的数据收发时序如图1.1所示:

                                                                          图1.1   UART传输时序

2.UART模块总体设计

根据UART协议,要实现UART串行通讯,需要三个基本功能:波特率生成、接收数据采样、发送数据保持控制。

其中波特率生成是为了给系统生成一个采样保持时钟,以这个时钟为指示对数据线进行操作。但是,在FPGA设计中,不能使用专用时钟计数分频后的结果作为时钟。由于计数器分频后的时钟掺杂有组合逻辑,它有极大的可能存在有毛刺,而作为一个依靠边沿触发驱动电路工作的时钟,显然是不合适的。因此是实际设计中,分频后得到的波特率时钟应当作为电平控制信号而使用。

而接收数据采样将被集成在数据接收模块。该模块根据UART通信协议规定,利用状态机在波特率信号的指示下读取总线上的串行数据。当一帧信息被完整的接收后,该模块生成一个rd信号,告诉外部模块可以从接收缓冲区读取数据。

发送数据保持控制也是采用状态机实现功能的实现。当待发送数据被送至UART控制模块后,数据有效标志信号告诉数据发送模块开始工作,此时控制状态机从空闲状态变换至2开始状态,将TX总线的高电平拉低,然后逐位的发送数据,当数据发送完毕后,状态机回归空闲,将TX总线置高,等待下一次发送数据的到来。

本设计相较于网上其他设计,增添了写控制。读控制、发送数据有效位等三个控制信号,根据三个控制信号所携带的信息,足以实现数据连续接收发送的有效实现。

3.Verilog代码


3.1 顶层模块

module uart(clk,rst_n,data_wr,data_rd,tx,rx,rd,wr,data_v);input clk,rst_n;input rx; //UART串行输入output tx; //数据串行输出input[7:0]data_wr; //数据写入UART模块output[7:0]data_rd; //数据从UART中读出input data_v; //写入数据有效信号output rd; //数据可以从UART模块中读出output wr; //数据可以写入UART模块//wire bps;//波特率时钟信号,作通讯电平判别////波特率产生模块bps_crate U1(.clk(clk),.rst_n(rst_n),.bps(bps));////数据串行接收模块uart_rx U2(.clk(clk),.rst_n(rst_n),.bps(bps),.rx(rx),.data_rd(data_rd),.rd(rd));////数据串行发送模块uart_tx U3(.clk(clk),.rst_n(rst_n),.bps(bps),.data_v(data_v),.tx(tx),.data_wr(data_wr),.wr(wr));
endmodule


3.2 波特率生成模块

module bps_crate(clk,rst_n,bps);//input clk, rst_n;output bps;//reg bps;reg[7:0]cnt;//分频计数器parameter division &#61; 10;//调整分频常数确定波特率//always&#64;(posedge clk or negedge rst_n)if(!rst_n) begincnt <&#61; 8&#39;d0;bps <&#61; 1&#39;b0;endelse if(cnt &#61;&#61; division)begincnt <&#61; 8&#39;d0;bps <&#61; 1&#39;b1;endelsebegincnt <&#61; cnt &#43; 8&#39;d1;bps <&#61; 1&#39;b0;end
endmodule

3.3 接收模块

module uart_rx(clk,rst_n,bps,rx,data_rd,rd);///input clk,rst_n;input bps;//波特率时钟input rx;//串行输入output[7:0]data_rd;//一帧接收数据output rd;//接收数据可读出///reg[7:0]data_rd;reg rd;reg[3:0]state;//串行数据接收状态机///parameter idle &#61; 4&#39;d0;parameter bit_0 &#61; 4&#39;d1;parameter bit_1 &#61; 4&#39;d2;parameter bit_2 &#61; 4&#39;d3;parameter bit_3 &#61; 4&#39;d4;parameter bit_4 &#61; 4&#39;d5;parameter bit_5 &#61; 4&#39;d6;parameter bit_6 &#61; 4&#39;d7;parameter bit_7 &#61; 4&#39;d8;/////状态机状态转移判定always&#64;(posedge clk or negedge rst_n)if(!rst_n)beginstate <&#61; 4&#39;d0;endelse if(state &#61;&#61; idle)beginif(bps&~rx) state <&#61; bit_0;else state <&#61; idle;endelse if(state &#61;&#61; bit_0)beginif(bps) state <&#61; bit_1;else state <&#61; bit_0;endelse if(state &#61;&#61; bit_1)beginif(bps) state <&#61; bit_2;else state <&#61; bit_1;endelse if(state &#61;&#61; bit_2)beginif(bps) state <&#61; bit_3;else state <&#61; bit_2;endelse if(state &#61;&#61; bit_3)beginif(bps) state <&#61; bit_4;else state <&#61; bit_3;endelse if(state &#61;&#61; bit_4)beginif(bps) state <&#61; bit_5;else state <&#61; bit_4;endelse if(state &#61;&#61; bit_5)beginif(bps) state <&#61; bit_6;else state <&#61; bit_5;endelse if(state &#61;&#61; bit_6)beginif(bps) state <&#61; bit_7;else state <&#61; bit_6;endelse if(state &#61;&#61; bit_7)beginif(bps) state <&#61; idle;else state <&#61; bit_7;endelsestate <&#61; idle;//状态机always&#64;(posedge clk or negedge rst_n)if(!rst_n)begindata_rd <&#61; 8&#39;d0;rd <&#61; 1&#39;b0;endelse if(bps)case(state)idle : begindata_rd <&#61; 8&#39;d0;rd <&#61; 1&#39;b0;endbit_0 : begindata_rd[0] <&#61; rx;endbit_1 : begindata_rd[1] <&#61; rx;endbit_2 : begindata_rd[2] <&#61; rx;endbit_3 : begindata_rd[3] <&#61; rx;endbit_4 : begindata_rd[4] <&#61; rx;endbit_5 : begindata_rd[5] <&#61; rx;endbit_6 : begindata_rd[6] <&#61; rx;endbit_7 : begindata_rd[7] <&#61; rx;rd <&#61; 1&#39;d1;end default : begindata_rd <&#61; 8&#39;d0;rd <&#61; 1&#39;d0;endendcaseendmodule


3.4 发送模块

module uart_tx(clk,rst_n,bps,tx,data_wr,wr,data_v);///input clk,rst_n;input bps;//波特率时钟output tx;//串行输出input data_v;input[7:0]data_wr;//一帧发送数据output wr;//发送数据可写入///reg ready;//数据备好标志reg tx;reg[7:0]data;reg wr;reg[3:0]state;//串行数据接收状态机///parameter idle &#61; 4&#39;d0;parameter start &#61; 4&#39;d1;parameter bit_0 &#61; 4&#39;d2;parameter bit_1 &#61; 4&#39;d3;parameter bit_2 &#61; 4&#39;d4;parameter bit_3 &#61; 4&#39;d5;parameter bit_4 &#61; 4&#39;d6;parameter bit_5 &#61; 4&#39;d7;parameter bit_6 &#61; 4&#39;d8;parameter bit_7 &#61; 4&#39;d9;//有效数据输入标志上升沿检测模块reg flag;reg v1;always&#64;(posedge clk or negedge rst_n)if(!rst_n) v1 <&#61; 1&#39;b0;else v1 <&#61; data_v;always&#64;(posedge clk or negedge rst_n)if(!rst_n) flag <&#61; 1&#39;b0;else flag <&#61; data_v&~v1;//状态机状态转移判定always&#64;(posedge clk or negedge rst_n)if(!rst_n)beginstate <&#61; 4&#39;d0;endelse if(state &#61;&#61; idle)beginif(bps&ready) state <&#61; start;else state <&#61; idle;endelse if(state &#61;&#61; start)beginif(bps) state <&#61; bit_0;else state <&#61; start;endelse if(state &#61;&#61; bit_0)beginif(bps) state <&#61; bit_1;else state <&#61; bit_0;endelse if(state &#61;&#61; bit_1)beginif(bps) state <&#61; bit_2;else state <&#61; bit_1;endelse if(state &#61;&#61; bit_2)beginif(bps) state <&#61; bit_3;else state <&#61; bit_2;endelse if(state &#61;&#61; bit_3)beginif(bps) state <&#61; bit_4;else state <&#61; bit_3;endelse if(state &#61;&#61; bit_4)beginif(bps) state <&#61; bit_5;else state <&#61; bit_4;endelse if(state &#61;&#61; bit_5)beginif(bps) state <&#61; bit_6;else state <&#61; bit_5;endelse if(state &#61;&#61; bit_6)beginif(bps) state <&#61; bit_7;else state <&#61; bit_6;endelse if(state &#61;&#61; bit_7)beginif(bps) state <&#61; idle;else state <&#61; bit_7;endelsestate <&#61; idle;//状态机always&#64;(posedge clk or negedge rst_n)if(!rst_n)beginready <&#61; 1&#39;b0;wr <&#61; 1&#39;b0;endelse case(state)idle : beginwr <&#61; 1&#39;b1;if(flag) ready <&#61; 1&#39;b1;tx <&#61; 1&#39;b1;endstart : beginready <&#61; 1&#39;b0;wr <&#61; 1&#39;b0;tx <&#61; 1&#39;b0;endbit_0 : begintx <&#61; data_wr[0];endbit_1 : begintx <&#61; data_wr[1];endbit_2 : begintx <&#61; data_wr[2];endbit_3 : begintx <&#61; data_wr[3];endbit_4 : begintx <&#61; data_wr[4];endbit_5 : begintx <&#61; data_wr[5];endbit_6 : begintx <&#61; data_wr[6];endbit_7 : begintx <&#61; data_wr[7];end default : begintx <&#61; 1&#39;d0;wr <&#61; 1&#39;d0;endendcase
endmodule

4.总结

本设计是一个简单的UART控制器设计&#xff0c;由于设计中设定了一帧数据为8位&#xff0c;不存有铰检位&#xff0c;因此结构显得略微粗糙。本文设计主要解决的是变换数据的连续接收发送&#xff0c;相对于网上其他设计&#xff0c;可控性、可移植性有所提升。


推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 加密世界下一个主流叙事领域:L2、跨链桥、GameFi等
    本文介绍了加密世界下一个主流叙事的七个潜力领域,包括L2、跨链桥、GameFi等。L2作为以太坊的二层解决方案,在过去一年取得了巨大成功,跨链桥和互操作性是多链Web3中最重要的因素。去中心化的数据存储领域也具有巨大潜力,未来云存储市场有望达到1500亿美元。DAO和社交代币将成为购买和控制现实世界资产的重要方式,而GameFi作为数字资产在高收入游戏中的应用有望推动数字资产走向主流。衍生品市场也在不断发展壮大。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
author-avatar
mobiledu2502873187
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有