99在线精品免费视频九九视-99在线精品视频-99在线精品视频免费观里-99在线精品视频在线观看-99在线免费播放



官方論壇
官方淘寶
官方博客
微信公眾號
點擊聯系吳工 點擊聯系周老師

【案例】出租車計費系統設計

發布時間:2021-06-16   作者:admin 瀏覽量:

本文為明德揚原創及錄用文章,轉載請注明出處


1.1 總體設計


1.1.1 概述


    學習了明德揚至簡設計法和明德揚設計規范,本人設計了一個基于FPGA的出租車計費系統。該系統由一個按鍵表示出租車上是否有乘客,再通過檢測出租車的檔位和輪胎的轉速來獲得乘客所需支付的總費用。在本案例的設計過程中,包含了按鍵定義和消抖、計數器、數碼管顯示等技術。經過逐步改進、調試等一系列操作之后,完成了此設計,下面將完整的設計記錄與大家分享。

本工程使用VIVADO進行仿真,未進行上板。


1.1.2 設計目標


     此設計可以完成出租車計費的功能。在按鍵按下后(乘客上車),開始按如下規則計費:

   起步價5元,超過3KM以每公里2元計費,如果遇到紅綠燈、堵車等需要停車等待時,則以每20分鐘1元計費。

     再按一下按鍵(乘客下車)則計費結束,算出乘客所需支付的總費用,并在數碼管上顯示。

1.1.3 系統結構框圖

     

 系統結構框圖如下所示:



1.1.4 模塊功能



  • key模塊實現功能


      由于按鍵信號是異步信號,所以對該將信號打兩拍處理,將異步信號同步化;

      實現10ms按鍵消抖功能,并輸出按鍵信號key_flag,每按一次按鍵,key_flag取反。

    其中key_flag=1表示有乘客,key_flag=0表示沒有乘客。

  • speed模塊實現功能
    通過按鍵信號key_flag有效,對輪胎轉速進行每秒取樣,進而獲得乘客行駛總的路程和判斷按時計費的使能信號en_0是否有效。

  • fare模塊實現功能
    通過speed模塊的總路程和按時計費有效使能信號來獲得乘客所需支付的總費用,再取得總費用的個位、十位和百位數值并輸出。

  • show模塊實現功能
    采用動態掃描3個數碼管的方式顯示fare模塊中獲得的三個數值即為乘客所需支付的總費用。

1.1.5 頂層信號
信號名
接口方向
定義
clk
輸入
系統時鐘
rst_n
輸入
系統復位
d_w
輸入
出租車的檔位信號,空擋為0,其他檔位為1
rev
輸入
出租車輪胎轉速信號。通過該信號,可以用來計算汽車行駛的距離。
key_in
輸入
乘客上、下車指示信號。
使用按鍵來表示。
seg_sel
輸出
3個數碼管的位選信號,在低電平是該位置數碼管亮。
segment
輸出
段選信號,共8位。由低到高,分別表示數碼管的a,b,c,d,e,f,g,點。當該比特為0時,表示點亮相應位置;為1時熄滅。

1.1.6 頂層代碼
module taxi_fare(
    clk     ,
    rst_n   ,
    d_w     ,
    rev     ,
    key_in  ,

    seg_sel  ,
    segment  
    );

    input             clk       ;      
    input             rst_n     ;
    input             d_w       ;          //檔位
    input  [15:0]     rev       ;          //轉速
    input             key_in    ;          //按鍵,表示乘客是否上車

    output            seg_sel   ;          //位選信號,在低電平是該位置數碼管亮。
    output            segment   ;          //段選信號,共8位。由低到高,分別表示數碼管的a,b,c,d,e,f,g,點。當該比特為0時,表示點亮相應位置;為1時熄滅。

    wire   [3-1:0]      seg_sel    ;
