FPGA Verilog 实现串口发送任意字节数据(8的倍数)已通过上板验证支持常用波特率

FPGA Verilog 实现串口发送任意字节数据(8的倍数)


前言

// ** 功能 : 1、基于FPGA的串口多字节发送模块;
// 2、可设置一次发送的字节数、波特率BPS、主时钟CLK_FRE;
// 3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
// 4、每发送1次多字节后拉高指示信号一个周期,指示一次多字节发送结束;
// 5、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8’h34,再发送单字节8’h12。


一、工程代码

代码参考gitHub上一位博主的代码 需要的同志可以直接去这里下载

顶层模块

 
 
module uart_bytes_tx
#(
	parameter	integer	BYTES 	 = 2			,				//发送字节数,单字节8bit
	parameter	integer	BPS		 = 1000000		,				//发送波特率
	parameter 	integer	CLK_FRE	 = 100_000_000					//输入时钟频率
)
(
//系统接口
	input 							sys_clk			,			//系统时钟
	input 							sys_rst_n		,			//系统复位,低电平有效
//用户接口	
	//input	[(BYTES * 8 - 1):0] 	uart_bytes_data	,			//需要通过UART发送的多字节数据,在uart_bytes_en为高电平时有效
	input							   uart_bytes_en	,			//发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送	
	output							uart_bytes_done	,			//成功发送完所有字节数据后拉高1个时钟周期
	output 							uart_txd					//UART发送数据线tx
);
reg [(BYTES * 8 - 1):0] 	uart_bytes_data=16'b1101001010010110;
//reg define
reg	[(BYTES*8-1):0]		uart_bytes_data_reg;					//寄存接收到的多字节数据
reg						work_en;								//高电平表示处于发送状态,低电平表示空闲状态
reg	[9:0]				byte_cnt;								//发送的字节个数计数(因为懒直接用10bit计数,最大可以表示1024BYTE,大概率不会溢出)			
reg	[7:0]				uart_sing_data;							//拆解的要发送的单个字节数据
reg						uart_sing_en;							//要发送的单个字节数据发送使能
reg						uart_bytes_done_reg;					//所有字节发送完毕打拍
reg						uart_sing_done_reg;						//单个字节数据发送完毕打拍
	
//wire define				
wire					uart_sing_done;							//单个字节发送完成标志信号
 
//对端口赋值
assign uart_bytes_done = uart_bytes_done_reg;
 
 
//当发送使能信号到达时,寄存待发送的多字节数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_bytes_data_reg <= 0;
	else if(uart_bytes_en && ~work_en)							//要发送有效的数据,且并未处于发送状态
		uart_bytes_data_reg <= uart_bytes_data;					//寄存需要发送的数据			
	else if(uart_sing_done)										
		uart_bytes_data_reg <= uart_bytes_data_reg >> 8;		//发送完一个数据后,把多字节数据右移8bit
	else 
		uart_bytes_data_reg <= uart_bytes_data_reg;
end	
 
//当发送使能信号到达时,进入工作状态
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		work_en <= 1'b0;
	else if(uart_bytes_en && ~work_en)							//要发送有效的数据且未处于工作状态
		work_en <= 1'b1;										//进入发送状态			
	else if(uart_sing_done && byte_cnt == BYTES - 1)			//发送完了最后一个字节的数据
		work_en <= 1'b0;										//发送完毕,退出工作状态	
	else 		
		work_en <= work_en;		
end			
		
//在工作状态对发送的数据个数进行计数		
always @(posedge sys_clk or negedge sys_rst_n)begin		
	if(!sys_rst_n)		
		byte_cnt <= 0;		
	else if(work_en)begin										//处于发送状态则需要对发送的字节个数计数
		if(uart_sing_done && byte_cnt == BYTES - 1)				//计数到了最大值则清零
			byte_cnt <= 0;										
		else if(uart_sing_done)									//发送完一个单字节则计数器+1
			byte_cnt <= byte_cnt + 1'b1;						
		else		
			byte_cnt <= byte_cnt;			
	end		
	else														//不处于发送状态则清零
		byte_cnt <= 0;	
end
 
//打拍凑时序·~·
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_sing_done_reg <= 0;
	else 
		uart_sing_done_reg <= uart_sing_done;	
end
	
//发送单个字节的数据
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_sing_data <= 8'd0;
	else if(uart_bytes_en && ~work_en)						//进入工作状态后马上发送第一个数据
		uart_sing_data <= uart_bytes_data[7:0];				//发送最低字节
	else if(uart_sing_done_reg)								//发送完一个字节则发送另一个字节
		uart_sing_data <= uart_bytes_data_reg[7:0];			//先右移8bit,然后取低8bit
	else
		uart_sing_data <= uart_sing_data;					//保持稳定
end
 
//发送单个字节的数据使能
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_sing_en <= 1'b0;
	else if(uart_bytes_en && ~work_en)						//进入工作状态后马上发送第一个数据
		uart_sing_en <= 1'b1;		
	else if(uart_sing_done_reg && work_en)					//发送完一个字节则发送另一个字节
		uart_sing_en <= 1'b1;		
	else		
		uart_sing_en <= 1'b0;								//其他时候则为0
end
 
//所有数据发送完毕
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_bytes_done_reg <= 1'b0;
	else if(uart_sing_done && byte_cnt == BYTES - 1)
		uart_bytes_done_reg <= 1'b1;
	else 
		uart_bytes_done_reg <= 1'b0;
