FPGA实现串口回环

文章目录

  • 前言
  • 一、串行通信
    • 1、分类
      • 1、同步串行通信
      • 2、异步串行通信
    • 2、UART串口通信
      • 1、UART通信原理
      • 2、串口通信时序图
  • 二、系统设计
    • 1、系统框图
    • 2.RTL视图
  • 三、源码
    • 1、串口发送模块
    • 2、接收模块
    • 3、串口回环模块
    • 4、顶层模块
  • 四、测试效果
  • 五、总结
  • 六、参考资料


前言

环境:
1、Quartus18.0
2、vscode
3、板子型号:原子哥开拓者2(EP4CE10F17C8)
要求:
上位机通过串口调试助手发送数据给 FPGA,FPGA 通过 USB 串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。(这里我们以波特率115200、数据位为8位、无校验位、停止位为1位作为案例)。


一、串行通信

1、分类

1、同步串行通信

同步串行通信需要通信双方在同一时钟的控制下进行同步传输数据。

2、异步串行通信

异步串行通信指通信双方使用各自的时钟控制数据的发送和接收。

2、UART串口通信

1、UART通信原理

UART串口通信是一种采用异步串行通信方式的通用异步收发传输器,在收发数据过程中通过串并转换实现数据的发送、接收。UART串行通信一般需要两根信号线实现,一根用于串口发送,一根用于串口接收。下面是其通信的数据格式:
在这里插入图片描述

从通信格式看,我们可知收发的一帧数据涵盖了4部分:起始位、数据位、校验位、以及停止位。校验位一般是奇检验、偶检验,就是发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇数或偶数。数据位有5、6、7、8位,一般选择8位。串口通信的速率用波特率进行表示,表示每秒传输二进制的位数,常用9600、115200等。

  • 注意:
    UART 通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。

2、串口通信时序图

在这里插入图片描述

二、系统设计

1、系统框图

在这里插入图片描述

2.RTL视图

在这里插入图片描述

uart_recv 为串口接收模块:从串口接收端口 uart_rxd 来接收上位机发送的串行数据,并在一帧数据接收结束后给出通知信号 uart_done。
uart_send 为串口发送模块:以 uart_en 为发送使能信号。uart_en 的上升沿将启动一次串口发送过程,
将 uart_din 接口上的数据通过串口发送端口 uart_txd 发送出去。
uart_loop 模块负责完成串口数据的环回功能:它在 uart_recv 模块接收完成后,将接收到的串口数据发送到 uart_send 模块,并通过 send_en 接口给出一个上升沿,以启动发送过程。

三、源码

1、串口发送模块

module uart_send(input sys_clk               ,input sys_rst_n             ,input uart_en               ,//发送使能input [7:0] uart_din        ,//待发送的数据output uart_tx_busy         ,//发送忙output en_flag              ,output reg tx_flag          ,//发送过程标志output reg [7:0] tx_data    ,//寄存发送数据output reg [3:0] tx_cnt     ,//发送数据计数器output reg uart_txd         //发送端口
);parameter CLK_FREQ = 50_000_000;
parameter UART_BPS =9600;
localparam BPS_CNT = CLK_FREQ/UART_BPS;reg uart_en_d0;//打拍信号
reg uart_en_d1;
reg [15:0] clk_cnt;assign uart_tx_busy = tx_flag;//捕获上升沿
assign en_flag = (~uart_en_d1) & uart_en_d0;//打拍
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_en_d0 <= 1'b0;uart_en_d1 <= 1'b0;endelse beginuart_en_d0 <= uart_en;uart_en_d1 <= uart_en_d0;endend//当上升沿来时,寄存待发送的数据,进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begintx_flag <= 1'b0;tx_data <= 8'd0;endelse if(en_flag) begin//上升沿tx_flag <= 1'b1;tx_data <= uart_din;endelse if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT - (BPS_CNT/16)))begintx_flag <= 1'b0;tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;end
end//开启时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginclk_cnt <= 16'd0;endelse if(tx_flag) begin//发送过程if(clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 16'd0;endelseclk_cnt <= 16'd0;
end
//开启发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begintx_cnt <= 4'd0;endelse if(tx_flag) beginif(clk_cnt == BPS_CNT - 1)tx_cnt <= tx_cnt + 1'b1;elsetx_cnt <= tx_cnt;endelsetx_cnt <= 4'd0;
end
//并转串
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_txd <= 1'b1;endelse if(tx_flag)case(tx_cnt)4'd0: uart_txd <= 1'b0;//起始位4'd1: uart_txd <= tx_data[0];//低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7];4'd9: uart_txd <= 1'b1;default: ;endcaseelseuart_txd <= 1'b1;
end
endmodule

