【学习笔记】串口通信RS232

     UART 是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。串口作为常用的三大低速总线之一。不同于 SPI、IIC 是同步通信接口,UART是全双工异步通信接口,接受方是在数据的起始位和停止位的帮助下实现信息同步的。UART 通信只有两根信号线,串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。

        区别异步与同步通信以及单工、半双工与全双工通信:

       1、 在异步通信中,数据被分成小块,每个小块都包含一些数据以及同步信息。发送和接收之间没有固定的时间间隔。一方发送,不考虑另一方是否收到,直接进行下一次传输。异步通信常用于短距离数据传输,例如串口通信。

        2、在同步通信中,数据被分成连续的块,每个块都有一个固定的大小,并且发送方和接收方使用共同的时钟源来保持数据的同步。

        3、单工通信只能在一个方向上传输数据。例如,广播电台只能向听众发送信息,而不能接收来自听众的信息。

       4、 半双工通信允许数据在两个方向之间进行传输,但是不能同时进行。在半双工通信中,每个方向上只能有一个方向进行数据传输。例如iic通信。

        5、全双工通信允许数据在两个方向上同时传输。这意味着通信双方可以同时发送和接收数据。例如串口和spi通信。

        波特率(baud rate)是指数字通信中单位时间内传输的符号数。在串行通信中,数据被拆分为一个个符号,每个符号由一定数量的比特(bit)组成,每个比特在一定时间内传输完成。波特率就是指每秒钟可以传输的比特数。串口常见的波特率有 4800、9600、115200 等。如波特率为9600,串口发送或者接收 1bit 数据的时间则为一个波特,即 1/9600 秒。正常情况下,用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1 * 10^9) / 9600) / 20 ≈ 5208 个系统时钟周期。在串口RS232通信过程中,接收方必须检测到一个波特时间的起始位才能开始接收数据,再接收一个波特时间的停止位。

        在FPAG设计中,亚稳态通常指的是时序亚稳态,即由于电路中存在的不完美、不均匀等因素,导致电路的时序存在一定的不稳定性。具体而言,时序亚稳态可能会导致电路在某些情况下无法正常工作或者工作不稳定。可以采用时钟树设计来保证时序的稳定性;可以采用布线规则来保证信号的可靠传输;可以采用静态时序分析和动态时序分析来预测电路的时序特性等。

而对于用Verilog设计来说,时钟设计是考虑较多的,而时钟设计需要考虑以下几个方面:

  1. 时钟树设计:时钟树是将时钟信号分配给所有时序元件的网络。

  2. 时钟缓冲器的选择:时钟缓冲器是将时钟信号从一个时钟域转换到另一个时钟域的关键元件。选择合适的时钟缓冲器可以提高时钟信号的稳定性和传输质量。常用触发器同步器和FIFO来解决。

            i、触发器同步器通常由两个触发器和一些逻辑门组成(也可能多个触发器,如本项目工程中使用三个触发器)。其中,一个触发器用于接收异步输入信号,并在时钟边沿触发后将输入信号存储到寄存器中;另一个触发器用于接收同步时钟信号,并在时钟边沿触发时将存储的数据输出。逻辑门用于控制时序和信号方向,从而实现数据的同步传输(注意:对异步信号进行同步处理只能减少亚稳态产生的概率,无法彻底消除亚稳态的现象)。 i i、FIFO(First-In, First-Out)是一种常用的存储器件,可以缓存数据并按照输入的顺序输出。(具体不展开)
  3. 时钟分频:时钟分频是将时钟信号的频率降低到更低的频率,以减少时钟信号传输时延和抖动。时钟分频还可以降低电路的功耗和热功耗,提高电路的可靠性和性能。

uart接收模块


module	uart_rs232_rx(
	input		wire	clk_sys			,
	input		wire	rst_n_sys		,
	input		wire	rx_data			,		//接收的数据
	
	output 	reg	[7:0]po_data	,		
	output 	reg	po_flag	
	);	

reg 	rx_reg1,rx_reg2,rx_reg3	;
reg	[12:0]baud_cnt				;	
reg	[3:0]	bit_cnt				;
reg	start_flag					;
reg	work_en						;
reg	baud_flag					;
reg	rx_flag						;
reg	[7:0]rx_data_reg				;

parameter	BIT_CNT_MAX	=13'D19	;//仿真用,需自行修改
parameter	BIT_CNT_X	=13'D9	;
parameter	BAUD_CNT_MAX=4'D8		;

always@(posedge clk_sys	or negedge	rst_n_sys)
	if(~rst_n_sys)
		begin			
			rx_reg1<=1'b1			;
			rx_reg2<=1'b1			;
			rx_reg3<=1'b1			;
		end
	else
		begin
			rx_reg1<=rx_data		;
			rx_reg2<=rx_reg1		;
			rx_reg3<=rx_reg2		;
		end
