目录

  • 前言
  • 一丶通信方式
    • 1.串行通信
    • 2.并行通信
  • 二丶UART 串口通信
  • 三丶模块设计
  • 四丶发送模块
    • 1.代码
    • 2.仿真
  • 五丶接收模块
    • 1.代码
    • 2.仿真
  • 六丶顶层模块
    • 1.代码
    • 2.模块原理图
    • 3.仿真
  • 七丶上板验证
  • 八丶源码

前言

嵌入式电子产品是一种互连电路(处理器或其他集成电路)以创建共生系统。

为了使这些单独的电路交换其信息,它们必须共享公共通信协议

已经定义了数百种通信协议来实现这种数据交换,并且通信的方式主要可以分为两类:并行或串行

这一章我们需要理解串行通信,并行通信两种方式。并理清UART串口通信的整个模块设计,然后依次编写发送模块,接收模块

一丶通信方式

1.串行通信

定义:串行通信是指利用一条传输线将数据一位位地顺序传送。(也就是说串行通信传输的数据是1比特1比特的传送的

串行通信又称为点对点通信,对于点对点之间的通信
根据数据的同步方式,又分为 异步通信 和 同步通信 两种方式
根据数据的传输方向与时间关系,又可分为:①单工通信、②半双工通信及③全双工通信三种方式。

1.同步通信
同步通信一般有一个同步时钟,如下图,一根数据线,一根时钟线。一个时钟传输一个Bit位。

2.异步通信
异步通信中,在异步通信中有两个比较重要的指标:字符帧格式和波特率。
数据通常以字符或者字节为单位组成字符帧传送,是通过双方约定好的波特率进行数据传输。
假如双方波特率不一致,则接收到数据就是乱码。


单工
只允许数据按照一个固定的方向传送,在任何时刻都只能进行一个方向的通信,一个设备固定为发送设备,一个设备固定为接收设备。

半双工
两个设备之间可以收发数据,但是不能在同一时刻进行,每次只能有一个设备发送,另一个站接收。

全双工
在同一时刻,两个设备之间可以同时进行发送和接收数据。

2.并行通信

并行是指多比特数据同时通过并行线进行传送,这样数据传送速度大大提高。

但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错。

并行接口同时传输多个位。它们通常需要数据总线(八、十六或更多线路),以1和0的波形传输数据。

如下图:使用9线的并行通信,由时钟控制的8位数据总线,每个时钟脉冲发送一个字节。

二丶UART 串口通信

它是全双工异步通信
串口通信的信号线需要两条线完成,TX和RX TX发送端 RX为接收端
它的协议主要由四部分组成

  • 起始位(1 bit)
  • 数据位(6/7/8 bit)
  • 奇偶校验位(1 bit)
  • 停止位(1 bit)

原理图如下:

注意:此原理图既作为接收的数据帧,也作为发送的数据帧。因为串行通信中,在数据线上传输的数据是一位一位的,所以在收发数据的时候要进行串并之间的转换

收发流程

  • 发送:首先是空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间T1,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位停止位(停止位为高电位),一帧数据发送结束。
  • 接收:首先是空闲状态,线路处于高电位;当检测到线路的下降沿(线路电位由高电位变为低电位)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备准备接收数据或存入缓存,最后接收到停止位(停止位为高电位),一帧数据接收结束。

三丶模块设计

  • rx为接收端口
  • tx为发送端口
    上图所示分为主机从机
    我们把FPGA当作从机,来接收上位机(也就是我们的PC)发送过来的数据,再把数据发送给上位机,内部就需要两个模块 uart_rxuart_tx

uart_rx:
串口接收模块从 rx 接收到数据后,在内部实现接收方法,接收完毕后将数据进行串转并,然后发送给控制模块

uart_tx:
串口发送模块接收到uart_rx模块传来的数据,将数据进行并转串,通过tx端口发送出去

四丶发送模块

1.代码

uart_tx.v

module uart_tx (input  wire         clk,input  wire         rst_n,input  wire [7:0]   din,input  wire         din_vld,output reg          tx
);
//定义一个寄存器来锁存 din_vld 时的din
reg [9:0]       data;//波特率计数器
reg [8:0]       cnt_bps;
wire            add_cnt_bps;
wire            end_cnt_bps;//比特计数器
reg [4:0]       cnt_bit;
wire            add_cnt_bit;
wire            end_cnt_bit;reg             flag;   //计数器开启标志位parameter       BPS_115200=434;  //发送一bit数据需要的周期数//data
always @(posedge clk or negedge rst_n) beginif (!rst_n) begindata<=0;endelse if(din_vld) begindata<={1'b1,din,1'b0};   //拼接起始位和停止位endelsedata<=data;
end//发送数据 tx
always @(posedge clk or negedge rst_n) beginif (!rst_n) begintx<=1'b1;endelse if(cnt_bps==1) begintx<=data[cnt_bit];end
end//flag
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginflag<=0;endelse if(din_vld) beginflag<=1;endelse if(end_cnt_bit) begin   //发送完成关闭计数器flag<=0;endelseflag<=flag;
end//cnt_bps
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_bps<=0;endelse if(add_cnt_bps) beginif (end_cnt_bps) begincnt_bps<=0;endelsecnt_bps<=cnt_bps+1;end
endassign add_cnt_bps=flag;
assign end_cnt_bps=add_cnt_bps&&cnt_bps==BPS_115200-1;//cnt_bit
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_bit<=0;endelse if(add_cnt_bit) beginif (end_cnt_bit) begincnt_bit<=0;endelsecnt_bit<=cnt_bit+1;end
endassign add_cnt_bit=end_cnt_bps;
assign end_cnt_bit=add_cnt_bit&&cnt_bit==9;endmodule //uart_tx

2.仿真

testbench

`timescale 1ns/1ns
module tb_uart_tx ();
reg         clk;
reg         rst_n;
reg [7:0]   din;
reg         din_vld;
wire        tx;parameter       CYCLE=20;uart_tx uart_tx(.clk              (clk),.rst_n            (rst_n),.din              (din),.din_vld          (din_vld),.tx               (tx)
);always #(CYCLE/2)  clk=~clk;initial beginclk=1;rst_n=1;#(CYCLE*10);rst_n=0;din_vld=0;#(CYCLE*10);rst_n=1;din=8'b1001_0101;din_vld=1;#CYCLE;din_vld=0;#(CYCLE*434*12);$stop;endendmodule //tb_uart_tx

如下图所示:
一开始我们进行复位之后,用仿真模拟了uart_rx接收模块,把din(也就是本应该在uart_tx模块进行串转并)和din_vld信号给uart_tx模块,检测到din_vld拉高,data锁存住din,同时开启波特率计数器,从起始位开始发送,直到停止位

因为我们仿真文件给din赋值为8’b1001_0101,在uart_tx模块中用data拼接起始位和停止位,拼接之后的data为10位的1_1001_0101_0
仿真时序图中可以看到tx端口为0101010011,按顺序输出

五丶接收模块

1.代码

uart_rx

module uart_rx (input  wire         clk,input  wire         rst_n,input  wire         rx,output reg  [7:0]   dout,    //由接收到的数据进行并转串输出给uart_tx模块output reg          dout_vld //接收完成标志位
);reg             flag;       //rx下降沿来临flag拉高
reg [9:0]       dout_r;     //锁存rx接收到的数据(从起始位 到数据为 到停止位)reg             rx_r0;      //同步
reg             rx_r1;      //打拍
wire            nedge;      //下降沿检测//波特率计数器
reg [8:0]       cnt_bps;
wire            add_cnt_bps;
wire            end_cnt_bps;//bit计数器
reg [3:0]       cnt_bit;
wire            add_cnt_bit;
wire            end_cnt_bit;parameter       BPS_115200 = 434;//同步
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginrx_r0<=1;endelserx_r0<=rx;
end
//打拍
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginrx_r1<=1;   endelserx_r1<=rx_r0;
endassign nedeg=~rx_r0 & rx_r1;   //下降沿检测,起始位为低电平//flag
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginflag<=0;endelse if(nedeg) beginflag<=1;endelse if(end_cnt_bit) beginflag<=0;end
end//cnt_bps
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_bps<=0;endelse if(add_cnt_bps) beginif (end_cnt_bps) begincnt_bps<=0;endelsecnt_bps<=cnt_bps+1;end
endassign add_cnt_bps=flag;
assign end_cnt_bps=add_cnt_bps&&cnt_bps==BPS_115200-1;//cnt_bit
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_bit<=0;endelse if(add_cnt_bit) beginif (end_cnt_bit) begincnt_bit<=0;endelsecnt_bit<=cnt_bit+1;end
endassign add_cnt_bit=end_cnt_bps;
assign end_cnt_bit=add_cnt_bit&&cnt_bit==10 -1;//dout_r
always @(posedge clk or negedge rst_n) beginif (!rst_n) begindout_r<=0;endelse if(cnt_bps==(BPS_115200>>1)) begindout_r[cnt_bit]<=rx_r0;end
end//dout
always @(posedge clk or negedge rst_n) beginif (!rst_n) begindout<=0;endelse if(end_cnt_bit) begindout<=dout_r[8:1];        //只用把数据位传给uart_tx模块,所以取中间的8位end
end//dout_vld
always @(posedge clk or negedge rst_n) beginif (!rst_n) begindout_vld<=0;endelse if(end_cnt_bit) begindout_vld<=1;        //给发送模块的数据有效信号endelsedout_vld<=0;
end
endmodule //uart_rx

2.仿真

testbench

`timescale 1ns/1ns
module tb_uart_rx ();
reg         clk;
reg         rst_n;
reg         rx;
wire[7:0]   dout;
wire        dout_vld;integer i;parameter       CYCLE=20;uart_rx uart_rx(.clk               (clk),.rst_n             (rst_n),.dout              (dout),.dout_vld          (dout_vld),.rx                (rx)
);always #(CYCLE/2)  clk=~clk;initial beginclk=1;rst_n=1;#(CYCLE*10);rst_n=0;rx=1;#(CYCLE*10);rst_n=1;rx=0;   //起始位#(CYCLE*434);for (i = 0; i<8; i=i+1) begin#(CYCLE*434);rx={$random}%2;  //产生0~1的随机数endrx=1;   //停止位#(CYCLE*1000);$stop;endendmodule

结果如下图所示:

我们在接收模块需要把接收到的串行数据转换成并行数据再传给uart_tx模块
空闲状态的时候,rx处于高电平状态,我们在仿真文件中模拟rx拉低,也就是接收到了起始位,波特率计数器(add_cnt_bps)开启

rx接收到停止位,也就是一共接收了10bit(1bit起始位+8bit数据位+1bit停止位),此时bit计数器拉低(end_cnt_bit),并行数据dout锁存,拉高一个周期的数据有效信号(dout_vld)

六丶顶层模块

1.代码

uart.v

module uart (input  wire             clk,input  wire             rst_n,input  wire             rx,    //接收output wire             tx     //发送
);
wire [7:0]      dout;
wire            dout_vld;  uart_tx  u_uart_tx(           //将发送的并行数据转换成串行数据.clk            (clk),.rst_n          (rst_n),.tx             (tx),     //串行数据.din            (dout),   //并行数据.din_vld        (dout_vld)
);uart_rx  u_uarr_rx(           //将接收到的串行数据转换成并行数据.clk            (clk),.rst_n          (rst_n),.rx             (rx),     //串行数据.dout           (dout),   //并行数据.dout_vld       (dout_vld)
);
endmodule //uart

2.模块原理图

3.仿真

对于顶层的仿真,我们需要模拟出一个上位机来给我们的FPGA发送数据,
所以在例化模块的时候,我们首先例化一个uart_tx当作上位机,来给我们的从机发送数据,然后例化一个uart模块当作从机,也就是例化顶层模块

思路清晰了,进行testbench的编写

`timescale 1ns/1ns
module tb_uart ();
reg         clk;
reg         rst_n;
reg [7:0]   din;
reg         din_vld;
wire        tx_r;   //用来连接上位机的tx和从机的rx
wire        rx;parameter       CYCLE=20;//例化从机(顶层模块,包含了一个uart_rx和一个uart_tx)
uart uart(.clk            (clk),.rst_n          (rst_n),.rx             (tx_r),    //接收.tx             (tx)     //发送
);//例化上位机(用来给从机发送数据)
uart_tx uart_tx(.clk              (clk),.rst_n            (rst_n),.din              (din),.din_vld          (din_vld),.tx               (tx_r)
);always #(CYCLE/2)  clk=~clk;initial beginclk=1;rst_n=1;#(CYCLE*10);rst_n=0;din_vld=0;#(CYCLE*10);rst_n=1;din=8'b1001_0101;din_vld=1;#CYCLE;din_vld=0;#(CYCLE*434*22);$stop;endendmodule //tb_uart_tx

首先,我们模拟上位机发送数据,在仿真文件中拉高dout_vld,并给dout一个8位的数据1001_0101,经过拼接起始位和停止位,data锁存这一帧的数据:1_1001_0101_0

放大观察细节,在时钟上升沿检测到cnt_bps为1,则把data的第0位给tx发送
对照uart_tx的这一部分代码来理解


发送了起始位之后,从机(FPGA)收到起始位

下图 第3步 从机的接收模块接收完成之后,给自己的发送模块 传送并行数据 dout 以及数据有效信号dout_vld

发送模块接收到数据有效信号dout_vld拉高,将数据缓存,并开启计数器(意味着开始传数据,!!!从起始位开始,这里缓存数据也要拼接起始位和停止位

之后就跟上位机发送数据一模一样了

七丶上板验证

管脚绑定

本次实验的结果:上位机(PC)给我们的从机(FPGA)发送数据之后,从机收到数据并将数据发送给上位机,如图显示的一样
下一章,我们将使用FIFO作为数据缓存单元,完成数据回环

八丶源码

https://github.com/xuranww/uart.git

参考文章:
1.https://blog.csdn.net/Rocher_22/article/details/116590629
2.https://blog.csdn.net/ybhuangfugui/article/details/109465401


  1. 本文采用的比特率是115200bps,也就是1s传输115200bit的数据,
    且我们使用的时钟是20MHz
    计算传输一次需要的时间:
    计数时间 = 1_000_000_000ns/115200 = 8680ns
    50MHz的时钟周期为20ns,所以计数传输一个比特的次数为8680 / 20 = 434 ↩︎

【FPGA】UART串口通信相关推荐

  1. 基于FPGA Uart串口通信实验

    基于FPGA Uart串口通信实验 首先需要了解uart串口通信协议,根据个人专业需求不同,了解的层面可以不同. UART简介 通用异步收发传输器(Universal Asynchronous Rec ...

  2. 基于FPGA的UART串口通信实验(VHDL语言实现)

    一.前言: 最近在做UART串口通信的相关实验时,在网上查了很多资料,发现网上的大部分文章只注重理论,不注重代码,很多代码有错误不说,而且难以理解.故在完成此实验后,起了写一篇博客的心思,以供有想做相 ...

  3. 【FPGA】八、UART串口通信

    文章目录 前言 一.UART简介 1.基本概念 2.UART协议 3.波特率简介 二.UART串口回环实验 1.设计思路 2.程序代码 ① 串口接收模块 ② 串口发送模块 ③ 串口顶层模块 ④ 串口仿 ...

  4. 【FPGA练习】(一): UART串口通信实验

    由于之前学习FPGA的过程中,没有做一个良好的记录,以及已学知识的扩展,所以从今天开始每一个实验例程和扩展应用,都要做文档记录.本实验,是基于正点原子达芬奇xc7a35tfgg484-2开发板.开发板 ...

  5. (一)FPGA之串口通信(UART)

    (一)FPGA之串口通信(UART) 回到梦开始的地方,如今回过头来看串口协议,确实清晰了很多,但是奈何好记性不如烂笔头,我还是要重新记录一下学习的知识点,方便查找和学习. 波特率(Band Rate ...

  6. 【正点原子MP157连载】 第十六章 UART串口通信实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  7. [FPGA] UART串口回环

    文章目录 前言 一.UART是什么? 二.UART协议 1.内容 2.系统模块划分 2.1.接收模块 2.2.发送模块 2.3.控制模块 2.4.顶层模块 3.仿真 前言 上期介绍了串行通信的基本概念 ...

  8. UART串口通信(回环测试)

    一 UART串口通信简介 UART(Universal Asynchronous Receiver-Transmitter)是采用异步串行通信方式的通用异步收发传输器,在发送数据时将并行数据转换为串行 ...

  9. UART 串口通信实验

            串口是"串行接口"的简称,即采用串行通信方式的接口.串行通信将数据字节分成一位一位的形式在一条数据线上逐个传送,其特点是通信线路简单,但传输速度较慢.因此串口广泛应 ...

最新文章

  1. 《数据结构与抽象:Java语言描述(原书第4版)》一JI2.3 抛出异常
  2. 正整数的中文表示python_使用Python列表理解计算列表中的正整数元素
  3. S4 KNUMH的设计原理
  4. 建立数据库的原则(怎样建立一个好的数据库)
  5. 我的梦幻动画实习工作总结
  6. 《转》 在C++中使用TinyXML2解析xml
  7. 为什么有那么多人选择Python,真的有那么好吗?
  8. 修复IPSEC Services服务无法启动的问题
  9. 2021年电力电缆作业考试题库
  10. InletexEMC共享Windows屏幕的永久免费软件
  11. 计算机flash听课记录范文,听课记录范文
  12. LinkTrack UWB定位系统NLink协议解析方法
  13. 20145212 罗天晨 《网络对抗》Exp3 Advanced 恶意代码伪装技术实践
  14. 嵌入式工程师“中年危机”应对策略上
  15. 计算机网络课设--监控IP、解析IP数据包(MFC版,可直接运行)
  16. docker mysql volum_Docker 存储卷 Volume 删除和孤单 volume 清理
  17. 2007人类还有什么未解之谜
  18. WindowsBuilder管家婆记账软件
  19. 地级市学校数、教师数和学生数
  20. Artemis逾期消息(14)

热门文章

  1. VideoReTalking:用于野外会说话的头部视频编辑的基于音频的唇形同步
  2. 高校实验室应该怎样建设(实验室建设规划)
  3. 3GPP 5G协议规范说明
  4. OpenCV4Android中NDK开发(二)---图片转为灰度图
  5. memset实例(一)
  6. spdlog linux编译出错,【C++】spdlog--log4cxx有点笨重,试一试spdlog
  7. 浅谈--常见的引流方式及变现
  8. 2023年电大中专怎么报名?靠谱吗?启程别告诉你
  9. 二、基本语法(笔记)
  10. 【云计算】云计算、雾计算、边缘计算到底是怎么回事?