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

FPGA之手把手教你寫串口協(xié)議解析(STM32與FPGA數(shù)據(jù)互傳)

這篇具有很好參考價(jià)值的文章主要介紹了FPGA之手把手教你寫串口協(xié)議解析(STM32與FPGA數(shù)據(jù)互傳)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。


博主的念叨

最近趁熱打鐵做了一個(gè)關(guān)于STM32與FPGA通信并且控制高速DA模塊產(chǎn)生不同頻率信號(hào)的正弦波、方波、三角波和鋸齒波的項(xiàng)目,從中收獲到了很多東西,也踩了一些雷和坑,將分為幾篇文章將整個(gè)過程分享出來。

這一次準(zhǔn)備分享的是對(duì)串口數(shù)據(jù)的解析和賦值。解析的數(shù)據(jù)由STM32發(fā)出,通過串口連接至FPGA開發(fā)板的串口上,通過串口接收模塊接收到數(shù)據(jù)后在回環(huán)模塊中將數(shù)據(jù)進(jìn)行處理,最終將處理的數(shù)據(jù)送到FPGA的串口發(fā)送模塊中并將數(shù)據(jù)反饋回STM32中。

本文參考正點(diǎn)原子EP4CE系列開發(fā)板的源碼,做了部分修改


一、任務(wù)介紹

1、本文目標(biāo)

實(shí)現(xiàn)STM32和FPGA的串口通信,并將STM32傳輸過來的頻率信息和波形信息解析存入定義的reg變量中。后續(xù)其他外設(shè)需要調(diào)用頻率信息或者波形信息只需要實(shí)例化此串口模塊即可。

2、設(shè)計(jì)思路

根據(jù)設(shè)計(jì)需求,需要對(duì)串口數(shù)據(jù)進(jìn)行接收解析,同時(shí)需要把解析的數(shù)據(jù)傳送給內(nèi)存單元以及串口發(fā)送單元。其中發(fā)送單元的作用為方便調(diào)試觀察數(shù)據(jù)是否被成功解析,解析的數(shù)據(jù)傳輸給內(nèi)存單元單獨(dú)設(shè)置一個(gè)v文件進(jìn)行賦值。底層文件設(shè)置好以后設(shè)置頂層文件,實(shí)例化底層v文件,其中輸入為時(shí)鐘線、復(fù)位信號(hào)以及串口輸入信號(hào),輸出為解析出的數(shù)據(jù)及串口輸出信號(hào)。

3、設(shè)計(jì)注意事項(xiàng)

1、串口通信屬于異步通信方式,因此出現(xiàn)信號(hào)是不確定的,為了防止亞穩(wěn)態(tài)的產(chǎn)生,需要對(duì)信號(hào)打拍子避免亞穩(wěn)態(tài)的產(chǎn)生。

2、一般采集數(shù)據(jù)在計(jì)數(shù)到數(shù)據(jù)位中間時(shí)采集較為穩(wěn)定

3、串口波特率要對(duì)的上,否則無法進(jìn)行正常通信

4、請(qǐng)事先了解一下串口通信協(xié)議

5、本文串口通信協(xié)議是 EA 頻率高位 頻率中位 頻率低位 波形 AE,其中EA代表起始標(biāo)志,AE代表停止標(biāo)志。頻率高位代表頻率/65536,中位代表%65536/256,低位代表%65526%256

二、設(shè)計(jì)代碼

1.串口接收代碼

//****************************************Copyright (c)***********************************//                             
//----------------------------------------------------------------------------------------
// File name:           uart_recv
// Last modified Date:  2023/4/27 15:02:00
// Last Version:        V1.1
// Descriptions:        UART串口接收模塊
//----------------------------------------------------------------------------------------
// Created by:          技術(shù)小董
// Created date:        2023/4/27 15:02:00
// Version:             V1.0
// Descriptions:        The original version
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_recv(
    input			     sys_clk,                  //系統(tǒng)時(shí)鐘
    input              sys_rst_n,                //系統(tǒng)復(fù)位,低電平有效
    
    input              uart_rxd,                 //UART接收端口
    output  reg        uart_done,                //接收一幀數(shù)據(jù)完成標(biāo)志
    output  reg        rx_flag,                  //接收過程標(biāo)志信號(hào)
	output  reg        start_status,             //開始接收標(biāo)志
	output  reg        stop_status,              //停止接收標(biāo)志   
    output  reg [ 3:0] rx_cnt,                   //接收數(shù)據(jù)計(jì)數(shù)器
    output  reg [ 7:0] rxdata,
    output  reg [ 7:0] uart_data                 //接收的數(shù)據(jù)
    );   
//parameter define
parameter  CLK_FREQ = 50000000;                  //系統(tǒng)時(shí)鐘頻率
parameter  UART_BPS = 9600;                      //串口波特率
localparam  BPS_CNT  = CLK_FREQ/UART_BPS;        //為得到指定波特率,需要對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)BPS_CNT次                                                                                 
//reg define
reg        uart_rxd_d0;
reg        uart_rxd_d1;
reg [15:0] clk_cnt;                              //系統(tǒng)時(shí)鐘計(jì)數(shù)器
//wire define
wire       start_flag;
//*****************************************************
//**                    main code
//*****************************************************
//捕獲接收端口下降沿(起始位),得到一個(gè)時(shí)鐘周期的脈沖信號(hào)
assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0);    
//對(duì)UART接收端口的數(shù)據(jù)延遲兩個(gè)時(shí)鐘周期
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if (!sys_rst_n) begin 
        uart_rxd_d0 <= 1'b0;
        uart_rxd_d1 <= 1'b0;          
    end
    else begin
        uart_rxd_d0  <= uart_rxd;                   
        uart_rxd_d1  <= uart_rxd_d0;
    end   
end

