基于FPGA的UART串口发送模块

本文基于FPGA实现了UART串口发送模块。
开发环境:Quartus17.1、Modelsim SE-64 10.2c、友善调试助手、Gvim编辑器
硬件:小梅哥AC6102_V2开发板

关于UART介绍参考了别人的csdn博客,博客地址--------UART介绍

1.什么是UART

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UATR,是一种异步收发传输器。将数据由串行通信与并行通信间做传输转换,作为并行输入称为串行输出的芯片。UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。

2.UART通信协议

UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中各位的意义如下:
起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
资料位:紧接着起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
奇偶校验位:资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

基于FPGA的UART串口发送模块_第1张图片

3.UART工作原理

发送数据过程:空闲状态,线路处于高电平;当收到发送指令后,拉低线路的一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位,一帧数据发送完成。
数据接收过程:空闲状态,线路处于高电平;当检测到线路的下降沿(高电平变为低电平)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备接收数据或存入缓冲。
  由于UART是异步传输,没有传输同步时钟,为了保证数据的正确性,UART采用16倍数据波特率的时钟进行采样。每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误吗。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。
  UART的接收数据时序为:当检测到数据的下降沿时,表明线路上有数据进行传输,这是计数器CNT开始计数,当计数器为24=16+8时,采样的值为第0位数据;当计数器的值为40时,采样的值为第一位数据,依次类推,进行后面6个数据的采样。如果需要进行奇偶校验,则当计数器的值为152时,采样的值即为奇偶位;当计数器的值为168时,采样的值为“1”表示停止位,数据接收完成。

一个标准的10位异步串行通信协议(1个起始位、1个停止位和8个数据位)收发时序,如下图所示:
基于FPGA的UART串口发送模块_第2张图片

4.verilog实现UART发送模块

1.信号定义
  下面列出比较重要的几个输入输出信号
①clk-------------(输入)系统时钟,开发板50M晶振,可以通过FPGA锁相环产生。
②baut_set-----(输入)波特率设置,设置波特率大小
③data_byte—(输入)要发送的八位一字节数据
④send_en-----(输入)发送使能信号,高电平有效,持续一个时钟脉冲
⑤rs_232_tx—(输出)uart串行输出
⑥uart_state—(输出)uart状态指示,1为繁忙,0位空闲
2.功能实现
现在我们要做的就是通过定义的信号来实现UART发送功能。时序图如下:

①检测到send_en发送使能信号,进入发送状态,定义一个寄存器flag_send来指示,1表示繁忙,0表示空闲。
②在flag_send为1的条件内,使用两个计数器,一个计数生成bps_clk,一个计数表示要发送数据的第几位。
③发送完停止位以后,flag_send置为0。

下面是发送模块的verilog代码:

module  uart_tx(
        input                   clk                             ,//50M
        input                   rst_n                           ,
        input         [ 7: 0]   data_byte                       ,
        input                   send_en                         ,
        input         [ 3: 0]   baud_set                        ,
        output  reg             rs232_tx                        ,
        output  reg             tx_done                         ,
        output  wire            uart_state 
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
localparam                      BEGIN_BIT       =   1'b0        ;
localparam                      STOP_BIT        =   1'b1        ;
localparam                      DATA_BITS       =   10          ;
localparam                      BAUD_RATE_9600  =   5208        ;
localparam                      BAUD_RATE_19200 =   2604        ;
localparam                      BAUD_RATE_38400 =   1302        ;
localparam                      BAUD_RATE_115200=   434         ;

reg     [12: 0]                 BAUD_RATE                       ;

reg                             bps_clk                         ;
reg     [ 7: 0]                 data_byte_reg                   ;

reg                             flag_send                       ;

wire    [10: 0]                 tx_data_temp                    ;

reg     [19: 0]                 cnt0                            ;
wire                            add_cnt0                        ;
wire                            end_cnt0                        ;

reg     [ 3: 0]                 cnt1                            ;
wire                            add_cnt1                        ;
wire                            end_cnt1                        ;



//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
//data_byte_reg,寄存data_byte
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        data_byte_reg   <=  8'd0;
    end
    else if(send_en)begin
        data_byte_reg   <=  data_byte;
    end
end

//BAUD_RATE
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        BAUD_RATE   <= BAUD_RATE_9600; 
    end
    else begin
        case(baud_set)
            4'd0:   BAUD_RATE   <= BAUD_RATE_9600;
            4'd1:   BAUD_RATE   <= BAUD_RATE_19200;
            4'd2:   BAUD_RATE   <= BAUD_RATE_38400;
            4'd3:   BAUD_RATE   <= BAUD_RATE_115200;
            default:BAUD_RATE   <= BAUD_RATE_9600;
        endcase
    end
end

//flag_send
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flag_send   <=  1'b0;
    end
    else if(send_en)begin
        flag_send   <=  1'b1;
    end
    else if(tx_done)begin
        flag_send   <=  1'b0;
    end
end

//cnt0
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
    else begin
        cnt0 <= 0;
    end
end

assign  add_cnt0        =       flag_send;
assign  end_cnt0        =       add_cnt0 && cnt0 == BAUD_RATE-1;

//cnt1
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end

assign  add_cnt1        =       end_cnt0;
assign  end_cnt1        =       add_cnt1 && cnt1 == DATA_BITS-1;

//tx_done
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        tx_done <=  1'b0;
    end
    else if(end_cnt1)begin
        tx_done <=  1'b1;
    end
    else begin
        tx_done <=  1'b0;
    end
end

//bps_clk
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        bps_clk <=  1'b0;
    end
    else if(cnt0 == 1'b1)begin
        bps_clk <=  1'b1;
    end
    else begin
        bps_clk <=  1'b0;
    end
end

//tx_data_temp
assign  tx_data_temp    =   {STOP_BIT, data_byte_reg, BEGIN_BIT };

//rs232_tx 串行发送数据
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        rs232_tx    <=  1'b1;
    end
    else if(flag_send && bps_clk)begin
        rs232_tx    <=  tx_data_temp[cnt1]; 
    end
    else if(!flag_send)begin
        rs232_tx    <=  1'b1;
    end
end

//uart_state
assign  uart_state  =   flag_send;

endmodule

下面是整个uart串口发送模块的整个工程,可以通过按键发送,同时在串口调试软件上收到
下载链接:https://download.csdn.net/download/qq_31348733/11249021

你可能感兴趣的:(基于FPGA的UART串口发送模块)