串口RS232的学习

RS232通信协议简介

1、RS232是UART的一种,没有时钟线,只有两根数据线,分别是rx和tx,这两根线都是1bit位宽的。其中rx是接受数据的线,tx是发送数据的线。

2、rx的位宽为1bit,PC机通过串口调试助手往FPGA发送8bit数据时,FPGA通过串口线rx一位一位的接收数据,从最低位到最高位依次接收,最后在FPGA里面位拼接成8bit数据。

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 继续进入空闲状态,然后等待下一次的数据传输。如下图所示 为一个最基本的 RS232 帧结构。

PS:Rx:一共是发送十位数据,但是第一位和第十位分别是0和1,中间八位任意,只需要对中间八位进行传输即可 

5、波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进 行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元 的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口的编写与仿真。

6、比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为 “每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。

7、由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟 频率下计数 5208 次

8、上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。

程序设计 

1、顶层模块框图如下

串口RS232的学习_第1张图片

可以看到,顶层的框图一共包含两个模块,分别是接收和发送模块。

2、接收模块

接收模块的作用是将从PC输入的串行数据转换为并行数据 。

一帧数据一共有10bit,其中起始位和停止位分别为0和1,接收模块只接收中间八位数据,将中间8位的串行数据转化为并行,然后把8位的并行数据输入到发送模块。

接收模块的框图如下图所示:

串口RS232的学习_第2张图片

 接收模块波形图如下:

串口RS232的学习_第3张图片

对波形图的一些理解: 接收的Rx信号需要同步到系统时钟sys_clk下,即单比特信号从低速时钟域同步到快速时钟域,通常使用的方法都是打两拍。但为何要同步三次呢?因为start_flag需要提取一个下降沿,而第一个同步的时钟是一个不稳定的信号,所以需要用第二个和第三个同步后的来提取下降沿。

 根据波形图写出代码如下:

module uart_rx
#(
	parameter	uart_bps	= 'd9600,
	parameter	clk_freq	= '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		
    );
		localparam	boud_cnt_MAX	=	clk_freq/uart_bps	;
	
		reg			rx_reg1			;
		reg			rx_reg2			;
		reg			rx_reg3			;
		reg			start_flag		;
		reg			work_en			;
		reg	[13:0]	boud_cnt		;
		reg			bit_flag		;
		reg	[3:0]	bit_cnt			;
		reg	[7:0]	rx_data			;
		reg			rx_flag			;

//regesiter delay, two clock time
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;

//start_flag
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		start_flag<=1'b0;
	else	if((~rx_reg2)&&(rx_reg3))
		start_flag<=1'b1;
	else
		start_flag<=1'b0;

