一、UART简介

  UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器。一般来说,UART总是和RS232成对出现,那RS232又是什么呢? RS232也就是我们计算机上的串口,它的全称是EIA-RS-232C (简称232,或者是RS232 )。其中EIA(Electronic Industry Association)代表美国电子工业协会,RS是Recommended Standard的缩写,代表推荐标准,232 是标识符,C表示修改次数,它被广泛用于计算机串行接口外设连接。如果你的计算机上还有串口的话,那么你就可以在主机箱后面看到RS232的接口:

  随着时代的发展,这种借口已经很少用了,取而代之的是“USB转串口”,功能和原先一样,但接口更高效了。

  串口的主要功能为:在发送数据时将并行数据转换成串行数据进行传输,在接收数据时将接收到的串行数据转换成并行数据。这应该是大多数人接触电子后学习到的第一个通信协议吧。

二、通信格式

  下面来说说串口的具体要点:

1.传输时序

  UART串口通信需要两个信号线来实现,一根用于串口发送,另外一根负责串口接收。一开始高电平,然后拉低表示开始位,接着8个数据位,然后校验位,最后拉高表示停止位,并且进入空闲状态,等待下一次的数据传输。

  很多时候我们的校验位是允许省略的,所以协议就变成了:开始+数据+停止。

2.传输速率:波特率

  串口通信的速率用波特率表示,它表示麦苗传输二进制数据的位数,单位是bps(位/秒)。常用的波特率有9600、19200、35400、57600以及115200等。

  FPGA开发串口时,设计波特率的方法:FPGA的时钟频率/波特率。例如我的FPGA开发板时钟频率为50Mhz,即50_000_000hz,我想使用的波特率为9600bps,因此我需要的计数为:50000000/9600≈5208。

三、串口回环设计

  现在用FPGA开发板做一个串口回环的实验,要求是PC端通过串口助手发送数据给FPGA,FPGA接收到数据后返回给PC端,并在串口助手处显示数值。即串口助手发什么就能收回什么。实验框图如下:

1.uart_rx

 //**************************************************************************
// *** 名称 : uart_rx.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-01-10
// *** 描述 : 串口接收模块,计数9.5下,其中停止位0.5下
// 因为串口助手发送本次停止位和下次开始位中间没有留空闲位
// 若计满10下,则才结束本次传输下次数据就来了,会来不及接收
//************************************************************************** module uart_rx
//========================< 参数 >==========================================
#(
parameter CLK = 50_000_000 , //系统时钟,50Mhz
parameter BPS = , //波特率
parameter BPS_CNT = CLK/BPS //波特率计数
)
//========================< 端口 >==========================================
(
input wire clk , //时钟,50Mhz
input wire rst_n , //复位,低电平有效
input wire din , //输入数据
output reg [:] dout , //输出数据
output reg dout_vld //输出数据的有效指示
);
//========================< 信号 >==========================================
reg rx0 ;
reg rx1 ;
reg rx2 ;
wire rx_en ;
reg flag ;
reg [:] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ :] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg [ :] data ; //==========================================================================
//== 消除亚稳态 + 下降沿检测
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rx0 <= ;
rx1 <= ;
rx2 <= ;
end
else begin
rx0 <= din;
rx1 <= rx0;
rx2 <= rx1;
end
end assign rx_en = rx2 && ~rx1; //==========================================================================
//== 接收状态指示
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
flag <= ;
else if(rx_en)
flag <= ;
else if(end_cnt1)
flag <= ;
end //==========================================================================
//== 波特率计数
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt0 <= ;
else if(add_cnt0) begin
if(end_cnt0)
cnt0 <= ;
else
cnt0 <= cnt0 + ;
end
end assign add_cnt0 = flag;
assign end_cnt0 = cnt0== BPS_CNT- || end_cnt1; //==========================================================================
//== 开始1位(不接收) + 数据8位 + 停止0.5位(不接收),共10位
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt1 <= ;
else if(add_cnt1) begin
if(end_cnt1)
cnt1 <= ;
else
cnt1 <= cnt1 + ;
end
end assign add_cnt1 = end_cnt0;
assign end_cnt1 = cnt1==- && cnt0==BPS_CNT/-; //==========================================================================
//== 缓存数据
//==========================================================================
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)
data <= 'd0;
else if(cnt1>= && cnt1<= && cnt0==BPS_CNT/-) //中间采样
data[cnt1-] <= rx2; //或 dout <= {rx2,dout[7:1]};
end //==========================================================================
//== 输出数据
//==========================================================================
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)
dout <= ;
else if(end_cnt1)
dout <= data;
end always @ (posedge clk or negedge rst_n)begin
if(!rst_n)
dout_vld <= ;
else if(end_cnt1)
dout_vld <= ;
else
dout_vld <= ;
end endmodule