//當(dāng)脈沖信號(hào)start_flag到達(dá)時(shí),進(jìn)入接收過程           
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)                                  
        rx_flag <= 1'b0;
    else begin
        if(start_flag)                          //檢測到起始位
            rx_flag <= 1'b1;                    //進(jìn)入接收過程,標(biāo)志位rx_flag拉高
        //計(jì)數(shù)到停止位中間時(shí),停止接收過程
        else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))begin
            rx_flag <= 1'b0;                    //接收過程結(jié)束,標(biāo)志位rx_flag拉低
		  end	
        else
            rx_flag <= rx_flag;
    end
end

//進(jìn)入接收過程后,啟動(dòng)系統(tǒng)時(shí)鐘計(jì)數(shù)器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)                             
        clk_cnt <= 16'd0;                                  
    else if ( rx_flag ) begin                   //處于接收過程
        if (clk_cnt < BPS_CNT - 1)
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 16'd0;               	//對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)達(dá)一個(gè)波特率周期后清零
    end
    else                              				
        clk_cnt <= 16'd0;						//接收過程結(jié)束,計(jì)數(shù)器清零
end

//進(jìn)入接收過程后,啟動(dòng)接收數(shù)據(jù)計(jì)數(shù)器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)                             
        rx_cnt  <= 4'd0;
    else if ( rx_flag ) begin                   //處于接收過程
        if (clk_cnt == BPS_CNT - 1)				//對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)達(dá)一個(gè)波特率周期
            rx_cnt <= rx_cnt + 1'b1;			//此時(shí)接收數(shù)據(jù)計(jì)數(shù)器加1
        else
            rx_cnt <= rx_cnt;       
    end
	 else
        rx_cnt  <= 4'd0;						//接收過程結(jié)束,計(jì)數(shù)器清零
end

//根據(jù)接收數(shù)據(jù)計(jì)數(shù)器來寄存uart接收端口數(shù)據(jù)
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if ( !sys_rst_n)  
        rxdata <= 8'd0;                                     
    else if(rx_flag)                            //系統(tǒng)處于接收過程
        if (clk_cnt == BPS_CNT/2) begin         //判斷系統(tǒng)時(shí)鐘計(jì)數(shù)器計(jì)數(shù)到數(shù)據(jù)位中間
            case ( rx_cnt )
             4'd1 : rxdata[0] <= uart_rxd_d1;   //寄存數(shù)據(jù)位最低位
             4'd2 : rxdata[1] <= uart_rxd_d1;
             4'd3 : rxdata[2] <= uart_rxd_d1;
             4'd4 : rxdata[3] <= uart_rxd_d1;
             4'd5 : rxdata[4] <= uart_rxd_d1;
             4'd6 : rxdata[5] <= uart_rxd_d1;
             4'd7 : rxdata[6] <= uart_rxd_d1;
             4'd8 : rxdata[7] <= uart_rxd_d1;   //寄存數(shù)據(jù)位最高位
             default:;                                    
            endcase
        end
        else 
            rxdata <= rxdata;
    else
        rxdata <= 8'd0;
end

//數(shù)據(jù)接收完畢后給出標(biāo)志信號(hào)并寄存輸出接收到的數(shù)據(jù)
always @(posedge sys_clk or negedge sys_rst_n) begin        
    if (!sys_rst_n) begin
        uart_data <= 8'd0;                               
        uart_done <= 1'b0;
		  start_status <= 1'b0;
		  stop_status <= 1'b0;
    end
    else if(rx_cnt == 4'd9) begin               //接收數(shù)據(jù)計(jì)數(shù)器計(jì)數(shù)到停止位時(shí)
        case(rxdata)
		      8'hea : begin 
				    uart_data <= 8'haa;             //回復(fù)收到
					 start_status <= 1'b1;           //開始處理數(shù)據(jù)
		          end			 
				8'hae : begin
				    uart_data <= 8'hbb;             //回復(fù)結(jié)束
					 stop_status <= 1'b1;            //結(jié)束處理數(shù)據(jù)
					 end
				default:uart_data <= rxdata;
		  endcase	
        uart_done <= 1'b1;                      //并將接收完成標(biāo)志位拉高
    end
    else begin
        uart_data <= 8'd0;                                   
        uart_done <= 1'b0; 
		  start_status <= 1'b0;
		  stop_status <= 1'b0;
    end    
end
endmodule	

輸入總共定義三個(gè)變量,其中uart_rxd代表串口輸入接入。uart_done的意思是接收完了一幀數(shù)據(jù),會(huì)拉高一個(gè)時(shí)鐘周期,需要注意的是在本設(shè)計(jì)之中接收完了一個(gè)數(shù)據(jù)后就需要對(duì)這個(gè)數(shù)據(jù)進(jìn)行處理同時(shí)傳給發(fā)送端口進(jìn)行數(shù)據(jù)的發(fā)送。rx_flag信號(hào)代表串口處于接收狀態(tài),start_status代表我們定義的協(xié)議數(shù)據(jù)傳輸開始,stop_status代表協(xié)議數(shù)據(jù)傳輸結(jié)尾。rx_cnt為4位的數(shù)據(jù),分別代表串口的10個(gè)過程,開始信號(hào),八個(gè)數(shù)據(jù)位,停止信號(hào)。rxdata為此次接收到的數(shù)據(jù),uart_data代表要輸出給其它需要實(shí)例化此v文件的串口當(dāng)前數(shù)據(jù)。

定義了兩個(gè)全局變量和一個(gè)本地變量。方便實(shí)例化時(shí)更改波特率。定義的uart_rxd_d0和uart_rxd_d1兩個(gè)變量是為了抑制突然輸入進(jìn)來的串口數(shù)據(jù)下降沿,通過打拍子的方式讓輸入信號(hào)延時(shí)兩拍,避免亞穩(wěn)態(tài)產(chǎn)生,同時(shí)也為了后面檢測下降沿做準(zhǔn)備。clk_cnt為時(shí)鐘計(jì)數(shù)器,貫穿整個(gè)計(jì)數(shù)周期中。定義的wire變量start_flag,用來檢測串口輸入的下降沿信號(hào)。需要注意的是,當(dāng)串口開始發(fā)送數(shù)據(jù)時(shí)會(huì)拉低串口信號(hào),這樣當(dāng)接收機(jī)檢測到串口輸入引腳電平被拉低后,也即產(chǎn)生了下降沿以后就代表串口已經(jīng)準(zhǔn)備要開始發(fā)送數(shù)據(jù)了。

檢測下降沿的通用代碼:

assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0); 
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if (!sys_rst_n) begin 

        uart_rxd_d0 <= 1'b0;
        uart_rxd_d1 <= 1'b0;          
    end
    else begin
        uart_rxd_d0  <= uart_rxd;                   
        uart_rxd_d1  <= uart_rxd_d0;
    end   
