串行接口(UART)——verilog实现串口发送模块

  • Post author:
  • Post category:其他


前面一篇博客实现已经分析并实现串行接口的接收模块。其中,串口的波特率对串口来说是一个比较重要的概念,因为其决定了接收或者发送一位数据所用的时间。由于FPGA所用的时钟通常远比串口的波特率快,所以在使用FPGA的时钟发送或者接收数据时,都需要一个串口波特率定时模块来产生定时脉冲,以此确保每位数据只被接收或者发送一次。

串口发送过程如图1所示,由图可知,其基本原理跟串口的接收时序一致,唯一区别就是串口发送模块不需要开始标志(串口接收数据时需要开始标志,其起始位必须为0);每次发送数据也是11位,包含1位起始位、8位数据位、1位校验位、1位停止位。

图1中,sampling_signal是由波特率定时模块产生的,对应TX_Pin_Out的每一位数据,都有一个sampling_signal,表明每个数据在这个波特率的时间内只被发送一次。

图1 串口发送过程

一、串口发送数据流程

图2 串口发送过程

串口发送数据流程如图2所示, 在s0状态,计数器电路加载0,如果cnt_en串口发送数据信号使能,进入s1状态。在s1状态,加载待发送的数据,如果采样信号有效,则计数器使能,计数器开始计数;若计数器计数到count=1,进入s2状态,在s2状态,主要通过一位寄存器将并行数据转为串行发送,若采样信号有效,则计数器、移位寄存器信号使能,此时移位寄存器开始一位一位的发送出去,同时计数器计数发送数据的个数;若计数到count=11,进入到s3数据发送完成状态,在s3状态,如果采样信号有效,发送完成标志位拉高,计数器和移位寄存器信号使能,此时串口发送数据完毕。若计数器count=0,则进入到s0状态,等待下一次数据的发送。

二、数据路径

由图2可知,串口发送模块需要的电路的基本模块包括计数器、并行转串行移位寄存器,波特率定时(计数器)。数据路径如图3所示:

图3 数据路径

三、控制信号

由数据路径可知,右移寄存器的控制信号包括使能信号en_a和数据加载信号load_a,计数器的控制信号包括计数器使能信号en_b和计数器加载信号(加载0)load_b,波特率定时计数器包含波特率定时计数器使能信号。可以在图2中的各个状态中得出这些信号何时有效。

图4 控制信号

由图4可知,在s0状态,计数器加载信号load_b有效,此时计数器加载0;在s1状态右移寄存器记载信号load_a使能,此时移位寄存器加载要发送的数据,在该状态,如果波特率定时采样信号simpling_signal有效,计数器使能信号en_b有效,计数发已经送数据的个数;在s2状态如果simpling_signal有效,移位寄存器使能信号en_a和计数器使能信号en_b有效;在s3状态,如果simpling_signal有效,en_a和n_b有效,发送最后一位数据,同时发送完成信号标志tx_done信号拉高。

四、verilog描述

用verilog代码来描述图3所示的数据路径,代码如下:

1、代码顶层部分:

module TRANSMIT_MODULE(input clk_in,
							  input rst,
							  input [10:0]tx_data,
							  input cnt_en,发送数据使能信号
							  output reg tx_done,
							  output tx_pin_data,
							  output clk_50m
    );
// Instantiate the module
clk_ip clk_ip (
    .CLKIN_IN(clk_in), 
    .CLKFX_OUT(clk_50m), 
    .CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT), 
    .CLK0_OUT(CLK0_OUT)
    );
wire [3:0]count;
wire simpling_signal;
reg load_b;
reg load_a;
reg en_b;		
reg en_a;
parameter [3:0]s0 = 'b0001;
parameter [3:0]s1 = 'b0010;
parameter [3:0]s2 = 'b0100;
parameter [3:0]s3 = 'b1000;
reg [3:0]current_state = 'd0;
reg [3:0]next_state = 'd0;
//
always @(posedge clk_50m)
	if(!rst)
		current_state <= s0;
	else
		current_state <= next_state;
