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

    FPGA實現串口UART自收發

     quasiceo 2016-01-07

    FPGA實現串口UART自收發

    0推薦
    發表于 2014/7/22 9:45:03 閱讀(2105) 評論(0)

           串行接口是最簡單的一種通信方式,串口通信有兩種方式,一種是同步串行,如SPI接口;另一種則是異步串行,即我們所說的UART。這個項目向大家展示了如何使用FPGA來模擬UART收發器。

    (一) UART接口工作原理

    普遍意義上講,UART接口是分為兩種:

    a)TTL電平接口

    b)RS232電平接口

           通常我們看不到設備直接裸露出來的TTL電平接口,TTL電平接口一般為芯片直接連接的引腳,一般是供我們調試設備用的端口。實際上我們看得到的都是RS232電平接口,這個在臺式機上很常見,有DB9和DB25兩種規格插頭。

    TTL電平UART和電腦相連接,有以下幾種方式:

    a)TTL電平接口通過PL2303(PL2302)即USB轉TTL電平芯片和電腦相連

    b)如果電腦上直接有RS232接口,則可通過MAX232芯片將RS232電平與TTL電平轉換

    c)當然,如果FPGA端已經板載了MAX232,你又不想重新自己引出TTL電平UART的話,如果電腦上又沒有RS232接口,則通過一根USB轉串口線(PL2303+MAX232)與FPGA端RS232接口連接

    異步串行通訊

    RS-232使用異步通訊協議,也就是說數據的傳輸沒有時鐘信號。數據以每次一位的方式傳輸;每條線用來傳輸一個方向的數據。通常是以8位數據為1個字節,先發送最低有效位,最后發送最高有效位。接收端必須有某種方式,使之與接收數據同步。

    對于RS-232來說,是這樣處理的:

    1. 串行線纜的兩端事先約定好串行傳輸的參數:傳輸速度(波特率)、傳輸格式(幾位數據,有無校驗位,有幾位停止位)等
    2. 當沒有數據傳輸的時候,發送端向數據線上發送"1" ,也即空閑狀態,總線默認拉高。
    3. 每傳輸一個數據之前,發送端先發送一個"0"來表示傳輸已經開始。接收端檢測到下降沿后便開始接收數據。
    4. 開始傳輸后,數據以約定的速度和格式傳輸,所以接收端可以與之同步

    每次傳輸完成一個字節之后,都在其后發送一個停止位("1")

    (二) 波特率發生器

           我們選擇的是9600的波特率,這個參數可以根據實際需要來調整。實際上,針對固定的時鐘頻率,可以事先做成一個分頻比表格,這樣就可以方便的調節波特率了。

    FPGA通常運行在遠高于9600Hz的時鐘頻率上(對于今天的標準的來說RS-232真是太慢了),我們使用板載的50MHz的晶振作為波特率發生器的輸入時鐘,這就意味著我們需要用一個較高的時鐘來分頻產生盡量接近于9600Hz的時鐘信號。

    1)        循環波特率發生器

    我們希望5000000是2的整數冪,但很可惜,它不是。所以我們改變分頻比,"5000000/9600" 約等于 "2^17/25" = 5242.88. 這跟我們要求的分頻比5208.333很比較近,并且使得在FPGA上實現起來相當有效。

    reg [17:0] acc; //一共18位

    always @(posedge clk)

    acc <= acc[16:0] + 25; //我們使用上一次結果的低17位,但是保留18位結果

    wire BaudTick = acc[17]; //第18位作為進位輸出

           使用 50MHz 時鐘, "BaudTick" 為 9537波特,與理想的9600波特存在 0.65% 的誤差,誤差太高,實際上我們采樣數據的時候都是在波特率周期一半的時間采樣,似乎影響不大,但是考慮到發送數據量大的時候會積累出很大的周期偏移,故我們不采用此種分配方式。

           我們的全局時鐘周期為1/50MHz=20ns,而要求的9600波特率的周期為1/9600Hz=104.2us,兩者的倍數關系為5210,即按照上述算出來的波特率周期為104.9us。按照標準波特率產生的數據,進行采樣的時候,為了保證采樣數據的穩定正確,我們在數據的中間點采樣,由于我們產生的波特率偏小,導致每次應該在52.1us采樣的數據推延到52.45us,亦即每1bit會使實際的采樣點延后正常的采樣點3.45us,故發送完15bit之后,數據的采樣點會偏移到下一個字符,數據便會出現紊亂,出現亂碼。

    2)        參數化FPGA波特率發生器

    由于前面所述的波特率發生器設置方法產生的偏差過大,故我們采用以下波特率產生方式即暴力直接累加法。


    //以下波特率分頻計數值可參照需要設計的參數進行更改
    `define   BPS_PARA        5208      //波特率為9600時的分頻計數值
    `define BPS_PARA_2           2604      //波特率為9600時的分頻計數值的一半,用于數據采樣
    always @ (posedge clk or negedge rst_n)
           if(!rst_n) cnt <= 13'd0;
           else if((cnt == `BPS_PARA) || !bps_start) cnt <= 13'd0;  //波特率計數清零
           else cnt <= cnt+1'b1;                //波特率時鐘計數啟動

    這就是整個的設計方法了。

    按照此種方法設計的波特率發生器,波特率為50MHz/5208=9600.6,與標準9600波特率誤差為0.00625%,已經相當精確。

    (三) TX發送模塊

    下面是我們所想要實現的:

     

    接收模塊傳送8位數據到發送模塊,rx_int信號使能tx_en,8位數據被串行輸出。("tx_en"置位后開始傳輸)。

    TX發送模塊的參數是固定的: 8位數據, 1個停止位, 無奇偶校驗。

           數據串行化

           經過上述的波特率發生器,我們已經產生了9600的波特率。

    由于我們的程序功能實現的是,將接收來的數據發送回去,程序如下:

    if(neg_rx_int) begin   //接收數據完畢,準備把接收到的數據發回去

                         bps_start_r <= 1'b1;

                         tx_data <= rx_data;   //把接收到的數據存入發送數據寄存器

                         tx_en <= 1'b1;            //進入發送數據狀態中

    在always模塊中進行數據發送的判斷,

    if(tx_en) begin                         //使能發送的信號
                         if(clk_bps)    begin            //波特率時鐘到后開始發送
                                       num <= num+1'b1;
                                       case (num)
                                              4'd0: rs232_tx_r <= 1'b0;        //發送起始位
                                              4'd1: rs232_tx_r <= tx_data[0];      //發送bit0
                                              4'd2: rs232_tx_r <= tx_data[1];      //發送bit1
                                              4'd3: rs232_tx_r <= tx_data[2];      //發送bit2
                                              4'd4: rs232_tx_r <= tx_data[3];      //發送bit3
                                              4'd5: rs232_tx_r <= tx_data[4];      //發送bit4
                                              4'd6: rs232_tx_r <= tx_data[5];      //發送bit5
                                              4'd7: rs232_tx_r <= tx_data[6];      //發送bit6
                                              4'd8: rs232_tx_r <= tx_data[7];      //發送bit7
                                              4'd9: rs232_tx_r <= 1'b1;  //發送結束位
                                             default: rs232_tx_r <= 1'b1;
                                              endcase
                                end
                         else if(num==4'd10) num <= 4'd0; //復位
                  end
    end

    最后將發送的數據送到總線上,

    assign rs232_tx = rs232_tx_r;

    (四) RX接收模塊

    下面是我們想要實現的模塊: 

     我們的設計目的是這樣的:

         1.當rs232_rx線上有數據時,接收模塊負責識別rs232_rx線上的數據

         2.當收到一個字節的數據時,鎖存接收到的數據到"rx_data"總線,并使"rx_int"有效一個周期。

    注意:只有 當"rx_int"有效時," rx_data "總線的數據才有效,其他的時間里不允許使用" rx_data "總線上的數據,因為新的數據可能已經改變了其中的部分數據。

           數據采樣

           異步接收機必須通過一定的機制與接收到的輸入信號同步(接收端沒有辦法得到發送斷的時鐘),這里采用如下辦法:

           為了確定新數據的到來,需檢測開始位,我們在波特率時鐘周期的一半處進行數據的采樣。

           首先,接收到的" rx_data "信號與我們的時鐘沒有任何關系,所以采用4個D觸發器對其進行采樣,并且使之我我們的時鐘同步,同時也是對接收到的數據進行濾波,這樣可以防止毛刺信號被誤認為是開始信號。

    reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;   //接收數據寄存器,濾波用
    wire neg_rs232_rx;    //表示數據線接收到下降沿
    always @ (posedge clk or negedge rst_n) begin
           if(!rst_n) begin
                         rs232_rx0 <= 1'b1;
                         rs232_rx1 <= 1'b1;
                         rs232_rx2 <= 1'b1;
                         rs232_rx3 <= 1'b1;
                  end
           else begin
                         rs232_rx0 <= rs232_rx;
                         rs232_rx1 <= rs232_rx0;
                         rs232_rx2 <= rs232_rx1;
                         rs232_rx3 <= rs232_rx2;
                  end
    end

           //下面的下降沿檢測可以濾掉<20ns-40ns的毛刺(包括高脈沖和低脈沖毛刺),

           //這里就是用資源換穩定(前提是我們對時間要求不是那么苛刻,因為輸入信號打了好幾拍)

           //我們的有效低脈沖信號肯定是遠遠大于40ns的,104us。

    assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;     //接收到下降沿后neg_rs232_rx置高一個時鐘周期

     一旦檢測到"開始位",使用如下的代碼可以檢測出接收到每一位數據。

    if(rx_int) begin    //一旦檢測到開始位,即rs232_rx下降沿,rx_int置位
                  if(clk_bps) begin //讀取并保存數據,接收數據為一個起始位,8bit數據,1個結束位            
                                num <= num+1'b1;
                                case (num)
                                              4'd1: rx_temp_data[0] <= rs232_rx;      //鎖存第0bit
                                              4'd2: rx_temp_data[1] <= rs232_rx;      //鎖存第1bit
                                              4'd3: rx_temp_data[2] <= rs232_rx;      //鎖存第2bit
                                              4'd4: rx_temp_data[3] <= rs232_rx;      //鎖存第3bit
                                              4'd5: rx_temp_data[4] <= rs232_rx;      //鎖存第4bit
                                              4'd6: rx_temp_data[5] <= rs232_rx;      //鎖存第5bit
                                              4'd7: rx_temp_data[6] <= rs232_rx;      //鎖存第6bit
                                              4'd8: rx_temp_data[7] <= rs232_rx;      //鎖存第7bit
                                              default: ;//起始位和停止位均通過default去除
                                       endcase
           使用一個寄存器來存儲接受到的數據,
                   if(num == 4'd10) begin   //標準接收模式下只有1+8+1=10bit有效數據
                                num <= 4'd0;                     //接收到STOP位后結束,num清零
                                rx_data_r <= rx_temp_data;    //把數據鎖存到數據寄存器rx_data
                         end

    利用此寄存器來驅動模塊間接口rx_data,

    assign rx_data = rx_data_r;

    RX模塊中,以下兩處num清零及波特率發生信號關閉的信號的輸出,在num==4'd10或者num==4'd9時做出判斷程序的功能正常,我的理解是:

    在num==4'd10做出判斷是正常的選擇,因為要接受第10位停止位;而在num==4'd9做出判斷功能正常,原因在于,雖然沒有接收第10位,但是因為第10位是停止位,是高電平,在接收下一個符號的時候我只監測總線上的下降沿,不管高電平的時間長度,故num==4‘d也不影響程序的功能。僅為個人看法,拋磚引玉。

      if(num == 4'd10) begin   //標準接收模式下只有1+8+1=10bit有效數據
                                num <= 4'd0;                     //接收到STOP位后結束,num清零
                                rx_data_r <= rx_temp_data;    //把數據鎖存到數據寄存器rx_data
                         end
    if(num==4'd10) begin		//接收完有用數據信息
    			bps_start_r <= 1'b0;	//數據接收完畢,釋放波特率啟動信號
    			rx_int <= 1'b0;			//接收數據中斷信號關閉

    (五)  發送模塊和接收模塊的連接

           為了更好的驗證功能,我們設計的UART接口實現如下的功能:

           整個UART模塊對外提供了RX、TX接口,實現的是接收與之連接的設備發送來的數據,而后又發送回去。應用在電腦上,就是說RX模塊接收上位機串口調試助手發送給FPGA的數據,然后利用其中的TX模塊將數據又送回到上位機,顯示在電腦的串口調試助手中。

           可以看到,RX模塊和TX模塊的端口定義是有一定關系的,兩個模塊中,clk,rst_n為全局時鐘和復位信號,也為外部硬件的輸入信號端口,bps_start為輸出的波特率啟動信號,clk_bps為波特率時鐘信號。

     

           在TX模塊中,rs232_tx為硬件輸出端口,rx_data以及rx_int為輸入信號,

    module my_uart_tx(

                                clk,rst_n,

                                rx_data,rx_int,rs232_tx,

                                clk_bps,bps_start

                         );

           RX模塊,rs232_rx為硬件輸入端口,rx_data以及rx_int為輸出信號,

    module my_uart_rx(

                                clk,rst_n,

                                rs232_rx,rx_data,rx_int,

                                clk_bps,bps_start

                         );

           可以很清楚的看到,兩個模塊,通過rx_int以及rx_data作為信號交換的接口,來完成輸入數據的轉發。

           到這里,UART接口的設計就完成了。

    以下為整個工程UART代碼。

      本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
      轉藏 分享 獻花(0

      0條評論

      發表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 欧美视频专区一二在线观看| 免费无码又爽又刺激高潮虎虎视频| 小污女小欲女导航| 重口SM一区二区三区视频| 亚洲国产成人久久精品APP| 成人无码小视频在线观看| 亚洲男人AV天堂午夜在| 精品乱码一区二区三区四区 | 粗大的内捧猛烈进出小视频| 超频97人妻在线视频| 欧美嫩交一区二区三区| 欧美丰满熟妇xxxx性| 久久亚洲精品情侣| 亚洲中文久久久精品无码| 国产肉丝袜在线观看| 深夜释放自己在线观看| 好吊妞国产欧美日韩免费观看| 中文字幕国产日韩精品| 大学生久久香蕉国产线看观看| 亚洲偷自拍国综合| 亚洲午夜久久久久久久久电影网| 亚洲国产良家在线观看| 97人妻碰碰视频免费上线| 精品无码久久久久成人漫画| 国产成人一区二区不卡| 天天夜碰日日摸日日澡性色AV| 婷婷色香五月综合缴缴情香蕉| 久久精品国产亚洲AV忘忧草18| 在线理论三级午夜电影| 午夜免费福利小电影| 宝贝扒开下面自慰给我看| 国内少妇偷人精品免费| 国产成人精品无码播放| 日韩精品久久久久久久电影蜜臀| 最新国产AV最新国产在钱| 丝袜人妻一区二区三区网站| 人妻在线无码一区二区三区| 国产精品电影久久久久电影网 | 日韩精品一区二区三区影院| 草裙社区精品视频播放| 国产成熟女人性满足视频|