文章目录
1. 实验设计
1.1 硬件
本次UART实验使用了type-c接口,而不是传统的DB9接口,因此在主板上使用了一块CH340C芯片作为U转串的芯片。
1.2 实验方法
- 以笔记本电脑作为上位机,FPGA作为下位机
- 笔记本电脑给FPGA发送数据,FPGA接收数据后再发送回串口
2. 程序设计
2.1 系统模型
- 接收模块:接收数据,并把数据一位以为存到一个寄存器中,存完给一个信号
- 环回模块:协调接收模块与发送模块,确保接收和发送不会互相打断
- 发送模块:将接收的数据按照时序一位一位发给上位机
2.2 协议设计
数据位为8位,停止位为1位,无校验位。波特率为115200bps
时序图如下
- sys_clk:系统时钟
- uart_rxd:FPGA接收的数据
- start_flag:当接收到start bit时,抓一个上升沿,抓到后让rx_flag拉高
- rx_flag:拉高代表正在接收中
- clk_cnt:时钟周期计数,通过十的九次方ns除以波特率除以20ns(50M的一个时钟周期),本次实验中,每满434判断数据发送了一个字节
- rx_cnt:接收到的字节个数
- uart_done:当接收完八位数据位时拉高,接收过程结束,即rx_flag拉低后拉低
- uart_data:接收到的值
3.程序编写
编写了uart_loop.v,uart_loopback_top.v,uart_recv.v,uart_send.v,uart.xdc,源码如下所示
3.1 uart_loop.v
module uart_loop(
input sys_clk,
input sys_rst_n,
input recv_down,
input [7 : 0] recv_data,
input tx_busy,
output reg send_en,
output reg [7 : 0] send_data
);
reg recv_down_d0;
reg recv_down_d1;
reg tx_ready;
wire recv_down_flag;
assign recv_down_flag = (~recv_down_d1) & recv_down_d0;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
recv_down_d0 <= 1'd0;
recv_down_d1 <= 1'd0;
end
else begin
recv_down_d0 <= recv_down;
recv_down_d1 <= recv_down_d0;
end
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
send_en <= 1'd0;
send_data <= 8'd0;
tx_ready <= 1'd0;
end
else begin
if(recv_down_flag) begin
tx_ready <= 1'd1;
send_en <= 1'd0;
send_data <= recv_data;
end
else if((~tx_busy)&&tx_ready) begin
tx_ready <= 1'd0;
send_en <= 1'd1;
end
end
end
endmodule
3.2 uart_loopback_top.v
module uart_loopback_top(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output uart_txd
);
wire uart_en;
wire [7 : 0] uart_din;
wire [7 : 0] uart_data;
wire uart_done;
wire uart_tx_busy;
uart_recv uart_recv_u(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_data (uart_data),
.uart_done (uart_done)
);
uart_send uart_send_u(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_en (uart_en),
.uart_din (uart_din),
.uart_txd (uart_txd),
.uart_tx_busy(uart_tx_busy)
);
uart_loop uart_loop_u(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.recv_down (uart_done) ,
.recv_data (uart_data) ,
.tx_busy (uart_tx_busy) ,
.send_en (uart_en) ,
.send_data (uart_din)
);
endmodule
3.3 uart_recv.v
module uart_recv(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output reg [7 : 0] uart_data,
output reg uart_done
);
parameter CLK_FREQ = 50_000_000; //鏃堕挓棰戠巼
parameter UART_BPS = 115200; //娉㈢壒鐜?
localparam BPS_cnt = CLK_FREQ/UART_BPS ;
reg uart_rxd_d0;
reg uart_rxd_d1;
wire start_flag;
reg rx_flag;
reg [3 : 0] rx_cnt;
reg [15 : 0] clk_cnt;
reg [7 : 0] rx_data;
assign start_flag = (~uart_rxd_d0) & uart_rxd_d1;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
uart_rxd_d0 <= 1'd0;
uart_rxd_d1 <= 1'd0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) rx_flag <= 1'b0;
else begin
if(start_flag) rx_flag <= 1'b1;
else if(rx_cnt == 4'd9 && clk_cnt == BPS_cnt/2) rx_flag <= 1'b0;
else rx_flag <= rx_flag;
end
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) clk_cnt <= 16'b0;
else if(rx_flag) begin
if(clk_cnt < BPS_cnt - 1) clk_cnt <= clk_cnt + 1'b1;
else clk_cnt <= 16'd0;
end
else clk_cnt <= 16'd0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) rx_cnt <= 4'b0;
else if(rx_flag) begin
if(clk_cnt == BPS_cnt - 1) rx_cnt <= rx_cnt + 1'b1;
else rx_cnt <= rx_cnt;
end
else rx_cnt <= 4'b0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) rx_data <= 8'b0;
else if(rx_flag) begin
if(clk_cnt == BPS_cnt/2) begin
case(rx_cnt)
4'd1 : rx_data[0] <= uart_rxd_d1;
4'd2 : rx_data[1] <= uart_rxd_d1;
4'd3 : rx_data[2] <= uart_rxd_d1;
4'd4 : rx_data[3] <= uart_rxd_d1;
4'd5 : rx_data[4] <= uart_rxd_d1;
4'd6 : rx_data[5] <= uart_rxd_d1;
4'd7 : rx_data[6] <= uart_rxd_d1;
4'd8 : rx_data[7] <= uart_rxd_d1;
default : ;
endcase
end
else rx_data <= rx_data;
end
else rx_data <= 8'b0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
uart_data <= 8'd0;
uart_done <= 1'd0;
end
else if(rx_flag) begin
if(rx_cnt == 4'd9) begin
uart_done <= 1'd1;
uart_data <= rx_data;
end
else begin
uart_data <= 8'd0;
uart_done <= 1'd0;
end
end
else begin
uart_data <= 8'd0;
uart_done <= 1'd0;
end
end
endmodule
3.4 uart_send.v
module uart_send(
input sys_clk,
input sys_rst_n,
input uart_en,
input [7 : 0] uart_din,
output reg uart_txd,
output uart_tx_busy
);
parameter CLK_FREQ = 50_000_000; //鏃堕挓棰戠巼
parameter UART_BPS = 115200; //娉㈢壒鐜?
localparam BPS_cnt = CLK_FREQ/UART_BPS ;
reg uart_en_d0;
reg uart_en_d1;
wire en_flag;
reg tx_flag;
reg [7 : 0] tx_data;
reg [3 : 0] tx_cnt;
reg [15 : 0] clk_cnt;
assign en_flag = (~uart_en_d1) & uart_en_d0;
assign uart_tx_busy = tx_flag;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
uart_en_d0 <= 1'd0;
uart_en_d1 <= 1'd0;
end
else begin
uart_en_d0 <= uart_en;
uart_en_d1 <= uart_en_d0;
end
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
tx_flag <= 1'b0;
tx_data <= 8'b0;
end
else if(en_flag) begin
tx_flag <= 1'b1;
tx_data <= uart_din;
end
else if(tx_cnt == 4'd9 && clk_cnt == BPS_cnt - BPS_cnt/16) begin
tx_flag <= 1'b0;
tx_data <= 8'b0;
end
else begin
tx_flag <= tx_flag;
tx_data <= tx_data;
end
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) clk_cnt <= 16'b0;
else if(tx_flag) begin
if(clk_cnt < BPS_cnt - 1) clk_cnt <= clk_cnt + 1'b1;
else clk_cnt <= 16'd0;
end
else clk_cnt <= 16'd0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) tx_cnt <= 4'b0;
else if(tx_flag) begin
if(clk_cnt == BPS_cnt - 1) tx_cnt <= tx_cnt + 1'b1;
else tx_cnt <= tx_cnt;
end
else tx_cnt <= 4'b0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) uart_txd <= 1'b1;
else if(tx_flag) begin
case(tx_cnt)
4'd0 : uart_txd <= 1'b0;//寮?濮嬬殑鏃跺?欐槸浣庣數骞?
4'd1 : uart_txd <= tx_data[0];
4'd2 : uart_txd <= tx_data[1];
4'd3 : uart_txd <= tx_data[2];
4'd4 : uart_txd <= tx_data[3];
4'd5 : uart_txd <= tx_data[4];
4'd6 : uart_txd <= tx_data[5];
4'd7 : uart_txd <= tx_data[6];
4'd8 : uart_txd <= tx_data[7];
4'd9 : uart_txd <= 1'b1;//缁撴潫鏄珮鐢靛钩
default : ;
endcase
end
else uart_txd <= 1'b1;
end
endmodule
3.5 uart.xdc(根据自己的板子调整引脚,电平等)
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN D17 IOSTANDARD LVCMOS33} [get_ports uart_txd]
4.验证
4.1 原理图
4.2 下载到芯片,并用串口调试助手调试
多次发送不同数据,发送的数据能返回到上位机,实验成功