///
always @(*)
	case(current_state)
		s0:	begin
			if(cnt_en)
				next_state = s1;
			else
				next_state = s0;
		end
		s1:	begin
			if(count == 'd1)
				next_state = s2;
			else
				next_state = s1;
		end
		s2:	begin
			if(count == 'd11)
				next_state = s3;
			else
				next_state = s2;
		end
		s3:	begin
			if(count == 'd0)
				next_state = s0;
			else
				next_state = s3;
		end
	default:	next_state = s0;
	endcase
/
always @(*)
	case(current_state)
		s0:	begin
			load_b = 'd1;
			load_a = 'd0;
			en_b = 'd0;		
			en_a = 'd0;
			tx_done = 'd0;
		end
		s1:	begin
			load_b = 'd0;	
			tx_done = 'd0;		
			en_a = 'd0;	
			load_a = 'd1;			
			if(simpling_signal)	begin
//				load_a = 'd1;
				en_b = 'd1;
			end
			else	begin
//				load_a = 'd0;
				en_b = 'd0;
			end
		end
		s2:	begin
			load_b = 'd0;
			load_a = 'd0;
			tx_done = 'd0;
			if(simpling_signal)	begin
				en_a = 'd1;
				en_b = 'd1;
			end
			else	begin
				en_a = 'd0;
				en_b = 'd0;				
			end	
		end
		s3:	begin
			load_b = 'd0;
			load_a = 'd0;

			if(simpling_signal)	begin
				tx_done = 'd1;
				en_b = 'd1;	
				en_a = 'd1;				
			end
			else	begin
				tx_done = 'd0;
				en_b = 'd0;	
				en_a = 'd0;				
			end
		end
		default:	begin
			load_b = 'd1;
			load_a = 'd0;
			en_b = 'd0;		
			en_a = 'd0;
			tx_done = 'd0;
		end
	endcase
// Instantiate the module
BPS_TIMER BPS_TIMER (
    .clk_50m(clk_50m), 
    .cnt_en(cnt_en), 
    .simpling_signal(simpling_signal)
    );
// Instantiate the module
COUNT_NUMBER COUNT_NUMBER (
    .clk_50m(clk_50m), 
    .load_b(load_b), 
    .en_b(en_b), 
    .count(count)
    );
// Instantiate the module
right_shifter right_shifter (
    .clk_50m(clk_50m), 
    .load_a(load_a), 
    .en_a(en_a), 
    .tx_data(tx_data), 
    .tx_pin_data(tx_pin_data)
    );
endmodule

2、波特率定时模块:

由于使用的时钟是50mhz的,而串口的波特率是9600bps,即串口发送数据的时钟是9600hz,因此需要使用50mhz的时钟产生个计数器,
///使其每1/9600s产生一个允许采样脉冲。
//计数器大小设置:500*10^3/96 = 5208,\,因此计数器需要计数5208个数,由于在数据中间在中间时刻更稳定,因此,在5208/2=2604时对数据进行采样更准确,
//由于计数是从零开始,因此在2603时对数据进行采样,数据计数到5207清零。
//
module BPS_TIMER(input clk_50m,
					  input cnt_en,
					  output simpling_signal
    );
reg [12:0] cnt = 'd0;
always @(posedge clk_50m)
	if(cnt_en)	begin
		if(cnt == 'd5207)
			cnt <= 'd0;
		else
			cnt <=  cnt + 'd1;
	end
	else
		cnt <= 'd0;
assign simpling_signal = (cnt == 'd2603)?'b1:'b0;
endmodule

3、计数器模块:

module COUNT_NUMBER(input clk_50m,
						  input load_b,
						  input en_b,
						  output reg[3:0] count
    );
always @(posedge clk_50m)
	if(load_b)
		count <= 'd0;
	else if(en_b)	begin
		if(count == 'd11)
			count <= 'd0;
		else
			count <= count + 'd1;
	end
	else
		count <= count;

endmodule

4、移位寄存器模块:

module right_shifter(input clk_50m,
						  input load_a,
						  input en_a,
						  input [10:0]tx_data,
						  output tx_pin_data
    );
reg [10:0]data;
always @(posedge clk_50m)
	if(load_a)
		data <= tx_data;
	else if(en_a)
		data <= {1'b0,data[10:1]};
	else
		data <= data;
assign tx_pin_data = data[0];

endmodule

5、仿真激励文件:

module test;

	// Inputs
	reg clk_in;
	reg rst;
	reg [10:0] tx_data;
	reg cnt_en;

	// Outputs
	wire tx_done;
	wire tx_pin_data;
	wire clk_50m;

	// Instantiate the Unit Under Test (UUT)
	TRANSMIT_MODULE uut (
		.clk_in(clk_in), 
		.rst(rst), 
		.tx_data(tx_data), 
		.cnt_en(cnt_en), 
		.tx_done(tx_done), 
		.tx_pin_data(tx_pin_data), 
		.clk_50m(clk_50m)
	);

	initial begin
		// Initialize Inputs
		clk_in = 0;
		rst = 0;
		tx_data = 0;
		cnt_en = 0;

		// Wait 100 ns for global reset to finish
		#100;
        
		// Add stimulus here

	end
      always #5 clk_in = !clk_in;
reg [2:0] cnt = 'd0;
always @(posedge clk_50m)
	if(cnt == 'd5)
		cnt <= 'd5;
	else
		cnt <= cnt + 'd1;
always @(posedge clk_50m)
	if(cnt<=3)
		rst <= 'd0;
	else
		rst <= 'd1;
reg[20:0]count = 'd0;
always @(posedge clk_50m)
	if(count == 'd10000)	
		count <= 'd0;
	else
		count <= count + 'd1;

always @(posedge clk_50m)
		if(tx_done)	begin
			cnt_en <= 'd0;
			tx_data <= 'd0;
		end
		else if(count == 'd10000)	begin
			cnt_en <= 'd1;
			tx_data <= 'b10101010101;
		end
		else	begin
			cnt_en <= cnt_en;
			tx_data <= tx_data;
		end
endmodule

6、Isim仿真结果如下:发送的数据 tx_data <= ‘b10101010101;

图5 仿真结果

由仿真结果可知,在一个发送数据周期,en_a有效11次。待所有数据发送完毕tx_done信号拉高。



版权声明:本文为weixin_43070186原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。