目录
一、UART通信协议简介
二、UART通信时序
三、UART、RS232、TTL关系阐述
1.简介
2.电平转换
四、实例
1.程序代码
2.仿真验证
总结
一、UART通信协议简介
UART(Universal Asynchronous Receiver-Transmitter)是一种通用串行数据总线,用于异步通信,是一种低速、全双工的通信数据总线,在FPGA中,UART常应用于PC与FPGA之间的慢速通信。UART有tx(发送数据线),rx(接收数据线)两条数据传输线,因此是一种全双工的、异步通信模式。在通信的过程中,双方需要事先约定好通信速率,UART常用的通信速率9600bite/s、19200bite/s、115200bite/s等。
二、UART通信时序
UART异步串行通信是以字符为传输单位,一位一位的顺序输送。由于UART通信无时钟线,因此UART通信不同于IIC通信和SPI通信,属于异步串行通信,通信双方需要事先约定好通信速率,即误码率:每秒传输二进制bite的个数。
下图给出了UART通信的时序图:
空闲位:高电平表示空闲状态
起始位:由高电平到低电平的跳变,表示进入到起始位,依靠检测起始位来实现发送与接收方的时
间的自同步。
数据传输:可以是4、5、6、7、8位逻辑0或者1,构成一个字符,从最低位开始传送。
奇偶校验:数据位加上这一位后,使得“1”的位数为偶数(偶校验)或奇数(奇校验)以此来校验
数据传送的正确性,可以没有该部分。
结束位:它是一个字符数据的结束标志,可以是1位、1.5位、2位的高电平,适用于停止位的位数
越多,不同时钟同步的容忍程度越大,数据传输率同时也越低。
三、UART、RS232、TTL关系阐述
1.简介
uart即上述所说的一种通用的异步收发器,或者说是串行通信的一种协议,这里不再赘述。
CMO口是指针对串行通信协议的一种端口,即串行通讯端口,简称串口。大部分为9针D型,常见于台式电脑的后边,目前DB9型的接口基本已经被USB接口取代;还有一部分为4针杜邦头,也就是我们常说的四针串口,常见于pcb板上预留接口。RS-232和RS-485是CMO口的两种协议,或者说是通信的电平标准:RS-232采用的是负逻辑,即逻辑“1”表示-3V到-15V之间的电平,逻辑“0”表示+3V到+15V之间的电平,实际上RS-232电平标准已经在PC界成为了默认的标准;RS-485利用的是两根数据线之间的电压差进行数据传输,即逻辑“1”以两线间的电压差+2V~+6V表示,逻辑“0”以两线间的电压差-6V~-2V表示。两者之间的区别主要在于以下几点:1.传输方式不同,S-232采取不平衡传输方式,即所谓单端通讯. 而RS485则采用平衡传输,即差分传输方式。2.传输距离不同。RS-232适合本地设备之间的通信,传输距离一般不超过20m。而RS-485的传输距离为几十米到上千米。3.RS-232 只允许一对一通信,而RS-485 接口在总线上是允许连接多达128个收发器。
TTL全名是晶体管-晶体管逻辑集成电路(Transistor-Transistor Logic),也是一种通信电平的标准。TTL对应的物理电平,始终是在0V和Vcc之间,其中常见的Vcc是5V或3.3V。TTL 采用的是正逻辑,即逻辑“1”表示>=2.4V的电平,逻辑"0"表示<=0.5V的电平。
2.电平转换
对于同样传输8'b01010101来说,RS232和TTL的时序对比如下:
RS232和TTL之间的转换,可以通过MAX3232芯片,把TTL电平转换成RS232电平,该芯片同样也可以实现RS232转换成TTL电平。
USB转TTL电平可以通过PL2303或者CP2102芯片来完成转换,目前市面上一些USB转TTL串口的小板使用起来相对比较方便,如下图所示,可以随时完成PC端与FPGA开发板之间的数据通信。
四、实例
该例子实现了PC主机与FPGA之间的数据环回,即主机通过UART串口通信协议向FPGA发送一串数据,FPGA将接收到的数据再经串口发送给主机。
下边是整个系统模块的示意图:
下边是接收端时序图:
下边是发送端时序图:
1.程序代码
主程序Verilog代码:
module uart_receive (
input clk,
input res,
input rxd,
output reg [7:0] data,
output reg done
);
parameter clk_fre = 50000000;
parameter uart_bps = 115200;
localparam BPS_CONT = clk_fre/uart_bps;
reg rx_d0;
reg rx_d1;
reg rx_flag;
reg [15:0] clk_cnt;
reg [3:0] rx_cnt;
reg [7:0] rdata;
wire start_flag;
assign start_flag = (~rx_d0)& rx_d1;
always @(posedge clk or negedge res) begin
if(!res) begin
rx_d0 <= 1'b0;
rx_d1 <= 1'b0;
end
else begin
rx_d0 <= rxd;
rx_d1 <= rx_d0;
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
rx_flag <= 1'b0;
end
else begin
if(start_flag)
rx_flag <= 1'b1;
else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CONT/2))
rx_flag <= 1'b0;
else
rx_flag <= rx_flag;
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
rx_cnt <= 4'd0;
clk_cnt <= 16'd0;
end
else if(rx_flag) begin
if(clk_cnt <= BPS_CONT-1) begin
clk_cnt <= clk_cnt + 1'd1;
rx_cnt <= rx_cnt;
end
else begin
clk_cnt <= 16'd0;
rx_cnt <= rx_cnt + 1'd1;
end
end
else begin
rx_cnt <= 4'd0;
clk_cnt <= 16'd0;
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
rdata <= 8'd0;
end
else if (rx_flag)begin
if(clk_cnt == BPS_CONT/2)begin
case (rx_cnt)
4'd1: rdata[0] <= rxd;
4'd2: rdata[1] <= rxd;
4'd3: rdata[2] <= rxd;
4'd4: rdata[3] <= rxd;
4'd5: rdata[4] <= rxd;
4'd6: rdata[5] <= rxd;
4'd7: rdata[6] <= rxd;
4'd8: rdata[7] <= rxd;
default:;
endcase
end
else
rdata <= rdata;
end
else
rdata <= 8'd0;
end
always @(posedge clk or negedge res) begin
if(!res) begin
done <= 1'b0;
data <= 8'd0;
end
else if(rx_cnt == 4'd9) begin
done <= 1'b1;
data <= rdata;
end
else begin
done <= 1'b0;
data <= 8'd0;
end
end
endmodule
module uart_send(
input clk,
input res,
input [7:0] data,
input en,
output reg txd
);
reg tx_d0;
reg tx_d1;
reg tx_flag;
reg [15:0] clk_cnt;
reg [3:0] tx_cnt;
reg [7:0] tdata;
wire start_flag;
parameter clk_fre = 50000000;
parameter uart_bps = 115200;
localparam BPS_CONT = clk_fre/uart_bps;
assign start_flag = (~tx_d1) & tx_d0;
always @(posedge clk or negedge res) begin
if(!res) begin
tx_d0 <= 1'b0;
tx_d1 <= 1'b0;
end
else begin
tx_d0 <= en;
tx_d1 <= tx_d0;
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
tx_flag <= 1'b0;
tdata <= 8'b0;
end
else begin
if(start_flag) begin
tx_flag <= 1'b1;
tdata <= data;
end
else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CONT/2)) begin
tx_flag <= 1'b0;
tdata <= 8'b0;
end
else begin
tx_flag <= tx_flag;
tdata <= tdata;
end
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
tx_cnt <= 4'd0;
clk_cnt <= 16'd0;
end
else if(tx_flag) begin
if(clk_cnt <= BPS_CONT-1) begin
clk_cnt <= clk_cnt + 1'd1;
tx_cnt <= tx_cnt;
end
else begin
clk_cnt <= 16'd0;
tx_cnt <= tx_cnt + 1'd1;
end
end
else begin
tx_cnt <= 4'd0;
clk_cnt <= 16'd0;
end
end
always @(posedge clk or negedge res) begin
if(!res) begin
txd <= 1'b1;
end
else if(tx_flag)begin
case (tx_cnt)
4'd0: txd <= 1'b0;
4'd1: txd <= tdata[0];
4'd2: txd <= tdata[1];
4'd3: txd <= tdata[2];
4'd4: txd <= tdata[3];
4'd5: txd <= tdata[4];
4'd6: txd <= tdata[5];
4'd7: txd <= tdata[6];
4'd8: txd <= tdata[7];
4'd9: txd <= 1'b1;
default:;
endcase
end
else
txd <= 1'b1;
end
endmodule
下边给出顶层模块的代码:
module uart(
input sys_clk,
input sys_res,
input uart_rxd,
output uart_txd
);
parameter clk_fre = 50000000;
parameter uart_bps = 115200;
wire [7:0] uart_data;
wire uart_done_en;
uart_receive #(
.clk_fre(clk_fre),
.uart_bps(uart_bps)
) u_uart_receive(
.clk(sys_clk),
.res(sys_res),
.rxd(uart_rxd),
.data(uart_data),
.done(uart_done_en)
);
uart_send #(
.clk_fre(clk_fre),
.uart_bps(uart_bps)
) u_uart_send(
.clk(sys_clk),
.res(sys_res),
.data(uart_data),
.en(uart_done_en),
.txd(uart_txd)
);
endmodule
2.仿真验证
`timescale 1ns/1ns
module tb_uart;
reg sys_clk;
reg sys_res;
reg uart_rxd;
wire uart_txd;
reg [0:0] mem1_16[31:0];
reg [15:0] clk_cnt;
reg clk_flag;
parameter T = 20;
parameter clk_fre = 50000000;
parameter uart_bps = 115200;
localparam BPS_CONT = clk_fre/uart_bps;
initial begin
sys_clk = 1'b0;
sys_res = 1'b0;
#(5*T) sys_res = 1'b1;
end
always #(T/2) sys_clk <= ~sys_clk;
initial begin
$readmemb("./data.txt",mem1_16);
end
initial begin
uart_rxd = 1'b1;
#180 indata();
end
always @(posedge sys_clk or negedge sys_res) begin
if(!sys_res) begin
clk_cnt <= 16'd0;
clk_flag <= 1'b0;
end
else begin
if(clk_cnt <= BPS_CONT-1) begin
clk_cnt <= clk_cnt + 1'd1;
clk_flag <= 1'b0;
end
else begin
clk_cnt <= 16'd0;
clk_flag <= 1'b1;
end
end
end
task indata();
integer i;
begin
for(i=0;i<256;i=i+1)
begin
@(posedge clk_flag)
uart_rxd <= mem1_16[i[31:0]];
end
end
endtask
uart u_uart(
.sys_clk(sys_clk),
.sys_res(sys_res),
.uart_rxd(uart_rxd),
.uart_txd(uart_txd)
);
endmodule
上述tb_uart.v文件中通过声明了一个memeory数据类型的寄存器,用于寄存从data.txt文件中读取的数据,data.txt文件中的数据如下图所示:
下图是Modelsim中得到的仿真结果:
红色框中给出了uart_recive模块接收到八位数据10010111(先高位后低位),与上述data.txt文件中发送的数据11101001(先低位后高位)一致,说明程序的可执行性。
总结
该例子算是我初学FPGA后编写的第一个系统小程序,内容相对比较基础。文章中罗列了一些基本的概念和设计的流程,十分适合初学者学习。初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)