FPGA UART通信实现(二)
前言
在上一章简单介绍了FPGA实现与计算机UART通信的几种方式,本章节采用Verilog语言编写UART程序。FPGA UART通信实现(一)
一、顶层模块
顶层模块定义了UART通信的基本协议:波特率、接收位数、奇偶校验以及停止位数,例化接收模块和发送模块。
module Uart_top(
input clk,
input rst_p,
input rxd,
output txd,
input [7:0] tx_data, //发送数据
input tx_ready, //发送数据准备好信号
output tx_done, //数据发送完毕
output [7:0] rx_data,
output rx_ready
);
parameter Clk_freq = 48000000; //UART模块工作时钟
parameter Uart_Baud = 115200; //波特率
parameter Data_width = 8; //数据位数
parameter Parity = 1; //奇偶校验位 0:无校验 1:奇校验 2:偶检验
parameter Stop_width = 1; //停止位
//奇校验(odd parity):如果数据位中'1'的数目是偶数,则校验位为'1',如果'1'的数目是奇数,校验位为'0'。
//偶校验(even parity):如果数据为中'1'的数目是偶数,则校验位为'0',如果为奇数,校验位为'1'。
//
Uart_rx //UART 接收模块
#(
.Clk_freq(Clk_freq),
.Uart_Baud(Uart_Baud) ,
.Data_width(Data_width) ,
.Parity(Parity),
.Stop_width(Stop_width)
)
Uart_rx(
.clk(clk),
.rxd(rxd),
.rx_data(rx_data),
.rx_ready(rx_ready)
);
Uart_tx //UART 发送模块
#(
.Clk_freq(Clk_freq),
.Uart_Baud(Uart_Baud) ,
.Data_width(Data_width) ,
.Parity(Parity),
.Stop_width(Stop_width)
)
Uart_tx(
.clk(clk),
.txd(rxd),
.tx_data(tx_data),
.tx_ready(tx_ready),
.tx_done(tx_done)
);
endmodule
二、接收模块
module Uart_rx(
input clk,
input rxd,
output [7:0] rx_data,
output rx_ready
);
parameter Clk_freq = 48000000; //时钟48M
parameter Uart_Baud = 115200; //波特率
parameter Data_width = 8; //数据位数
parameter Parity = 1; //奇偶校验位
parameter Stop_width = 1; //停止位
//
parameter Baud_div = Clk_freq/Uart_Baud;
reg [7:0] recv_cnt = 0; //接收bit计数器
reg [15:0] Baud_cnt = 0; //波特率计数器
reg [7:0] recv_data = 0; //接收到的数据
reg recv_ready = 0; //数据有效信号
reg [1:0] Parity_bit = Parity; //奇偶校验
reg Odd_bit = 0; //接收到的奇偶校验位
reg rxd_r1 = 1;
reg rxd_r2 = 1;
reg rx_negedge = 0;
reg [3:0] rx_state = 0;
assign rx_data = recv_data;
assign rx_ready = recv_ready;
always @ (posedge clk) begin
rxd_r1 <= rxd ;
rxd_r2 <= rxd_r1 ;
if(rxd_r1==0 && rxd_r2==1)
rx_negedge<=1;
else
rx_negedge<=0;
end
always @ (posedge clk) begin
case(rx_state)
0:begin //空闲态
Baud_cnt<=0;
recv_cnt<=0;
if(rx_negedge==1)
rx_state<=1;
else
rx_state<=0;
end
1:begin //起始位
if(Baud_cnt == Baud_div-1)
begin
Baud_cnt<=0;
rx_state<=2;
end
else
begin
Baud_cnt<=Baud_cnt+1;
rx_state<=1;
end
end
2:begin //数据位
if(Baud_cnt == Baud_div-1)
begin
Baud_cnt<=0;
if(recv_cnt==Data_width-1)
begin
recv_cnt<=0;
rx_state<=3;
end
else
begin
recv_cnt<=recv_cnt+1;
end
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
3:begin //校验位
if(Parity_bit == 0)
rx_state<=4;
else
begin
if(Baud_cnt==Baud_div-1)
begin
Baud_cnt<=0;
rx_state<=4;
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
end
4:begin //停止位
if(Baud_cnt==Baud_div/2)
begin
Baud_cnt<=0;
rx_state<=5;
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
default: rx_state<=0;
endcase
end
always @ (posedge clk) begin //接收数据位
if(Baud_cnt == Baud_div/2 && rx_state==2)
recv_data[recv_cnt]<=rxd;
end
always @ (posedge clk) begin //接收校验位
if(Baud_cnt == Baud_div/2 && Parity_bit>0 && rx_state==3)
Odd_bit<=rxd;
end
always @ (posedge clk) begin //校验位 校验
if( rx_state==4)
begin
if(Parity_bit==0)
recv_ready<=1;
else if(Parity_bit==1 && (Odd_bit!=(^recv_data)))
recv_ready<=1;
else if(Parity_bit==2 && (Odd_bit==(^recv_data)))
recv_ready<=1;
end
else
recv_ready<=0;
end
endmodule
三、发送模块
module Uart_tx(
input clk,
output txd,
input [7:0] tx_data,
input tx_ready,
output tx_done
);
parameter Clk_freq = 48000000; //时钟48M
parameter Uart_Baud = 115200; //波特率
parameter Data_width = 8; //数据位数
parameter Parity = 1; //奇偶校验位
parameter Stop_width = 1; //停止位
//
parameter Baud_div = Clk_freq/Uart_Baud;
reg [7:0] send_cnt = 0; //发送bit计数器
reg [15:0] Baud_cnt = 0; //波特率计数器
reg [7:0] send_data = 0; //需要发送的数据
reg send_done = 0; //发送结束信号
reg [1:0] Parity_bit = Parity; //
reg Odd_bit = 0; //奇偶校验位
reg txd_r = 1; //
reg [3:0] tx_state = 0;
assign tx_done = (tx_state==4) ? 1 : 0;
assign txd = txd_r ;
always @ (posedge clk) begin
case(tx_state)
0:begin //空闲态
Baud_cnt<=0;
send_cnt<=0;
if(tx_ready==1)
tx_state<=1;
else
tx_state<=0;
end
1:begin //起始位
send_data<=tx_data;
if(Baud_cnt == Baud_div-1)
begin
Baud_cnt<=0;
tx_state<=2;
end
else
begin
Baud_cnt<=Baud_cnt+1;
tx_state<=1;
end
end
2:begin //数据位
if(Baud_cnt == Baud_div-1)
begin
Baud_cnt<=0;
if(send_cnt==Data_width-1)
begin
send_cnt<=0;
tx_state<=3;
end
else
begin
send_cnt<=send_cnt+1;
end
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
3:begin //校验位
if(Parity_bit == 0)
tx_state<=4;
else
begin
if(Baud_cnt==Baud_div-1)
begin
Baud_cnt<=0;
tx_state<=4;
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
end
4:begin //停止位
if(Baud_cnt==Baud_div*Stop_width)
begin
Baud_cnt<=0;
tx_state<=5;
end
else
begin
Baud_cnt<=Baud_cnt+1;
end
end
default: tx_state<=0;
endcase
end
always @ (posedge clk) begin //奇偶校验位
if(Parity_bit == 1)
Odd_bit <= ~(^send_data);
else if (Parity_bit == 2)
Odd_bit <= ^send_data;
end
always @ (posedge clk) begin //Bit发送
case(tx_state)
0: txd_r <=1;
1: txd_r <= 0;
2: txd_r <= send_data[send_cnt];
3: txd_r <= Odd_bit;
4: txd_r <=1;
endcase
end
endmodule
ISim仿真
编写仿真测试TestBench,将UART模块的TXD与RXD连接,形成闭环。发送数据使用递增数,每秒发送8000个字节,按波特率算,总bit数=8000*(起始位1bit+数据位8bit+校验位1bit+停止位1bit)=88000(小于波特率115200)。 仿真代码如下module Test_Uart(
);
reg clk;
reg rst_p;
reg rxd;
wire txd;
reg [7:0] tx_data = 0;
reg tx_ready = 0;
wire tx_done;
wire [7:0] rx_data;
wire rx_ready;
Uart_top Uart_top(
.clk(clk),
.rst_p(rst_p),
.rxd(txd),
.txd(txd),
.tx_data(tx_data), //发送数据
.tx_ready(tx_ready), //发送数据准备好信号
.tx_done(tx_done), //数据发送完毕
.rx_data(rx_data),
.rx_ready(rx_ready)
);
/
initial
begin
clk = 0;
rst_p = 0;
end
always #10 clk = ~clk ;
reg [15:0] clk_div = 0;
always @(posedge clk) begin // 每秒8000个数
if(clk_div==6000)
clk_div<=0;
else
clk_div<=clk_div+1;
end
always @(posedge clk) begin //准备发送数据
if(clk_div>=3000 && clk_div<=3500)
tx_ready<=1;
else
tx_ready<=0;
end
always @(posedge clk) begin //递增数
if(clk_div==2000)
tx_data<=tx_data+1;
end
endmodule
UART数据发送仿真波形如下:
UART数据接收仿真波形如下:
UART数据接收及发送