国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

FPGA入門 —— FPGA UART 串口通信

這篇具有很好參考價值的文章主要介紹了FPGA入門 —— FPGA UART 串口通信。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

FPGA入門 —— FPGA UART 串口通信

串口簡介

UART 通用異步收發(fā)傳輸器( Universal Asynchronous Receiver/Transmitter) ,通常稱作 UART。 UART 是一種通用的數(shù)據(jù)通信協(xié)議,也是異步串行通信口(串口)的總稱,它在發(fā)送數(shù)據(jù)時將并行數(shù)據(jù)轉(zhuǎn)換成串行數(shù)據(jù)來傳輸,在接收數(shù)據(jù)時將接收到的串行數(shù)據(jù)轉(zhuǎn)換成并行數(shù)據(jù)。 它包括了ch340、 RS232、 RS499、 RS423、 RS422 和 RS485 等接口標準規(guī)范和總線標準規(guī)范

串口作為常用的三大低速總線(UART、 SPI、 IIC)之一,在設(shè)計眾多通信接口和調(diào)試時占有重要地位。但 UART 和 SPI、 IIC 不同的是,它是異步通信接口,異步通信中的接收方并不知道數(shù)據(jù)什么時候會到達,所以雙方收發(fā)端都要有各自的時鐘,在數(shù)據(jù)傳輸過程中是不需要時鐘的,發(fā)送方發(fā)送的時間間隔可以不均勻,接受方是在數(shù)據(jù)的起始位和停止位的幫助下實現(xiàn)信息同步的。而 SPI、 IIC 是同步通信接口,同步通信中雙方使用頻率一致的時鐘,在數(shù)據(jù)傳輸過程中時鐘伴隨著數(shù)據(jù)一起傳輸,發(fā)送方和接收方使用的時鐘都是由主機提供的

UART 通信只有兩根信號線,一根是發(fā)送數(shù)據(jù)端口線叫 tx(Transmitter),一根是接收數(shù)據(jù)端口線叫 rx(Receiver),對于 PC 來說它的 tx 要和對于 FPGA 來說的 rx 連接,同樣 PC 的 rx 要和 FPGA 的 tx 連接,如果是兩個 tx 或者兩個 rx 連接那數(shù)據(jù)就不能正常被發(fā)送出去和接收到,所以不要弄混,記住 rx 和 tx 都是相對自身主體來講的。UART 可以實現(xiàn)全雙工,即可以同時進行發(fā)送數(shù)據(jù)和接收數(shù)據(jù)

串口回環(huán)模塊如下圖所示,使用電腦上位機串口模塊完成對 FPGA 串口模塊的環(huán)回實驗

fpga 串口,fpga開發(fā)

串口時序

下面我們來看一下 RS232 協(xié)議:

  1. RS232 是 UART 的一種,沒有時鐘線,只有兩根數(shù)據(jù)線,分別是 rx 和 tx,這兩根線都是 1bit 位寬的。其中 rx 是接收數(shù)據(jù)的線, tx 是發(fā)送數(shù)據(jù)的線

  2. rx 位寬為 1bit, PC 機通過串口調(diào)試助手往 FPGA 發(fā) 8bit 數(shù)據(jù)時, FPGA 通過串口線rx 一位一位地接收,從最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特數(shù)據(jù),也就是我們常說的串轉(zhuǎn)并

  3. tx 位寬為 1bit, FPGA 通過串口往 PC 機發(fā) 8bit 數(shù)據(jù)時, FPGA 把 8bit 數(shù)據(jù)通過 tx線一位一位的傳給 PC 機,從最低位到最高位依次發(fā)送,最后上位機通過串口助手按照RS232 協(xié)議把這一位一位的數(shù)據(jù)位拼接成 8bit 數(shù)據(jù),并行數(shù)據(jù)轉(zhuǎn)換成串行數(shù)據(jù)進行發(fā)送

  4. 串口數(shù)據(jù)的發(fā)送與接收是基于幀結(jié)構(gòu)的,即一幀一幀的發(fā)送與接收數(shù)據(jù)。每一幀除了中間包含 8bit 有效數(shù)據(jù)外,還在每一幀的開頭都必須有一個起始位,且固定為 0;在每一幀的結(jié)束時也必須有一個停止位,且固定為 1,即最基本的幀結(jié)構(gòu)(不包括校驗等)有10bit。在不發(fā)送或者不接收數(shù)據(jù)的情況下, rx 和 tx 處于空閑狀態(tài),此時 rx 和 tx 線都保持高電平,如果有數(shù)據(jù)幀傳輸時,首先會有一個起始位,然后是 8bit 的數(shù)據(jù)位,接著有 1bit的停止位,然后 rx 和 tx 繼續(xù)進入空閑狀態(tài),然后等待下一次的數(shù)據(jù)傳輸

rs232時序圖入下圖所示:

