博主的念叨
最近趁熱打鐵做了一個(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)用。文章來源:http://www.zghlxwxcb.cn/news/detail-459712.html
總結(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)!