串口模块总结

在这里插入图片描述
上图串口传输的时序图。串口传输数据都是一帧数据 11 位。其中第0位为起始位,1-7位为数据位,第9位为校验位,第10位是停止位。
在串口的总线上“高电平”是默认的状态,当一帧数据的开始传输必须先拉低电平,这就是第 0 位的作用。第 0 位过后就是 8 个数据位,这八个数据位才是一帧数据中最有意义的东西。最后的两位是校验位和停止位,作用如同命名般一样,基本上是没有重要意义。
串口传输还有另一个重要参数就是“波特率”。很多朋友都误解“波特率”是串口传输的传输速度,这样的理解在宏观上是无误。但是在微观上“波特率”就是串口传输中“一个位的周期”,换句话说亦是“一个位所逗留的时间”,常用的波特率有 9600 bps 和 115200 bps ( bit per second )。“9600 bps” 表示每秒可以传输 9600 位。但是经过公式计算“一个位的周期”就会暴露出来。

一个位的周期 = 1 / bps
= 1/ 9600= 0.000104166666666667
我们明白一个事实 :9600 bps ,一位数据占用 0.000104166666666667s 时间。如果是一帧 11 位的数据,就需要0.000104166666666667 x 11 = 0.00114583333333334,那么一秒钟内可以传输1 / 0.00114583333333334 = 872.727272727268872.727272727268 个帧数据。当然这只是在数字上计算出来而已,但是实际上还有许多看不见的延迟因数。

1、接收模块
在这里插入图片描述
Rx_module.v 是一个组合模块,主要是包含 detect_module.v , bps_module.v 和rx_control_module.v,3 个功能模块。
detect_module.v 的输入是连结物理引脚 rx,它主要检测一帧数据的第 0 位,也就是起始位,然后产生一个高脉冲经 H2L_Sig 给 rx_control_module.v ,以表示一帧数据接收工作已经开始。
rx_bps_module.v 是产生波特率定时的功能模块。换一句话说,它是配置波特率的模块。当rx_control_module.v拉高Count_Sig, bps_module.v经BPS_CLK对 rx_control_module.v 产生定时。
rx_control_module.v 是核心控制模块。针对串口的配置主要是 1 帧 11 位的数据,重视八位数据位,无视起始位,校验位和结束位。当 RX_En_Sig 拉高,这个模块就开始工作,它将采集来自 RX_Pin_In 的数据,当完成一帧数据接收的时候,就会产生一个高脉冲给RX_Done_Sig。
在这里插入图片描述
如上图所示,数据采集都是在“每位数据的中间”进行着。在上图中 RX_Pin_In 输入一帧数据,当 detect_module.v 检测到低电平(起始位),rx_control_module.v 和 rx_bps_module.v 就产生定时(与 RX_Pin_In的波特率是一致)。然而 rx_bps_module.v 产生的定时是在每个位时间的中间。在第 0 位数据,采取忽略的态度,然后接下来的 8 位数据位都被采集,最后校验位和停止位,却是采取了忽略的操作。有一点你必须好好注意,串口传输数据“从最低位开始,到最高位结束”。

module detect_module 
(
CLK, RSTn,
RX_Pin_In,
H2L_Sig
);

input CLK;
input RSTn;
input RX_Pin_In;
output H2L_Sig;

/******************************/

reg H2L_F1;
reg H2L_F2;

always @ ( posedge CLK or negedge RSTn )
	if( !RSTn )
		begin
			H2L_F1 <= 1'b1;
			H2L_F2 <= 1'b1;
		end
	else
		begin
			H2L_F1 <= RX_Pin_In;
			H2L_F2 <= H2L_F1;
		end
	
	assign H2L_Sig = H2L_F2 & !H2L_F1;
endmodule

detect_module.v 这个功能模块是为了检查电平由高变低。当检测到电平又高变低,在assign处就会输出高脉冲。

module rx_bps_module
(
CLK, RSTn,
Count_Sig, 
BPS_CLK
);

input CLK;
input RSTn;
input Count_Sig;
output BPS_CLK;

/***************************/

reg [12:0]Count_BPS;