end

此代碼的意義在于當(dāng)出現(xiàn)下降沿以后,start_flag會(huì)被拉高一個(gè)時(shí)鐘周期。當(dāng)串口一直處于高電平時(shí),打了一拍的信號(hào)和打了兩拍的信號(hào)都為高電平,所以srat_flag=1&0也就是0。當(dāng)信號(hào)產(chǎn)生下降沿以后,d0信號(hào)為低電平,此時(shí)d1信號(hào)還處于上一個(gè)周期的電平高電平,所以start_flag=1&1=1,故此刻start_flag被拉高。過了一個(gè)時(shí)鐘周期后,d1和d0信號(hào)都為低電平了,因此start_flag為低電平,相當(dāng)于當(dāng)信號(hào)下降沿來臨后,延時(shí)一個(gè)周期將產(chǎn)生start_flag信號(hào)的上升沿。

在第二個(gè)always里面,如果判斷start_flag為高電平,即檢測到串口線下降沿產(chǎn)生,則將rx_flag置1,表示此刻串口的接收端正在工作中,不允許其他數(shù)據(jù)進(jìn)入。else if里面的判斷語句含義是當(dāng)rx_cnt計(jì)數(shù)到9且單次計(jì)數(shù)到波特率計(jì)數(shù)的一半時(shí),代表串口接收過程結(jié)束,rx_flag拉低代表串口處于空閑狀態(tài),可進(jìn)行下一次數(shù)據(jù)的接收。在均不滿足條件的情況下,rx_flag變量不變。

第三個(gè)always里面是對(duì)波特率進(jìn)行計(jì)數(shù),前文提到了FPGA主頻為50MHz,BPS_CNT=主頻除以波特率,故當(dāng)我們用主頻從0計(jì)數(shù)到BPS_CNT-1就代表一個(gè)波特率周期。

第四個(gè)always是對(duì)波特率周期進(jìn)行計(jì)數(shù),當(dāng)clk_cnt到達(dá)BPS_CNT-1時(shí),對(duì)應(yīng)的rx_cnt,也就是串口對(duì)應(yīng)的位標(biāo)識(shí)+1,當(dāng)rx_flag==0也就是忙碌狀態(tài)結(jié)束后,此位標(biāo)識(shí)將會(huì)自動(dòng)歸零。

第五個(gè)always是對(duì)數(shù)據(jù)進(jìn)行寄存,在else if語句中,如果計(jì)數(shù)到波特率的中間時(shí)期,將根據(jù)rx_cnt對(duì)數(shù)據(jù)進(jìn)行賦值,當(dāng)rx_cnt為0的時(shí)候,代表處于起始位或者沒有開始串口傳輸,因此不對(duì)任何數(shù)據(jù)進(jìn)行處理。當(dāng)rx_cnt非零后,根據(jù)不同的rx_cnt的值,將當(dāng)前接收到的二進(jìn)制數(shù)據(jù)賦值給rxdata對(duì)應(yīng)的位,這樣當(dāng)rx_flag沒有歸零之前,rxdata里面是寄存了串口發(fā)送的數(shù)據(jù)。

第六個(gè)always是對(duì)數(shù)據(jù)進(jìn)行處理。當(dāng)rx_cnt等于9的時(shí)候,也就是進(jìn)入到最后停止位截至之前判斷的這一段時(shí)間,對(duì)rxdata數(shù)據(jù)進(jìn)行判斷。此刻如果rxdata數(shù)據(jù)為0xea,則代表是我們協(xié)議的開頭八位,故此刻將uart_data,也就是轉(zhuǎn)發(fā)給TX的數(shù)據(jù)賦值為aa,代表接收到了協(xié)議開頭,同時(shí)拉高start_status數(shù)據(jù)。如果rxdata數(shù)據(jù)為0xae,就代表接收到了協(xié)議末尾,同時(shí)拉高stop_status數(shù)據(jù)。若不屬于協(xié)議開頭也不屬于協(xié)議結(jié)尾,則將收到的串口數(shù)據(jù)直接賦值給TX發(fā)送。

那么根據(jù)這個(gè)原理,我們可以任意定義協(xié)議開頭或者協(xié)議結(jié)尾,相對(duì)應(yīng)拉高開始信號(hào)和結(jié)束信號(hào),在下面的v文件中將會(huì)講到開始信號(hào)和結(jié)束信號(hào)的作用,此處我們只需要記得對(duì)這個(gè)數(shù)據(jù)進(jìn)行判斷。

2.串口發(fā)送代碼