//work_en
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		work_en<=1'b0;
	else	if(bit_cnt==4'd8&&bit_flag==1'b1)
		work_en<=1'b0;
	else	if(start_flag==1'b1)
		work_en<=1'b1;
	else
		work_en<=work_en;
		
//boud_cnt
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		boud_cnt<=14'd0;
	else	if((boud_cnt==boud_cnt_MAX-1'b1)||(work_en==1'b0))
		boud_cnt<=14'd0;
	else	if(work_en==1'b1)
		boud_cnt<=boud_cnt+1'b1;
		
//bit_flag
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		bit_flag<=1'b0;
	else	if(boud_cnt==boud_cnt_MAX/2-1'b1)
		bit_flag<=1'b1;
	else
		bit_flag<=1'b0;

//bit_cnt		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		bit_cnt<=1'b0;
	else	if(bit_cnt==4'd8&&bit_flag==1'b1)
		bit_cnt<=1'b0;
	else	if(bit_flag==1'b1)
		bit_cnt<=bit_cnt+1'b1;
	else
		bit_cnt<=bit_cnt;
		
//rx_data[7:0]
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		rx_data<=8'b0;
	else	if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))
		rx_data<={rx_reg3,rx_data[7:1]};
		
//rx_flag
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		rx_flag<=1'b0;
	else	if(bit_flag==1'b1&&bit_cnt==4'd8)
		rx_flag<=1'b1;
	else
		rx_flag<=1'b0;

//po_data
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		po_data<=8'd0;
	else	if(rx_flag==1'b1)
		po_data<=rx_data;
//po_flag
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		po_flag<=1'b0;
	else	if(rx_flag==1'b1)
		po_flag<=1'b1;
	else
		po_flag<=1'b0;
	
endmodule

 接收模块仿真代码如下:

`timescale 1ns / 1ns
module tb_uart_rx();

reg			sys_clk			 ;
reg         sys_rst_n		 ;
reg         rx				 ;
							
wire	[7:0]	po_data		;
wire       		po_flag		;


initial
	begin
		sys_clk=1'b1;
		sys_rst_n<=1'b0;
		rx<=1;
		#20
		sys_rst_n<=1'b1;
	end

always #10	sys_clk=~sys_clk;

initial
	begin
		#200
		rx_bit(8'd0);
		rx_bit(8'd1);
		rx_bit(8'd2);
		rx_bit(8'd3);
		rx_bit(8'd4);
		rx_bit(8'd5);
		rx_bit(8'd6);
		rx_bit(8'd7);
	end

task rx_bit(
	input	[7:0]	data
);
	integer i;
	for(i=0;i<10;i=i+1)
		begin
			case(i)
			0:rx<=1'b0;
			1:rx<=data[0];
			2:rx<=data[1];
			3:rx<=data[2];
			4:rx<=data[3];
			5:rx<=data[4];
			6:rx<=data[5];
			7:rx<=data[6];
			8:rx<=data[7];
			9:rx<=1'b1;
			endcase
			#(5208*20);
		end
endtask

uart_rx	inst
(
.sys_clk		(sys_clk)		,
.sys_rst_n		(sys_rst_n)		,
.rx				(rx)			,
		
.po_data		(po_data)		,
.po_flag		(po_flag)		
    );
endmodule

 Vivado仿真波形图

整体波形如下

串口RS232的学习_第4张图片

对仿真波形图的理解: 在空闲状态下,Rx保持高电平,当检测到低电平时,波特计数器开始计数,当波特计数器计数到最大值的时候,则开始传输数据,一个8bit的数据传输完成后,po_data才开始输出。 

 3、发送模块

发送模块的作用的接收来自接收模块的八位并行数据,然后将这八位并行数据转换位串行数据,一位一位的发送出去 。

一帧共有10bit,第一位和最后一位分别位0和1,来自接收模块的八位数据在中间。

模块设计

我们将串口接收模块取名为 uart_tx,根据功能简介我们对整个设计要求有了大致的了解,其中设计的关键点是如何将串并行数据转化为串行数据并发送出去,也就是按照顺序将并行数据发送至 PC 机上。FPGA 发送的串行数据同样没有时钟,所以要和 PC 机接约定好使用相同的波特率,一个一个地发送比特,为了后面做串口的回环测试我们仍选择使用 9600bps的波特率

模块的Visio框图如下:

串口RS232的学习_第5张图片

 发送模块的波形图如下:

串口RS232的学习_第6张图片

ps:注意发送同样是10bit的数据,当第一个bit_flag=1的时候开始发送起始位0,当bit_flag=9的时候发送停止位1,其余时间Tx保持空闲状态。

根据波形图可以写出如下代码:

module uart_tx
#(
	parameter	uart_bps	= 'd9600,
	parameter	clk_freq	= 'd50_000_000
)
(
	input	wire			sys_clk			,
	input	wire			sys_rst_n		,
	input	wire	[7:0]	pi_data			,
	input	wire			pi_data_flag	,
	output	reg				tx				
	
    );
	localparam	boud_cnt_max=clk_freq/uart_bps;
	reg				work_en		;
	reg		[12:0]	boud_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_en<=1'b0;
	else	if((bit_cnt==4'd9)&&(bit_flag==1'b1))	
		work_en<=1'b0;
	else	if(pi_data_flag==1'b1)
		work_en<=1'b1;
	else
		work_en<=work_en;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		boud_cnt<=13'd0;
	else	if((boud_cnt==boud_cnt_max-1'b1)||work_en==1'b0)
		boud_cnt<=13'd0;
	else	if(work_en==1'b1)
		boud_cnt<=boud_cnt+1'b1;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		bit_flag<=1'b0;
	else	if(boud_cnt==13'd1)
		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<=4'd0;
	else	if((bit_cnt==4'd9)&&(bit_flag==1'b1))
		bit_cnt<=4'd0;
	else	if((bit_flag==1'b1)&&(work_en==1'b1))
		bit_cnt<=bit_cnt+1'b1;
	else
		bit_cnt<=bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n==1'b0)
		tx<=1'b1;
	else	if(bit_flag==1'b1)	
		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
	
endmodule

发送模块的仿真代码如下:

`timescale 1ns / 1ns

module tb_uart_tx();

reg			sys_clk		;
reg			sys_rst_n	;
reg	[7:0]	pi_data		;
reg			pi_data_flag;
wire		tx			;

initial
	begin
		sys_clk=1'b1;
		sys_rst_n<=1'b0;
		#20
		sys_rst_n<=1'b1;
	end
always #10 sys_clk=~sys_clk;

initial
	begin
		pi_data<=8'b0;
		pi_data_flag<=1'b0;
		#200
//transport the data 0
		pi_data<=8'd0;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 1		
		pi_data<=8'd1;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 2		
		pi_data<=8'd2;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 3		
		pi_data<=8'd3;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 4		
		pi_data<=8'd4;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 5		
		pi_data<=8'd5;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 6		
		pi_data<=8'd6;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
//transport the data 7		
		pi_data<=8'd7;
		pi_data_flag<=1'b1;
		#20
		pi_data_flag<=1'b0;
		#(5208*20*10);
	end

uart_tx	inst
(
	.sys_clk		(sys_clk)	,
	.sys_rst_n		(sys_rst_n)	,
	.pi_data		(pi_data)	,
	.pi_data_flag	(pi_data_flag)	,
	.tx				(tx)	
    );
endmodule

Vivado 仿真波形如下:

串口RS232的学习_第7张图片

顶层模块 

串口RS232的学习_第8张图片

顶层模块代码如下:

module top_rs232(

	input	wire		sys_clk		,
	input	wire		sys_rst_n	,
	input	wire		rx			,
									
	output	wire		tx			

    );
	parameter	uart_bps	= 14'd9600;
	parameter	clk_freq	= 26'd50_000_000;
	wire	[7:0]	po_data;
	wire			po_flag;
	
uart_rx	
#(
	.uart_bps(uart_bps),
	.clk_freq(clk_freq)
)
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(uart_bps),
	.clk_freq(clk_freq)
)
uart_tx_inst1
(
.sys_clk			(sys_clk),
.sys_rst_n			(sys_rst_n),
.pi_data			(po_data),
.pi_data_flag		(po_flag),

.tx					(tx)
	
    );	
	
endmodule

仿真测试代码如下:

`timescale 1ns / 1ns

module tb_top_rs232();

reg		sys_clk		;
reg		sys_rst_n	;
reg		rx			;

wire	tx			;

initial
	begin
		sys_clk=1'b1;
		sys_rst_n<=1'b0;
		rx<=1'b1;
		#20
		sys_rst_n<=1'b1;
	end
always #10 sys_clk=~sys_clk;

initial
	begin
		#200
		rx_byte();
	end

task	rx_byte();
	integer	j;
	for(j=0;j<8;j=j+1)
		rx_bit(j);
endtask


task rx_bit(
	input	[7:0]data
);
	integer i;
	for(i=0;i<10;i=i+1)
		begin
			case(i)
				0:rx<=1'b0;
				1:rx<=data[0];
				2:rx<=data[1];
				3:rx<=data[2];
				4:rx<=data[3];
				5:rx<=data[4];
				6:rx<=data[5];
				7:rx<=data[6];
				8:rx<=data[7];
				9:rx<=1'b1;
			endcase
			#(5208*20);
		end
endtask

top_rs232 inst(

.sys_clk	(sys_clk)	,
.sys_rst_n	(sys_rst_n)	,
.rx			(rx)	,

.tx			(tx)

    );

endmodule

Vivado仿真波形图如下:

串口RS232的学习_第9张图片

你可能感兴趣的:(FPGA,学习,fpga开发)