1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 串行接口(UART)------verilog实现串口接收模块

串行接口(UART)------verilog实现串口接收模块

时间:2020-02-04 07:59:18

相关推荐

串行接口(UART)------verilog实现串口接收模块

一、串口基本概念

串行接口(Serial port),主要用于串行式逐位数据传输。常见的有一般计算机应用的RS-232(使用 25 针或 9 针连接器)和工业计算机应用的半双工RS-485与全双工RS-422。----------维基百科

按照电气标准划分,串口可以分为RS-232-C、RS-422、RS-485。

RS-232-C:也称标准接口,是目前最常用的一种串行通讯接口。台式计算机一般有两个串行口:COM1和COM2,均为9针D形接口通常在计算机后面能看到,一般笔记本上有一个串口,超薄本除外。RS-232-C电气标准接口传输速率低,传输距离近。

RS-422:为改进RS-232-C传输距离近,传输速率低的特点,RS-422定义了一种平衡通信接口,传输速率提高到10Mb/s,并允许在一条接收总线上挂多个接收器。

RS-485:为扩展应用范围,EIA又于1983年在RS-422基础上制定了RS-485 标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为 TIA/EIA-485-A标准。

下图是串口公口实物图,在台式机和笔记本上一般都能找到。维基百科

图1 串口公口

2、串口传输时序-------参考【FPGA黑金开发板】Verilog HDL那些事儿--串口模块(十一)

图2 串口时序

图2是串口传输数据的时序图,串口总线默认是高电平,一帧数据开始必须先拉低电平,对应图中起始位。串口传输数据是以数据包为单位的,一个数据包有11位,其中先传输起始位,紧接着传输8个数据位,数据位是按照由低到高的顺序进行传输的,接着传输校验位,最后传输停止位。

串口还有一个重要的参数就是波特率,一般用波特率来形容串口传输数据的速度。常用的波特率有9600bps和115200bps,本文主要介绍9600bps。bps:bit per second,就是每秒钟传输多少位;9600bps就是每秒钟传输9600位,对应的传输一位所用的时钟周期就是9600Hz。接下来本文参考《verilog那些事儿——建模篇》中串口模块的划分来实现串口的接收部分verilog代码。

3、串口接收分析

由于这里是串口接收模块的介绍,为简单考虑从串口外部输入的信号只考虑RX_data_in。由于串口接收数据有明显的先后顺序,因此可以使用状态机来描述串口接收数据过程。使用算法状态机描述串口接收过程如下图所示:

图3 算法状态机

图3中s0状态实现数据的初始化,start信号对应串口发送的起始位数据,使用沿检测电路实现;s2状态是接收8位有效数据状态,由于串口是串行将数据接收进来,然后转成并行数据,因此s1部分的电路可以用右移的移位寄存器实现(串口先发送低位数据,所以选用右移移位寄存器);图中虽然画出了s3状态执行校验功能,但是本代码不考虑检验问题,因此s3状态相当于是空状态;s4状态是串口接收数据完毕发出的标志信号。根据算法状态机可以看出,实现串口的接收所需要的基本电路有移位寄存器电路、计数器电路和触发器电路。

4、数据路径

图4 数据路径

数据路径如图4所示,波特率定时信号的作用分析如下:

图5 FPGA接收数据过程

由于FPGA的工作时钟较快,串口发送数据较慢。如图5所示,串口发送一位数据可能占用多个FPGA时钟周期,因此需要增加采样信号sampling_signal,采样信号有效时占一个时钟周期,在采样信号有效时,FPGA才接收数据,保证每位数据只接收一次。波特率定时计数器就是用来产生采样信号的,在波特率定时计数器计数到一定数据后,采样信号有效,否则,采样信号无效,这样就可以产生如图5所示的采样信号。

5、控制信号

图6 控制信号