2.uart_tx

 //**************************************************************************
// *** 名称 : uart_tx.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-01-10
// *** 描述 : 串口接收模块,计数9.5下,其中停止位0.5下
// 因为极端情况是本次停止位和下次开始位中间没有留空闲位
// 若计满10下,则才结束本次传输下次数据就来了,会来不及发送
//************************************************************************** module uart_tx
//========================< 参数 >==========================================
#(
parameter CLK = 50_000_000 , //系统时钟,50Mhz
parameter BPS = , //波特率
parameter BPS_CNT = CLK/BPS //波特率计数
)
//========================< 端口 >==========================================
(
input wire clk , //时钟,50Mhz
input wire rst_n , //复位,低电平有效
input wire [:] din , //输入数据
input wire din_vld , //输入数据的有效指示
output reg dout //输出数据
);
//========================< 信号 >==========================================
reg flag ;
reg [ :] din_tmp ;
reg [:] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ :] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
wire [ :] data ; //==========================================================================
//== 数据暂存(din可能会消失,暂存住)
//==========================================================================
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
din_tmp <='d0;
else if(din_vld)
din_tmp <= din;
end //==========================================================================
//== 发送状态指示
//==========================================================================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
flag <= ;
else if(din_vld)
flag <= ;
else if(end_cnt1)
flag <= ;
end //==========================================================================
//== 波特率计数
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt0 <= ;
else if(add_cnt0) begin
if(end_cnt0)
cnt0 <= ;
else
cnt0 <= cnt0 + ;
end
end assign add_cnt0 = flag;
assign end_cnt0 = cnt0== BPS_CNT- || end_cnt1; //==========================================================================
//== 开始1位 + 数据8位 + 停止0.5位,共10位
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt1 <= ;
else if(add_cnt1) begin
if(end_cnt1)
cnt1 <= ;
else
cnt1 <= cnt1 + ;
end
end assign add_cnt1 = end_cnt0;
assign end_cnt1 = cnt1==- && cnt0==BPS_CNT/-; //==========================================================================
//== 数据输出(用case语句也行)
//==========================================================================
assign data = {'b1,din_tmp,1'b0}; //停止,数据,开始 always @(posedge clk or negedge rst_n) begin
if(!rst_n)
dout <= 'b1;
else if(flag)
dout <= data[cnt1];
end endmodule

3.top层

 //**************************************************************************
// *** 名称 : uart_top.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-01-10
// *** 描述 : 串口实验顶层文件
//************************************************************************** module uart_top
//========================< 端口 >==========================================
(
input wire clk , //时钟,50Mhz
input wire rst_n , //复位,低电平有效
input wire uart_rx , //FPGA通过串口接收的数据
output wire uart_tx //FPGA通过串口发送的数据
); //========================< 连线 >==========================================
wire [:] data ;
wire data_vld ; //==========================================================================
//== 模块例化
//==========================================================================
uart_rx
#(
.BPS_CNT ( ) //仿真用
)
u_uart_rx
(
.clk (clk ),
.rst_n (rst_n ),
.din (uart_rx ),
.dout (data ),
.dout_vld (data_vld )
); uart_tx
#(
.BPS_CNT ( ) //仿真用
)
u_uart_tx
(
.clk (clk ),
.rst_n (rst_n ),
.din_vld (data_vld ),
.din (data ),
.dout (uart_tx )
); endmodule