wire   [8-1:0]      segment   ;

    //中間量
    wire              key_flag   ;         //有乘客為1,無乘客為0
    wire              en_0      ;        //按時間計費使能信號
    wire   [19:0]     distance   ;         //行駛路程
    wire   [3:0]      x_g        ;         //總費用的個位值
    wire   [3:0]      x_s        ;         //總費用的十位值
    wire   [3:0]      x_b        ;         //總費用的百位值

    key_litter key_litter_0(
        .clk        (clk)      ,
        .rst_n      (rst_n)    ,
        .key_in     (key_in)   ,

        .key_flag   (key_flag)
    );

    speed speed_0(
        .clk           (clk)        ,
        .rst_n         (rst_n)      ,
        .d_w           (d_w)        ,
        .rev           (rev)        ,
        .key_flag      (key_flag)   ,
        //輸出信號
        .en_0          (en_0)       ,
        .distance      (distance)
    );

    fare fare_0(
        .clk           (clk)         ,
        .rst_n         (rst_n)       ,
        .distance      (distance)    ,
        .en_0          (en_0)        ,
        .key_flag      (key_flag)    ,

        .x_g           (x_g)         ,
        .x_s           (x_s)         ,
        .x_b           (x_b)
    );

    show show_0(
        .clk           (clk)            ,
        .rst_n         (rst_n)          ,
        .din           ({x_b,x_s,x_g})  ,
        .din_vld       (3'b111)         ,
        .disp_en       (1)              ,

        .seg_sel       (seg_sel)        ,
        .segment      (segment)   
         );

endmodule


1.2 key模塊設計


1.2.1 接口信號

信號
接口方向
定義
clk
輸入
系統時鐘
rst_n
輸入
系統復位
key_in
輸入
按鍵輸入。使用按鍵來表示乘客上、下車。
key_flag
輸出
輸出表示有無乘客。1為有乘客,0為沒有乘客



1.2.2 設計思路


    此模塊通過一個按鍵來表示車上是否有乘客,復位時輸出信號key_flag0,當乘客上車時,司機按一下按鍵,key_flag0,1,表明乘客已上車,系統開始計費。到達目的地時,司機再按一下按鍵,key_flag10,表明到達目的地乘客下車,系統結束計費。

  • 硬件電路

    

    獨立式按鍵工作原理如上圖所示,4條輸入線接到FPGAIO口上,當按鍵K1按下時,VCC通過電阻R1再通過按鍵K1最終進入GND形成一條通路,這條線路的全部電壓都加在R1上,則引腳P14是低電平。當松開按鍵后,線路斷開,就不會有電流通過,P14VCC就應該是等電位,為高電平。我們可以通過P14這個IO口的高低電平狀態來判斷是否有按鍵按下。其它按鍵原理與K1一致,當然本實驗只需要一個按鍵即可,任選一個按鍵都可以。


    從圖中可以看出,如果我們按下按鍵,那么按鍵就會接通并連接到電平GND,如我們沒有按下,那么按鍵就會斷開并接VCC因此按鍵為低電平有效。通常的按鍵所用開關為機械彈性開關,當機械觸點斷開或者閉合時,由于機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上穩定地接通,在斷開時也不會一下子斷開。因而機械式按鍵在閉合及斷開的瞬間均伴隨有一連串的抖動,如果不進行處理,會使系統識別到抖動信號而進行不必要的反應,導致模塊功能不正常,為了避免這種現象的產生,需要進行按鍵消抖的操作。

  • 按鍵消抖
    按鍵消抖主要分為硬件消抖和軟件消抖。兩個與非門構成一個RS觸發器為常用的硬件軟件方法抖,即檢測出鍵閉合后執行一個延時程序,抖動時間的長短由按鍵的機械特性決定,一般為5ms20ms讓前沿抖動消失后再一次檢測鍵的狀態,如果仍保持閉合狀態電平,則確認按下按鍵操作有效。當檢測到按鍵釋放后,也要給5ms20ms的延時,待后沿抖動消失后才能轉入該鍵的處理程序。經過按鍵消抖的行人優先按鍵,判斷按鍵有效后,按鍵信號傳遞給控制系統,控制系統再進入相應的處理程序。如還不明白之處,見實驗的PDF。





5.1.2按鍵消抖示意圖


1.2.3 參考代碼


    使用明德揚的計數器模板,可以很快速很熟練地寫出按鍵消抖模塊。

    每10ms掃描一次按鍵輸入key_in,可以達到消抖的目的,再用寄存器緩存一下,按鍵為低電平有效;但本實驗是需要按鍵按下松開這樣一次完整的按按鍵操作后輸出key_flag才發生變化,所以檢測當檢測到按鍵有上升沿變化時,代表該按鍵被按下松開,按鍵輸出key_flag才發生變化。

  本模塊設計了一個狀態機,用于按鍵的檢測。該狀態機共包括4個狀態。


  其各個狀態的含義如下。


  空閑狀態(IDLE):表示按鍵沒有被按下,檢測到低電平進入下一狀態。

  延時確認狀態(S1):開始10ms延時計數,若計數完成且依然為低電平則進入下一狀態,若計數期間按鍵出現高電平說明為抖動回到初始狀態。

  檢測釋放狀態(S2):表示按鍵按下未松開,檢測到高電平進入下一狀態。

  延時確認狀態(S3):開始10ms延時計數,若計數完成且依然為高電平視為有效松手行為進入初始狀態再檢測下一次按鍵按下,若計數期間按鍵出現低電平說明為抖動回到S2狀態。

   代碼如下:

module key_litter(
    clk    ,
    rst_n  ,
    key_in ,

    key_flag
    );

    //消抖的狀態
    parameter      IDLE  =  4'b0000 ;
    parameter      S1  =  4'b0001   ;
    parameter      S2  =  4'b0010   ;
    parameter      S3  =  4'b0100   ;
    parameter      S4  =  4'b1000   ;

    //輸入信號定義
    input               clk    ;
    input               rst_n  ;
    input               key_in ;

    //輸出信號定義
    output              key_flag;                      //輸出,表示是否有乘客

    //輸出信號reg定義
    reg                 key_flag;

    //中間信號定義
    reg   [19:0]        cnt     ;

    reg                 key_in_1;                      //寄存器
    reg                 key_in_2;                      //寄存器

    reg         [3:0]        state_c ;
    reg         [3:0]        state_n ;

    wire                 idl2s1_start  ;
    wire                 s12s2_start   ;
    wire                 s12s2_end     ;
    wire                 s22s3_start   ;
    wire                 s32s4_start   ;
    wire                 s32s4_end     ;

    wire                add_cnt       ;                 //計數進行
    wire                end_cnt       ;                 //計數清零
    wire                cnt_during    ;                 //計數過程中

    //打兩拍
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            key_in_1 <= 0;
            key_in_2 <= 0;
        end
        else begin
            key_in_1 <= key_in;
            key_in_2 <= key_in_1;
        end
    end

    //消抖
    //四段式狀態機

    //第一段:同步時序always模塊,格式化描述次態寄存器遷移到現態寄存器(不需更改)
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end
    //第二段:組合邏輯always模塊,描述狀態轉移條件判斷
    always@(*)begin
        case(state_c)
            IDLE:begin
                if(idl2s1_start)begin
                    state_n = S1;
                end
                else begin
                    state_n = state_c;
                end
            end
            S1:begin
                if(s12s2_start)begin
                    state_n = S2;
                end
                else if(s12s2_end)begin
                   state_n = IDLE ;
                end
                else begin
                    state_n = state_c;
                end
            end
            S2:begin
                if(s22s3_start)begin
                    state_n = S3;
                end
                else begin
                    state_n = state_c;
                end
            end
            S3:begin
                if(s32s4_start)begin
                    state_n = IDLE;
                end
                else if(s32s4_end)begin
                    state_n = S2;
                end
                else begin
                    state_n = state_c;
                end
            end
            default:begin
                state_n = IDLE;
            end
        endcase
    end
    //第三段:設計轉移條件
    assign idl2s1_start  =  key_in_2 == 0;
    assign s12s2_start =    key_in_2==0 && end_cnt == 1;
    assign s12s2_end  =     key_in_2==1 && cnt_during ;
    assign s22s3_start  =   key_in_2 == 1;
    assign s32s4_start =    key_in_2 == 1 && end_cnt == 1;
    assign s32s4_end  =     key_in_2 == 0 && cnt_during ;
    //第四段:同步時序always模塊,格式化描述寄存器輸出(可有多個輸出)
    always  @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            key_flag <=1'b0 ;     //初始化
        end
        else if(s32s4_start)begin
            key_flag = ~key_flag;
        end
        else begin
            key_flag <= key_flag;
        end
    end

    //計數器,計數10ms
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
        else
            cnt <= 0;
    end
    assign add_cnt = state_c == S1 || state_c == S3;      
    assign end_cnt = add_cnt && cnt== /*仿真時使用以減少仿真時間5 - 1;*/ 500000-1;  
    assign cnt_during = add_cnt && end_cnt == 0;  

    endmodule



1.3 speed模塊設計


1.3.1 接口信號

信號
接口方向
定義
clk
輸入
系統時鐘
rst_n
輸入
系統復位
d_w
輸入
出租車檔位信號,空擋為0、其他檔位為1
rev
輸入
出租車輪胎轉速信號。通過該信號,可以用來計算汽車行駛的距離。
key_flag
輸入
表示有無乘客上車,有乘客為1,沒有為0
en_o
輸出
表示出租車按時間計費信號,1位按時間計費,0則不按時間計費。等待紅綠燈或堵車等需要停車過程中,使用時間計費;行駛過程中,使用距離計費。
distance
輸出
輸出乘客乘坐的總路程



1.3.2 設計思路


   消抖后的按鍵信號輸入到本模塊中,同樣使用明德揚至簡設計法和計數器模板,可以快速寫出計算總路程和獲得按時計費信號en_0有效的代碼。當key_flag1有效且轉速rev>=3r/s時,計數器開始計數,每計一秒鐘對轉速信號rev取樣,獲得每秒行駛路程并累加,當key_flag0時,計數停止,累加也停止,此時獲得的累加值即為總路程。當key_flag1有效且rev<3r/s時,en_0拉高為1,表示此時需要按時計費。


1.3.3 參考代碼
module speed(
    clk    ,
    rst_n  ,
    d_w    ,
    rev    ,
    key_flag,
    //輸出信號
    en_0   ,
    distance
    );

    //參數定義
    parameter      DATA_W =         20;

    //輸入信號定義
    input               clk    ;
    input               rst_n  ;
    input               d_w    ;                         //檔位
    input               rev    ;                         //轉速
    input               key_flag;                        //有無乘客信號

    //輸入信號定義
    wire       [15:0]  rev    ;
    //輸出信號定義
    output[DATA_W-1:0]  distance   ;                     //路程
    output              en_0       ;                     //按時計費信號

    //輸出信號reg定義
    reg   [DATA_W-1:0]  distance   ;
    reg                 en_0       ;

    //中間信號定義
    reg    [15:0]       rev_1      ;                     //每秒取樣轉速信號
    reg   [25:0]        cnt        ;                     //一秒的計數器

    wire                high_en_0  ;                     //拉高按時計費信號的信號
    wire                add_cnt    ;
    wire                end_cnt    ;

    //按時計費
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            en_0 <= 0;
        end
        else if(high_en_0) begin
            en_0 <= 1;
        end
        else
            en_0 <= 0;
    end
    assign high_en_0 = ( d_w == 0 && rev < 3 || rev < 3 ) && key_flag == 1;

    //計數1
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
        else if(key_flag == 0)
            cnt <= 0;
    end
    assign add_cnt = rev >= 3 && key_flag == 1;      
    assign end_cnt = add_cnt && cnt ==  /*仿真時使用以節省仿真時間500-1;*/50000000-1 ;  

    //每秒取樣rev
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            rev_1 <= 0;
        end
        else if(end_cnt) begin
            rev_1 <= rev;
        end
    end

    //distance
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            distance <= 0;
        end
        else if(end_cnt) begin
            distance <=distance +  rev_1/3;
        end
    end

    endmodule

1.4 fare模塊設計


1.4.1 接口信號


信號
接口方向
定義
clk
輸入
系統時鐘
rst_n
輸入
系統復位
distance
輸入
乘客乘坐總路程
en_0
輸入
按時間計費信號,1為按時間計費,0為不按時間計費
key_flag
輸入
表示是否有乘客,有為1,沒有為0,且在沒有乘客時,所有信號歸0
x_g
輸出
輸出總費用的個位數值
x_s
輸出
輸出總費用的十位數值
x_b
輸出
輸出總費用的百位數值



1.4.2 設計思路


     從speed模塊中得到乘客乘坐總路程distance和按時計費使能信號en_0,然后以5元起步價,超過3KM以每滿1公里2元的計費方式計算出按路程計費的總費用,再通過en_020分鐘1元的計費方式計算出按時間計費的總費用,再求和獲得總費用,最后得到總費用得個位、十位、百位,分別是x_gx_sx_b

1.4.3 參考代碼

module fare(
    clk    ,
    rst_n  ,
    distance,
    en_0   ,
    key_flag,

    x_g    ,
    x_s    ,
    x_b   
    );

    //參數定義
    parameter      DATA_W =         4;

    //輸入信號定義
    input               clk    ;
    input               rst_n  ;

    input               en_0   ;                           //按時計費
    input   [19:0]      distance;                          //路程
    input               key_flag;                          //有無乘客

    //輸出信號定義
    output[DATA_W-1:0]  x_g    ;                           //數碼管顯示個位
    output[DATA_W-1:0]  x_s    ;                           //數碼管顯示十位
    output[DATA_W-1:0]  x_b    ;                           //數碼管顯示百位



    //輸出信號reg定義
    reg   [DATA_W-1:0]  x_g    ;
    reg   [DATA_W-1:0]  x_s    ;
    reg   [DATA_W-1:0]  x_b    ;

    //中間信號定義
    reg   [9:0]              taxi_fare  ;                   //總車費
    reg   [9:0]              taxi_fare_1;                   //按路程車費
    reg   [9:0]              taxi_fare_2;                   //按時間車費
    reg   [35:0]             cnt        ;                   //計數20分鐘
    reg                      key_flag1  ;                   //寄存器
    reg                      key_flag2  ;                   //寄存器
    reg                      key_flag_low  ;                //乘客下車信號
    reg                      key_flag_low1 ;                //乘客下車后隔一個時鐘周期信號
    reg   [2:0]              state      ;

    wire                 add_taxi_fare_1;                  
    wire                 add_taxi_fare_2;
    wire                 add_cnt        ;
    wire                 end_cnt        ;

    //獲取key_flag下降沿,表示乘客下車
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            key_flag1 <= 0;
        end
        else begin
            key_flag1 <= key_flag;
        end
    end
    always@(posedge clk or negedge rst_n)begin
            if(rst_n==1'b0)begin
                key_flag2 <= 0;
            end
            else begin
                key_flag2 <= key_flag1;
            end
        end
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            key_flag_low <= 0;
        end
        else if(key_flag2==1&&key_flag1==0)begin
            key_flag_low <= 1;
        end
        else begin
            key_flag_low <= 0;
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            key_flag_low1 <= 0;
        end
        else
            key_flag_low1 <= key_flag_low;
    end

    //按路程計費
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            taxi_fare_1 <= 0;
        end
        else if(add_taxi_fare_1)begin
            if(distance<=3000)begin
                taxi_fare_1 <= 5;
            end
            else if(distance>3000)begin
                taxi_fare_1 <= 3'd5+ (distance-3000) / 500;//* 0.002;               
            end
        end
    end
    assign add_taxi_fare_1 = key_flag_low && en_0 == 0 ;

    //按時間計費
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            taxi_fare_2 <= 0;
        end
        else if(add_taxi_fare_2)begin
            taxi_fare_2 <= taxi_fare_2 + 1;
        end
    end
    assign add_taxi_fare_2 = end_cnt == 1;

    //計數20分鐘
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
    end
    assign add_cnt = en_0 == 1 && key_flag == 1;      
    assign end_cnt = add_cnt && cnt== /*仿真時使用以節省仿真時間500*1200-1;*/ 50000000*1200-1;  

    //獲得總車費
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            taxi_fare <= 0;
        end
        else if(key_flag_low1==1)begin  
            taxi_fare <= taxi_fare_1+ taxi_fare_2;
        end
    end

    //輸出
    always  @(*)begin
        if(rst_n == 1'b0)begin
            x_g <= 0;
            x_s <= 0;
            x_b <= 0;
            state <= 1;
        end
        else if(taxi_fare != 0)begin
            case(state)
            1: begin  x_g <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 2;end
            2: begin  x_s <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 3;end
            3: begin  x_b <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 1;end
            endcase
        end
      end

    endmodule




1.5 show模塊設計


1.5.1 接口信號


信號
接口方向
定義
clk
輸入
系統時鐘
rst_n
輸入
系統復位
disp_en
輸入
打開數碼管顯示的使能信號。1表示打開顯示,0表示不顯示。
din
輸入
輸入總費用{x_bx_s,x_g}
din_vld
輸入
數碼管顯示數據刷新使能信號。當其值為1有效時,刷新要顯示的數據值。
seg_sel
輸出
3個數碼管的位選信號,在低電平是該位置數碼管亮。
segment
輸出
段選信號,共8位。由低到高,分別表示數碼管的a,b,c,d,e,f,g,點。當該比特為0時,表示點亮相應位置;為1時熄滅。



1.5.2 設計思路


    由于在fare模塊中已經獲得總費用的個位、十位和百位的值,所以在本模塊只需要控制3個數碼管對其數值進行顯示即可。

    本模塊在設計過程中采用動態掃描3個數碼管的方式進行顯示,并且直接使用明德楊提供的數碼管顯示規范代碼。動態掃描方式相比于使用3個獨立的數碼管顯示會節約資源,硬件電路更簡單,且數碼管越多優勢越明顯。


1.5.3 參考代碼

接口定義:
clk               : 時鐘信號,頻率是50MHz
rst_n             : 復位信號,在低電平時有效
seg_sel        : 位選信號,在低電平是該位置數碼管亮。
segment       : 段選信號,共8位。由低到高,分別表示數碼管的a,b,c,d,e,f,g,點。當該比特為0時,表示點亮相應位置;為1時熄滅。
**********old.mdy-edu.com 明德揚科教注釋結束****************/

module  show(
                 rst_n       ,
                 clk         ,
                 disp_en     ,
                 din         ,
                 din_vld     ,

                 seg_sel     ,
                 segment      
             );

/*********old.mdy-edu.com 明德揚科教注釋開始****************
參數定義,明德揚規范要求,verilog內的用到的數字,都使用參數表示。
參數信號全部大寫
**********old.mdy-edu.com 明德揚科教注釋結束****************/

parameter  SEG_WID        =       8;
parameter  SEG_NUM        =       3;
parameter  COUNT_WID      =       26;
parameter  TIME_20US      =       20'd1000;


parameter  NUM_0          =       8'b1100_0000;
parameter  NUM_1          =       8'b1111_1001;
parameter  NUM_2          =       8'b1010_0100;
parameter  NUM_3          =       8'b1011_0000;
parameter  NUM_4          =       8'b1001_1001;
parameter  NUM_5          =       8'b1001_0010;
parameter  NUM_6          =       8'b1000_0010;
parameter  NUM_7          =       8'b1111_1000;
parameter  NUM_8          =       8'b1000_0000;
parameter  NUM_9          =       8'b1001_0000;
parameter  NUM_F          =       8'b1011_1111;
parameter  NUM_ERR        =       8'b1000_0110;


input                             clk       ;
input                             rst_n     ;
input                             disp_en   ;
input  [SEG_NUM*4-1:0]            din       ;
input  [SEG_NUM-1:0]              din_vld   ;
output [SEG_NUM-1:0]              seg_sel   ;
output [SEG_WID-1:0]              segment   ;

reg    [SEG_NUM-1:0]              seg_sel   ;
reg    [SEG_WID-1:0]              segment   ;
reg    [COUNT_WID-1:0]            cnt0      ;
wire                              add_cnt0  ;
wire                              end_cnt0  ;
reg    [SEG_NUM-1:0]              cnt1      ;
wire                              add_cnt1  ;
wire                              end_cnt1  ;
reg    [4*SEG_NUM-1:0]            din_ff0   ;
reg    [        4-1:0]            seg_tmp   ;
wire                              flag_20us ;
integer                           ii        ;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
end

assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0==TIME_20US-1 ;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==SEG_NUM-1 ;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        seg_sel <= {SEG_NUM{1'b1}};
    end
    else if(disp_en)
        seg_sel <= ~(1'b1 << cnt1);
    else
        seg_sel <= {SEG_NUM{1'b1}};
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        din_ff0 <= 0;
    end
    else begin
        for(ii=0;ii<SEG_NUM;ii=ii+1)begin
            if(din_vld[ii]==1'b1)begin
                din_ff0[(ii+1)*4-1 -:4] <= din[(ii+1)*4-1 -:4];
            end
            else begin
                din_ff0[(ii+1)*4-1 -:4] <= din_ff0[(ii+1)*4-1 -:4];
            end
        end
    end
end

always  @(*)begin
    seg_tmp = din_ff0[(cnt1+1)*4-1 -:4];
end


always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        segment<=NUM_0;
    end
    else if(seg_tmp==0)begin
          segment<=NUM_0;
    end
    else if(seg_tmp==1)begin
          segment<=NUM_1;
     end
    else if(seg_tmp==2)begin
          segment<=NUM_2;
    end
    else if(seg_tmp==3)begin
          segment<=NUM_3;
    end
    else if(seg_tmp==4)begin
          segment<=NUM_4;
    end
    else if(seg_tmp==5)begin
          segment<=NUM_5;
    end
    else if(seg_tmp==6)begin
          segment<=NUM_6;
    end
    else if(seg_tmp==7)begin
          segment<=NUM_7;
    end
    else if(seg_tmp==8)begin
          segment<=NUM_8;
    end
    else if(seg_tmp==9)begin
          segment<=NUM_9;
    end
    else if(seg_tmp==4'hf)begin
          segment<=NUM_F;
    end
    else begin
        segment<=NUM_ERR;   
    end
end

endmodule




1.6 效果和總結



  • 仿真驗證特殊說明:


    由于本系統涉及1秒、20分鐘等時間節點,時間很長,所以在仿真時有一定的困難,為此我將系統中的時間節點全部乘上10^-5),讓所有狀態提前到達,方便我們仿真驗證。

    由于乘上了10^-5),所以原消抖10ms在仿真中為100ns

    系統中原1s取樣轉速rev在仿真中為10us;
    原按時計費20分鐘/元在仿真中為12ms/元;



  • 測試文件和理論計算
    在測試文件中轉速rev>=3r/s的時間是50000000ns

    總路程distance=15/3*50000000/10000=25km;

    由于在測試文件中按鍵消抖有一段時間且這一段時間在rev>=3r/s的時間內,所以系統實際對速度采樣的次數會少一次,所以最終的總路程會少5m

    所以,按路程計費的總費用taxi_fare_1=5+25 - 3*2 – 0.005*2=48.99(元);

    去除小數部分taxi_fare_1=48(元);

    在測試文件中轉速rev<3r/s的時間是24000000ns;

    按時間計費的總費用taxi_fare_2=24000000/12000000=2(元);

    所以總車費taxi_fare_1=taxi_fare_1+taxi_fare_2=50(元);


`define clk_period 20
module taxi_fare_tb(

    );

    reg             clk       ;
    reg             rst_n     ;
    reg             d_w       ;
    reg  [15:0]     rev       ;
    reg             key_in    ;

    wire [3-1:0]           seg_sel    ;
    wire [8-1:0]           segment    ;

    taxi_fare taxi_fare_0(
        .clk    (clk)         ,
        .rst_n  (rst_n)       ,
        .d_w    (d_w)         ,
        .rev    (rev)         ,
        .key_in (key_in)      ,

        .seg_sel (seg_sel)      ,
        .segment (segment)      
    );

    initial clk = 1;
        always#(`clk_period/2) clk = ~clk;

    initial begin
        rst_n = 0;
        #100;
        rst_n = 1;
    end

    initial begin
        d_w = 0;
        #10000;
        d_w = 1;
        #200000000;
        d_w = 0;
    end

    initial begin
        rev = 0;
        #10000;
        rev = 15;
        #50000000;
        rev = 1;
        #24000000;
        rev = 0;
    end

    initial begin
        key_in = 1;
        #1000;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #9000;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #74000000;
        key_in = 0;
        #9000;
        key_in = 1;
    end

endmodule


  •  仿真驗證結果
    由仿真結果可以看到seg_sel=1101位數碼管顯示時segment=11000000seg_sel=1012位數碼管顯示時segment=10010010seg_sel=0113位數碼管顯示時segment=11000000,在數碼管上分別對應數值為050,表示為50元。

    仿真結果和理論結構都是50元,符合我們的預期,驗證成功。

    在這個設計中,使用明德楊的至簡設計法,讓我的思路非常清晰,邏輯非常嚴謹,雖然沒有做到一遍成功,但在調試過程中我都比較快速的找到問題,并快速解決。對于學習FPGA的同學,我非常推薦使用明德楊至簡設計法和明德楊模塊進行學習和設計。


    感興趣的朋友也可以訪問明德揚論壇(http://www.fpgabbs.cn/)進行FPGA相關工程設計學習,也歡迎大家在評論與我們進行討論!

   拓展閱讀
主站蜘蛛池模板: 可以免费看黄的网站 | 九九久久精品 | 尤物免费网站 | 国产精品v在线播放观看 | 精品中文字幕制服中文 | 亚洲大片 | 日韩中文视频 | 国产亚洲欧美日韩在线看片 | 亚洲美色综合天天久久综合精品 | 99久久中文字幕伊人情人 | 免费又黄又爽又猛大片午夜 | 久久精品综合 | 涩久久 | 亚洲欧美精品一区天堂久久 | 爱爱激情网 | 久久国产欧美 | 一级片黑人 | 一区二区三区视频在线 | 国产一区二区三区在线 | 日韩一级一欧美一级国产 | 成年人黄色在线观看 | 92精品国产自产在线观看48页 | 国产二区三区毛片 | 日韩午夜伦y4480私人影院 | 国产成人a一在线观看 | 黄色网址在线播放 | 国产亚洲精品国产一区 | 亚洲s码 | 国产大陆亚洲精品国产 | 污污网站免费入口链接 | 欧美精品国产综合久久 | 特级黄色录像片 | 黄色免费小视频 | 日韩久久网 | 久久五十路 | 久久久久久综合对白国产 | 亚洲图色在线 | 福利毛片 | 极品国产高颜值露脸在线 | 中文字幕久久综合 | 在线播放免费人成毛片乱码 |