always @ ( posedge CLK or negedge RSTn )
	if( !RSTn )
		Count_BPS <= 13'd0;
	else if( Count_BPS == 12'd5207 )
		Count_BPS <= 13'd0;
	else if( Count_Sig )
		Count_BPS <= Count_BPS + 1'b1;
	else
		Count_BPS <= 13'd0;

assign BPS_CLK = ( Count_BPS == 13'd2604 ) ? 1'b1 : 1'b0;

endmodule

9600 bps 传输速度使一位数据的周期是 0.000104166666666667s 。以 50Mhz 时钟频率要得到上述的定时需要:N = 0.000104166666666667 / ( 1 / 50Mhz ) = 5208,如果从零开始算起 5208 - 1 亦即 5207 个计数(20 行)。然而,采集数据要求“在周期的中间”,那么结果是 5208 / 2 ,结果等于 1041(29 行)。基本上 rx_bps_module.v 只有在 Count_Sig 拉高的时候(22 行),模块才会开始计数。

module rx_control_module
(
CLK, RSTn,
H2L_Sig, RX_Pin_In, BPS_CLK, RX_En_Sig,
Count_Sig, RX_Data, RX_Done_Sig

);

input CLK;
input RSTn;
input H2L_Sig;
input RX_En_Sig;
input RX_Pin_In;
input BPS_CLK;

output Count_Sig;
output [7:0]RX_Data;
output RX_Done_Sig;

reg [3:0]i;
reg [7:0]rData;
reg isCount;
reg isDone;

always @ ( posedge CLK or negedge RSTn )
	if( !RSTn )
		begin
			i <= 4'd0;
			rData <= 8'd0;
			isCount <= 1'b0;
			isDone <= 1'b0;
		end
	else if ( RX_En_Sig )
		case (i)	
		4'd0:if( H2L_Sig ) 
				begin 
					i <= i + 1'b1; 
					isCount <= 1'b1; 
				end
		4'd1:if( BPS_CLK )
				begin
					i <= i + 1'b1;
				end
		4'd2,4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :
			 if( BPS_CLK )
				begin
					i <= i + 1'b1;
					rData[ i - 2 ] <= RX_Pin_In;
				end
		4'd10:if( BPS_CLK )
				begin
					i <= i + 1'b1;
				end
		4'd11:if( BPS_CLK )
				begin
					i <= i + 1'b1;
				end
		4'd12:	begin 
					i <= i + 1'b1; 
					isDone <= 1'b1; 
					isCount <= 1'b0; 
				end
		4'd13:	begin 
					i <= 1'b0; 
					isDone <= 1'b0; 
				end
		endcase
	
	assign Count_Sig = isCount;
	assign RX_Data = rData;
	assign RX_Done_Sig = isDone;
endmodule

状态机那部分,是 rx_control_module.v 的核心控制功能。在else if ( RX_En_Sig )中,表示了如果 RX_En_Sig 如果没有拉高,这个模块是不会工作。(前面定义了 isCount 标志寄存器,为了使能rx_bps_module.v 输出采集定时信号)当 rx_control_module.v 模块被使能,该模块就会处于就绪状态,一旦 detect_module.v 检查到由高变低的电平变化(状态机第0步),会使步骤 i 进入第 0 位采集,然而 isCount 标志寄存器同时也会被设置为逻辑 1,rx_bps_module.v 便会开始产生波特率的定时。我们知道rx_bps_module.v 产生的定时是在“每位数据的中间”。当i=1,第一次的定时采集时第 0 位数据(起始位),保持忽略态度。i=2至9时,定时采集的是八位数据位,每一位数据位会依低位到最高位储存入 rData 寄存器。i=10-11时,是最后两位的定时采集(校验位,停留位),同时采取忽略的态度。当进入i=12-13,这表示一帧数据的采集工作已经结束。最后会产生一个完成信号的高脉冲,同时间 isCount 会被设置为逻辑 0,亦即停止 rx_bps_module.v 的操作。至于 assign的三个连接,isCount 标志寄存器是 Count_Sig 输出的驱动器,rData 寄存器是RX_Data 输出的驱动器,最后 isDone 标志寄存器是 RX_Done_Sig 输出的驱动寄存器。

module rx_module
(
CLK, RSTn,
RX_Pin_In, RX_En_Sig,
RX_Done_Sig, RX_Data
);

input CLK;
input RSTn;
input RX_Pin_In;
input RX_En_Sig;

output [7:0]RX_Data;
output RX_Done_Sig;

wire H2L_Sig;
detect_module U1
(
.CLK		( CLK 		),
.RSTn		( RSTn 		),
.RX_Pin_In	( RX_Pin_In ), // input - from top
.H2L_Sig	( H2L_Sig 	) // output - to U3
);

wire BPS_CLK;
rx_bps_module U2
(
.CLK		( CLK 		),
.RSTn		( RSTn		 ),
.Count_Sig	( Count_Sig ), // input - from U3
.BPS_CLK	( BPS_CLK 	) // output - to U3
);

wire Count_Sig;
rx_control_module U3
(
.CLK			( CLK 			),
.RSTn			( RSTn 			),
.H2L_Sig		( H2L_Sig 		), // input - from U1
.RX_En_Sig		( RX_En_Sig 	), // input - from top
.RX_Pin_In		( RX_Pin_In 	), // input - from top
.BPS_CLK		( BPS_CLK	    ), // input - from U2
.Count_Sig		( Count_Sig 	), // output - to U2
.RX_Data		( RX_Data 		), // output - to top
.RX_Done_Sig	( RX_Done_Sig 	) // output - to top
);

endmodule

当 rx_module.v 组合模块组织一切以后只保留的输入输出有:输入信号 RX_Pin_In 和RX_En_Sig:输出信号 RX_Data 和 RX_Done_Sig。在试验中, rx_bps_module.v 扮演着“配置波特率”,然而 rx_control_module.v 的工作则对“数据作出过滤”。在典型的应用上“1 位起始位,8 位数据位,1 位校验位,和 1 位停止位”已经成为主流。当然波特率的选择是 9600 bps 还是 115200 bps 这视需要而定。

RTL图
我们要建立如下图 rx_module_demo.v 组合模块 ,它里边包含了一个控制模块对rx_module.v 的调用。一开始 control_module.v 会拉高 RX_En_Sig 使能 rx_module.v 。当有一阵数据经 RX_Pin_In 传入, rx_module.v 就会接收,然后过滤后的数据出输出致 RX_Data , 然后再产生一个高脉冲给 RX_Done_Sig。当 control_module.v 接收到RX_Done_Sig 的高脉冲,拉低 RX_En_Sig,并且将 RX_Data 的“前四位”输出致 4 位LED 资源。 然后过程再一次重复不断。
在这里插入图片描述

module control_module
(
CLK, RSTn,
RX_Done_Sig,
RX_Data,
RX_En_Sig,
Number_Data
);

input CLK;
input RSTn;
input RX_Done_Sig;
input [7:0]RX_Data;
output RX_En_Sig;
output [7:0]Number_Data;

reg [7:0]rData;
reg isEn;

always @ ( posedge CLK or negedge RSTn )
	if( !RSTn )
		rData <= 8'd0;
	else if( RX_Done_Sig ) 
		begin 
			rData <= RX_Data; 
			isEn <= 1'b0; 
		end
	else isEn <= 1'b1;

assign Number_Data = rData;
assign RX_En_Sig = isEn;

endmodule

always模块中一开始的时候就将 isEn 设置为逻辑 1, 这个标志寄存器驱动着 RX_En_Sig。换句话说,此时的 rx_module.v 已经进入就绪状态,然后 control_module.v 等待着 RX_Done_Sig 反馈(23行)。一旦一帧数据接收完毕,RX_Done_Sig 就会产生高脉冲, 然后 rData 就会寄存 RX_Data 的值。同时间 isEn 被设置为逻辑 0(25-26行)。在下一个来临, control_module.v 会再一次设置 isEn 为逻辑 1,已做好接收下一组数据的准备。

module rx_module_demo
(
CLK, RSTn,
RX_Pin_In,
Number_Data
);

input CLK;
input RSTn;
input RX_Pin_In;
output [3:0]Number_Data;
wire RX_Done_Sig;
wire [7:0]RX_Data;

rx_module U1
(
.CLK		 ( CLK 			),
.RSTn		 ( RSTn 		),
.RX_Pin_In	 ( RX_Pin_In 	), // input - from top
.RX_En_Sig	 ( RX_En_Sig 	), // input - from U2
.RX_Done_Sig ( RX_Done_Sig 	), // output - to U2
.RX_Data	 ( RX_Data 		) // output - to U2
);

wire RX_En_Sig;
wire [7:0]Output_Data;

control_module U2
(
.CLK( CLK ),
.RSTn( RSTn ),
.RX_Done_Sig( RX_Done_Sig ), // input - from U1
.RX_Data( RX_Data ), // input - from U1
.RX_En_Sig( RX_En_Sig ), // output - to U1
.Number_Data( Output_Data ) // output - to top
);

assign Number_Data = Output_Data[3:0];

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值