module uart_send(
    input	      sys_clk,                  //系統(tǒng)時(shí)鐘
    input         sys_rst_n,                //系統(tǒng)復(fù)位,低電平有效
    
    input         uart_en,                  //發(fā)送使能信號(hào)
    input  [7:0]  uart_din,                 //待發(fā)送數(shù)據(jù)
    output        uart_tx_busy,             //發(fā)送忙狀態(tài)標(biāo)志 
    output        en_flag     ,
    output  reg   tx_flag,                  //發(fā)送過程標(biāo)志信號(hào)
    output  reg [ 7:0] tx_data,             //寄存發(fā)送數(shù)據(jù)
    output  reg [ 3:0] tx_cnt,              //發(fā)送數(shù)據(jù)計(jì)數(shù)器
    output  reg   uart_txd                  //UART發(fā)送端口
    );
    
//parameter define
parameter  CLK_FREQ = 50000000;            //系統(tǒng)時(shí)鐘頻率
parameter  UART_BPS = 9600;                //串口波特率
//其余本地參量
localparam  BPS_CNT  = CLK_FREQ/UART_BPS;   //為得到指定波特率,對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)BPS_CNT次
//reg define
reg        uart_en_d0; 
reg        uart_en_d1;  
reg [15:0] clk_cnt;                           //系統(tǒng)時(shí)鐘計(jì)數(shù)器
//*****************************************************
//**                    main code
//*****************************************************
//在串口發(fā)送過程中給出忙狀態(tài)標(biāo)志
assign uart_tx_busy = tx_flag;
//捕獲uart_en上升沿,得到一個(gè)時(shí)鐘周期的脈沖信號(hào)
assign en_flag = (~uart_en_d1) & uart_en_d0;
//對(duì)發(fā)送使能信號(hào)uart_en延遲兩個(gè)時(shí)鐘周期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        uart_en_d0 <= 1'b0;                                  
        uart_en_d1 <= 1'b0;
    end                                                      
    else begin                                               
        uart_en_d0 <= uart_en;                               
        uart_en_d1 <= uart_en_d0;                            
    end
end
//當(dāng)脈沖信號(hào)en_flag到達(dá)時(shí),寄存待發(fā)送的數(shù)據(jù),并進(jìn)入發(fā)送過程          
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin                                  
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
    end 
    else if (en_flag) begin                 //檢測到發(fā)送使能上升沿                      
            tx_flag <= 1'b1;                //進(jìn)入發(fā)送過程,標(biāo)志位tx_flag拉高
            tx_data <= uart_din;            //寄存待發(fā)送的數(shù)據(jù)
        end
                                            //計(jì)數(shù)到停止位結(jié)束時(shí),停止發(fā)送過程
        else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT - (BPS_CNT/16))) begin                                       
            tx_flag <= 1'b0;                //發(fā)送過程結(jié)束,標(biāo)志位tx_flag拉低
            tx_data <= 8'd0;
        end
        else begin
            tx_flag <= tx_flag;
            tx_data <= tx_data;
        end 
end
//進(jìn)入發(fā)送過程后,啟動(dòng)系統(tǒng)時(shí)鐘計(jì)數(shù)器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)                             
        clk_cnt <= 16'd0;                                  
    else if (tx_flag) begin                 //處于發(fā)送過程
        if (clk_cnt < BPS_CNT - 1)
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 16'd0;               //對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)達(dá)一個(gè)波特率周期后清零
    end
    else                             
        clk_cnt <= 16'd0; 				    //發(fā)送過程結(jié)束
end
//進(jìn)入發(fā)送過程后,啟動(dòng)發(fā)送數(shù)據(jù)計(jì)數(shù)器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)                             
        tx_cnt <= 4'd0;
    else if (tx_flag) begin                 //處于發(fā)送過程
        if (clk_cnt == BPS_CNT - 1)			//對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)達(dá)一個(gè)波特率周期
            tx_cnt <= tx_cnt + 1'b1;		//此時(shí)發(fā)送數(shù)據(jù)計(jì)數(shù)器加1
        else
            tx_cnt <= tx_cnt;       
    end
    else                              
        tx_cnt  <= 4'd0;				    //發(fā)送過程結(jié)束
end
//根據(jù)發(fā)送數(shù)據(jù)計(jì)數(shù)器來給uart發(fā)送端口賦值
always @(posedge sys_clk or negedge sys_rst_n) begin        
    if (!sys_rst_n)  
        uart_txd <= 1'b1;        
    else if (tx_flag)
        case(tx_cnt)
            4'd0: uart_txd <= 1'b0;         //起始位 
            4'd1: uart_txd <= tx_data[0];   //數(shù)據(jù)位最低位
            4'd2: uart_txd <= tx_data[1];
            4'd3: uart_txd <= tx_data[2];
            4'd4: uart_txd <= tx_data[3];
            4'd5: uart_txd <= tx_data[4];
            4'd6: uart_txd <= tx_data[5];
            4'd7: uart_txd <= tx_data[6];
            4'd8: uart_txd <= tx_data[7];   //數(shù)據(jù)位最高位
            4'd9: uart_txd <= 1'b1;         //停止位
            default: ;
        endcase
    else 
        uart_txd <= 1'b1;                   //空閑時(shí)發(fā)送端口為高電平
end
endmodule	          

輸入有四個(gè)變量,分別是系統(tǒng)時(shí)鐘,系統(tǒng)復(fù)位,發(fā)送使能信號(hào)以及發(fā)送數(shù)據(jù)。其中發(fā)送使能信號(hào)是觸發(fā)信號(hào),也需要考慮亞穩(wěn)態(tài)的問題,故與串口接收模塊類似,需要進(jìn)行打拍去除亞穩(wěn)態(tài)。輸出變量有uart_tx_busy,為高電平時(shí)代表此時(shí)正處于串口發(fā)送狀態(tài)。en_flag為捕獲上升沿,類比于接收模塊中的start_flag。tx_flag代表串口正在發(fā)送中,通過assign語句賦值給busy信號(hào)。tx_data為發(fā)送的數(shù)據(jù),tx_cnt為計(jì)數(shù)器,uart_txd為串口發(fā)送線。