图6给出了控制信号在哪个状态有效。在s0状态,移位寄存器和计数器加载0,D触发器使能信号en_b有效,检测串口发送的数据rx_pin_in的起始信号,待检测到起始信号后,start信号有效,进入s1状态,在s1状态使能波特率采样计时信号cnt_en,判断串口采样信号sinpling_signal是否有效,若有效,设置计数器使能信号有效,反之,设置计数器使能信号无效;在计数器cnt=1时,进入s2状态,此状态开始接收串口发送的8位数据位,首先使能波特率采样计时信号cnt_en,判断sinpling_signal是否有效,若有效,计数器使能信号和移位寄存器使能信号均设置为有效,反之,设置为无效;待8位数据位接收完毕后,进入s3状态,此时仍然使能波特率采样计时信号cnt_en,判断采样信号sinpling_signal是否有效,若有效,设置计数器使能信号有效,反之,设置无效。待计数器记到10时,进入s3状态,串口发送的11位信号接收完毕标志信号done拉高,此时仍然使能波特率采样计时信号cnt_en,判断sinpling_signal是否有效,若有效设置计数器使能信号有效,反之,设置无效。然后进入s0空闲状态,等待下一次数据接收。

6、verilog描述

接下来就可以使用verilog语言描述上述电路:

module RECEIVE_MODULE(input clk_in,input rst,input rx_pin_in,output [7:0] rx_data,output reg done,output clk_50m);//wire clk_50m; clk_1 clk_1 (.CLKIN_IN(clk_in), .CLKFX_OUT(clk_50m), .CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT), .CLK0_OUT(CLK0_OUT));parameter s0 = 'b00001;parameter s1 = 'b00010;parameter s2 = 'b00100;parameter s3 = 'b01000;parameter s4 = 'b10000;reg[4:0]current_state = 'd0;reg[4:0]next_state = 'd0;//wire start;reg en_b;reg cnt_en;wire simpling_signal;reg load_c;reg en_c;reg en_a;reg load_a;wire [3:0]count;/always @(posedge clk_50m or negedge rst)if(!rst)current_state <= s0;elsecurrent_state <= next_state;/always @(*)case(current_state)s0:beginif(start)next_state = s1;elsenext_state = s0;ends1:begin///起始位if(count == 'd1)next_state = s2;elsenext_state = s1;ends2:begin///数据位if(count == 'd9)next_state = s3;elsenext_state = s2;ends3:beginif(count == 'd10)next_state = s4;elsenext_state = s3;ends4:beginif(count == 'd0)next_state = s0;elsenext_state = s4;enddefault:next_state = s0;endcase/always @(*)case(current_state)s0:beginen_b = 'd1;load_a = 'd1;load_c = 'd1;cnt_en = 'd0;en_a = 'd0;en_c = 'd0;done = 'd0;ends1:beginen_b = 'd1;load_a = 'd0;load_c = 'd1;cnt_en = 'd1;en_c = 'd0;done = 'd0;if(simpling_signal)beginen_a = 'd1;endelsebeginen_a = 'd0;endends2:beginen_b = 'd0;load_a = 'd0;load_c = 'd0;cnt_en = 'd1;done = 'd0;if(simpling_signal)beginen_c = 'd1;en_a = 'd1;endelsebeginen_c = 'd0;en_a = 'd0;endends3:beginen_b = 'd0;load_a = 'd0;load_c = 'd0;cnt_en = 'd1;en_c = 'd0;done = 'd0;if(simpling_signal)en_a = 'd1;elseen_a = 'd0;ends4:beginen_b = 'd0;load_a = 'd0;load_c = 'd0;cnt_en = 'd1;en_c = 'd0;done = 'd1;if(simpling_signal)en_a = 'd1;elseen_a = 'd0;enddefault:beginen_b = 'd0;load_a = 'd0;load_c = 'd0;cnt_en = 'd0;en_a = 'd0;en_c = 'd0;done = 'd0;endendcase// Instantiate the moduleedge_detection edge_detection (.clk_50m(clk_50m), .en_b(en_b), .rx_pin_in(rx_pin_in), .start(start));// Instantiate the moduleBPS_TIMER BPS_TIMER (.clk_50m(clk_50m), .cnt_en(cnt_en), .simpling_signal(simpling_signal));// Instantiate the moduleright_shifter right_shifter (.clk_50m(clk_50m), .load_c(load_c), .en_c(en_c), .rx_pin_in(rx_pin_in), .rx_data(rx_data));// Instantiate the moduleCOUNT_NUM COUNT_NUM (.clk_50m(clk_50m), .load_a(load_a), .en_a(en_a), .count(count));endmodule

