uart串口通信

Posted liujiahong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uart串口通信相关的知识,希望对你有一定的参考价值。

以下知识是通过学习小梅哥FPGA后所总结的思路

USBTTL电路图:

技术图片

 

UART发送端发送一个字节数据时序图:

 

 技术图片

 

对于其中的每一位进行采样一般情况下每一位数据的中间点是最稳定的,因此一般应用中,采集中间时刻时的数据即可,下图所示:

技术图片

但是在工业应用中,往往有非常强的电磁干扰,只采一次就作为该数据的电平判定,是不保险的,有可能恰好采集到干扰的信号而导致结果出错,因此需要使用多次采样概率的方式进行。以下为改进型的单bit数据接收方式示意图:

 

 

 

技术图片

       在这张图中,将每一位数据又平均分成了16小段对于Bit_x这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据有可能不稳定的用红色标出的两段),这两个时间段采集数据,很有可能得到错误的结果,因此两段时间的电平无效,采集时直接忽略。中间这一时间段绿色标出),数据本身比较稳定的,一般都代表了正确的结果。但是也不排除该段数据受强电磁干扰而出现错误的电平脉冲,因此这一段电平,进行多次采样,求高低电平发生的概率,6次采集结果中,取出现次数多的电平作为采样结果。例如,采样6的结果分别为1/1/1/1/0/1/取电平结果为1若为0/0/1/0/0/0,则取电平结果为06采样结果10各占一半(3则可判断当前通信线路环境非常恶劣,数据不具有可靠性。

 

串口发送模块包含两个主要组件:

1、 起始位检测进程

2、 波特率产生模块

3、 数据接收进程

 

串口接收模块整体结构体:

技术图片

 

                    串口发送模块详细结构图

 

 

 

 

波特率时钟计算:

 

系统时钟周期为System_clk_period

9600

104167ns

104167/ System_clk_period/16

325-1

19200

52083ns

52083/ System_clk_period/16

163-1

38400

26041ns

26041/ System_clk_period/16

81-1

57600

17361ns

17361/ System_clk_period/16

54-1

115200

8680ns

8680/ System_clk_period/16

27-1

 

 

 

//串口接收模块
module uart_byte_tx(
    Clk,
    Rst_n,
    data_byte,
    send_en,
    baud_set,
    
    Rs232_Tx,
    Tx_Done,
    uart_state
);

    input Clk;
    input Rst_n;
    input [7:0]data_byte;
    input send_en;                    //发送标志信号位
    input [2:0]baud_set;            //波特率设定
    
    output reg Rs232_Tx;
    output reg Tx_Done;                //串口发送完成标志
    output reg uart_state;                
    
    reg bps_clk;    //波特率时钟周期
    
    reg [15:0]div_cnt;//分频计数器:每计数满一轮,即计数满一个波特率周期,
                      //  即div_cnt = bps_DR 时该寄存器则清零重新计数
    
    reg [15:0]bps_DR;//分频计数最大值:可方便随时更换发送波特率
    
    reg [3:0]bps_cnt;//波特率时钟周期计数器:用于判断此时该发送到第几位数据
    
    reg [7:0]r_data_byte;   //发送的数据寄存器
    
    localparam START_BIT = 1b0;
    localparam STOP_BIT = 1b1;
    
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        uart_state <= 1b0;
    else if(send_en)
        uart_state <= 1b1;
    else if(bps_cnt == 4d11)
        uart_state <= 1b0;
    else
        uart_state <= uart_state;
    
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        r_data_byte <= 8d0;
    else if(send_en)
        r_data_byte <= data_byte;
    else
        r_data_byte <= r_data_byte;
    
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        bps_DR <= 16d5207;
    else begin
        case(baud_set)                //不同波特率所对应的分频计数最大值
            0:bps_DR <= 16d5207;
            1:bps_DR <= 16d2603;
            2:bps_DR <= 16d1301;
            3:bps_DR <= 16d867;
            4:bps_DR <= 16d433;
            default:bps_DR <= 16d5207;            
        endcase
    end    
    
    //counter
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        div_cnt <= 16d0;
    else if(uart_state)begin
        if(div_cnt == bps_DR)
            div_cnt <= 16d0;
        else
            div_cnt <= div_cnt + 1b1;
    end
    else
        div_cnt <= 16d0;
    
    // bps_clk gen
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        bps_clk <= 1b0;
    else if(div_cnt == 16d1)
        bps_clk <= 1b1;
    else
        bps_clk <= 1b0;
    
    //bps counter
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)    
        bps_cnt <= 4d0;
    else if(bps_cnt == 4d11)
        bps_cnt <= 4d0;
    else if(bps_clk)
        bps_cnt <= bps_cnt + 1b1;
    else
        bps_cnt <= bps_cnt;
        
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        Tx_Done <= 1b0;
    else if(bps_cnt == 4d11)
        Tx_Done <= 1b1;
    else
        Tx_Done <= 1b0;
        
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        Rs232_Tx <= 1b1;
    else begin
        case(bps_cnt)                    //注意:在一个波特率周期里是一直在发送数据
            0:Rs232_Tx <= 1b1;            //bps_cnt的值会持续一个波特率周期才会改变
            1:Rs232_Tx <= START_BIT;    //一个波特率周期 = 发送一个bit位数据的时间 
            2:Rs232_Tx <= r_data_byte[0];
            3:Rs232_Tx <= r_data_byte[1];
            4:Rs232_Tx <= r_data_byte[2];
            5:Rs232_Tx <= r_data_byte[3];
            6:Rs232_Tx <= r_data_byte[4];
            7:Rs232_Tx <= r_data_byte[5];
            8:Rs232_Tx <= r_data_byte[6];
            9:Rs232_Tx <= r_data_byte[7];
            10:Rs232_Tx <= STOP_BIT;
            default:Rs232_Tx <= 1b1;
        endcase
    end    