與接收模塊類似的地方就不提了,需要注意的是捕獲上升沿與下降沿的區(qū)別在于,前者是對(duì)d1信號(hào)進(jìn)行取反再與d0相與,后者是對(duì)d0信號(hào)取反再與d1相與,不同的與方法將會(huì)檢測到不同的邊沿。

第二個(gè)always里面通過判斷發(fā)送使能是否出現(xiàn)高電平,執(zhí)行相對(duì)應(yīng)的功能。如果檢測到出現(xiàn)高電平,則將tx_flag賦值為高電平,同時(shí)通過assign語句賦值給uart_tx_busy,提示當(dāng)前串口正在發(fā)送數(shù)據(jù)。通過tx_data寄存獲得的串口發(fā)送數(shù)據(jù),同樣也有對(duì)停止位的判斷,不過略有區(qū)別在于串口發(fā)送的停止位時(shí)間會(huì)略長一點(diǎn),主要是為了保持發(fā)送的連續(xù)性。

第三個(gè)always里面是對(duì)波特率的計(jì)數(shù)。

第四個(gè)always是對(duì)位標(biāo)志的計(jì)數(shù)。

第五個(gè)always是通過位標(biāo)志的不同值將數(shù)據(jù)賦值給串口發(fā)送接口uart_txd,與串口接收略有區(qū)別的在于在位標(biāo)志為0時(shí)賦值低電平給輸出接口,原因在于從機(jī)接收主機(jī)發(fā)送的串口信息的判斷依據(jù)是數(shù)據(jù)被拉低,故此處進(jìn)行了拉低操作。

3.串口解析代碼

//****************************************Copyright (c)***********************************//                             
//----------------------------------------------------------------------------------------
// File name:           uart_loop
// Last modified Date:  2023/4/27 15:02:00
// Last Version:        V1.1
// Descriptions:        UART串口數(shù)據(jù)處理模塊
//----------------------------------------------------------------------------------------
// Created by:          技術(shù)小董
// Created date:        2023/4/27 15:02:00
// Version:             V1.0
// Descriptions:        The original version
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module uart_loop(
    input	         sys_clk,                   //系統(tǒng)時(shí)鐘
    input            sys_rst_n,                 //系統(tǒng)復(fù)位,低電平有效
     
    input            recv_done,                 //接收一幀數(shù)據(jù)完成標(biāo)志
    input      [7:0] recv_data,                 //接收的數(shù)據(jù)
	 
	input            start_status,              //定義啟動(dòng)狀態(tài)
	input            stop_status,               //定義停止?fàn)顟B(tài)
     
    input            tx_busy,                   //發(fā)送忙狀態(tài)標(biāo)志      
    output reg       send_en,                   //發(fā)送使能信號(hào)
    output reg [7:0] send_data,                 //待發(fā)送數(shù)據(jù) 
	output reg [23:0]freq,                      //頻率數(shù)據(jù)
	output reg [7:0] wave                       //波形數(shù)據(jù)
    );
reg [7:0] message[0:3];                         //存儲(chǔ)數(shù)據(jù)的地址
reg [3:0] message_count;
//reg define
reg recv_done_d0;
reg recv_done_d1;
reg recv_done_d2;
reg start_done_d0;
reg start_done_d1;
reg stop_done_d0;
reg stop_done_d1;
reg tx_ready;
//wire define
wire recv_done_flag;
wire start_done_flag;
wire stop_done_flag;
//*****************************************************
//**                    main code
//*****************************************************
//捕獲recv_done上升沿,得到一個(gè)時(shí)鐘周期的脈沖信號(hào)
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;
assign start_done_flag = (~start_done_d1) & start_done_d0;
assign stop_done_flag = (~stop_done_d1) & stop_done_d0;                                       
//對(duì)發(fā)送使能信號(hào)recv_done延遲兩個(gè)時(shí)鐘周期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        recv_done_d0 <= 1'b0;                                  
        recv_done_d1 <= 1'b0;
		  recv_done_d2 <= 1'b0;
    end                                                      
    else begin                                               
        recv_done_d0 <= recv_done;                               
        recv_done_d1 <= recv_done_d0; 
        recv_done_d2	<= recv_done_flag;	  
    end
end
//對(duì)發(fā)送使能信號(hào)recv_done延遲兩個(gè)時(shí)鐘周期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        start_done_d0 <= 1'b0;                                  
        start_done_d1 <= 1'b0;
    end                                                      
    else begin                                               
        start_done_d0 <= start_status;                               
        start_done_d1 <= start_done_d0;                            
    end
end
//對(duì)停止處理信號(hào)stop_status延遲兩個(gè)時(shí)鐘周期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        stop_done_d0 <= 1'b0;                                  
        stop_done_d1 <= 1'b0;
    end                                                      
    else begin                                               
        stop_done_d0 <= stop_status;                               
        stop_done_d1 <= stop_done_d0;                            
    end
