FPGA——串口收发

文章详细介绍了UART串口通信的基本原理,包括起始位、数据位、停止位的组成,以及FPGA如何实现UART的接收和发送模块。在接收模块中,通过检测下降沿、计数器和数据移位来恢复并行数据;在发送模块中,根据时钟和数据位发送串行数据。同时,给出了顶层模块的连接示例。
摘要由CSDN通过智能技术生成

常用的三大低速串口通信有UART,SPI,IIC。这里使用FPGA实现UART通信。UART通信只有两根线,一根是发送数据tx,一根是接受数据rx。PC和PFGA通过两个线实现数据通信。

在这里插入图片描述
完成FPGA部分的收发设计实现串口通信。

RS232
1、RS232 是 UART 的一种,没有时钟线,只有两根数据线,分别是 rx 和 tx,这两根线都是 1bit 位宽的。其中 rx 是接收数据的线,tx 是发送数据的线。
2、rx 位宽为 1bit,PC 机通过串口调试助手往 FPGA 发 8bit 数据时,FPGA 通过串口线rx 一位一位地接收,从最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特数据。
3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如图 28-7 所示为一个最基本的 RS232 帧结构。
在这里插入图片描述
1bit表示二进制中1or0的一位。一个二进制的码元(1or0)就能携带1bit的信息(1or0)。一个八进制的码元就可以携带3bit的信息。
s232串口波特率就也是码元传输速率。9600Baud/s.表示每秒传输9600个码元。bit(bps)比特率=(Bps)波特数单个调制状态对应的二进制,上图就是9600/1。Ps:串口发送或者接受一个数据时间为波特。串口波特率为9600Bps,那么一个波特时间就为1/9600s。如果系统时钟为50Mhz,那么在9600波特下,一个波特就相当于1/9600s再除以20ns=5208个系统周期。也就是串口每传输一个bit的信息,系统时钟要保持5208个周期。
FPGA接受模块

module	uart_rx
#(
	parameter		UART_BAUD = 'd9600, 	//波特率
	parameter		CLK_FRE	 = 'd50_000_000	//系统时钟
)
(
	input	wire		sys_clk		,
	input	wire		sys_rst_n	,
	input	wire		rx			,//由助手发过来的串行数据
	
	
	
	output	reg [7:0]	po_data		,//传出的并行数据
	output	reg			po_flag		 //数据发送的标志信号
);

parameter		BAUD_MAX = CLK_FRE/UART_BAUD;//每传输一个bit数据所需要的传输时间

reg			rx_reg1;//打三拍,减少亚稳态的影响
reg			rx_reg2;//由于信号达到寄存器的建立时间和保持时间不满足要求
reg			rx_reg3;//所以产生了亚稳态
reg			start_nedge;
reg			work_on;
reg	[12:0]	baud_cnt;
reg			bit_flag;
reg	[3:0]	bit_cnt;
reg	[7:0]	rx_data;
reg			rx_flag;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rx_reg1 <= 1'b1;
	else
		rx_reg1 <= rx;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rx_reg2 <= 1'b1;
	else
		rx_reg2 <= rx_reg1;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rx_reg3 <= 1'b1;
	else
		rx_reg3 <= rx_reg2;