2、接收模块

module uart_recv(input sys_clk               ,input sys_rst_n             ,input uart_rxd              ,//接收端口output reg uart_done        ,//接收一帧数据结束标志output reg rx_flag          ,//接收过程标志信号output reg [3:0] rx_cnt     ,//接收数据计数器output reg [7:0] rxdata     ,output reg [7:0] uart_data  //接收的数据
);parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 9600;//波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS;//为得到指定波特率需要计数的值reg uart_rxd_d0;//用于打拍判定下降沿决定开始信号
reg uart_rxd_d1;reg [15:0] clk_cnt;
wire start_flag;assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);//打拍
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n) beginuart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0;endelse beginuart_rxd_d0 <= uart_rxd;uart_rxd_d1 <= uart_rxd_d0;end
end//下降沿到来时,开始接收
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rx_flag <= 1'b0;else beginif(start_flag)//下降沿到来rx_flag <= 1'b1;else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))//计数到第10位,且在第十位中间时接收结束rx_flag <= 1'b0;elserx_flag <= rx_flag;end
end//进入接收过程后开启时钟计数器,为了满足特定波特率
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)clk_cnt <= 16'd0;else if(rx_flag) beginif(clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 16'd0;endelseclk_cnt <= 16'd0;
end//进入接收过程,开启接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rx_cnt <= 4'd0;else if(rx_flag)beginif(clk_cnt == BPS_CNT - 1)rx_cnt <= rx_cnt + 1'b1;elserx_cnt <= rx_cnt;endelserx_cnt <= 4'd0;
end//根据接收数据计数器寄存uart接收端的数据
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rxdata <= 8'd0;else if(rx_flag)if(clk_cnt == BPS_CNT/2) begin//在中间取值较为稳妥case(rx_cnt)4'd1: rxdata[0] <= uart_rxd_d1;//低位4'd2: rxdata[1] <= uart_rxd_d1;4'd3: rxdata[2] <= uart_rxd_d1;4'd4: rxdata[3] <= uart_rxd_d1;4'd5: rxdata[4] <= uart_rxd_d1;4'd6: rxdata[5] <= uart_rxd_d1;4'd7: rxdata[6] <= uart_rxd_d1;4'd8: rxdata[7] <= uart_rxd_d1;default:;endcaseendelserxdata <= rxdata;elserxdata <= 8'd0;
end//数据接收完毕给出结束标志并寄存输出接收的数据
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_data <= 8'd0;uart_done <= 1'b0;endelse if(rx_cnt == 4'd9)beginuart_data <= rxdata;uart_done <= 1'b1;endelse beginuart_data <= 8'd0;uart_done <= 1'b0;end
end
endmodule

3、串口回环模块

module uart_loop(input               sys_clk               ,input               sys_rst_n             ,input               recv_done             ,//接收一帧数据结束标志input [7:0]         recv_data             ,//接收的数据input               tx_busy               ,//发送忙output reg          send_en               ,//发送使能output reg [7:0]    send_data              //待发送数据
);//打拍信号
reg recv_done_d0;
reg recv_done_d1;reg tx_ready;
wire recv_done_flag;//捕获上升沿,一帧结束
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;//打拍
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) beginrecv_done_d0 <= 1'b0;recv_done_d1 <= 1'b0;endelse beginrecv_done_d0 <= recv_done;recv_done_d1 <= recv_done_d0;end
end//一帧接收结束后进入准备阶段,寄存接收的数据用于发送
//当准备阶段且发送空闲时,使能发送信号always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n) begintx_ready <= 1'b0;send_en <= 1'b0;send_data <= 8'd0;endelse beginif(recv_done_flag)begintx_ready <= 1'b1;send_en <= 1'b0;send_data <= recv_data;endelse if(tx_ready && (~tx_busy))begintx_ready <= 1'b0;send_en <= 1'b1;endend
end
endmodule

4、顶层模块

