串行接口(UART)------verilog实现串口发送模块
前面一篇博客实现已经分析并实现串行接口的接收模块。其中,串口的波特率对串口来说是一个比较重要的概念,因为其决定了接收或者发送一位数据所用的时间。由于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、代码顶层部分:
[code]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、波特率定时模块:
[code]////////由于使用的时钟是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、计数器模块:
[code]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、移位寄存器模块:
[code]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、仿真激励文件:
[code]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信号拉高。
阅读更多
- 串口通信,C#,C++,短信发送模块实现
- 串口通讯控制器实现之----发送模块
- verilog语言RS232串口接收模块设计——串口调试工具发送数据在数码管显示
- verilog语言RS232串口发送模块设计——采集ps2键盘数据在串口调试工具显示
- FT232RL 实现USB到串行UART接口的转换芯片
- UART异步串行接口模块设计
- UART(串口发送模块)
- FT232RL为接口转换芯片,可以实现USB到串行UART接口的转换
- UART异步串行接口模块设计
- 用verilog实现的串口通信模块
- 用串口连接GSM手机发送和接收短消息,在应用程序中如何编程实现?
- 串口通信程序中十六进制格式发送和接收实现
- 用串口连接GSM手机发送和接收短消息,在应用程序中如何编程实现?
- 中断方式实现发送数据串口驱动
- 第四部分 串行接口UART和Console
- FPGA UART TX,简单的FPGA串口发送模块
- C# 使用SMS接口实现手机短信发送功能
- 串口信号发送,运用vc++6.0的实现方法
- C#实现串口数据循环发送
- python使用第三方模块实现给注册用户发送验证码