end
//狀態(tài)的切換
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)begin
	     freq <= 24'd500_000;
		  wave <= 8'd0;
		  message_count <= 1'b0;
	 end
    else if(start_done_flag && message_count == 4'd0)
		  message_count <= 1'b1;	 
	 else if(stop_done_flag && message_count == 4'd5)begin
	     message_count <= 1'b0;
		  freq[23:16] <= message[0];
		  freq[15:8]  <= message[1];
		  freq[7:0]   <= message[2];
		  wave        <= message[3];       
	 end
	 else if(recv_done_flag && message_count)begin
	     message[message_count-1'b1] <= recv_data;
		  message_count <= message_count + 1'b1;
	 end 

end
//判斷接收完成信號(hào),并在串口發(fā)送模塊空閑時(shí)給出發(fā)送使能信號(hào)
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        tx_ready  <= 1'b0; 
        send_en   <= 1'b0;
        send_data <= 8'd0;
    end                                                      
    else begin         
        if(recv_done_d2)begin            //檢測串口接收到數(shù)據(jù)
            tx_ready  <= 1'b1;                  //準(zhǔn)備啟動(dòng)發(fā)送過程
            send_en   <= 1'b0;
				case (message_count)
				  4'd2 : send_data <= freq[23:16];
				  4'd3 : send_data <= freq[15:8];
				  4'd4 : send_data <= freq[7:0];
				  default : send_data <= recv_data;             //寄存串口接收的數(shù)據(jù)
            endcase
        end
        else if(tx_ready && (~tx_busy)) begin   //檢測串口發(fā)送模塊空閑
            tx_ready <= 1'b0;                   //準(zhǔn)備過程結(jié)束
            send_en  <= 1'b1;                   //拉高發(fā)送使能信號(hào)
        end
    end
end

endmodule 

串口數(shù)據(jù)處理模塊總共有七個(gè)輸入?yún)⒘亢退膫€(gè)輸出參量。其中輸入?yún)⒘糠謩e為系統(tǒng)時(shí)鐘,系統(tǒng)復(fù)位。recv_done和recv_data是由串口接收模塊傳遞過來的數(shù)據(jù)接收完畢標(biāo)志和接收到的數(shù)據(jù),start_status和stop_status是表明定義的協(xié)議開頭和協(xié)議結(jié)束的標(biāo)志。tx_busy是串口發(fā)哦那個(gè)模塊傳遞過來的串口是否處于空閑狀態(tài)的標(biāo)志。輸出參量中的send_en是傳遞給發(fā)送模塊的發(fā)送使能標(biāo)志,send_data是傳遞給發(fā)送模塊的發(fā)送數(shù)據(jù),freq是從協(xié)議中解析出的頻率數(shù)據(jù),wave是從協(xié)議中解析出的波形數(shù)據(jù)。

定義的reg變量里面message是定義的數(shù)組變量,分別存儲(chǔ)頻率的高位,中位,低位,以及波形數(shù)據(jù),用作后面頻率的賦值和波形的賦值。message_count是代表當(dāng)前存儲(chǔ)的是協(xié)議中第幾-1位數(shù)據(jù),不包括協(xié)議的開頭和結(jié)尾。

下方定義的d0和d1變量其實(shí)都是為了打節(jié)拍避免亞穩(wěn)態(tài),tx_ready代表發(fā)送的中間變量。

第四個(gè)always語句是對(duì)頻率信號(hào)和波形數(shù)據(jù)進(jìn)行解析。當(dāng)檢測到start_done_flag數(shù)據(jù)為高電平且當(dāng)前檢測數(shù)據(jù)計(jì)數(shù)為0的時(shí)候,將message_count置1,代表數(shù)據(jù)傳輸即將開始。當(dāng)檢測到stop_done_flag數(shù)據(jù)為高電平且message_count為5時(shí)復(fù)位message_count且將采集到的數(shù)據(jù)賦值給freq和wave變量。在接下來的else if中每次對(duì)message_count數(shù)據(jù)進(jìn)行判斷,如果不為0的話通過賦值語句將當(dāng)前接收到的數(shù)據(jù)賦值給message數(shù)組,當(dāng)message_count==1時(shí),將當(dāng)前recv_data數(shù)據(jù)賦值給message[0],依次類推。

第五個(gè)always是當(dāng)檢測到串口接收到數(shù)據(jù)以后,先進(jìn)入到啟動(dòng)發(fā)送過程,將send_en拉低,此時(shí)串口不發(fā)送數(shù)據(jù)。在這個(gè)階段根據(jù)message_count的值將對(duì)應(yīng)的頻率數(shù)據(jù)發(fā)送給串口模塊,這一段主要是為了驗(yàn)證我們采集到的頻率數(shù)據(jù)是否正確。message_count==2,3,4正好對(duì)應(yīng)freq的三個(gè)變量。wave數(shù)據(jù)由于只有一位,所以直接賦值即可。當(dāng)要發(fā)送的數(shù)據(jù)被賦值好了以后,下一個(gè)時(shí)鐘周期tx_reday為1時(shí)就會(huì)執(zhí)行tx_ready=0,send_en=1的語句,即觸發(fā)了串口發(fā)送。

4.頂層代碼

module uart_loopback_top(
    input           sys_clk,            //外部50M時(shí)鐘
    input           sys_rst_n,          //外部復(fù)位信號(hào),低有效

    input           uart_rxd,           //UART接收端口
    output          uart_txd,           //UART發(fā)送端口
	output  [23:0]  freq,
	output  [7:0]   wave
    );

//parameter define
parameter  CLK_FREQ = 50000000;         //定義系統(tǒng)時(shí)鐘頻率
parameter  UART_BPS = 115200;           //定義串口波特率    
//wire define   
wire       uart_recv_done;              //UART接收完成
wire [7:0] uart_recv_data;              //UART接收數(shù)據(jù)
wire       uart_send_en;                //UART發(fā)送使能
wire [7:0] uart_send_data;              //UART發(fā)送數(shù)據(jù)
wire       uart_tx_busy;                //UART發(fā)送忙狀態(tài)標(biāo)志
wire       uart_start_status;
wire       uart_stop_status;
//*****************************************************
//**                    main code
//*****************************************************
//串口接收模塊     
uart_recv #(                          
    .CLK_FREQ       (CLK_FREQ),         //設(shè)置系統(tǒng)時(shí)鐘頻率
    .UART_BPS       (UART_BPS))         //設(shè)置串口接收波特率