module uart_loopback_top(input sys_clk   ,input sys_rst_n ,input uart_rxd  ,output uart_txd
);parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 115200;wire        uart_recv_done  ;//接收完成标志
wire [7:0]  uart_recv_data  ;//接收数据
wire        uart_send_en    ;//发送使能
wire [7:0]  uart_send_data  ;//发送数据
wire        uart_tx_busy    ;//发送忙状态标志//串口接收模块
uart_recv #(.CLK_FREQ(CLK_FREQ),.UART_BPS(UART_BPS)) uart_recv_inst(/*input           */    .sys_clk    (sys_clk  )           ,/*input           */    .sys_rst_n  (sys_rst_n)           ,/*input           */    .uart_rxd   (uart_rxd )           ,//接收端口/*output reg      */    .uart_done  (uart_recv_done)      ,//接收一帧数据结束标志/*output reg [7:0]*/    .uart_data  (uart_recv_data)//接收的数据
);//串口发送模块
uart_send #(.CLK_FREQ(CLK_FREQ),.UART_BPS(UART_BPS)) uart_send_inst(/*input           */    .sys_clk        (sys_clk  )       ,/*input           */    .sys_rst_n      (sys_rst_n)       ,/*input           */    .uart_en        (uart_send_en  )       ,//发送使能/*input [7:0]     */    .uart_din       (uart_send_data)         ,//待发送的数据/*output          */    .uart_tx_busy   (uart_tx_busy)      ,//发送忙/*output reg      */    .uart_txd       (uart_txd)  //发送端口
);//串口回环模块
uart_loop uart_loop_inst(/*input           */    .sys_clk               (sys_clk  ),/*input           */    .sys_rst_n             (sys_rst_n),/*input           */    .recv_done             (uart_recv_done),//接收一帧数据结束标志/*input [7:0]     */    .recv_data             (uart_recv_data),//接收的数据/*input           */    .tx_busy               (uart_tx_busy),//发送忙/*output reg      */    .send_en               (uart_send_en  ),//发送使能/*output reg [7:0]*/    .send_data             (uart_send_data) //待发送数据
);
endmodule

四、测试效果

在这里插入图片描述


五、总结

串口通信其实算是几种通信协议里较简单的一种了,看着通信时序图就已经对UART串行通信理解了许多,但是里面还是有许多细节需要注意,所以为了巩固在UART串口通信的知识,打算在最近通过FPGA实现UART串口通信实现一个测量温湿度并自动传回到上位机的学习。还有这里请允许我偷个懒,没有进行波形的仿真和SignalTap II在线抓波形,今天没什么心情。

六、参考资料

以上资料均来自正点原子的教学视频或开拓者2开发教程:原子官方

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

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

相关文章

【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(7 月 21 日论文合集)

文章目录 一、检测相关(15篇)1.1 Representation Learning in Anomaly Detection: Successes, Limits and a Grand Challenge1.2 AlignDet: Aligning Pre-training and Fine-tuning in Object Detection1.3 Cascade-DETR: Delving into High-Quality Universal Object Detectio…

《Docker与持续集成/持续部署:构建高效交付流程,打造敏捷软件交付链》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

c语言修炼之指针和数组笔试题解析(1.2)

前言&#xff1a; 书接上回&#xff0c;让我们继续开始今天的学习叭&#xff01;废话不多说&#xff0c;还是字符数组的内容上代码&#xff01; char *p是字符指针&#xff0c;*表示p是个指针&#xff0c;char表示p指向的对象类型是char型&#xff01; char*p"abcdef&q…

使用Plist编辑器——简单入门指南

本指南将介绍如何使用Plist编辑器。您将学习如何打开、编辑和保存plist文件&#xff0c;并了解plist文件的基本结构和用途。跟随这个简单的入门指南&#xff0c;您将掌握如何使用Plist编辑器轻松管理您的plist文件。 plist文件是一种常见的配置文件格式&#xff0c;用于存储应…

7.6Java EE——Bean的生命周期

Bean在不同作用域内的生命周期 Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中&#xff0c;Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中&#xff0c;Spring容器可以管理Bean的生命周期&#xff0c;控制…

vue父组件和子组件数据传递

vue --父组件向子组件传递数据 父组件&#xff1a; <template><div class"parent"><p>父组件&#xff1a;{{ msg }}</p><Child message"Hello, I am parent!"></Child></div> </template><script>…

【Linux】udp客户端windows版以及Tcp服务器的实现

windows版客户端更适合大多数人~ 文章目录 一. udp客户端windows版二.Tcp服务器的实现总结 一、udp客户端windows版 首先我们将上一篇文章中实现的udp大型聊天室的代码进行修改&#xff0c;注意我们只修改服务端代码将代码修改的很简单就好&#xff0c;因为我们只是做一个如何…

