FPGA接口系列——UART
一、UART简介
UART是一种采用异步串行通信方式的通用异步收发传输器。这里我们主要弄明白两个问题:①什么是串行通信,与并行通信有什么区别 ②同步串行通信和异步串行通信有什么区别 ③我们常说的UART和 RS232 以及 RS485 又有什么区别?
串行通信和并行通信
串行通信只用一根数据线,将数据转换成一个bit一个bit进行发送;并行通信则可以采用8根或16根数据线,将数据逐个Byte或者Word发送出去。
同步串行通信和异步串行通信:
同步串行通信需要双方在同意时钟的控制下,同步传输数据;异步串行通信的原理大致是Transmitter端在发送数据之前会给出一个跳变信号,随即进行信号的传输,而不需要额外的时钟线,这个时候Transmitter的发送频率就叫做波特率,Receiver端的采样频率一般要比Transmitter端的频率高出4-16倍。
UART,RS232和RS485
串口、COM口是指的物理接口形式(硬件),TTL、RS232、RS485是指的电平标准(电信号)。TTL标准是低电平为0,高电平为1(+5V电平)。RS-232标准是正电平为0,负电平为1(±15V电平),是单端输入输出。RS-485与RS-232类似,但是采用差分信号负逻辑。一般我们见到的串口,有D型9针插头和4针杜邦头两种。
二、数据格式
1.传输时序
一般常用的数据位是:一个起始位、八个数据位和一个校验位以及一个停止位;关于校验位,奇校验要求校验位和数据位中“1”的结果是一个奇数;一个题外话就是在verilog中使用奇校验应为数据位异或取反。
奇校验 = ~(^data); 偶校验 = ^ data
2.传输速率:波特率
串口通信的速率用波特率表示,它表示麦苗传输二进制数据的位数,单位是bps(位/秒)。常用的波特率有9600、19200、35400、57600以及115200等
项目中使用时,考虑计数器的计数阈值就要结合波特率来考虑。比如波特率设计为9600,那么就是9600bit/s,如果系统时钟是50MHz,(1/9600)÷(1/50M)=5208.333333,也就是说,系统计数器计数到5209则可以完成一个bit数据的transmit。省略校验位,大概一个Byte数据的transmit需要10位(1起始加上8数据加上1停止)
3.程序设计
程序的设计和验证主要分为核心文件和testbench文件的编写,下面简要阐述一下发生的问题:
- 注意接口与接口之间的对应,切忌在文件编写例化调用的时候定义错数据的位宽,这种操作往往为造成数据的高阻态
- 在编写testbench仿真的时候出现了很多细小的问题,比如数据传输的问题,应该去找到源头进行分析,比如你定义的txt文件为“1 2 3 4”,实际仿真得到接受的字符却是“ 24 56 78 21”这种,就要考虑字符的失序,时序可能存在没有对齐的操作,像我这一步就是因为没有留出足够的空闲时间(因为前面有打过三拍)造成了数据的丢失与失序。
4.上板验证
上板验证中出现了一个待解决的问题
这里主要的变动是因为:我将原作者 咸鱼FPGA 代码中的(1起始+8数据+0.5停止)改成了(1起始+8数据+1停止)然后就发生了失序,这是一个待解决的问题,主要的猜想可能是,设计0.5个停止位就是空出时间以待接受新的数据?少了这0.5个余量之后可能发生了失序。
module uart_rx
#(
parameter CNT_MAX = 'd5208 ,
parameter CNT_HALF = 'd2603
)
(
//system signals
input clk , //系统时钟 50M
input rst_n ,
//input and output signals
input wire din ,
output reg [7:0] dout ,
output reg da_vld
);
//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/
reg [2:0] din_reg ; // 寄存器打拍信号,用于消除亚稳态
wire din_neg ; // 下降沿检测信号
reg flag ; // 状态检测信号
wire flag_convert ; // 数据转换标志信号
reg [12:0] cnt_50M ; // 系统时钟计数器
reg [3:0] cnt_bit ; // 传输位数计数器
reg [7:0] data ; // 缓存PC机传输过来的数据
wire begin_cnt1 ;
wire begin_cnt2 ;
wire end_cnt1 ;
wire end_cnt2 ;
//=============================================================================
//**************************** Main Code *******************************
//=============================================================================
//=============================================================================
//== 消除亚稳态加上下降沿检测
//=============================================================================
assign din_neg = din_reg[2] & (~din_reg[1]);
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
din_reg <= 1'b0;
end
else begin
din_reg <= {din_reg[1],din_reg[0],din};
end
end
//=============================================================================
//== 接受状态指示
//=============================================================================
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
flag <= 1'b0;
end
else if (din_neg) begin
flag <= 1'b1;
end
else if (end_cnt2)begin
flag <= 1'b0;
end
end
//=============================================================================
//== 波特率计数
//=============================================================================
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
cnt_50M <= 1'b0;
end
else if (begin_cnt1) begin
if (end_cnt1)
begin
cnt_50M <= 1'b0;
end
else
begin
cnt_50M <= cnt_50M + 1'b1;
end
end
end
assign begin_cnt1 = flag;
assign end_cnt1 = cnt_50M == CNT_MAX || end_cnt2;
//=============================================================================
//== 1起始8数据和0.5停止
//=============================================================================
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
cnt_bit <= 1'b0;
end
else if (begin_cnt2) begin
if (end_cnt2)
begin
cnt_bit <= 1'b0;
end
else
begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign begin_cnt2 = end_cnt1;
assign end_cnt2 = (cnt_50M == CNT_MAX) && (cnt_bit == 'd9);
//=============================================================================
//== 数据转换与缓存
//=============================================================================
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
data <= 1'b0;
end
else if (flag_convert) begin
data[cnt_bit-1] <= din_reg[2];
end
end
assign flag_convert = flag && (cnt_bit > 0) && (cnt_bit < 9) && (cnt_50M == CNT_HALF);
//=============================================================================
//== 输出数据
//=============================================================================
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
dout <= 1'b0;
end
else if (end_cnt2) begin
dout <= data;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
// reset
da_vld <= 1'b0;
end
else if (end_cnt2) begin
da_vld <= 1'b1;
end
else begin
da_vld <= 1'b0;
end
end
endmodule
标签:din,wire,FPGA,UART,通信,接口,串行,reg
From: https://www.cnblogs.com/qier0220/p/18097641