project1--1.1UART串口通信

UART串口通信

  1. uart概述
    uart全称叫做异步收发传传输器,能够将多bit数据变为一位一位的进行传输,uart作为一种通用串行数据总线,用于异步通信,可实现全双工接发。RS232是uart的一种,是比较常见的一种串行通讯接口,用于PC机和外部板级进行通信。
  2. RS232通信协议的理解
    1.RS232有两根位宽为1bit的数据线,分别为rx和tx,rx用于接收数据,tx用于发送数据。
    2.rx作为接收线,当上位机通过串口向FPGA发送8位的数据的时候,从数据的最低位到最高位依次发送,FPGA会从rx线一位一位的接收到数据然后拼接还原成原来的8bit数据。同理,当FPGA向上位机发送数据的时候,也是从最低位到最高位,上位机通过tx线接收到每个bit数据后拼接还原成8bit数据。
    3.当rx/tx处于空闲状态时,都处于高电平状态。当开始需要发送数据的时候,数据线会进入一个起始状态即低电平状态,接着是8bit的数据位,数据结束后会有一个停止位,停止位为高电平。若后面数据传送,则会进入空闲态;如果继续收发数据的话,停止位后面会另外接上起始位即0电平。如下图:
    在这里插入图片描述
    4.关于波特率:即串口通信时的速率,单位时间内载波变化的次数,这里选用的是9600Bd,即发送一比特数据需要的时间为 1/9600 秒。串口收发内容包含着1bit起始位+8位数据位+1bit停止位,每个bit都需要1/9600s。上位机在发送数据的时候,在发送8bit数据之前自动发送1bit的起始位,当8bit数据位发送完成的时候后自动发送1bit的停止位。同理上位机在接收数据时也是如此。
    5.板卡的时钟是50mhz,因为每个bit收发的时间是1/9600s,用该时钟来计数时需要cnt=(1/9600)/20=5208这么多个时钟才能传输该bit,即每个bit需要5208个系统时钟之后再传输下个bit。
  3. RS232回环测试
    测试的原理其实很简单,上位机通过串口助手把数据通过rx发送到FPGA中,然后在通过tx传回到上位机,当回传的数据和发送的数据一致时,证明uart收发是没有问题的。
  4. 顶层框图的设计:
    在这里插入图片描述
    根据上面这个框图,需要设计出uart_rx和uart_tx两个部分,单独验证完每个模块之后再例化到顶层中完成回环测试。
  5. 根据时序写出对应的代码,uart_rx和uart_tx,然后例化到顶层文件top_uart中。
    uart_rx代码:
module uart_rx(

input 	wire 		clk,
input 	wire 		rst_n,
input 	wire 		rx,
output 	wire 		po_flag,
output 	wire [7:0]	po_data

	);

parameter 	CNT_BAUD_END 		= 5207;//  (1/9600)s/20ns
parameter 	CNT_BAUD_END_HALF 	= 2603;

reg 				rx1,rx2,rx3;
reg 				rx_flag;

reg 	[12:0] 		cnt_baud;
reg 		       	bit_flag;
reg 	[3:0]		bit_cnt;
reg 	[7:0]		po_data_r;
reg 				po_flag_r;

assign po_data = po_data_r;
assign po_flag = po_flag_r;

always @(posedge clk) begin
	{rx3,rx2,rx1} <= {rx2,rx1,rx};
end