end
 
 
//例化发送驱动模块
uart_tx #(
	.BPS			(BPS			),		
	.CLK_FRE		(CLK_FRE		)		
)	
uart_tx_inst(	
	.sys_clk		(sys_clk		),			
	.sys_rst_n		(sys_rst_n		),
	
	.uart_tx_data	(uart_sing_data	),			
	.uart_tx_en		(uart_sing_en	),
	.uart_tx_done	(uart_sing_done	),
	.uart_txd		(uart_txd		)	
);
 
endmodule 

子模块

 
 
module uart_tx
#(
	parameter	integer	BPS		= 1000000		,	//发送波特率
	parameter 	integer	CLK_FRE	= 100_000_000	//主时钟频率
)
(
//系统接口
	input 			sys_clk			,			//系统时钟
	input 			sys_rst_n		,			//系统复位,低电平有效
//用户接口	
	input	[7:0] 	uart_tx_data	,			//需要通过UART发送的数据,在uart_tx_en为高电平时有效
	input			   uart_tx_en		,			//发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送	
	output	reg		uart_tx_done	,			//成功发送1BYTE数据后拉高一个周期
	output 	reg		uart_txd			,		//UART发送数据线tx
	output   reg      uart_change
);
 
//param define
localparam	integer	BPS_CNT  = CLK_FRE / BPS;	//根据波特率计算传输每个bit需要计数多个系统时钟
localparam	integer	BITS_NUM = 10			;	//发送格式确定需要发送的bit数,10bit = 1起始位 + 8数据位 + 1停止位
 
//reg define
reg 		   tx_state			;				//发送标志信号,拉高代表发送过程正在进行
reg [7:0]  	uart_tx_data_reg	;				//寄存要发送的数据
reg [31:0] 	clk_cnt				;				//计数器,用于计数发送一个bit数据所需要的时钟数
reg [3:0]  	bit_cnt				;				//bit计数器,标志当前发送了多少个bit
 
 
//当发送使能信号到达时,寄存待发送的数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_tx_data_reg <=8'd0;
	else if(uart_tx_en)							//要发送有效的数据
		uart_tx_data_reg <= uart_tx_data;		//寄存需要发送的数据			
	else 
		uart_tx_data_reg <= uart_tx_data_reg;
end		
 
//当发送使能信号到达时,进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		tx_state <=1'b0;	
	else if(uart_tx_en)												
		tx_state <= 1'b1;						//发送信号有效则进入发送过程
	//发送完了最后一个数据则退出发送过程		
	else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))		
		tx_state <= 1'b0;                                          		
	else 
		tx_state <= tx_state;	
end
 
//发送数据完毕后拉高发送完毕信号一个周期,指示一个字节发送完毕
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_tx_done <=1'b0;
	//发送数据完毕后拉高发送完毕信号一个周期 		
	else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))	                                         	
		uart_tx_done <=1'b1;										
	else 
		uart_tx_done <=1'b0;
end
 
//进入发送过程后,启动时钟计数器与发送个数bit计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		clk_cnt <= 32'd0;
		bit_cnt <= 4'd0;
	end
	else if(tx_state) begin										//在发送状态
		if(clk_cnt < BPS_CNT - 1'd1)begin						//一个bit数据没有发送完
			clk_cnt <= clk_cnt + 1'b1;							//时钟计数器+1
			bit_cnt <= bit_cnt;									//bit计数器不变
		end					
		else begin												//一个bit数据发送完了	
			clk_cnt <= 32'd0;									//清空时钟计数器,重新开始计时
			bit_cnt <= bit_cnt+1'b1;							//bit计数器+1,表示发送完了一个bit的数据
			uart_change <= ~uart_change;
		end					
	end					
	else begin													//不在发送状态
		clk_cnt <= 32'd0;                   					//清零
		bit_cnt <= 4'd0;                    					//清零
	end
end
 
//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		uart_txd <= 1'b1;										//默认为高状态,空闲状态
	else if(tx_state)                                  			//处于发送状态
		case(bit_cnt)											//数据发送从低位到高位
			4'd0: uart_txd <= 1'b0;								//起始位,拉低
			4'd1: uart_txd <= uart_tx_data_reg[0];     			//发送最低位数据
			4'd2: uart_txd <= uart_tx_data_reg[1];     			//
			4'd3: uart_txd <= uart_tx_data_reg[2];     			//
			4'd4: uart_txd <= uart_tx_data_reg[3];     			//
			4'd5: uart_txd <= uart_tx_data_reg[4];     			//
			4'd6: uart_txd <= uart_tx_data_reg[5];     			//
			4'd7: uart_txd <= uart_tx_data_reg[6];     			//
			4'd8: uart_txd <= uart_tx_data_reg[7];     			//发送最高位数据
			4'd9: uart_txd <= 1'b1;								//终止位,拉高
			default:uart_txd <= 1'b1;			
		endcase			
	else 														//不处于发送状态
		uart_txd <= 1'b1;										//默认为高状态,空闲状态
end
 
endmodule 

二、注意事项

1.找对串口
2.设置好波特率
3.停止位 数据位设置等
在这里插入图片描述


总结

本代码已通过实际上板验证,仿真也没问题,如果用起来有问题请评论区告诉我。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值