久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    【精品博文】詳細解析基于FPGA的串口通信

     ChinaAET 2020-10-31

    贏一個雙肩背包

    有多難?

    戳一下試試看!

    →_→

    長摁識別

    【主題】:詳細解析基于FPGA的串口通信

    【作者】:LinCoding

    【時間】:2016.11.28

      串口通信,用的實在太廣泛了,原理也很簡單,在串口通信中最重要的是波特率的概念,大家不懂的可以百度,然后就是數(shù)據(jù)的格式,1位起始位,8位數(shù)據(jù)位,1位校驗位(可選),1位停止位。

    先看結(jié)果(PC端將數(shù)據(jù)下發(fā)至FPGA,F(xiàn)PGA接收到數(shù)據(jù)后原封不動的將數(shù)據(jù)上發(fā)至PC端):

    (源碼出自CrazyBingo,尊重版權(quán))

      下面直接看程序。

      1、串口接收模塊

    module uart_receiver ( input clk, //global clock input rst_n, //global reset input clk_16bps, input rxd, output reg [7:0]         rxd_data, output reg rxd_flag );

     第一部分是輸入輸出定義,有兩點需要注意:

     1、對于串口接收模塊,原理上要使用16倍的波特率去采樣,因此輸入clk_16bps,而這個輸入信號來自于——《詳細解析基于FPGA的任意分頻》這篇文章的divide_clken的輸出。

     2、對于一個系統(tǒng)而言是由眾多模塊組成的,模塊與模塊之間肯定是要溝通的,那么對于輸入模塊而言,就就少不了“輸入完成標志位信號”,并且,這個標志位信要與輸入的數(shù)據(jù)同步輸出,如本例中的rxd_flag,還有之前按鍵消抖文章中的key_flag,SPI文章中的rxd_flag都是如此,因此,這也要成為一個固定的模式!

    //----------------------------------- //synchronize the input signal reg rxd_sync; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) rxd_sync <= 1'b1; else rxd_sync <= rxd; end

     第二部分,由于是接收模塊,接收的信號是來自其他時鐘域的,因此,這里需要打一拍將信號同步到自己的時鐘域,當然,打兩拍也是可以的!

    //----------------------------------- //receive FSM: encode localparam R_IDLE  = 4'd0; localparam R_START = 4'd1; localparam R_SAMPLE = 4'd2; localparam R_STOP = 4'd3; //----------------------------------- //receive FSM localparam SMP_TOP  = 4'd15; localparam SMP_CENTER = 4'd7; reg [3:0] smp_cnt; reg [3:0] rxd_cnt; reg [3:0] rxd_state; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) begin smp_cnt <= 4'd0; rxd_cnt <= 4'd0; rxd_state <= R_IDLE; end else case ( rxd_state ) R_IDLE: begin smp_cnt <= 4'd0; rxd_cnt <= 4'd0; if ( ! rxd_sync ) rxd_state <= R_START; else rxd_state <= R_IDLE; end R_START: if ( clk_16bps ) begin smp_cnt <= smp_cnt + 1'b1; if ( smp_cnt == SMP_CENTER && rxd_sync ) begin rxd_cnt <= 4'd0; rxd_state <= R_IDLE; end else if ( smp_cnt == SMP_TOP ) begin rxd_cnt <= 4'd1; rxd_state <= R_SAMPLE; end else begin rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end end else begin smp_cnt <= smp_cnt; rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end R_SAMPLE: if ( clk_16bps ) begin smp_cnt <= smp_cnt + 1'b1; if ( smp_cnt == SMP_TOP )     begin if ( rxd_cnt < 4'd8 )     begin rxd_cnt        <= rxd_cnt + 1'b1; rxd_state      <= R_SAMPLE;              end else     begin rxd_cnt <= 4'd9; rxd_state <= R_STOP;     end     end else begin rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end end else begin smp_cnt <= smp_cnt; rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end R_STOP: if ( clk_16bps ) begin smp_cnt <= smp_cnt + 1'b1; if ( smp_cnt == SMP_TOP ) begin rxd_cnt <= 4'd0; rxd_state <= R_IDLE; end else begin rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end end else begin smp_cnt <= smp_cnt; rxd_cnt <= rxd_cnt; rxd_state <= rxd_state; end default: begin smp_cnt <= 4'd0; rxd_cnt <= 4'd0; rxd_state <= R_IDLE; end endcase end

       第三部分就是長長的狀態(tài)機了,真的是太長了,不過基本都是重復(fù)的內(nèi)容,有以下幾點需要注意:

      1、對于串口接收模塊,我們?yōu)榱耸菇邮盏降臄?shù)據(jù)最大程度的穩(wěn)定,使用了16倍波特率去采樣,也就是在每個數(shù)據(jù)位上,有16個采樣點,這樣的話,當然在最中間的采樣點的數(shù)據(jù)原則上是最穩(wěn)定的,事實也確實如此。因此需要一個smp_cnt信號去計數(shù)當前是第幾個采樣點,并且當采樣到16個點后使得rxd_cnt加1以采樣下一位數(shù)據(jù)。

     2、使用rxd_cnt信號 來計數(shù)獲得當前采樣的第幾位數(shù)據(jù)。需要注意的是本always塊只涉及rxd_cnt的變遷,而在另一個always中會根據(jù)rxd_cnt的情況來取值各個位上的數(shù)據(jù)。

     3、由于串口傳輸?shù)奶匦裕鹗嘉蛔優(yōu)榈碗娖揭暈閭鬏數(shù)拈_始,因此也就意味著,起始位其實是個傳輸開始的使能信號,所以要使用狀態(tài)機的IDLE態(tài)來始終監(jiān)測起始位是否變化。

    reg [7:0] rxd_data_r; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) rxd_data_r <= 8'd0; else if ( rxd_state == R_SAMPLE && clk_16bps && smp_cnt == SMP_CENTER ) case ( rxd_cnt ) 4'd1 : begin rxd_data_r[0] <= rxd_sync; end 4'd2 : begin rxd_data_r[1] <= rxd_sync; end 4'd3 : begin rxd_data_r[2] <= rxd_sync; end 4'd4 : begin rxd_data_r[3] <= rxd_sync; end 4'd5 : begin rxd_data_r[4] <= rxd_sync; end 4'd6 : begin rxd_data_r[5] <= rxd_sync; end 4'd7 : begin rxd_data_r[6] <= rxd_sync; end 4'd8 : begin rxd_data_r[7] <= rxd_sync; end default : begin rxd_data_r <= 8'd0;  end endcase else if ( rxd_state == R_STOP ) rxd_data_r <= rxd_data_r; else rxd_data_r <= rxd_data_r; end

     第四部分就是取值了,根據(jù)rxd_cnt的值來取值不同位上的數(shù)據(jù)。

    always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) begin rxd_data <= 8'd0; rxd_flag <= 1'b0; end else if ( rxd_state == R_STOP && clk_16bps && smp_cnt == SMP_TOP ) begin rxd_data <= rxd_data_r; rxd_flag <= 1'b1; end else begin rxd_data <= rxd_data; rxd_flag <= 1'b0; end end

       第五部分就是同步輸出rxd_data和rxd_flag信號了。而為了同步輸出,同樣又采用了一級D觸發(fā)器來給rxd_data打了一拍,使其與rxd_flag同步輸出。

    2、串口發(fā)送模塊

    module uart_transfer ( input clk, //global clock input rst_n, //global reset input clk_16bps, input txd_en, input [7:0]         txd_data, output reg          txd, output reg txd_flag );

     第一部分是輸入輸出定義, 既然是發(fā)送模塊,當然需要發(fā)送使能信號了,其次,還需要發(fā)送完成標志位信號。當然了,既然有了發(fā)送使能信號,狀態(tài)機是不可避免的了。clk_16bps與串口接收模塊同理。

    //----------------------------------- //receive FSM: encode localparam T_IDLE  = 4'd0; localparam T_SEND = 4'd1; //----------------------------------- //receive FSM localparam SMP_TOP  = 4'd15; localparam SMP_CENTER = 4'd7; reg [3:0] smp_cnt; reg [3:0] txd_cnt; reg [3:0] txd_state; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) begin smp_cnt <= 4'd0; txd_cnt <= 4'd0; txd_state <= T_IDLE; end else case ( txd_state ) T_IDLE: begin smp_cnt <= 4'd0; txd_cnt <= 4'd0; if ( txd_en ) txd_state <= T_SEND; else txd_state <= T_IDLE; end T_SEND: if ( clk_16bps ) begin smp_cnt <= smp_cnt + 1'b1; if ( smp_cnt == SMP_TOP )     begin         if ( txd_cnt < 4'd9 )     begin         txd_cnt      <= txd_cnt + 1'b1; txd_state    <= T_SEND;     end else          begin      txd_cnt <= 4'd0; txd_state <= T_IDLE;     end end else begin txd_cnt <= txd_cnt; txd_state <= txd_state; end end else begin smp_cnt <= smp_cnt; txd_cnt <= txd_cnt; txd_state <= txd_state; end default: begin smp_cnt <= 4'd0; txd_cnt <= 4'd0; txd_state <= T_IDLE; end endcase end

     第二部分就是長長的發(fā)送狀態(tài)機了,相比串口接收模塊相對簡單,而與之不同的是,由于是發(fā)送,我們只需在數(shù)據(jù)的第一個采樣點將數(shù)據(jù)發(fā)送出去,在采樣點到達16個時,換下一位數(shù)據(jù)進行發(fā)送就可以了。

    always @ ( * ) begin if ( txd_state == T_SEND ) case ( txd_cnt ) 4'd0 : begin txd = 1'b0;   end 4'd1 : begin txd = txd_data[0]; end 4'd2 : begin txd = txd_data[1]; end 4'd3 : begin txd = txd_data[2]; end 4'd4 : begin txd = txd_data[3]; end 4'd5 : begin txd = txd_data[4]; end 4'd6 : begin txd = txd_data[5]; end 4'd7 : begin txd = txd_data[6]; end 4'd8 : begin txd = txd_data[7]; end 4'd9 : begin txd = 1'b1;   end default : begin txd = 1'b1;   end endcase else txd = 1'b1; end

       第三部分就是根據(jù)txd_cnt的計數(shù)來發(fā)送數(shù)據(jù)了,注意的是本模塊為組合邏輯,為的就是使數(shù)據(jù)對齊!

    圖1

    圖2

      圖2是圖1的放大版本,可見,在時鐘邊沿是對齊的。

      如果改為時序邏輯,如下程序:

    always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) txd <= 1'b1; else if ( txd_state == T_SEND ) case ( txd_cnt ) 4'd0 : begin txd = 1'b0;   end 4'd1 : begin txd = txd_data[0]; end 4'd2 : begin txd = txd_data[1]; end 4'd3 : begin txd = txd_data[2]; end 4'd4 : begin txd = txd_data[3]; end 4'd5 : begin txd = txd_data[4]; end 4'd6 : begin txd = txd_data[5]; end 4'd7 : begin txd = txd_data[6]; end 4'd8 : begin txd = txd_data[7]; end 4'd9 : begin txd = 1'b1;   end default : begin txd = 1'b1;   end endcase else txd = 1'b1; end

       則如下圖所示:

      txd比txd_cnt晚了一個clk,當然晚了一個clk是無所謂的,但是為了時序的完美,還是用組合邏輯吧!

    always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) txd_flag <= 1'b0; else if ( txd_state == T_SEND && clk_16bps && smp_cnt == SMP_TOP && txd_cnt == 4'd9 ) txd_flag <= 1'b1; else txd_flag <= 1'b0; end

      最后一個部分就是輸出發(fā)送完成標志位信號。

      這樣一個基于FPGA的串口通信就完成了,在頂層模塊中我們可以將接收到的數(shù)據(jù)直接交給發(fā)送模塊,通過PC端的串口調(diào)試助手下發(fā)數(shù)據(jù),然后FPGA會原封不動的將數(shù)據(jù)再發(fā)回來完成板級驗證。

    總結(jié):

     1、對于接收類的模塊,接收完成后需要有接收完成標志位信號,并且要與所接收的數(shù)據(jù)同步輸出。

      2、對于發(fā)送類的模塊,要有發(fā)送使能信號和發(fā)送完成標志位信號,并且使用狀態(tài)機的IDLE態(tài)來監(jiān)測發(fā)送使能信號的變化。

      轉(zhuǎn)藏 分享 獻花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 欧美日韩精品一区二区三区高清视频| 久久精品日日躁夜夜躁欧美| 天堂中文官网在线| 18禁美女裸体爆乳无遮挡| 四虎影视一区二区精品| 亚洲AV美女在线播放啊| 午夜无码区在线观看亚洲| 中国熟妇毛多多裸交视频| 国产欧美日韩A片免费软件| 377P欧洲日本亚洲大胆| 国产香蕉尹人在线视频你懂的| 一本一道av中文字幕无码| 国产欧美日韩高清在线不卡| 97人人添人人澡人人澡人人澡 | 亚洲一区在线成人av| 久久精品国产亚洲AV麻豆长发| 欧美激情一区二区三区成人 | 99RE6在线观看国产精品| 亚洲AV无码专区在线电影天堂| 综合色一色综合久久网| 国产精品无码专区| 又色又污又爽又黄的网站| 中文字幕无码av不卡一区| 亚洲 校园 欧美 国产 另类| 亚洲精品一区二区麻豆| 午夜男女爽爽爽影院在线视频| 亚洲国产日韩一区三区| AV色欲无码人妻中文字幕| 天天影视网色香欲综合网| 国产精品大片中文字幕| 精品欧美一区二区在线观看| 成人无码潮喷在线观看| 人妻少妇不满足中文字幕| AV人摸人人人澡人人超碰| 亚洲第一精品一二三区| 国产日产久久高清欧美一区| 波多野结衣久久一区二区| 国产成人亚洲综合图区| 无码高潮爽到爆的喷水视频| 国产SM重味一区二区三区| 国产久免费热视频在线观看|