边沿检测模块代码:

module edge_detection(input clk_50m,input en_b,input rx_pin_in,output start);reg rx_pin_in_1 = 'd0;always @(posedge clk_50m)if(en_b)rx_pin_in_1 <= rx_pin_in;elserx_pin_in_1 <= rx_pin_in_1;assign start = (!rx_pin_in) & rx_pin_in_1;endmodule

波特率定时模块代码:

由于使用的时钟是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

右移寄存器模块代码:

//此模块用于将串行发送的数据转成并行接收//module right_shifter(input clk_50m,input load_c,input en_c,input rx_pin_in,output reg[7:0]rx_data);always @(posedge clk_50m)if(load_c)rx_data <= 'd0;else if(en_c)rx_data <= {rx_pin_in,rx_data[7:1]};elserx_data <= rx_data;endmodule

计数器模块代码:

module COUNT_NUM(input clk_50m,input load_a,input en_a,output reg [3:0]count);always @(posedge clk_50m)if(load_a)count <= 'd0;else if(en_a)beginif(count == 'd10)count <= 'd0;elsecount <= count + 'd1;endelsecount <= count;endmodule

仿真激励文件代码:

module test;// Inputsreg clk_in;reg rst;reg rx_pin_in = 'd1;// Outputswire [7:0] rx_data;wire done;wire clk_50m;// Instantiate the Unit Under Test (UUT)RECEIVE_MODULE uut (.clk_in(clk_in), .rst(rst), .rx_pin_in(rx_pin_in), .rx_data(rx_data), .done(done),.clk_50m(clk_50m));initial begin// Initialize Inputsclk_in = 0;rst = 0;rx_pin_in = 1;// Wait 100 ns for global reset to finish#100;// Add stimulus hereendreg clk1= 'd1;always #5 clk_in = ~clk_in;reg [15:0]cnt = 'd0;always @(posedge clk_50m)if(cnt>0)rst <= 'd1;elserst <= rst;always @(posedge clk_50m)if(cnt == 'd60000)/'d57287cnt <= 'd0;elsecnt <= cnt + 'd1;always @(posedge clk_50m)if(cnt>'d10&&cnt<='d5208)rx_pin_in <= 'd0;//1else if(cnt>='d5208&&cnt<='d10415)rx_pin_in <= 'd1;//2else if(cnt>='d10416&&cnt<='d15623)rx_pin_in <= 'd0;3else if(cnt>='d15624&&cnt<='d20831)rx_pin_in <= 'd1;///4else if(cnt>='d20832&&cnt<='d26039)rx_pin_in <= 'd0;5else if(cnt>='d26040&&cnt<='d31247)rx_pin_in <= 'd1;///6else if(cnt>='d31248&&cnt<='d36455)rx_pin_in <= 'd0;///7else if(cnt>='d36456&&cnt<='d41663)rx_pin_in <= 'd1;///8else if(cnt>='d41664&&cnt<='d46871)rx_pin_in <= 'd1;///9else if(cnt>='d46872&&cnt<='d52079)rx_pin_in <= 'd1;///10else if(cnt>='d52080&&cnt<='d57287)rx_pin_in <= 'd1;///11endmodule

Isim行为仿真结果:

图7 仿真结果

从仿真波形中可以看出,当串行发送信号01010101111时,在done信号拉高时接收数据完毕,显示接收的数据是11010101。因此,从仿真上看串口的接收可以正常工作。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。