fpga 串口,fpga開發(fā)
  1. 波特率:在信息傳輸通道中,攜帶數(shù)據(jù)信息的信號單元叫碼元(因為串口是 1bit 進行傳輸?shù)?,所以其碼元就是代表一個二進制數(shù)), 每秒鐘通過信號傳輸?shù)拇a元數(shù)稱為碼元的傳輸速率,簡稱波特率,常用符號“Baud”表示,其單位為“波特每秒(Bps)”。串口常見的波特率有 4800、9600、 115200 等

  2. 比特率:每秒鐘通信信道傳輸?shù)男畔⒘糠Q為位傳輸速率,簡稱比特率,其單位為“每秒比特數(shù)(bps)”。比特率可由波特率計算得出,公式為:比特率=波特率 * 單個調(diào)制狀態(tài)對應(yīng)的二進制位數(shù)。如果使用的是 115200 的波特率,其串口的比特率為: 115200Bps *1bit= 115200bps

  3. 由計算得串口發(fā)送或者接收 1bit 數(shù)據(jù)的時間為一個波特,即 1/9600 秒,如果用 50MHz(周期為 20ns)的系統(tǒng)時鐘來計數(shù),需要計數(shù)的個數(shù)為 cnt = (1s * 10^9)ns /115200bit)ns / 20ns ≈434 個系統(tǒng)時鐘周期,即每個 bit 數(shù)據(jù)之間的間隔要在 50MHz 的時鐘頻率下計數(shù) 434 次

串口模塊設(shè)計

我們先繪制一下串口回環(huán)的整個模塊圖:

fpga 串口,fpga開發(fā)

由整個回環(huán)模塊圖,我們可以看出在整個回環(huán)實驗中,我們需要再設(shè)計兩個模塊,一個接收模塊和一個發(fā)送模塊。

接收模塊

先看一下接收模塊的波形圖:

fpga 串口,fpga開發(fā)

輸入信號為系統(tǒng)時鐘、復(fù)位信號以及接收信號;為了消除跨時鐘域所帶來的的亞穩(wěn)態(tài)現(xiàn)象,需要對 rx 信號進行打 3 拍操作,跨時鐘域處理推薦是 3 拍,當然有些人覺得 2 拍足矣,只要驗證沒問題,這個你隨意。

進行完打拍操作后,需要取第三拍的下降沿操作,也就是 ~rx_reg2 & rx_reg3 ,至于為何這樣,可以參考邊沿檢測部分。

得到 start_flag 信號之后,就可以進行一個 rx 的接收了,本次接收模塊使用的波特率為 115200,系統(tǒng)時鐘為 50M,所以波特率為 50_000_000/115200=434,當然這里波特率也可以根據(jù)需要進行修改

為了每位數(shù)據(jù)的接收的穩(wěn)定,所以可以數(shù)據(jù)的標志信號可以在波特率信號的中間位置進行拉高。然后將每 bit 的數(shù)據(jù)都拼接到我們接收數(shù)據(jù)的最高位,以此類推,接收到的第8位數(shù)據(jù),就拼接在了 rx_data 的最高位,最先接收的數(shù)據(jù)就存儲在了 rx_data 的最低位

接收完8位數(shù)據(jù)之后,我們的接收標志信號拉高,表示已經(jīng)完成一次8位數(shù)據(jù)的接收。在接收完成標志位拉高后,將我們寄存的串轉(zhuǎn)并的數(shù)據(jù)賦值 po_data,同樣 po_flag 拉高一個周期

根據(jù)波形圖與文字分析,可以編寫代碼如下:

UART_recv.v