四、仿真调试

1、testbench

 `timescale 1ns/1ps  //时间精度
`define Clock //时钟周期 module uart_top_tb; //========================< 端口 >==========================================
reg clk ; //时钟,50Mhz
reg rst_n ; //复位,低电平有效
reg uart_rx ;
wire uart_tx ; //==========================================================================
//== 模块例化
//==========================================================================
uart_top u_uart_top
(
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rx ),
.uart_tx (uart_tx )
); //==========================================================================
//== 时钟信号和复位信号
//==========================================================================
initial begin
clk = ;
forever
#(`Clock/) clk = ~clk;
end initial begin
rst_n = ; #(`Clock*+);
rst_n = ;
end //==========================================================================
//== task任务
//==========================================================================
reg [:] mem[:] ; //位宽为8,深度为16个数据
integer i ;
integer j ; //读取外部数据
initial $readmemh("./data.txt",mem); //位赋值
task rx_bit
(
input [:] data
);
begin
for(i=;i<=;i=i+) begin //10个bit为
case(i)
: uart_rx = 'b0;
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = data[i-];
: uart_rx = 'b1;
endcase
#; //一个完整波特延时:52*20=1040
end //考虑到空闲位,也可以设置得1040稍大一些
end
endtask //字节赋值
task rx_byte;
begin
for(j=;j<=;j=j+) //16个byte数据
rx_bit(mem[j]);
end
endtask //==========================================================================
//== 调用task
//==========================================================================
initial begin
#(`Clock*+);
rx_byte();
end initial begin
#;
$stop;
end endmodule

2、data.txt

  testbench中调用了一个 data.txt 文本文档,里面存储了此次仿真的16个数据,将其放置到 Modelsim 软件的工程目录中(非 work)即可。


a
b
c
d
e
f

3、仿真波形

  由波形可以看到,本次设计应该是成功的。

五、上板验证

  本次上位机采用友善串口助手,无校验位,停止位为1。当串口助手发送数据给FPGA后,FPGA很快又将原数据返回给上位机。

  经上板验证,本次设计成功!

参考资料:

[1]明德扬FPGA教程

[2]正点原子FPGA教程

[2]威三学院FPGA教程

协议——UART(RS232)的更多相关文章

  1. UART\RS232与RS485的关系

    https://blog.csdn.net/lhl161123/article/details/53510593 串口通讯是电子工程师面对的最基本的一个通讯方式,RS-232是其中最简单的一种.然而, ...

  2. UART RS232 的CTS与RTS

    目前较为常用的串口有9针串口(DB9)和25针串口(DB25),通信距离较近时(<12m),可以用电缆线直接连接标准RS232端口(RS422,RS485较远),若距离较远,需附加调制解调器(M ...

  3. z-stack协议uart分析(DMA)

    1.从ZMain里面的main函数开始分析 2.进入int main( void ); HalDriverInit();   //硬件相关初始化,有DMA初始化和UART初始化 3.进入HalDriv ...

  4. 串行通讯协议--起止式异步通讯协议(UART)

    起止式异步通讯协议: 特点与格式: 起止式异步协议的特点是一个字符一个字符传输,并且传送一个字符总是以起始位开始,以停止位结束,字符之间没有固定的时间间隔要求.其格式如图3 所示.每一个字符的前面都有 ...

  5. v3学院带你一次性认清UART、RS-232、RS-422、RS-485的区别

    通讯问题,和交通问题一样,也有高速.低速.拥堵.中断等等各种情况.如果把串口通讯比做交通,UART比作车站,那么一帧的数据就好比汽车.汽车跑在路上,要遵守交通规则.如果是市内,一般限速30.40,而高 ...

  6. [转]UART通信简介

    1.前言 UART通信,即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter). 串行通信是指利用一条传输线将资料一位位地顺序传送.特点是通信线 ...

  7. RTC实时时间系统学习笔记(一)---------------UART串口

    临近研三了,自己倾向于要找数字IC方面的工作,苦于教研室的项目一直都是调板子调板子调板子,真正用到FPGA的很少,,本着"工欲善其事必先利其器"的原则,在网上搜寻如何自学FPGA, ...

  8. USB、UART、SPI等总线速率

    1. USB总线 USB1.1: ---低速模式(low speed):1.5Mbps ---全速模式(full speed): 12Mbps USB2.0:向下兼容.增加了高速模式,最大速率480M ...

  9. USB、UART、SPI等总线速率(转)

    1. USB总线 USB1.1: ——-低速模式(low speed):1.5Mbps ——-全速模式(full speed): 12Mbps USB2.0:向下兼容.增加了高速模式,最大速率480M ...