endmodule

 

 

 

 

//串口接收模块
module uart_byte_rx(
    Clk,
    Rst_n,
    baud_set,
    Rs232_Rx,
    
    data_byte,
    Rx_Done
);

    input Clk;
    input Rst_n;
    input [2:0]baud_set;
    input Rs232_Rx;
    
    output reg [7:0]data_byte;
    output reg Rx_Done;
    
    reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器
    
    reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//数据寄存器
    
    reg [15:0]bps_DR;//分频计数器计数最大值
    reg [15:0]div_cnt;//分频计数器
    reg bps_clk;//
    reg [7:0]bps_cnt;
    
    reg uart_state;
    
    reg [2:0] r_data_byte [7:0];        //定义8个位宽为3的寄存器
    reg [7:0] tmp_data_byte;            
    reg [2:0] START_BIT,STOP_BIT;        //起始位和停止位
    
    wire nedege;
    
    //同步寄存器,消除亚稳态
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        s0_Rs232_Rx <= 1b0;
        s1_Rs232_Rx <= 1b0;    
    end
    else begin
        s0_Rs232_Rx <= Rs232_Rx;
        s1_Rs232_Rx <= s0_Rs232_Rx;    
    end
    
    //数据寄存器
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        tmp0_Rs232_Rx <= 1b0;
        tmp1_Rs232_Rx <= 1b0;    
    end
    else begin
        tmp0_Rs232_Rx <= s1_Rs232_Rx;
        tmp1_Rs232_Rx <= tmp0_Rs232_Rx;    
    end
    
    assign nedege = !tmp0_Rs232_Rx & tmp1_Rs232_Rx;
    
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        bps_DR <= 16d324;
    else begin
        case(baud_set)
            0:bps_DR <= 16d324;
            1:bps_DR <= 16d162;
            2:bps_DR <= 16d80;
            3:bps_DR <= 16d53;
            4:bps_DR <= 16d26;
            default:bps_DR <= 16d324;            
        endcase
    end
    
    //counter
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        div_cnt <= 16d0;
    else if(uart_state)begin
        if(div_cnt == bps_DR)
            div_cnt <= 16d0;
        else
            div_cnt <= div_cnt + 1b1;
    end
    else
        div_cnt <= 16d0;
        
    // bps_clk gen
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        bps_clk <= 1b0;
    else if(div_cnt == 16d1)
        bps_clk <= 1b1;
    else
        bps_clk <= 1b0;
    
    //bps counter
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)    
        bps_cnt <= 8d0;
    else if(bps_cnt == 8d159 | (bps_cnt == 8d12 && (START_BIT > 2)))
        bps_cnt <= 8d0;
    else if(bps_clk)
        bps_cnt <= bps_cnt + 1b1;
    else
        bps_cnt <= bps_cnt;

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        Rx_Done <= 1b0;
    else if(bps_cnt == 8d159)
        Rx_Done <= 1b1;
    else
        Rx_Done <= 1b0;
        