module UART_recv 
#(
    parameter  CLK         =   26'd50000000    ,    // 時鐘頻率
    parameter  BAUD        =   17'd115200           // 波特率
)
(
    input   wire            clk         ,
    input   wire            rstn        ,
    input   wire            UART_rx     ,

    output  reg             flag_out    ,   // 數(shù)據(jù)接收完成標志位,既發(fā)送開始標志位
    output  reg    [7 : 0]  data_out        // 接收的數(shù)據(jù)
);

    localparam Baud_Clk     =   CLK/BAUD       ;    // 傳輸每個 Baud 需要的時鐘數(shù)

    reg             rx_en       ;   // 接收使能
    reg             start_flag  ;   // 開始接收標志
    reg             flag_rx     ;   // 接收標志位,半個時鐘周期為 1 ,用于判斷數(shù)據(jù)已經(jīng)全部接收完成
    reg             flag_bit    ;   // 比特標志位,采用下降沿發(fā)送
    reg             rx_reg1     ;   // 接收寄存器1,同步打拍(打一拍延時一個時鐘周期)
    reg             rx_reg2     ;   // 接收寄存器2
    reg             rx_reg3     ;   // 接收寄存器3,取第三拍下降沿進行邊緣檢測
    reg [8 : 0]     cnt_baud    ;   // 波特率計數(shù)器
    reg [7 : 0]     data_rx     ;   // 接收數(shù)據(jù)寄存器
    reg [3 : 0]     cnt_bit     ;   // 比特計數(shù)器

    // 打三拍
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            rx_reg1 <= 1'b1;
            rx_reg2 <= 1'b1; 
            rx_reg3 <= 1'b1;
        end
        else begin
            rx_reg1 <= UART_rx;
            rx_reg2 <= rx_reg1;
            rx_reg3 <= rx_reg2;
        end
    end

    // 檢測第三拍下降沿,用作數(shù)據(jù)接收信號
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            start_flag <= 1'b0;
        end
        // 判斷第三拍下降沿
        else if(rx_reg3 && ~rx_reg2) begin
            start_flag <= 1'b1;
        end
        else begin
            start_flag <= 1'b0;
        end
    end

    // 接收使能
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            rx_en <= 1'b0;
        end
        // 下降沿接收
        else if(start_flag == 1'b1) begin
            rx_en <= 1'b1;
        end
        // 接收完成,輸入只需要判斷到數(shù)據(jù)位最后一位,輸出則需要判斷完整輸出
        else if(cnt_bit == 4'd8 && flag_bit == 1'b1) begin
            rx_en <= 1'b0;
        end
    end

     // 波特計數(shù)器
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_baud <= 9'd0;
        end
        // 傳輸完成所有波特或者使能失效,表示接收結(jié)束
        else if(cnt_baud == Baud_Clk - 1'b1 || rx_en == 1'b0) begin
            cnt_baud <= 9'd0;
        end
        // 只有輸入使能才能計數(shù)
        else if(rx_en == 1'b1) begin
            cnt_baud <= cnt_baud + 9'd1;
        end
    end

    // 比特標志位
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            flag_bit <= 1'b0;
        end
        // 半個周期反轉(zhuǎn)一次,輸入一個bit需要兩個時鐘周期,輸出需要三個
        else if(cnt_baud == Baud_Clk/2 - 1'b1) begin
            flag_bit <= 1'b1;
        end
        else begin
            flag_bit <= 1'b0;
        end
    end

    // 比特計數(shù)器
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_bit <= 4'd0;
        end
        // 輸入判斷完成
        else if(cnt_bit == 4'd8 && flag_bit == 1'b1) begin
            cnt_bit <= 4'd0;
        end
        // 前面判斷了輸入使能失效,無法進行波特計數(shù)
        else if(flag_bit == 1'b1) begin
            cnt_bit <= cnt_bit + 4'd1;
        end
    end

    // 接收數(shù)據(jù)
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            data_rx <= 8'd0;
        end
        // 只要開始接收,就開始存儲數(shù)據(jù),1-8為數(shù)據(jù)位,解析輸入數(shù)據(jù),從最低位向最高位輸入
        else if(cnt_bit >= 4'd1 && cnt_bit <= 4'd8 && flag_bit == 1'b1) begin
            data_rx <= {rx_reg3,data_rx[7:1]};
        end
    end

    // 接收標志位
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            flag_rx <= 1'b0;
        end
        // 數(shù)據(jù)接收完成,輸出半個時鐘周期的 1 用于數(shù)據(jù)轉(zhuǎn)存,將收到的數(shù)據(jù)再次發(fā)送出去
        else if(cnt_bit == 4'd8 && flag_bit == 1'b1) begin
            flag_rx <= 1'b1;
        end
        else begin
            flag_rx <= 1'b0;
        end
    end

    // 接收的串轉(zhuǎn)并數(shù)據(jù)
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            data_out <= 8'd0;
        end
        // 判斷數(shù)據(jù)已經(jīng)全部接收完成
        else if(flag_rx == 1'b1) begin
            data_out <= data_rx;
        end
    end

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            flag_out <= 1'b0;
        end
        else begin
            flag_out <= flag_rx;
        end
    end

endmodule //UART_recv

編寫接收模塊仿真代碼:

tb_uart_rx.v:

`timescale 1ns / 1ns
 
module tb_uart_rx();
 
reg               sys_clk     ;
reg               sys_rst_n   ;
reg               rx          ;
                              
wire      [7:0]   po_data     ;
wire              po_flag     ;
 
 
initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    #201
    sys_rst_n <= 1'b1;
end
 
always #10 sys_clk = ~sys_clk;
 
//模擬發(fā)送8次數(shù)據(jù),分別為0~7
initial begin
        #200
        rx_bit(8'd8);  //任務(wù)的調(diào)用,任務(wù)名+括號中要傳遞進任務(wù)的參數(shù)
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end
    
 
//定義一個名為rx_bit的任務(wù)
//任務(wù)以task開頭,后面緊跟的是任務(wù)名,調(diào)用時使用
task rx_bit(
    //傳遞到任務(wù)中的參數(shù),調(diào)用任務(wù)的適合從外部傳進來一個8位的值
        input   [7:0]   data
);
    
        integer i;      //定義一個常量
        
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(434*20);  //每發(fā)送1位數(shù)據(jù)時434個時鐘周期
        end
endtask
 
uart_rx
#(
    .BAUD_MAX    ('d115_200     ),
    .CLK_MAX     ('d50_000_000  )
)
uart_rx_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .rx          (rx       ),
                           
    .po_data     (po_data  ),
    .po_flag     (po_flag  )
    );
 
endmodule

打拍取沿及接收使能部分波形:

fpga 串口,fpga開發(fā)

波特計數(shù)器以及比特標志位(434/2拉高)波形:

fpga 串口,fpga開發(fā)

數(shù)據(jù)接收端以及數(shù)據(jù)接收完成標志位波形:

fpga 串口,fpga開發(fā)

發(fā)送模塊

同理按照時序圖繪制發(fā)送模塊的波形圖:

fpga 串口,fpga開發(fā)

因為設(shè)計一個串口回環(huán),發(fā)送和接收模塊的波特率是相同的,所以并沒有隊 pi_flag 信號進行打 3 拍處理。在接收信號拉高之后, tx_en 拉高,進入發(fā)送模式。

同樣波特計數(shù)器為 434,bit_flag 每次拉高,bit_cnt 計一個數(shù),第 0 位為起始位,拉低,然后進行數(shù)據(jù)的傳送,當計數(shù)到第九位時,使 tx 信號拉高,表示停止位。

同樣根據(jù)波形圖可以編寫出發(fā)送模塊的代碼:

UART_send.v:

module UART_send
#(
    parameter  CLK        =   26'd50000000    ,    // 時鐘頻率
    parameter  BAUD        =   17'd115200           // 波特率
)
(
    input   wire            clk         ,
    input   wire            rstn        ,   
    input   wire    [7 : 0] data_in     ,   // 需要發(fā)送的數(shù)據(jù)
    input   wire            flag_in     ,   // 數(shù)據(jù)接收標志位,既發(fā)送標志位

    output  reg             UART_tx         // 串口輸出位         
);

    localparam Baud_Clk     =   CLK/BAUD       ;    // 傳輸每個 Baud 需要的時鐘數(shù)

    reg             tx_en       ;   // 發(fā)送使能
    reg             flag_bit    ;   // 比特標志位,采用下降沿發(fā)送
    reg [8 : 0]     cnt_baud    ;   // 波特率計數(shù)器
    reg [3 : 0]     cnt_bit     ;   // 比特計數(shù)器

    // 發(fā)送使能
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            tx_en <= 1'b0;
        end
        // 已經(jīng)發(fā)送了十位 bit 并且到達下一個下降沿,輸入只需要判斷到數(shù)據(jù)位最后一位,輸出則需要判斷完整輸出
        else if(cnt_bit == 4'd9 && flag_bit == 1'b1) begin
            tx_en <= 1'b0;
        end
        else if(flag_in == 1'b1) begin
            tx_en <= 1'b1;
        end
    end

    // 波特計數(shù)器
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_baud <= 9'd0;
        end
        // 傳輸完成所有波特或者使能失效,表示發(fā)送結(jié)束
        else if(cnt_baud == Baud_Clk - 1'b1 || tx_en == 1'b0) begin
            cnt_baud <= 9'd0;
        end
        else begin
            cnt_baud <= cnt_baud + 9'd1;
        end
    end

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            flag_bit <= 1'b0;
        end
        // 只有剛開始發(fā)送的一瞬間會產(chǎn)生一個時鐘周期上升沿和下降沿
        else if(cnt_baud == 9'd1) begin
            flag_bit <= 1'b1;
        end
        else begin
            flag_bit <= 1'b0;
        end
    end

    // 計數(shù)10分有效數(shù)據(jù)位
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_bit <= 4'd0;
        end
        // 已經(jīng)發(fā)送了十位 bit 并且到達下一個下降沿
        else if(cnt_bit == 4'd9 && flag_bit == 1'b1) begin
            cnt_bit <= 4'd0;
        end
        // 使能有效,下降沿發(fā)送數(shù)據(jù)
        else if(flag_bit == 1'b1 && tx_en == 1'b1) begin
            cnt_bit <= cnt_bit + 4'd1;
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end

    // 滿足 RS232 協(xié)議 起始位為 0,停止位為 1,并按位輸出
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            UART_tx <= 1'd1;
        end
        // 下降沿發(fā)送數(shù)據(jù)
        else if(flag_bit == 1'b1) begin
            case (cnt_bit)
                0:       UART_tx <= 1'd0        ;
                1:       UART_tx <= data_in[0]  ;
                2:       UART_tx <= data_in[1]  ;
                3:       UART_tx <= data_in[2]  ;
                4:       UART_tx <= data_in[3]  ;
                5:       UART_tx <= data_in[4]  ;
                6:       UART_tx <= data_in[5]  ;
                7:       UART_tx <= data_in[6]  ;
                8:       UART_tx <= data_in[7]  ;
                9:       UART_tx <= 1'd1        ;
                default: UART_tx <= 1'd1        ;
            endcase
        end
    end

endmodule //UART_send

同樣進行發(fā)送代碼的仿真編寫:

tb_uart_tx.v:

`timescale 1ns / 1ns
 
module tb_uart_tx();
 
reg                sys_clk     ;
reg                sys_rst_n   ;                             
reg       [7:0]    pi_data     ;
reg                pi_flag     ;
        
wire               tx          ;
 
 
initial begin
    sys_clk = 1'b0;
    sys_rst_n <= 1'b0;
    #201
    sys_rst_n <= 1'b1;
end
 
 
always #10 sys_clk = ~sys_clk;
 
initial begin
    pi_flag <= 1'b0;
    pi_data <= 8'd0;
    #401
    pi_data <= 8'd0;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd15;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd2;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd3;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd4;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd5;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd6;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
    #(434*10*20)
    pi_data <= 8'd7;
    pi_flag <= 1'd1;
    #20
    pi_flag <= 1'd0;
end
 
uart_tx
#(
    .BAUD_MAX('d115_200     )       ,
    .CLK_MAX ('d50_000_000  )
)uart_tx_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
                           
    .pi_data     (pi_data  ),
    .pi_flag     (pi_flag  ),
                           
    .tx          (tx       )
);
 
endmodule

先看一下發(fā)送開始標志位波形:

fpga 串口,fpga開發(fā)

延遲一個時鐘周期,發(fā)送使能拉高:

fpga 串口,fpga開發(fā)

波特計數(shù)器等于 1 是,比特標志位拉高,比特位數(shù)加 1:

fpga 串口,fpga開發(fā)

如接收到的數(shù)據(jù)為 7,發(fā)送端從最低位 1110_0000 發(fā)送數(shù)據(jù) 7,注意這里的順序是先發(fā)送最低位,最后一位為最高位

例化回環(huán)模塊

最后將我們的兩個模塊例化到一起:

UART.v:

module UART (
    input   wire            clk         ,
    input   wire            rstn        ,
    input   wire            UART_rx     ,

    output  wire            UART_tx         
);

    localparam   CLK_50MHz   =   26'd50000000    ;    // 時鐘頻率
    localparam   BAUD        =   17'd115200      ;    // 波特率

    wire    [7:0]   data    ;
    wire            flag    ;

    UART_send
    #(
        .CLK         (CLK_50MHz ),// 時鐘頻率
        .BAUD        (BAUD      ) // 波特率
    )
    UART_send_init(
        .clk         (clk       ),
        .rstn        (rstn      ),   
        .data_in     (data      ),   // 需要發(fā)送的數(shù)據(jù)
        .flag_in     (flag      ),   // 數(shù)據(jù)接收標志位,既發(fā)送標志位
        .UART_tx     (UART_tx   )    // 串口輸出位         
    );

    UART_recv 
    #(
        .CLK         (CLK_50MHz ),    // 時鐘頻率
        .BAUD        (BAUD      )     // 波特率
    )
    UART_recv_init(
        .clk         (clk       ),
        .rstn        (rstn      ),   
        .UART_rx     (UART_rx   ),
        .flag_out    (flag      ),   // 數(shù)據(jù)接收完成標志位,既發(fā)送開始標志位,半個時鐘周期為 1 ,用于判斷數(shù)據(jù)已經(jīng)全部接收完成
        .data_out    (data      )    // 接收的數(shù)據(jù)
    );

endmodule //UART

燒錄效果演示

fpga 串口,fpga開發(fā)

串口發(fā)送多字節(jié)數(shù)據(jù)

這里我們采用發(fā)送多字節(jié)的方式進行發(fā)送,前面我們已經(jīng)介紹了 FPGA 如何使用超聲波并顯示在數(shù)碼管上,這里講超聲波距離數(shù)據(jù)通過串口發(fā)送到上位機,這里超聲波距離保留三位小數(shù),格式如下:xxx.xxxcm

這里我們通過數(shù)據(jù)處理后,轉(zhuǎn)為 ASCII,然后我們每次發(fā)送一位,按照我們上面給定的格式,整個數(shù)據(jù)共有 9 位,有由于我們需要每分鐘發(fā)送一次數(shù)據(jù),所以我們需要每分鐘發(fā)送 10 個數(shù)據(jù)

有些朋友可能會問,我們只有 9 位數(shù)據(jù),為什么要發(fā)送 10 位?

這是因為我們由于要使用 python 程序接收數(shù)據(jù),所以我們需要在數(shù)據(jù)最后發(fā)送換行表示一次數(shù)據(jù)發(fā)送完成

數(shù)據(jù)處理代碼如下:

UART_driver.v:

module UART_driver (
    input   wire            clk         ,
    input   wire            rstn        ,
    input   wire    [18:0]  data_in     , 
    input   wire            UART_rx     ,

    output  wire            UART_tx         
);

    localparam   CLK_50MHz   =   26'd50000000    ;    // 時鐘頻率
    localparam   BAUD        =   17'd115200      ;    // 波特率

    reg     [7:0]   data    ;
    wire            flag    ;
    wire            tx_done ;   
	wire 			flag_0	;	// 未啟動超聲波

    reg     [25:0]      cnt_clk         ;  
    reg     [3:0]       xcnt            ;
    reg     [71: 0]     data_out        ;   // 最終發(fā)送的數(shù)據(jù)

    reg		[3:0]	    cm_hund	        ;//100cm
	reg		[3:0]	    cm_ten	        ;//10cm
	reg		[3:0]	    cm_unit	        ;//1cm
	reg		[3:0]	    point_1	        ;//1mm
	reg		[3:0]	    point_2	        ;//0.1mm
	reg		[3:0]	    point_3	        ;//0.01mm

    localparam
		byte0  = "0",
		byte1  = "1",
		byte2  = "2",
		byte3  = "3",
		byte4  = "4",
		byte5  = "5",
		byte6  = "6",
		byte7  = "7",
		byte8  = "8",
		byte9  = "9",
		byte10 = "\n";


    always@(posedge clk or negedge rstn) begin
        if(!rstn)
            xcnt <= 0;
        else if(tx_done)
            xcnt <= xcnt + 1'd1;
        else if(xcnt == 10)
            xcnt <= 0;
    end

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_clk <= 0;
        end
        else if(flag) begin
            cnt_clk <= 0;
        end
        else begin
            cnt_clk = cnt_clk + 1;
        end
    end

    assign flag 	= cnt_clk == CLK_50MHz/11 - 1;	// 一秒鐘發(fā)送所有數(shù)據(jù)
	assign flag_0	= cm_hund == 0 && cm_ten == 0 && cm_unit == 0 && point_1 == 0 && point_2 == 0 && point_3 == 0;

	always @(posedge clk or negedge rstn)begin  
		if(!rstn)begin  
			cm_hund	<= 'd0;
			cm_ten	<= 'd0;
			cm_unit	<= 'd0;
			point_1	<= 'd0;
			point_2	<= 'd0;
			point_3	<= 'd0;
		end  
		else begin  
			cm_hund <= data_in % 10;
			cm_ten	<= data_in / 10 ** 1 % 10;
			cm_unit <= data_in / 10 ** 2 % 10;
			point_1 <= data_in / 10 ** 3 % 10;
			point_2 <= data_in / 10 ** 4 % 10;
			point_3 <= data_in / 10 ** 5 % 10;
		end  
	end 

    always @(*) begin
		case (xcnt)
			0    :    data = hex_data(point_3);
			1    :    data = hex_data(point_2);
			2    :    data = hex_data(point_1);
			3    :    data = "."              ;
			4    :    data = hex_data(cm_unit);
			5    :    data = hex_data(cm_ten) ;
			6    :    data = hex_data(cm_hund);
			7    :    data = "c"              ;
			8    :    data = "m"              ;
			9    :    data = "\n"             ;
			default:    data = 6'h30;
		endcase
	end

    // 函數(shù),4位輸入,7位輸出,判斷要輸出的數(shù)字
    function  [7:0]	hex_data; //函數(shù)不含時序邏輯相關(guān)
		input   [03:00]	data_i;//至少一個輸入
		begin
			case(data_i)
				4'd0:hex_data = 6'h30;
				4'd1:hex_data = 6'h31;
				4'd2:hex_data = 6'h32;
				4'd3:hex_data = 6'h33;
				4'd4:hex_data = 6'h34;
				4'd5:hex_data = 6'h35;
				4'd6:hex_data = 6'h36;
				4'd7:hex_data = 6'h37;
				4'd8:hex_data = 6'h38;
				4'd9:hex_data = 6'h39;
				default:hex_data = 6'h30;
			endcase	
		end 
	endfunction

    UART_send
    #(
        .CLK         (CLK_50MHz ),// 時鐘頻率
        .BAUD        (BAUD      ) // 波特率
    )
    UART_send_init(
        .clk         (clk       ),
        .rstn        (rstn      ),   
        .data_in     (data      ),   // 需要發(fā)送的數(shù)據(jù)
        .flag_in     (flag      ),   // 數(shù)據(jù)接收標志位,既發(fā)送標志位
        .UART_tx     (UART_tx   ),   // 串口輸出位         
        .tx_done     (tx_done   )  
    );



endmodule //UART

python 接收串口多字節(jié)數(shù)據(jù)

這里我們選擇 python 語言作為上位機數(shù)據(jù)處理語言

開始的時候,我們運行數(shù)據(jù)處理程序,但是并不知道板子上已經(jīng)發(fā)送到哪一位數(shù)據(jù),所以我們需要循環(huán)過濾掉第一次接收到的數(shù)據(jù):

while True:
    if ser.in_waiting:
        data = ser.read(ser.in_waiting)
        if str(data,encoding="utf-8") == '\n':
            break

然后我們就可以循環(huán)接收串口數(shù)據(jù):

while True:
    if ser.in_waiting:  # 如果串口接收到了數(shù)據(jù)
        data = ser.read(ser.in_waiting)  # 讀取所有可用的數(shù)據(jù)
        num_chars = len(data)  # 獲取收到的字符個數(shù)
        num_all = num_all + num_chars
        if num_all <= 7:
           char = char + str(data , encoding = "utf-8")
        elif num_all == 10:
            # distance = float(char)
            if char != "No Data":
                distance = float(char)
            # print(char[0:7])
            print("距離:",distance,"cm")
            char = ""
            num_all = 0
        # print(f"收到 {num_chars} 個字符:{data}")
        # print(data)

ser.close()  # 關(guān)閉串口

完整 python 代碼如下:

import serial

ser = serial.Serial('COM22', 115200)  # 假設(shè)您的串口是 COM1,波特率為 9600
ser.flushInput()  # 清空輸入緩沖區(qū)

num_all = 0
char = ""

while True:
    if ser.in_waiting:
        data = ser.read(ser.in_waiting)
        if str(data,encoding="utf-8") == '\n':
            break
while True:
    if ser.in_waiting:  # 如果串口接收到了數(shù)據(jù)
        data = ser.read(ser.in_waiting)  # 讀取所有可用的數(shù)據(jù)
        num_chars = len(data)  # 獲取收到的字符個數(shù)
        num_all = num_all + num_chars
        if num_all <= 7:
           char = char + str(data , encoding = "utf-8")
        elif num_all == 10:
            # distance = float(char)
            if char != "No Data":
                distance = float(char)
            # print(char[0:7])
            print("距離:",distance,"cm")
            char = ""
            num_all = 0
        # print(f"收到 {num_chars} 個字符:{data}")
        # print(data)

ser.close()  # 關(guān)閉串口

燒錄效果演示文章來源地址http://www.zghlxwxcb.cn/news/detail-703939.html

到了這里,關(guān)于FPGA入門 —— FPGA UART 串口通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • (五)零基礎(chǔ)學(xué)懂FPGA中的串口通信(UART)

    (五)零基礎(chǔ)學(xué)懂FPGA中的串口通信(UART)

    此篇為專欄 《FPGA學(xué)習(xí)筆記》 的第五篇,記錄我的學(xué)習(xí)FPGA的一些開發(fā)過程和心得感悟,剛接觸FPGA的朋友們可以先去此專欄置頂 《FPGA零基礎(chǔ)入門學(xué)習(xí)路線》來做最基礎(chǔ)的掃盲。 本篇內(nèi)容基于筆者實際開發(fā)過程和正點原子資料撰寫,將會詳細講解此FPGA實驗的全流程, 誠摯 地

    2024年02月04日
    瀏覽(26)
  • FPGA開發(fā)基礎(chǔ)篇之一(接口篇)UART串口

    寫在前面 從本文開始,將連載fpga開發(fā)基礎(chǔ)知識,將這幾年淺顯的fpga開發(fā)經(jīng)驗整理出來,一是梳理一下這幾年給別人做fpga的經(jīng)歷,同時也是分享給大家,也希望大牛批評指正。 一、UART串口通信基本概念 串口通信是非?;厩覒?yīng)用十分廣泛的低速通信接口,無論是在dsp、單片

    2024年02月02日
    瀏覽(21)
  • 【FPGA協(xié)議篇】UART通信及其verilog實現(xiàn)(代碼采用傳參實現(xiàn)模塊通用性,適用于快速開發(fā))

    【FPGA協(xié)議篇】UART通信及其verilog實現(xiàn)(代碼采用傳參實現(xiàn)模塊通用性,適用于快速開發(fā))

    ? 即通用異步收發(fā)器(Universal Asynchronous Receiver/Transmitter),是一種 串行、異步、全雙工 的通信協(xié)議。特點是通信線路簡單,適用于遠距離通信,但傳輸速度慢。 數(shù)據(jù)傳輸速率:波特率(單位:baud,波特) 常見波特率有:1200、2400、4800、19200、38400、57600等,最常用的是9600和11520

    2024年02月05日
    瀏覽(61)
  • FPGA實戰(zhàn) -- UART --- 實現(xiàn)串口回環(huán)(加FIFO)

    FPGA實戰(zhàn) -- UART --- 實現(xiàn)串口回環(huán)(加FIFO)

    FPGA基礎(chǔ) – 通信協(xié)議 — 了解UART以及電腦串口環(huán)境準備 咱們上一文章學(xué)過了UART的基礎(chǔ),已經(jīng)初步了解了協(xié)議的基礎(chǔ)知識和時序要求,接下來就是將學(xué)到的寫出東西來,本文通過上位機的串口助手發(fā)送數(shù)據(jù),F(xiàn)IFO將數(shù)據(jù)在RX和TX之間進行傳遞,并且傳遞回上位機的串口助手,修改

    2024年02月20日
    瀏覽(18)
  • 基于FPGA的超聲波測距——UART串口輸出

    基于FPGA的超聲波測距——UART串口輸出

    環(huán)境: 1、Quartus18.0 2、vscode 3、板子型號:EP4CE10F17C8 4、超聲波模塊:HC_SR04 要求: 使用 EP4CE10F17C8開發(fā)板驅(qū)動 超聲波檢測模塊(HC_SR04 ),并將所測得數(shù)據(jù)顯示到串口助手上。 HC-SR04超聲波測距模塊可提供2cm-400cm的非接觸式距離感測功能,測距精度可達高到3mm;模塊包括超聲波發(fā)

    2024年02月14日
    瀏覽(18)
  • FPGA_數(shù)碼管顯示UART串口接收的數(shù)據(jù)

    FPGA_數(shù)碼管顯示UART串口接收的數(shù)據(jù)

    ? ? ? 實驗?zāi)繕?:通過電腦調(diào)試助手向FPGA的UART串口接收模塊發(fā)送數(shù)據(jù),然后數(shù)據(jù)可以穩(wěn)定顯示 在數(shù)碼管上。 ? ? ? 實驗?zāi)康?: 練習(xí)UART串口模塊和數(shù)碼管的使用。之前已經(jīng)有文章詳細講解了串口和數(shù)碼管的開發(fā),故這里直接提供設(shè)計思路供大家參考。 (串口文章鏈接)ht

    2024年02月13日
    瀏覽(70)
  • 詳解UART通信協(xié)議以及FPGA實現(xiàn)

    詳解UART通信協(xié)議以及FPGA實現(xiàn)

    ??從《淺談UART,TTL,RS-232,RS-485的區(qū)別》這篇文章,我們知道了UART是一種串行、異步、全雙工的通信協(xié)議,屬于協(xié)議層;傳輸過程一般采用RS-232,RS-485電平標準,將所需傳輸?shù)臄?shù)據(jù)一位接一位地傳輸;整體傳輸框架如下: ??串口通信由發(fā)送端和接收端構(gòu)成,兩根信號線

    2024年04月28日
    瀏覽(22)
  • FPGA實現(xiàn)UART通信(1)---發(fā)送數(shù)據(jù)

    FPGA實現(xiàn)UART通信(1)---發(fā)送數(shù)據(jù)

    1、基本概念 通用異步收發(fā)傳輸器,是一種異步收發(fā)傳輸器,在發(fā)送數(shù)據(jù)通過將并行數(shù)據(jù)轉(zhuǎn)換成串行數(shù)據(jù)進行傳輸,在接收數(shù)據(jù)時將串行數(shù)據(jù)轉(zhuǎn)換成并行數(shù)據(jù)。 串行通信分為同步串行通信和異步串行通信。同步串行通信即需要時鐘的參與,通信雙方需要在同一時鐘的控制下,

    2024年02月04日
    瀏覽(20)
  • 【FPGA學(xué)習(xí)】狀態(tài)機實現(xiàn)UART通信

    【FPGA學(xué)習(xí)】狀態(tài)機實現(xiàn)UART通信

    ??在之前的文章中【FPGA學(xué)習(xí)】實例一、Cyclone IV串口通信(RS232)我們已經(jīng)能夠采用波形圖的方法,實現(xiàn)9600bps的Uart通信。近期筆者在整理了狀態(tài)機和計數(shù)器組合的設(shè)計方法以后,對狀態(tài)機的設(shè)計又有了新的感悟和體會,所以又把經(jīng)典的RS232協(xié)議拉出來當狀態(tài)機的例子練手了哈哈

    2023年04月11日
    瀏覽(19)
  • FPGA串行通信(UART,IIC,SPI)

    FPGA串行通信(UART,IIC,SPI)

    此篇為學(xué)習(xí)正點原子FPGA課程總結(jié) 串行/并行通信 串行通信即收發(fā)雙方通過單根線進行數(shù)據(jù)傳輸,發(fā)送方有并轉(zhuǎn)串邏輯,接收方有串轉(zhuǎn)并邏輯。優(yōu)點是占用IO少,成本低,缺點是速率低。 并行通信一次用多根數(shù)據(jù)線傳輸。優(yōu)點是速度快,缺點是占用IO多,成本高。 單工/半雙工

    2024年02月04日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包