【Flume 01】Flume简介、部署、组件

1 Flume简介 Flume是一个高可用、高可靠、分布式的海量日志采集、聚合和传输的系统 主要特性&#xff1a; 它有一个简单、灵活的基于流的数据流结构&#xff08;使用Event封装&#xff09;具有负载均衡机制和故障转移机制一个简单可扩展的数据模型(Source、Channel、Sink) Sou…

Zookeeper的基本概念以及安装

Zookeeper简介 Zookeeper是一个分布式的(多台机器同时干一件事情),开源的分布式应用程序协调服务,是Google公司Chubby产品,是Hadoop和Base重要的组件,.它是一个分布式应用程序提供一致性的服务的软件,提供的功能包括:配置服务,域名服务,分布式同步,组服务等 Zookeeper目…

多肽试剂1801415-23-5,Satoreotide,UNII-S58172SSTS,应用在多肽标记及修饰上

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ Satoreotide&#xff0c;UNII-S58172SSTS Product structure Product specifications 1.CAS No&#xff1a;1801415-23-5 2.Molecular formula&#xff1a;C58H72ClN15O14S2 3.Molecular weight&#xff1a;1302.9 4.Packa…

手机word文档怎么转换成pdf?分享两种方法

手机word文档怎么转换成pdf&#xff1f;在如今信息化的时代&#xff0c;电子文档已经成为人们日常办公不可或缺的一部分。随着科技的不断进步&#xff0c;电子文档的格式也在不断发展。PDF作为电子文档的一种重要格式&#xff0c;被广泛使用。那么&#xff0c;如何将手机上的Wo…

一)Stable Diffusion使用教程:安装

目前AI绘画最火的当属Midjorney和Stable Diffusion&#xff0c;但是由于Midjourney没有开源&#xff0c;因此我们主要分享下Stable Diffusion&#xff0c;后面有望补上Midjourney教程。 本节主要讲解Stable Diffusion&#xff08;以下简述SD&#xff09;的下载和安装。 1&…

MyBatis学习笔记——2

MyBatis学习笔记——2 一、MyBatis核心配置文件详解1.1、environment&#xff08;环境&#xff09;1.2、transactionManager&#xff08;事务管理器&#xff09;1.3、dataSource&#xff08;数据源&#xff09;1.4、properties1.5、mapper 二、在WEB中应用MyBatis&#xff08;使…

轮播图添加删除

轮播图页面和对话框搭建 页面简单布局 <template><div id"banner"><el-space direction"vertical" :size"20" style"width: 100%"><h1>轮播图管理</h1><div style"text-align: right"&g…

商城-学习整理-基础-分布式组件(三)

目录 一、前言二、Spring Cloud&Spring Cloud Alibaba1、Spring Cloud 与Spring Cloud Alibaba简介2、为什么使用Spring Cloud Alibaba3、版本选择4、项目中的依赖 三、Spring Cloud Alibaba-Nacos作为注册中心1、Nacos1&#xff09;、下载 nacos-server2&#xff09;、启动…

42. 接雨水

42.接雨水 这是一个简单的动态规划问题&#xff0c;虽然leetcode将它归结为困难。 但是我感觉它难度应该达不到&#xff0c;可能归结为中等比较合适0x1 题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨…

python flask 返回中文乱码

使用flask返回数据中带有中文的时候会显示成乱码(ascii)&#xff0c; 中文报文&#xff1a; ABAP中的三大财务报表是&#xff1a;* **资产负债表** * **收入证明** * **现金流量表**这些报表全面概述了公司的财务状况和业绩。* **资产负债表**显示公司在特定时间点的资产、负…

【Python数据分析】Python基础知识篇

&#x1f389;欢迎来到Python专栏~Python基础知识篇 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;Python学习专栏 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大…

LeetCode[1508]子数组和排序后的区间和

难度&#xff1a;Medium 题目&#xff1a; 给你一个数组 nums &#xff0c;它包含 n 个正整数。你需要计算所有非空连续子数组的和&#xff0c;并将它们按升序排序&#xff0c;得到一个新的包含 n * (n 1) / 2 个数字的数组。 请你返回在新数组中下标为 left 到 right &#…

将请求参数数据推送至RabbitMQ队列中并且捕捉消息没有到达交换机的异常

1&#xff1a;自定义mq信息类&#xff08;我的交换这些信息都从nacos上直接取的&#xff0c;怎么从nacos取配置信息看上篇文章&#xff09;&#xff1a; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;impo…