基于Xilinx FPGA的uart串口实验详解


1. 实验设计

1.1 硬件

本次UART实验使用了type-c接口,而不是传统的DB9接口,因此在主板上使用了一块CH340C芯片作为U转串的芯片。
在这里插入图片描述

1.2 实验方法

  1. 以笔记本电脑作为上位机,FPGA作为下位机
  2. 笔记本电脑给FPGA发送数据,FPGA接收数据后再发送回串口

2. 程序设计

2.1 系统模型

在这里插入图片描述

  1. 接收模块:接收数据,并把数据一位以为存到一个寄存器中,存完给一个信号
  2. 环回模块:协调接收模块与发送模块,确保接收和发送不会互相打断
  3. 发送模块:将接收的数据按照时序一位一位发给上位机

2.2 协议设计

数据位为8位,停止位为1位,无校验位。波特率为115200bps
在这里插入图片描述
时序图如下
在这里插入图片描述

  1. sys_clk:系统时钟
  2. uart_rxd:FPGA接收的数据
  3. start_flag:当接收到start bit时,抓一个上升沿,抓到后让rx_flag拉高
  4. rx_flag:拉高代表正在接收中
  5. clk_cnt:时钟周期计数,通过十的九次方ns除以波特率除以20ns(50M的一个时钟周期),本次实验中,每满434判断数据发送了一个字节
  6. rx_cnt:接收到的字节个数
  7. uart_done:当接收完八位数据位时拉高,接收过程结束,即rx_flag拉低后拉低
  8. 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 下载到芯片,并用串口调试助手调试

在这里插入图片描述
在这里插入图片描述
多次发送不同数据,发送的数据能返回到上位机,实验成功
在这里插入图片描述

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

普通的晓学生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值