FPGA学习-UART串口发送单字节(UART时序分析+真正的FPGA设计看图写代码)

首先看UART发送时序图:

        要发送一个完整字节,需要“1位起始位+ 8位数据位+1位停止位”,图上的第11位,是确认一个字节发送完的一位。

        重点是每一位之间的发送时间需要保持一致,也就是bps_clk的每个高脉冲之间的间隔相等稳定,bps_clk的频率就是波特率。例如波特率为9600,就是1秒内有9600个脉冲,可以发送9600位。

        因此,发送速率(波特率)需要严格控制,以便稳定发送。

发送模块简化图、RTL电路设计图 :

UART发送单字节模块,除去必要的clk系统时钟、rst_n系统复位,还需要三个输入分别是

        波特率选择bps_set,

        发送数据输入data_byte,

        发送开始标志单脉冲信号tx_en。

三个输出分别是

        发送输出端口rs232_tx,

        发送停止标志单脉冲信号tx_down,

        串口工作状态uart_state。

RTL电路图看起来比较复杂,分模块理解就比较容易了。

        首先为了发送稳定,需要特定的波特率,就需要一个计数器给系统时钟clk分频生成bps_clk;

        波特率肯定不知能只有一种,所以需要一个查找表,查找波特率对应的计数值;

        要对生成的bps_clk从1~11计脉冲数以便知道将要发送一个字节的第几位;

        通过一个比较器比较,当计数到11时,清零重新计数;

        data_byte+start_bit+stop_bit通过一个10选1的多路器,计数到哪一位时就选哪一位的数据进行发送;

        最后加一个描述uart_state状态的标志信号监视UART的工作状态。

RTL程序:

module uart_byte_tx(clk50M,rst_n,data_byte,tx_en,bps_set,rs232_tx,tx_down,uart_state);
input clk50M;
input rst_n;
input [7:0] data_byte;        //输入想要发送的8位数据
input [2:0] bps_set;          //波特率选择查找表的输入
input tx_en;                  //发送开始的标志单脉冲信号
output reg rs232_tx;          //输出发送数据端
output reg tx_down;           //发送停止的标志单脉冲信号
output reg uart_state;        //UART工作状态信号,1为发送,0为空闲

reg [15:0] counter;           //分频器计数寄存器
reg [15:0] bps_DR;            //选定的波特率的最大计数值
reg [3:0] bps_cnt;            //1~11的位计数
reg [7:0] r_data_byte;        //data_byte进来 先寄存一次,以便后续稳定发送
reg bps_clk;                  //波特率时钟


localparam start_bit = 1'b0;  //定义起始位为0低电平
localparam stop_bit = 1'b1;   定义停止位为1高电平

/*************波特率对应的分频计数值查找表**************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_DR<=16'd5207;
	else begin
		case(bps_set)
			3'd0:bps_DR<=16'd5207;//9600
			3'd1:bps_DR<=16'd2603;//19200
			3'd2:bps_DR<=16'd1301;//38400
			3'd3:bps_DR<=16'd867;//57600
			3'd4:bps_DR<=16'd433;//115200
			default:bps_DR<=16'd5207;
		endcase
	end
end
/****************************************************/


/********************分频计数器生成********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		counter<=16'd0;
	else if(uart_state) begin
		if(counter==bps_DR)
			counter<=16'd0;
		else
			counter<=counter+1'd1;
	end
	else
		counter<=16'd0;
end
/****************************************************/


/********************分频时钟生成*********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_clk<=1'b0;
	else if(counter==16'd1)
		bps_clk<=1'b1;
	else
		bps_clk<=1'b0;
end
/****************************************************/


/****************bps_clk计数(最大11)****************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_cnt<=4'd0;
	else if(bps_cnt==4'd11)
		bps_cnt<=4'd0;
	else if(bps_clk)
		bps_cnt<=bps_cnt+1'd1;
	else
		bps_cnt<=bps_cnt;
end
/****************************************************/



/************tx_down发送停止单脉冲标志信号*************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		tx_down<=1'b0;
	else if(bps_cnt==4'd11)
		tx_down<=1'b1;
	else
		tx_down<=1'b0;
end
/****************************************************/



/******************寄存一下data_byte******************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		r_data_byte<=8'd0;
	else if(tx_en)
		r_data_byte<=data_byte;
	else 
		r_data_byte<=r_data_byte;
end
/****************************************************/



/**********************发送模块***********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		rs232_tx<=1'b1;
	else begin
		case(bps_cnt)
			0:rs232_tx<=1'b1;
			1:rs232_tx<=start_bit;
			2:rs232_tx<=r_data_byte[0];
			3:rs232_tx<=r_data_byte[1];
			4:rs232_tx<=r_data_byte[2];
			5:rs232_tx<=r_data_byte[3];
			6:rs232_tx<=r_data_byte[4];
			7:rs232_tx<=r_data_byte[5];
			8:rs232_tx<=r_data_byte[6];
			9:rs232_tx<=r_data_byte[7];
			10:rs232_tx<=stop_bit;
			default:rs232_tx<=1'b1;
		endcase
	end
end
/****************************************************/



/********************状态标志信号*********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		uart_state<=1'b0;
	else if(tx_en)
		uart_state<=1'b1;
	else if(bps_cnt==4'd11)
		uart_state<=1'b0;
	else
		uart_state<=uart_state;	
end
/****************************************************/

endmodule

testbench文件:

`timescale 1ns/1ns
`define clock_period 20

module uart_byte_tx_tb;
	reg clk;
	reg rst_n;
	reg [7:0]data_byte;
	reg tx_en;
	reg [2:0]bps_set;
	wire rs232_tx;
	wire tx_down;
	wire uart_state;


	uart_byte_tx uart_byte_tx(
	.clk50M(clk),
	.rst_n(rst_n),
	.data_byte(data_byte),
	.tx_en(tx_en),
	.bps_set(bps_set),
	.rs232_tx(rs232_tx),
	.tx_down(tx_down),
	.uart_state(uart_state)
	);


	initial clk=1;
	always #(`clock_period/2) clk=~clk;
	initial begin
		//首先激励初始化,波特率查找表默认选4,也就是115200.然后复位结束,系统工作
		rst_n=0;               
		data_byte=0;
		tx_en=0;
		bps_set=4'd4;
		#(`clock_period*20);
		rst_n=1;
		#(`clock_period*50);
		
		//发送0xaa,等待tx_down到来时延时五千个系统周期
		tx_en=1;
		data_byte=8'haa;
		#(`clock_period);
		tx_en=0;
		@(posedge tx_down)
		#(`clock_period*5000);

		//发送0x37,等待tx_down到来时延时五千个系统周期。然后停止仿真
		tx_en=1;
		data_byte=8'h37;
		#(`clock_period);
		tx_en=0;
		@(posedge tx_down)
		#(`clock_period*5000);
		$stop;
	end
endmodule

波形图:

        可以看到先要发送的data_byte,和已经发送的rs232_tx一致。而且1~11是从 data_byte的低位到高位进行选取数据发送。每产生一个bps_cnt就发送一位。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值