u_uart_recv(                 
    .sys_clk        (sys_clk), 
    .sys_rst_n      (sys_rst_n),
	.start_status   (uart_start_status),              //定義啟動(dòng)狀態(tài)
	.stop_status    (uart_stop_status),      //定義停止?fàn)顟B(tài)       
    .uart_rxd       (uart_rxd),
    .uart_done      (uart_recv_done),
    .uart_data      (uart_recv_data)
    );

//串口發(fā)送模塊    
uart_send #(                          
    .CLK_FREQ       (CLK_FREQ),         //設(shè)置系統(tǒng)時(shí)鐘頻率
    .UART_BPS       (UART_BPS))         //設(shè)置串口發(fā)送波特率
u_uart_send(                 
    .sys_clk        (sys_clk),
    .sys_rst_n      (sys_rst_n),
     
    .uart_en        (uart_send_en),
    .uart_din       (uart_send_data),
    .uart_tx_busy   (uart_tx_busy),
    .uart_txd       (uart_txd)
    );
    
//串口環(huán)回模塊    
uart_loop u_uart_loop(
    .sys_clk            (sys_clk),             
    .sys_rst_n          (sys_rst_n),           
   
    .recv_done          (uart_recv_done),   //接收一幀數(shù)據(jù)完成標(biāo)志信號(hào)
    .recv_data          (uart_recv_data),   //接收的數(shù)據(jù)
	.start_status       (uart_start_status),              //定義啟動(dòng)狀態(tài)
	.stop_status        (uart_stop_status),      //定義停止?fàn)顟B(tài)   
    .tx_busy            (uart_tx_busy),     //發(fā)送忙狀態(tài)標(biāo)志      
    .send_en            (uart_send_en),     //發(fā)送使能信號(hào)
    .send_data          (uart_send_data),    //待發(fā)送數(shù)據(jù)
	.freq               (freq),      //頻率數(shù)據(jù)
	.wave               (wave)       //波形數(shù)據(jù)
    );
endmodule

在頂層文件中,除了定義輸入的時(shí)鐘和復(fù)位信號(hào),還定義了uart_rxd和uart_txd。分別為串口輸入和串口輸出。另外還輸出了頻率信號(hào)和波形信號(hào)。如果僅僅是只用到了串口模塊的話,可以去除掉freq和wave輸出,只需要在頂層v文件里面定義reg變量即可,但是如果需要將處理的數(shù)據(jù)送入其它的模塊中,就需要將freq和wave進(jìn)行輸出。本次設(shè)計(jì)是為了獲得頻率數(shù)據(jù)和波形數(shù)據(jù),所以需要將其進(jìn)行輸出,不需要的話去除即可。

定義了CLK_FREQ和UART_BPS,根據(jù)主頻和所需要的波特率可以一鍵修改。定義了各種中間變量進(jìn)行調(diào)用。此外,實(shí)例化的模塊并不一定需要輸出所有的參數(shù),因此可根據(jù)自己的需求輸出模塊參數(shù)。

在串口接收實(shí)例中,需要輸入sys_clk,sys_rst_n和uart_rxd,輸出start_status和stop_status以及recv_done和recv_data。這幾個(gè)變量將會(huì)同步實(shí)例化到uart_loop模塊中去作為輸入變量,輸出變量為uart_tx_busy,uart_send_en,uart_send_data,freq和wave。其中uart_tx_busy,uart_send_en,uart_send_data將會(huì)被實(shí)例化成uart_send模塊的輸入。

自此,就完成了頂層模塊對(duì)于各底層模塊的調(diào)用。


總結(jié)

有什么不懂的可以在下方留言,只要學(xué)會(huì)了方法,對(duì)于串口數(shù)據(jù)的解析會(huì)變得很簡單。這個(gè)僅僅是對(duì)數(shù)據(jù)進(jìn)行處理,后續(xù)會(huì)發(fā)一篇DDS信號(hào)發(fā)生器的總代碼,告訴大家如何調(diào)用PLL和ROM核生成特定頻率的波形。文章來源地址http://www.zghlxwxcb.cn/news/detail-459712.html