// rx_flag
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		rx_flag <= 1'b0;
	end
	else if (bit_cnt == 'd8 && bit_flag == 1'b1 && rx_flag == 1'b1) begin
		rx_flag <= 1'b0;
	end
	else if (rx3 == 1'b1 && rx2 ==1'b0) begin
		rx_flag <= 1'b1;
	end
end

// cnt_baud
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		cnt_baud <= 'd0;
	end
	else if (rx_flag == 1'b0) begin
		cnt_baud <= 'd0;
	end
	else if (rx_flag == 1'b1 && cnt_baud == CNT_BAUD_END) begin
		cnt_baud <= 'd0;
	end
	else if (rx_flag == 1'b1) begin
		cnt_baud <= cnt_baud + 1'b1;
	end
end

// bit_flag
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		bit_flag <= 1'b0;
	end
	else if (rx_flag == 1'b1 && cnt_baud == CNT_BAUD_END_HALF) begin
		bit_flag <= 1'b1;
	end
	else begin
		bit_flag <= 1'b0;
	end
end

// bit_cnt
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		bit_cnt <= 'd0;
	end
	else if (rx_flag == 1'b1 && bit_flag == 1'b1 && bit_cnt == 'd8) begin
		bit_cnt <= 'd0;
	end
	else if (rx_flag == 1'b1 && bit_flag == 1'b1) begin
		bit_cnt <= bit_cnt  + 1'b1;
	end
end

// po_data_r
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		po_data_r <= 'd0;
	end
	else if (bit_cnt >= 'd1 && bit_flag == 1'b1) begin
		po_data_r <= {rx3,po_data_r[7:1]};
	end
end

// po_flag_r
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		po_flag_r <= 1'b0;
	end
	else if (bit_cnt == 'd8 && bit_flag == 1'b1 && rx_flag == 1'b1) begin
		po_flag_r <= 1'b1;
	end
	else begin
		po_flag_r <=1'b0;
	end
end

endmodule

uart_tx代码:

module uart_tx(

input 	wire 		clk,
input 	wire 		rst_n,
input 	wire 		pi_flag,
input 	wire [7:0]	pi_data,
output 	wire 		tx

	);

parameter 	CNT_BAUD_END 		= 5207;//  (1/9600)s/20ns

reg 		[7:0] 	data_reg;
reg 				tx_flag;
reg 	[12:0] 		cnt_baud;
reg 		       	bit_flag;
reg 	[3:0]		bit_cnt;
reg 				tx_r;

reg  	[7:0]		shift_reg;

assign tx = tx_r;

// data_reg
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		data_reg <= 'd0;
	end
	else if (pi_flag == 1'b1) begin
		data_reg <= pi_data;
	end
end

// tx_flag
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		tx_flag <= 1'b0;
	end
	else if (cnt_baud == CNT_BAUD_END && bit_flag == 1'b1 && bit_cnt == 'd8) begin
		tx_flag <= 1'b0;
	end
	else if (pi_flag == 1'b1) begin
		tx_flag <= 1'b1;
	end
end

// cnt_baud
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		cnt_baud <= 'd0;
	end
	else if (tx_flag == 1'b1 && cnt_baud == CNT_BAUD_END) begin
		cnt_baud <= 'd0;
	end
	else if (tx_flag == 1'b1) begin
		cnt_baud <= cnt_baud + 1'b1;
	end
end

// bit_flag
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		bit_flag <= 1'b0;
	end
	else if (tx_flag == 1'b1 && cnt_baud == CNT_BAUD_END-1) begin
		bit_flag <= 1'b1;
	end
	else begin
		bit_flag <= 1'b0;
	end
end

// bit_cnt
always @(posedge clk or negedge rst_n) begin
	if (rst_n == 1'b0) begin
		bit_cnt <= 'd0;
	end
	else if (bit_flag == 1'b1 && bit_cnt == 'd8) begin
		bit_cnt <= 'd0;
	end
	else if (bit_flag == 1'b1) begin
		bit_cnt <= bit_cnt +1'b1;
	end
end

// tx_r
always @(posedge clk or  negedge rst_n) begin
	if (rst_n == 1'b0) begin
		tx_r <= 1'b1;
	end
	else if (pi_flag == 1'b1) begin
		tx_r <= 1'b0;
	end
	else if (bit_cnt <= 'd7 && bit_flag == 1'b1) begin
		tx_r <= data_reg[bit_cnt];
	end
	else if (bit_cnt == 'd8 && bit_flag == 1'b1) begin
		tx_r <= 1'b1;
	end
end

endmodule
  1. 写tb文件完成验证。tb的内容主体有三个部分:时钟部分,复位信号,rx数据的产生方式。这里重点记住rx数据是怎么产生的。自己先写一个数据文件,深度为16,位宽为8,然后进行读取。用$readmemh把数据读取到自己定义的变量中去,然后用两个task分别读取深度和宽度。读取深度的task中就有嵌套读取宽度的task。从而达到把写好的8位数据,一位一位的传到rx中,实现并转串。task部分代码如下:
task rd_byte();
		integer i;
		for(i=0;i<16;i=i+1)begin
			rd_bit(mem[i]);
		end
	endtask

	task rd_bit(input [7:0] data);
		integer i;
		for(i=0;i<10;i=i+1)begin
			case(i)
			  0:rx = 0;
			  1:rx = data[i-1];
			  2:rx = data[i-1];
			  3:rx = data[i-1];
			  4:rx = data[i-1];
			  5:rx = data[i-1];
			  6:rx = data[i-1];
			  7:rx = data[i-1];
			  8:rx = data[i-1];
			  9:rx = 1;
			endcase
			#104160;
		end
	endtask
  1. 完成以上部分后可以在modelsim中进行验证,若tx中的数据和rx中的是对应,再检查uart_rx和uart_tx部分无误后即可,若有错误,定位到错误到错误点。按照路径进行检查。
  2. 最后一部分则是进行板级验,连接好开发板后,进入ise生bit文件下载到板卡中,打开友善串口助手,把数据位宽和波特率设置好之后,发送数据,最终反馈回来相同的数据。如下:
    在这里插入图片描述
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小勇study

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

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

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

打赏作者

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

抵扣说明:

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

余额充值