//
always@(posedge clk_sys or negedge rst_n_sys)
	if	(~rst_n_sys)
		start_flag<=1'b0	;
	else if(rx_reg2==1'b0 && rx_reg3==1'b1 && work_en==1'b0)
		start_flag<=1'b1	;
	else 
		start_flag<=1'b0	;
		
always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		work_en<=1'b0		;
	else if(start_flag==1'b1)
		work_en<=1'b1		;
	else if(baud_cnt==BIT_CNT_X && bit_cnt==BAUD_CNT_MAX)
		work_en<=1'b0		;		
	else
		work_en<=work_en	;
	
always@(posedge clk_sys or negedge rst_n_sys)
	if	(~rst_n_sys)
		bit_cnt<=1'b0					;
	else if(work_en==1'b0)
		bit_cnt<=1'b0					;	
	else if(baud_flag==1'b1)
		bit_cnt<=bit_cnt+1'b1		;
	else	
		bit_cnt<=bit_cnt				;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		baud_flag<=1'b0		;
	else if(baud_cnt==BIT_CNT_X-1'b1)
		baud_flag<=1'b1		;
	else
		baud_flag<=1'b0		;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		baud_cnt<=13'b0				;
	else if(work_en==1'b0 || baud_cnt==BIT_CNT_MAX)
		baud_cnt<=13'b0				;
	else if(work_en==1'b1)
		baud_cnt<=baud_cnt+1'b1	;
	else
		baud_cnt<=baud_cnt			;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		rx_flag<=1'b0	;
	else if(baud_cnt==BIT_CNT_X && bit_cnt==BAUD_CNT_MAX)
		rx_flag<=1'b1	;
	else 
		rx_flag<=1'b0	;
		
always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		rx_data_reg<=8'd0									;
	else if(bit_cnt>=4'b1 && bit_cnt<=4'd8 && baud_flag==1'b1)
		rx_data_reg<={rx_reg3,rx_data_reg[7:1]}	;
	else
		rx_data_reg<=rx_data_reg						;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		po_data<=8'b0							;
	else if(rx_flag==1'b1)
		po_data<=rx_data_reg					;
	else 
		po_data<=po_data						;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		po_flag<=1'b0							;
	else 
		po_flag<=rx_flag						;
		
endmodule

 uart发送模块


module uart_rs232_tx(
	input	wire	clk_sys			,
	input	wire	rst_n_sys		,
	input	wire	[7:0]	pi_data	,
	input	wire	pi_flag			,
		
	output	reg	tx_data
	);	

reg	[3:0]		bit_cnt					;
reg	[12:0]	baud_cnt					;
reg	tx_work_en							;



parameter	BAUD_CNT_MAX=13'd19			;	//仿真用的波特时间400ns
parameter	BIT_CNT_MAX	=4'd8				;	//八个有效数据

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		tx_work_en<=1'b0	;
	else if(bit_cnt==BIT_CNT_MAX && baud_cnt==BAUD_CNT_MAX)
		tx_work_en<=1'b0	;
	else	if(pi_flag==1'b1)
		tx_work_en<=1'b1	;

always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		bit_cnt<=4'b0			;
	else if 	(tx_work_en==1'b0)
		bit_cnt<=4'b0			;
	else if	(baud_cnt==BAUD_CNT_MAX)
		bit_cnt<=bit_cnt+1'b1	;
	else 
		bit_cnt<=bit_cnt			;
	
always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		baud_cnt<=4'b0			;
	else if 	(tx_work_en==1'b0 || baud_cnt==BAUD_CNT_MAX)
		baud_cnt<=4'b0			;
	else 
		baud_cnt<=baud_cnt+1'b1	;
	
always@(posedge clk_sys or negedge rst_n_sys)
	if(~rst_n_sys)
		tx_data<=1'b1	;
	else if (baud_cnt==13'b0 && tx_work_en==1'b1)
		begin
			case (bit_cnt)
				4'd0:tx_data	<=	1'b0			;
				4'd1:tx_data	<=	pi_data[0]	;
				4'd2:tx_data	<=	pi_data[1]	;
				4'd3:tx_data	<=	pi_data[2]	;
				4'd4:tx_data	<=	pi_data[3]	;
				4'd5:tx_data	<=	pi_data[4]	;
				4'd6:tx_data	<=	pi_data[5]	;
				4'd7:tx_data	<=	pi_data[6]	;
				4'd8:tx_data	<=	pi_data[7]	;
				default:tx_data<=1'b1			;
			endcase
		end
	else if(tx_work_en==1'b0)
		tx_data<=1'b1		;
	else 
		tx_data<=tx_data	;
	
	
endmodule

        出现错误时,我们可以按这个顺序排查错误。首先检查是否使用了正确的波特率。波特率必须与发送端和接收端相同并且时钟频率必须与波特率相匹配。其次,检查是否使用了正确的数据位数和校验位。这些参数必须与发送端和接收端一致。最后,检查程序是否正确。确保程序没有逻辑错误,比如发送和接收的数据类型不匹配。最后,检查是否使用了正确的发送和接收协议。协议必须包括正确的开始位、数据位、校验位和停止位。

        总结个人收获与感受:我学会了如何使用Verilog语言来实现串口传输。我学会了如何编写Verilog代码来控制串口的收发数据,并将数据转换为适当的信号进行传输。我掌握了RS232串口传输的原理和工作方式。我学会了如何正确配置串口通信参数,包括波特率、数据位、停止位和校验位等,以及如何处理错误和异常情况。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值