uart串口发送模块功能实现及仿真

前言

博客上面,人人都说uart简单,刚上手时,笔者也是一脸懵逼。只能假装uart协议确实简单,只不过。。。。(手动笑脸)

00.串口发送原理图

在这里插入图片描述

01.串口发送模块uart_byte_tx.v

module uart_byte_tx(
	Clk				,//50M时钟
	Rst_n			,//复位信号
	send_en			,//发送使能信号
	baud_set		,//波特率选项
	data_byte		,//要发送的单字节数据
	
	rs232_tx		,//要发送出去的单比特数据
	tx_done			,//发送接收标志
	tx_state		 //发送的状态

);

	input 			Clk				;
   input 			Rst_n			;
   input 			send_en			;
   input 	[2:0]	baud_set		;
   input 	[7:0]	data_byte		;

	output 	reg		rs232_tx		;
	output 	reg		tx_done			;
	output 	reg 	tx_state		;
	
	//两级寄存器消除亚稳态
	reg [7:0] r_data_byte;
	
	//根据波特率选择查找表,选择分频计数时的最大值
	reg [15:0] baud_dr;
	
	//根据上面分频计数时的最大值,生成波特率时钟	
	reg 			bps_clk;
	reg [15:0] 	div_cnt;
	
	//这是根据波特时钟产生的计数器,方便后面进一步控制数据串并转换
	reg [3:0] bps_cnt;
	
	
	
	
//两级寄存器消除亚稳态
	//reg [7:0] r1_data_byte,r2_data_byte;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		r_data_byte <= 'd0;
	end
	else if(send_en) begin
		r_data_byte <= data_byte;
	end
	
//根据波特率选择查找表,选择分频计数时的最大值
	//reg [15:0] baud_dr;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		baud_dr <= 15'd5207;
	else begin 
		case(baud_set)
			0:baud_dr <= 16'd5207;
			1:baud_dr <= 16'd2603;
			2:baud_dr <= 16'd1301;
			3:baud_dr <= 16'd867;
			4:baud_dr <= 16'd433;
			default:baud_dr <= 16'd5207;
		endcase
	end
	
		
//根据上面分频计数时的最大值,生成波特率时钟	
	//reg 			bps_clk;
	//reg [15:0] 	div_cnt;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		div_cnt <= 'd0;
	else if(tx_state)
		if(div_cnt == baud_dr)//这里之前赋值为baud_set了,波形不对
			div_cnt <= 'd0;
		else 
			div_cnt <= div_cnt + 1'b1;
	else div_cnt <= 'd0;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		bps_clk <= 1'b0;
	//else if(div_cnt == baud_dr)//这是每次计数完的时候产生的波特率时钟,如果在计数结束时生成时钟就统一往后延迟了下,可以选择在div_cnt=1时生成
	else if(div_cnt == 1'b1)
		bps_clk <= 1'b1;
	else 
		bps_clk <= 1'b0;
	
//这是根据波特时钟产生的计数器,方便后面进一步控制数据串并转换
	//reg [3:0] bps_cnt;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		bps_cnt <= 'd0;
	else if(bps_cnt == 'd11)
		bps_cnt <= 'd0;
	else if(bps_clk)
		bps_cnt <= bps_cnt + 1'b1;
	else 
		bps_cnt <= bps_cnt;
		
//根据波特率时钟产生的计数器,选择数据的传输
	localparam START_BIT = 1'b0;
	localparam STOP_BIT = 1'b1;


	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		rs232_tx <= 1'b1;
	else 
		case(bps_cnt)
			'd0:rs232_tx <= 1'b1;
			'd1:rs232_tx <= START_BIT;
			'd2:rs232_tx <= r_data_byte[0];
			'd3:rs232_tx <= r_data_byte[1];
			'd4:rs232_tx <= r_data_byte[2];
			'd5:rs232_tx <= r_data_byte[3];
			'd6:rs232_tx <= r_data_byte[4];
			'd7:rs232_tx <= r_data_byte[5];
			'd8:rs232_tx <= r_data_byte[6];
			'd9:rs232_tx <= r_data_byte[7];
			'd10:rs232_tx <= STOP_BIT;
			default:rs232_tx <= 1'b1;
		endcase
		
//tx_done:当波特率时钟计数器计数到11时,计数就清零,方便进行下一轮的计数

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		tx_done <= 1'b0;
	else if(bps_cnt == 'd11)
		tx_done <= 1'b1;
	else 
		tx_done <= 1'b0;
	
	
//tx_state:通过send_en和波特率计数(bps_cnt == 11)描述出发送的状态

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		tx_state <= 1'b0;
	else if(send_en)
		tx_state <= 1'b1;
	else if(bps_cnt == 'd11)
		tx_state <= 1'b0;
	else 
		tx_state <= tx_state;
	
endmodule

02.串口发送仿真uart_byte_tx_tb.v

`timescale 1ns/1ns
`define clock_period 20

module uart_byte_tx_tb;

	reg 			Clk				;
	reg 			Rst_n			;
	reg 			send_en			;
	reg 	[2:0]	baud_set		;
	reg 	[7:0]	data_byte		;

	wire			rs232_tx		;
	wire			tx_done			;
	wire 			tx_state		;
	

	uart_byte_tx  uart_byte_tx_inst(
		.Clk		(Clk		),
		.Rst_n		(Rst_n		),
		.send_en	(send_en	),
		.baud_set	(baud_set	),
		.data_byte	(data_byte	),
	                
		.rs232_tx	(rs232_tx	),
		.tx_done	(tx_done	),
		.tx_state	(tx_state	)

	);

	//产生时钟
	initial  Clk = 1'b1;
	always #(`clock_period/2) Clk = ~Clk;

	//产生激励信号
	initial begin
		Rst_n = 1'b0;
		data_byte = 8'b0;
		send_en = 1'b0;
		baud_set = 3'd4;
		#(`clock_period*20 + 1);
		Rst_n = 1'b1;
		
		
		#(`clock_period*50);
		data_byte = 8'haa;
		send_en = 1'd1;
		#`clock_period;
		send_en = 1'd0;
		@(posedge tx_done);
		
		
		#(`clock_period*5000);
		data_byte = 8'h55;
		send_en = 1'd1;
		#`clock_period;
		send_en = 1'd0;
		@(posedge tx_done);
		
		
		#(`clock_period*5000);
		data_byte = 8'hdc;
		send_en = 1'd1;
		#`clock_period;
		send_en = 1'd0;
		@(posedge tx_done);
		
		#(`clock_period*5000);
		data_byte = 8'hcd;
		send_en = 1'd1;
		#`clock_period;
		send_en = 1'd0;
		@(posedge tx_done);
		
		#(`clock_period*5000);
		$stop;	
	end


endmodule

功能仿真如下:

在这里插入图片描述

后记

下面就开始,串口接收模块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杰之行

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

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

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

打赏作者

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

抵扣说明:

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

余额充值