到了這里,關(guān)于FPGA之手把手教你寫串口協(xié)議解析(STM32與FPGA數(shù)據(jù)互傳)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • QT初體驗(yàn):手把手帶你寫一個(gè)自己的串口助手

    QT初體驗(yàn):手把手帶你寫一個(gè)自己的串口助手

    本文記錄一下用QT Creator 寫一個(gè)基本功能齊全的串口助手的過程,整個(gè)工程只有幾百行代碼,跟著做下來對(duì)新手來說可以更快了解整個(gè)QT項(xiàng)目的開發(fā)過程和一些常用控件的使用方法。對(duì)新手學(xué)習(xí)QT能增強(qiáng)信心,話不多說,正文開始 先看成品: (1) 創(chuàng)建QMainWindow工程。這一步就不

    2024年02月05日
    瀏覽(87)
  • 正則表達(dá)式詳解(零基礎(chǔ)教學(xué),手把手教你寫正則)

    本篇文章將從零講解什么是正則表達(dá)式,以及正則表達(dá)式的規(guī)則、在python中的應(yīng)用,用通俗易懂的描述方式進(jìn)行零基礎(chǔ)級(jí)別的講解,盡量做到全網(wǎng)最全講解,力求最高質(zhì)量文章,歡迎關(guān)注!點(diǎn)擊目錄可直接進(jìn)行相關(guān)位置跳轉(zhuǎn)。 目錄: 什么是正則? 為什么需要正則? 元字符

    2023年04月08日
    瀏覽(21)
  • 【Java】手把手教你寫學(xué)生信息管理系統(tǒng)(窗口化+MYSQL)

    【Java】手把手教你寫學(xué)生信息管理系統(tǒng)(窗口化+MYSQL)

    ? ? ? ? ? ? (本項(xiàng)目使用到了數(shù)據(jù)庫的可視化軟件DataGrip,需要同學(xué)們自行下載并配置環(huán)境) 首先我們需要在DataGrip中建立一個(gè)student的框架 ????????????????????????????????????????????????????????然后建立一個(gè)studenttable表? ? ? ? ? ? ? ? ? ?

    2024年02月04日
    瀏覽(29)
  • [Kotlin]手把手教你寫一個(gè)安卓APP(第一章注冊(cè)登錄)

    [Kotlin]手把手教你寫一個(gè)安卓APP(第一章注冊(cè)登錄)

    開發(fā)軟件:Android Studio 1.創(chuàng)建項(xiàng)目默認(rèn)選擇Empty Activity ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?點(diǎn)擊Next ?2.生成項(xiàng)目設(shè)置包名選擇開發(fā)語言(這里我用的是kotlin) ?在生成項(xiàng)目后我們要做的就是添加需要的配置打開我們的app目錄下的 buil

    2023年04月23日
    瀏覽(31)
  • 手把手教你寫代碼——基于控制臺(tái)的通訊錄管理系統(tǒng)(單表)

    手把手教你寫代碼——基于控制臺(tái)的通訊錄管理系統(tǒng)(單表)

    本欄目專為入門java學(xué)習(xí)者設(shè)計(jì)的一些簡單的入門項(xiàng)目 本項(xiàng)目為簡單的基于控制臺(tái)的通訊錄管理系統(tǒng),所需要的環(huán)境僅僅為jdk以及mysql(版本不限)!只有一個(gè)簡單的eclipse軟件以及我們的mysql可視化工具(視頻使用navicat) 本項(xiàng)目數(shù)據(jù)庫表僅有一個(gè),單表操作,方便學(xué)習(xí)! 本項(xiàng)

    2024年02月15日
    瀏覽(23)
  • 【Golang項(xiàng)目實(shí)戰(zhàn)】手把手教你寫一個(gè)備忘錄程序|附源碼——建議收藏

    【Golang項(xiàng)目實(shí)戰(zhàn)】手把手教你寫一個(gè)備忘錄程序|附源碼——建議收藏

    博主簡介: 努力學(xué)習(xí)的大一在校計(jì)算機(jī)專業(yè)學(xué)生,熱愛學(xué)習(xí)和創(chuàng)作。目前在學(xué)習(xí)和分享:數(shù)據(jù)結(jié)構(gòu)、Go,Java等相關(guān)知識(shí)。 博主主頁: @是瑤瑤子啦 所屬專欄: Go語言核心編程 近期目標(biāo): 寫好專欄的每一篇文章 前幾天瑤瑤子學(xué)習(xí)了Go語言的基礎(chǔ)語法知識(shí),那么今天我們就寫個(gè)

    2024年02月06日
    瀏覽(28)
  • 數(shù)據(jù)結(jié)構(gòu):線性表————順序表的實(shí)現(xiàn)、項(xiàng)目和OJ題目(手把手教你寫代碼)

    數(shù)據(jù)結(jié)構(gòu):線性表————順序表的實(shí)現(xiàn)、項(xiàng)目和OJ題目(手把手教你寫代碼)

    ?? 個(gè)人主頁: 小新_- ??個(gè)人座右銘:“成功者不是從不失敗的人,而是從不放棄的人!”?? ??歡迎各位→點(diǎn)贊?? + 收藏?? + 留言?? ??所屬專欄:? 話說那些與C++的愛恨情仇 ? 歡迎訂閱,持續(xù)更新中~~~ ?? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ???讓小新帶著你

    2024年04月16日
    瀏覽(235)
  • 基于STM32F103RCT6之手把手教你寫智能家居項(xiàng)目(2)

    基于STM32F103RCT6之手把手教你寫智能家居項(xiàng)目(2)

    ??????? 上一節(jié)我們簡述了智能家居項(xiàng)目,實(shí)現(xiàn)了點(diǎn)燈的相關(guān)代碼編寫,還有WIFI模塊的固件燒錄。 連接什么平臺(tái): ??????? 我們想要遠(yuǎn)程控制家具的開關(guān)和獲取家中的狀態(tài),少不了一個(gè)可以傳輸數(shù)據(jù)的云平臺(tái)。我認(rèn)為易監(jiān)控是一個(gè)簡單好用的云平臺(tái)。 怎么連接平臺(tái):

    2024年02月20日
    瀏覽(908)
  • 手把手教你移植 tinyriscv 到FPGA上

    手把手教你移植 tinyriscv 到FPGA上

    我是 雪天魚 ,一名FPGA愛好者,研究方向是FPGA架構(gòu)探索和數(shù)字IC設(shè)計(jì)。 關(guān)注公眾號(hào)【集成電路設(shè)計(jì)教程】,獲取更多學(xué)習(xí)資料,并拉你進(jìn)“ IC設(shè)計(jì)交流群 ”。 QQ IC設(shè)計(jì)交流群 群號(hào): 866169462 。 所用開發(fā)板:正點(diǎn)原子達(dá)芬奇FPGA開發(fā)板 芯片型號(hào):Xilinx Artix-7 35T tinyriscv 官方庫鏈

    2023年04月17日
    瀏覽(41)
  • 手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

    手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

    FreeRTOS中一共有5種內(nèi)存分配的方法,分別在文件heap_1.c,heap_2.c, heap_3.c,heap_4.c,heap_5.c種。 雖然標(biāo)準(zhǔn)C庫中的 malloc()和 free()也可以實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存管理,但是它有以下缺陷: 1、在小型嵌入式系統(tǒng)種效率不高。 2、線程不安全。 3、具有不確定性,每次執(zhí)行的時(shí)間不同。 4、會(huì)導(dǎo)致內(nèi)

    2024年02月07日
    瀏覽(24)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包