//    always@(posedge Clk or negedge Rst_n)
//    if(!Rst_n)
//        data_byte <= 8‘d0;
//    else if(bps_cnt == 8‘d159)
//        data_byte <= tmp_data_byte;
//    else
//        data_byte <= data_byte;
        
        
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        data_byte <= 8d0;
    else if(bps_cnt == 8d159)begin
        data_byte[0] <= r_data_byte[0][2];  //r_data_byte[0]寄存器位宽为3,  000=0 , 001=1 ,010=2 ,011=3  
        data_byte[1] <= r_data_byte[1][2];                                //     100=4 , 101=5 ,110=6    ,111=7    
        data_byte[2] <= r_data_byte[2][2];    //可以发现当r_data_byte[0]寄存器的值>3时,data_byte[0]寄存器所接收的值为1,    
        data_byte[3] <= r_data_byte[3][2];    //而r_data_byte[0][2]的值恰好都=1。所以此处取r_data_byte[0]寄存器的的二位值作为接收到的有效数据    
        data_byte[4] <= r_data_byte[4][2];        
        data_byte[5] <= r_data_byte[5][2];        
        data_byte[6] <= r_data_byte[6][2];
        data_byte[7] <= r_data_byte[7][2];
    end    
        
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        START_BIT = 3d0;
        r_data_byte[0] <= 3d0;
        r_data_byte[1] <= 3d0;
        r_data_byte[2] <= 3d0;
        r_data_byte[3] <= 3d0;
        r_data_byte[4] <= 3d0;
        r_data_byte[5] <= 3d0;
        r_data_byte[6] <= 3d0;
        r_data_byte[7] <= 3d0;
        STOP_BIT = 3d0;
    end
    else if(bps_clk)begin
        case(bps_cnt)
            0:begin
                    START_BIT = 3d0;
                    r_data_byte[0] <= 3d0;
                    r_data_byte[1] <= 3d0;
                    r_data_byte[2] <= 3d0;
                    r_data_byte[3] <= 3d0;
                    r_data_byte[4] <= 3d0;
                    r_data_byte[5] <= 3d0;
                    r_data_byte[6] <= 3d0;
                    r_data_byte[7] <= 3d0;
                    STOP_BIT = 3d0;            
                end                        //用序列机的方法接收数据,并且采样6次数据的值累加存在r_data_byte寄存器
            6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
            22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;        //采样6次数据,如果6次数据的和大于3,则表示所采集的该bit位的数据位1,
            38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;        //如果如果6次数据的和小于3,则表示所采集的该bit位的数据位0,
            54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;        //如果如果6次数据的和等于3,则表示所采集的该bit位的数据位无效,
            70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
            86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
            102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
            118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
            134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
            150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
            default:
                begin
                    START_BIT = START_BIT;
                    r_data_byte[0] <= r_data_byte[0];
                    r_data_byte[1] <= r_data_byte[1];
                    r_data_byte[2] <= r_data_byte[2];
                    r_data_byte[3] <= r_data_byte[3];
                    r_data_byte[4] <= r_data_byte[4];
                    r_data_byte[5] <= r_data_byte[5];
                    r_data_byte[6] <= r_data_byte[6];
                    r_data_byte[7] <= r_data_byte[7];
                    STOP_BIT = STOP_BIT;                        
                end
        endcase
    end
    
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        uart_state <= 1b0;
    else if(nedege)
        uart_state <= 1b1;
    else if(Rx_Done || (bps_cnt == 8d12 && (START_BIT > 2)))
        uart_state <= 1b0;
    else
        uart_state <= uart_state;        

endmodule

 

以上是关于uart串口通信的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式Linux裸机开发——UART串口通信

进阶项目UART串口通信程序设计讲解

树莓派使用uart串口与串口设备通信

arduino(esp32c3) uart通信问题 Serial.write发送到哪了?

STM32学习笔记 二基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发

MicroPython+ESP8266:UART串口通信