//检测到rx_reg的下降沿时,产生一个时钟的高电平。检测下降沿时,可以把信号延迟
//一个时钟,然后通过如下提取出一个标志信号来。		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		start_nedge <= 1'b0;
	else	if((rx_reg2 == 1'b0) && (rx_reg3 == 1'b1) && (work_on == 1'b0))
		start_nedge <= 1'b1;
	else
		start_nedge <= 1'b0;
//可以通过提取出来的标志信号来确定一个使能信号。使用使能信号可以确定数据的采样范围		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		work_on <= 1'b0;
	else	if(start_nedge == 1'b1)
		work_on <= 1'b1;
	else	if(bit_flag == 1'b1 && bit_cnt == 4'd8)	
		work_on	<= 1'b0;
	else	
		work_on <= work_on;
//波特率计数器计数,一个bit信息所需要的时间。50_000_000/9600=5208
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		baud_cnt <= 13'd0;
	else	if(baud_cnt == BAUD_MAX - 1 || work_on == 1'b0)
		baud_cnt <= 13'd0;
	else	if(work_on == 1'b1)
		baud_cnt <= baud_cnt + 1'b1;
//当计数器计数到中间时采样的数据最稳定
//此时拉高一个标志信息表示数据可以被取走		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		bit_flag <= 1'b0;
	else	if(baud_cnt == BAUD_MAX/2 - 1)
		bit_flag <= 1'b1;
	else	
		bit_flag <= 1'b0;
//有效数据个数的计数器,8个有效数据(不含起始位0和停止位1)
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		bit_cnt <= 4'd0;
	else	if(bit_cnt == 4'd8 && bit_flag == 1'b1)
		bit_cnt <= 4'd0;
	else	if(bit_flag == 1'b1)
		bit_cnt <= bit_cnt + 1'b1;
//输入的数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rx_data <= 8'h00;
	else	if(bit_cnt >= 4'd1 && bit_cnt <= 4'd8 && bit_flag == 1'b1)
		rx_data <= {rx_reg3,rx_data[7:1]};
//输入的数据完成时,拉高一个时钟的电平		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rx_flag <= 1'b0;
	else	if(bit_cnt == 4'd8 && bit_flag == 1'b1)
		rx_flag <= 1'b1;
	else	
		rx_flag <= 1'b0;
//完成输出8位有效数据		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		po_data <= 8'h00;
	else	if(rx_flag == 1'b1)
		po_data <= rx_data;
//输出数据有效标志位
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		po_flag <= 1'b0;
	else
		po_flag <= rx_flag;




endmodule



FPGA发送模块

module	uart_tx
#(
	parameter 		UART_BPS = 'd9600,//串口波特率
	parameter		CLK_FRE = 'd50_000_000//时钟频率
)
(
	input	wire		sys_clk		,
	input	wire		sys_rst_n	,
	input	wire [7:0]	pi_data		,//模块输入的8bit数据
	input	wire 		pi_flag		,//并行数据的有效标志信号
	
	
	output	reg		tx				//串转并的1bit数据
);


parameter		BAUD_CNT = CLK_FRE/UART_BPS;//

reg			work_on;
reg	[12:0]	baud_cnt;
reg			bit_flag;
reg	[3:0]	bit_cnt;



always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		work_on <= 1'b0;
	else	if(pi_flag == 1'b1)
		work_on <= 1'b1;
	else	if(bit_flag == 1'b1 && bit_cnt == 4'd9)
		work_on <= 1'b0;
	else
		work_on <= work_on;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		baud_cnt <= 0;
	else	if(baud_cnt == BAUD_CNT - 1 || work_on == 1'b0)
		baud_cnt <= 0;
	else	if(work_on == 1'b1)
		baud_cnt <= baud_cnt + 1'b1;
		
		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		bit_flag <= 1'b0;
	else	if(baud_cnt == 0 && work_on == 1'b1)
		bit_flag <= 1'b1;
	else
		bit_flag <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		bit_cnt <= 0;
	else	if(bit_cnt == 4'd9 && bit_flag == 1'b1)
		bit_cnt <= 4'd0;
	else	if(bit_flag == 1'b1 && work_on == 1'b1)
		bit_cnt <= bit_cnt + 1'b1;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		tx <= 1'b1;
	else	if(bit_flag == 1'b1)
	begin
		case(bit_cnt)
			0:	tx <= 1'b0;
			1:	tx <= pi_data[0];
			2:	tx <= pi_data[1];
			3:	tx <= pi_data[2];
			4:	tx <= pi_data[3];
			5:	tx <= pi_data[4];
			6:	tx <= pi_data[5];
			7:	tx <= pi_data[6];
			8:	tx <= pi_data[7];
			9:	tx <= 1'b1;
			default: tx <= 1'b1;		
		endcase
	end



endmodule

顶层模块

module	rs232
(
	input	wire		sys_clk		,
	input	wire		sys_rst_n	,
	input	wire		rx			,
	
	output	wire		tx
);

wire	[7:0]	po_data;
wire			po_flag;





uart_rx
#(
	.UART_BAUD(9600), 	//波特率
	.CLK_FRE(50_000_000)	//系统时钟
)
uart_rx_inst
(
	.sys_clk		(sys_clk),
	.sys_rst_n		(sys_rst_n),
	.rx				(rx),//由助手发过来的串行数据

	.po_data		(po_data),//传出的并行数据
	.po_flag		(po_flag) //数据发送的标志信号
);


uart_tx
#(
	.UART_BPS(9600),
	.CLK_FRE(50_000_000)
)
uart_tx_inst
(
	.sys_clk		(sys_clk),
	.sys_rst_n		(sys_rst_n),
	.pi_data		(po_data),
	.pi_flag		(po_flag),


	.tx             (tx)
);




endmodule

仿真结果
在这里插入图片描述
串口助手调试结果
在这里插入图片描述

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值