随机推荐

  1. JMeter学习2

    JMeter学习(四)参数化 参数化:录制脚本中有登录操作,需要输入用户名和密码,假如系统不允许相同的用户名和密码同时登录,或者想更好的模拟多个用户来登录系统. 这个时候就需要对用户名和密码进行参数化 ...

  2. nodejs之express生成项目[windows平台]

    安装nvm,nvm下载地址   用于管理多个版本node,此处可省略! 安装nodejs,nodejs下载地址    淘宝镜像 安装cnpm命令,后面包可以使用cnpm命令安装,此处可省略,如果安装了 ...

  3. Hdu 5093 Battle Ship

    每个海面要么放要么不放,因此可以用二分图匹配, 考虑把同一行内的能互相看到的点放到一个行块里,同一列内能看到的点放到一个列块里,然后每一个行块都可以和该行块里所有海面的列块连边,选了这个行块,就必须选 ...

  4. hexo绑定个人域名

    前段时间用 hexo 搭建的 gitpage 个人博客,服务器用的是 github 的,然后域名默认也是 github 下的二级域名:username.github.io, 现在为了提升格调准备将自己 ...

  5. 第12组 Alpha冲刺(6/6)

    Header 队名:To Be Done 组长博客 作业博客 团队项目进行情况 燃尽图(组内共享) 展示Git当日代码/文档签入记录(组内共享) 注: 由于GitHub的免费范围内对多人开发存在较多限 ...

  6. 缺陷描述(Description)

    [tips1] 缺陷报告的用途在于: 记录bug 对bug进行分类(发现者.日期.版本.模块.严重程度.优先级) 跟踪bug(new-open-fixed-closed) 对bug进行统计分析.总结 ...

  7. 微信小程序 wxs的简单应用

    Demo地址:微信小程序wxs的简单应用 案例分析 张三.李四.王五,各自分别都有数量不等的车,现在需要列表显示名字及他们拥有车的数量, list数据结构如下,当我们使用wx:for进行显示时,发现个 ...

  8. python 运行当前目录下的所有文件

     查看当前目录下所有py文件(本身除外run) import os file_list = os.listdir(os.getcwd()) # 获取当前目录下所有的文件名print(file_list ...

  9. Android NFC M1卡读写&芯片卡读写(CPU卡读写)(RFID读写)

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/sgn5200/article/detai ...

  10. Android -------- BouncingJellyView 果冻视图(阻尼效果)

    分享一个不错的效果,分享给大家 BouncingJellyView 果冻视图,就像果冻一样伸缩弹跳,也叫阻尼效果.这个效果在MIUI上面到处都可以看到. 效果图: 使用 项目更目录bulid.grad ...