一、串口通信介绍
通用异步收发传输器,通常称为UART,是一种采用异步串行通信方式的收发传输器。在串行通信时,要求通信双方都采用一种标准接口,使不同的设备可以方便地连接起来进行通信。RS-232-C接口(又称EIARS-232-C)是常用的一种串行通信接口。
串口的主要功能为:在发送数据时将并行数据转换成串行数据进行传输,在接收数据时将接收到的串行数据转换成并行数据。
为了实现串口通信,这里使用FPGA-EGO1开发板做串口回环实验,要求PC端通过串口助手发送数据给FPGA,FPGA接收到数据后再发回PC端,并在串口助手处显示数值。即串口助手发送什么就能接收回什么。
在串行通信中,数据是按位传送的,因此数据传输速率用每秒钟传送二进制代码的位数表示,称为波特率。每秒传送一个格式位就是1波特,即1波特=1bit/s(位/秒)。常见的波特率有9600bit/s、115200bit/s等,其他标准的波特率有1200bit/s、2400bit/s、4800bit/s、19200bit/s、38400bit/s、57600bit/s。
二、源代码
1、顶层模块
module uart_top(clk,rst_n,uart_rx,uart_tx);
input clk; //时钟,100MHZ
input rst_n; //复位,低电平有效
input uart_rx; //FPGA通过串口接收的数据
output uart_tx; //FPGA通过串口发送的数据
wire [7:0]data;
wire data_vld;
//接收模块
uart_rx u1(.clk(clk),
.rst_n(rst_n),
.din(uart_rx),
.dout(data),
.dout_vld(data_vld)
);
//发送模块
uart_tx u2(.clk(clk),
.rst_n(rst_n),
.din_vld(data_vld),
.din(data),
.dout(uart_tx)
);
endmodule
2、发送
module uart_tx
#(parameter CLK = 100_000_000, //100MHZ时钟
parameter BPS = 9600, //9600波特率
parameter BPS_CNT = CLK/BPS //波特率计数
)
(clk,rst_n,din,din_vld,dout);
input wire clk;
input wire rst_n;
input wire [7:0]din; //输入数据
input wire din_vld; //输入数据的有效指示
output reg dout; //输出数据
reg flag;
reg [7:0]din_tmp;
reg [15:0]cnt0; //波特率计数
wire add_cnt0;
wire end_cnt0;
reg [3:0]cnt1; //数据位计数
wire add_cnt1;
wire end_cnt1;
wire [9:0]data; //发送数据
//数据暂存(din可能会消失)
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
din_tmp <= 8'd0;
else if(din_vld)
din_tmp <= din;
end
//发送状态指示
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
flag <= 0;
else if(din_vld)
flag <= 1;
else if(end_cnt1)
flag <= 0;
end
//波特率计数
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt0 <= 0;
else if(add_cnt0)
begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = flag;
assign end_cnt0 = (cnt0 == (BPS_CNT-1)) || end_cnt1;
//开始1位+数据8位+停止1位,共10位
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt1 <= 0;
else if(add_cnt1)
begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = (cnt1 == (10-1))&&(cnt0==(BPS_CNT/2-1));
//数据输出
assign data = {1'b1,din_tmp,1'b0};
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
dout <= 1'b1;
else if(flag)
dout <= data[cnt1];
end
endmodule
3、接收
module uart_rx
#(parameter CLK = 100_000_000, //100MHZ时钟
parameter BPS = 9600, //波特率
parameter BPS_CNT = CLK/BPS //波特率计数
)
(clk,rst_n,din,dout,dout_vld);
input clk,rst_n;
input din; //输入数据
output reg [7:0] dout; //输出数据
output reg dout_vld; //输出数据的有效指示
reg rx0;
reg rx1;
reg rx2;
wire rx_en;
reg flag;
reg [15:0]cnt0; //波特率计数
wire add_cnt0;
wire end_cnt0;
reg [3:0]cnt1; //数据位计数
wire add_cnt1;
wire end_cnt1;
reg [7:0]data;
//消除亚稳态+下降沿检测
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rx0 <= 1; rx1 <= 1; rx2 <= 1;
end
else
begin
rx0 <= din; rx1 <= rx0; rx2 <= rx1;
end
end
assign rx_en = rx2 && ~rx1; //rx2=1且rx1=0,检测下降沿
//接收状态机指示
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
flag <= 0;
else if(rx_en)
flag <= 1;
else if(end_cnt1)
flag <= 0;
end
//波特率计数
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt0 <= 0;
else if(add_cnt0)
begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = flag;
assign end_cnt0 = (cnt0 == BPS_CNT - 1) || end_cnt1;
//开始1位(不接收)+数据8位+停止1位(不接收),共10位
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt1 <= 0;
else if(add_cnt1)
begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = (cnt1 == (10-1))&&(cnt0 == (BPS_CNT/2-1));
//缓存数据
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
data <= 8'd0;
else if((cnt1>=1)&&(cnt1<=8)&&(cnt0==BPS_CNT/2-1))
data[cnt1-1] <= rx2;
end
//输出数据
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
dout <= 0;
else if(end_cnt1)
dout <= data;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
dout_vld <= 0;
else if(end_cnt1)
dout_vld <= 1;
else
dout_vld <= 0;
end
endmodule
4、端口配置说明
三、上板验证
fpga-uart