前面一篇博客实现已经分析并实现串行接口的接收模块。其中,串口的波特率对串口来说是一个比较重要的概念,因为其决定了接收或者发送一位数据所用的时间。由于FPGA所用的时钟通常远比串口的波特率快,所以在使用FPGA的时钟发送或者接收数据时,都需要一个串口波特率定时模块来产生定时脉冲,以此确保每位数据只被接收或者发送一次。
串口发送过程如图1所示,由图可知,其基本原理跟串口的接收时序一致,唯一区别就是串口发送模块不需要开始标志(串口接收数据时需要开始标志,其起始位必须为0);每次发送数据也是11位,包含1位起始位、8位数据位、1位校验位、1位停止位。
图1中,sampling_signal是由波特率定时模块产生的,对应TX_Pin_Out的每一位数据,都有一个sampling_signal,表明每个数据在这个波特率的时间内只被发送一次。
一、串口发送数据流程
串口发送数据流程如图2所示, 在s0状态,计数器电路加载0,如果cnt_en串口发送数据信号使能,进入s1状态。在s1状态,加载待发送的数据,如果采样信号有效,则计数器使能,计数器开始计数;若计数器计数到count=1,进入s2状态,在s2状态,主要通过一位寄存器将并行数据转为串行发送,若采样信号有效,则计数器、移位寄存器信号使能,此时移位寄存器开始一位一位的发送出去,同时计数器计数发送数据的个数;若计数到count=11,进入到s3数据发送完成状态,在s3状态,如果采样信号有效,发送完成标志位拉高,计数器和移位寄存器信号使能,此时串口发送数据完毕。若计数器count=0,则进入到s0状态,等待下一次数据的发送。
二、数据路径
由图2可知,串口发送模块需要的电路的基本模块包括计数器、并行转串行移位寄存器,波特率定时(计数器)。数据路径如图3所示:
三、控制信号
由数据路径可知,右移寄存器的控制信号包括使能信号en_a和数据加载信号load_a,计数器的控制信号包括计数器使能信号en_b和计数器加载信号(加载0)load_b,波特率定时计数器包含波特率定时计数器使能信号。可以在图2中的各个状态中得出这些信号何时有效。
由图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 moduleclk_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;elsecurrent_state <= next_state;///always @(*)case(current_state)s0:beginif(cnt_en)next_state = s1;elsenext_state = s0;ends1:beginif(count == 'd1)next_state = s2;elsenext_state = s1;ends2:beginif(count == 'd11)next_state = s3;elsenext_state = s2;ends3:beginif(count == 'd0)next_state = s0;elsenext_state = s3;enddefault:next_state = s0;endcase/always @(*)case(current_state)s0:beginload_b = 'd1;load_a = 'd0;en_b = 'd0;en_a = 'd0;tx_done = 'd0;ends1:beginload_b = 'd0;tx_done = 'd0;en_a = 'd0;load_a = 'd1;if(simpling_signal)begin//load_a = 'd1;en_b = 'd1;endelsebegin//load_a = 'd0;en_b = 'd0;endends2:beginload_b = 'd0;load_a = 'd0;tx_done = 'd0;if(simpling_signal)beginen_a = 'd1;en_b = 'd1;endelsebeginen_a = 'd0;en_b = 'd0;endends3:beginload_b = 'd0;load_a = 'd0;if(simpling_signal)begintx_done = 'd1;en_b = 'd1;en_a = 'd1;endelsebegintx_done = 'd0;en_b = 'd0;en_a = 'd0;endenddefault:beginload_b = 'd1;load_a = 'd0;en_b = 'd0;en_a = 'd0;tx_done = 'd0;endendcase// Instantiate the moduleBPS_TIMER BPS_TIMER (.clk_50m(clk_50m), .cnt_en(cnt_en), .simpling_signal(simpling_signal));// Instantiate the moduleCOUNT_NUMBER COUNT_NUMBER (.clk_50m(clk_50m), .load_b(load_b), .en_b(en_b), .count(count));// Instantiate the moduleright_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)beginif(cnt == 'd5207)cnt <= 'd0;elsecnt <= cnt + 'd1;endelsecnt <= '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)beginif(count == 'd11)count <= 'd0;elsecount <= count + 'd1;endelsecount <= 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]};elsedata <= data;assign tx_pin_data = data[0];endmodule
5、仿真激励文件:
module test;// Inputsreg clk_in;reg rst;reg [10:0] tx_data;reg cnt_en;// Outputswire 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 Inputsclk_in = 0;rst = 0;tx_data = 0;cnt_en = 0;// Wait 100 ns for global reset to finish#100;// Add stimulus hereendalways #5 clk_in = !clk_in;reg [2:0] cnt = 'd0;always @(posedge clk_50m)if(cnt == 'd5)cnt <= 'd5;elsecnt <= cnt + 'd1;always @(posedge clk_50m)if(cnt<=3)rst <= 'd0;elserst <= 'd1;reg[20:0]count = 'd0;always @(posedge clk_50m)if(count == 'd10000)count <= 'd0;elsecount <= count + 'd1;always @(posedge clk_50m)if(tx_done)begincnt_en <= 'd0;tx_data <= 'd0;endelse if(count == 'd10000)begincnt_en <= 'd1;tx_data <= 'b10101010101;endelsebegincnt_en <= cnt_en;tx_data <= tx_data;endendmodule
6、Isim仿真结果如下:发送的数据tx_data <= 'b10101010101;
由仿真结果可知,在一个发送数据周期,en_a有效11次。待所有数据发送完毕tx_done信号拉高。