色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于FPGA的計算器設計

FPGA研究院 ? 來源:數字站 ? 2024-10-24 14:28 ? 次閱讀

以下文章來源于數字站 ,作者數字站

01概括

本文通過FPGA實現8位十進制數的加、減、乘、除運算,通過矩陣鍵盤輸入數據和運算符,矩陣鍵盤的布局圖如下所示。該計算器可以進行連續運算,當按下等號后,可以直接按數字進行下次運算,或者按運算符,把上次運算結果作為本次運算的第一個操作數。

wKgZomcZ6VaAZjy8AAAcPdUhPdA034.jpg

圖1 矩陣鍵盤

通過clr可以清除之前輸入的所有數據,在輸入運算符時,可以輸入多次,但是只有最后一次輸入的運算符有效。計算器輸入的數字和計算結果通過8個數碼管進行顯示。

該工程的資源消耗圖如下所示,總共消耗六百多個LUT,實現27位除法運算和14位乘法運算,以及二進制轉BCD碼等。消耗的其余資源均不超過百分之二十。

pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg

wKgZoWcZ6VaAF0kwAAOCJ2K0a8w081.jpg

圖2 資源利用率

02頂層模塊設計

頂層模塊的信號列表如下所示:

表1 頂層模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
key_row O 4 矩陣鍵盤行輸出信號。
key_col I 4 矩陣鍵盤列輸入信號,低電平有效。
sclk O 1 74HC595移位時鐘信號,上升沿有效。
rclk O 1 74HC595鎖存時鐘信號,上升沿有效。
ds O 1 74HC595串行數據信號,在sclk下降沿更新數據。

頂層模塊只對子模塊進行連線,框圖如下所示,總共包括7個子模塊,每個子模塊執行不同功能。

wKgaoWcZ6YKAdbI6AAF1t2DUD6c557.jpg

圖3 頂層框圖

Key_scan模塊:對按鍵消抖,并且檢測被按下按鍵的位號,按鍵編號取值范圍[0,15],key_vld為高電平表示有按鍵被按下一次,key_out表示被按下按鍵的位號。

Operation_ctrl模塊:計算器的控制和計算模塊,通過檢測被按下的按鍵,執行相應的功能,并且把數碼管需要顯示的數據通過operat_out信號輸出,該信號為二進制數據。

Mult模塊:當使能信號為高電平時開始對輸入的數據進行乘法運算。乘數和被乘數均支持14位(4位十進制數大小),最大輸出27位數據(8位十進制最大數據的位寬)。因為只有8個數碼管,所以最大支持8位十進制數據輸出。該模塊通過移位和加法器實現乘法,能夠運行的頻率會更高。

Div模塊:當使能信號為高電平時對輸入的數據進行除法運算,除數和被除數均支持27位,通過移位和加法器實現除法運算。當quotient_vld為高電平表示除法運算結束。

Hex2bcd模塊:通過移位的方式實現二進制轉bcd碼,輸入數據高達27位,如果使用除法和取余實現二進制轉BCD碼,將消耗大量資源且時鐘運行頻率較低。

Seg_disp:數碼管的刷新控制模塊,8個數碼共用同一組數據線,每個數碼管通過位選的方式工作,該模塊實現位選及數據的控制,將輸入的8個BCD碼顯示在對應的8個數碼管上。

Hc595_drive模塊:該模塊實現74HC595芯片的驅動,通過三個管腳控制8個數碼管的顯示。

03矩陣鍵盤檢測模塊

本文只對模塊設計方法進行簡要概括,端口信號含義如下表所示。

表2 按鍵檢測模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
key_row O 4 矩陣鍵盤行輸出信號。
key_col I 4 矩陣鍵盤列輸入信號,低電平有效。
key_out O 4 被按下按鍵的位號。
key_vld O 1 高電平表示有按鍵被按下。

通過一個狀態機采用逐行掃描的方式對按鍵進行檢測,首先初始狀態四行全部輸出低電平,檢測列輸入信號是否全部為高電平(列信號被上拉到VCC)。如果列輸入不全為高電平,則表示有按鍵被按下,則啟用一個計數器cnt,對列輸入不全為高電平的時間進行計數。

如果時間能夠達到20MS,則認為按鍵真的被按下。如果沒有達到20MS就檢測到列輸入全為高電平,則判定為抖動,此時將計數器清零,繼續檢測。當確認按鍵被按下后,狀態機跳轉到行檢測,此時需要逐行輸出低電平,其余行輸出高電平,對所有行檢測一遍,從而確定出被按下按鍵的位置,輸出位號。最后狀態機跳轉到一個等待狀態,直到所有按鍵被釋放后,狀態機回到空閑狀態繼續檢測。

我只對頂層模塊寫了測試文件,所以其余模塊仿真通過modelsim添加對應模塊信號即可,該模塊仿真結果如下圖所示。TestBench文件里面通過調用編寫的任務實現按鍵按下的信號模擬,按下按鍵的前后都是模擬了抖動的,如下圖所示。

紅框部分都是按鍵按下前的抖動,計數器shake_cnt沒有計數到最大值key_col所有位都變為高電平了,此時計數器就會清零重新檢測,直到檢測到橙色框處,按鍵按下是俺才超過設定時間,狀態機才會跳轉。

wKgZoWcZ6YKAMQkcAAEhoFIX_X4978.jpg

圖4 抖動檢測仿真

行掃描的細節如下所示,此時對每行進行檢測,每行掃描時間持續16個時鐘周期,通過計數器row_cnt記錄一行掃描的時間,計數器row_index記錄掃描第幾行了。當掃描第0行時,列輸入不全為高電平,表示被按下的按鍵在第0行。

列輸入值為4’hd,第一列為低電平,表示被按下按鍵在第1列,則計算出位號為1,此時key_out輸出1,key_vld拉高一個時鐘周期,表示1號按鍵被按下一次。pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg

wKgaoWcZ6YKALg8NAAECxwVuAvA891.jpg

圖5 按鍵檢測

04控制模塊

該模塊主要實現被按下按鍵的識別,然后進行相應的計算,并且輸出對應的數據給數碼管進行顯示,模塊端口信號如下所示:

表3 控制模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
din_key I 4 被按下按鍵位號。
din_key_vld I 1 高電平表示有按鍵被按下1次。
div_vld I 1 高電平表示除法器計算結束。
div_in I 27 除法器運算結果。
mult_vld I 1 高電平表示乘法器計算結束。
mult_in I 27 乘法器計算結果。
div_en O 1 高電平驅動除法器開始工作。
mult_en O 1 高電平驅動乘法器開始工作。
data1 O 27 計算器的1號運算數,作為被除數/被乘數/被加數/被減數。
data2 O 27 計算器的2號運算數,作為除數/乘數/加數/減數。
dout O 27 數碼管需要顯示的二進制數據。

該模塊的功能稍微復雜一點,既要實現按鍵的識別,又要進行加法、減法計算,還要進行數碼管顯示數據的控制。均通過一個狀態機實現,該狀態機的跳轉與按下的按鍵有關。

狀態機的狀態轉換圖如下所示,狀態機初始位于空閑狀態(IDLE),當有數字按鍵被按下后,跳轉到輸入操作數1的狀態(IDTA1),在此狀態下,需要數據1,最多輸入8位十進制數據,檢測到有加減乘除運算符按下時,跳轉到運算符輸入狀態(OPERAT),該狀態下可以輸入很多運算符,但是只有離開該狀態時,輸入的最后一個運算符有效。

wKgZoWcZ6YKAEJHTAAEoT713J-o439.jpg

pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg圖6 狀態轉換圖

在輸入運算符的狀態下,如果檢測到數字按鍵被按下,則跳轉到輸入數字的狀態(IDTA2),也就是輸入第2個操作數,該狀態最多輸入8位十進制數據,否則會溢出。

在該狀態下,如果檢測到等號被按下,則跳轉到計算結果的狀態(RESULT),該狀態會根據輸入的運算符,對輸入的數據進行相應運算,運算符為乘法或除法時,將乘法器使能或除法器使能信號拉高一個時鐘,讓乘法器或除法器模塊工作,當乘法器或者除法器計算結束時,更新計算結果。

在該狀態下,如果檢測到數字按鍵被按下,則跳轉到輸入數據1狀態,進行下次運算。如果檢測到運算符按鍵被按下,說明用戶進行連續運算,則將本次運算結果賦值給操作數1,狀態機跳轉到輸入運算符狀態,繼續下次運算。

所有狀態,只要清零按鍵被按下,狀態機回到空閑狀態。

再說模塊輸出信號dout,當狀態機處于IDTA1狀態時,數碼管需要顯示輸入的數據1,則dout等于data1數值。當狀態機處于IDATA2狀態時,需要顯示輸入的數據2,則dout等于data2數值。當狀態機處于運算結果狀態時,則dout根據運算符不同,進行相應運算,輸出不同數值。

該模塊內部還包括一些數字信號、運算符檢測、信號對齊、輸入數據的變化等等,這些不做細講,可以通過源代碼自行閱讀,源代碼均有注釋,閱讀應該沒有難度。

對該模塊進行總體仿真,在TestBench文件中依次按下這些按鍵,先實現26+290,然后再減82。pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg

wKgaoWcZ6YKAZu-uAAJTo_eT5Tc866.jpg

圖7 仿真按下按鍵

仿真結果如下所示,dout是數碼管需要顯示的數據,din_key是按鍵消抖模塊檢測被按下的按鍵,通過位號譯碼,首先輸入26,數碼管現需要先顯示2,然后顯示26,3號按鍵代表加號,然后輸入290作為第二運算數,14號按鍵是等號,計算結果316。然后7號按鍵是減號,之后dout的值賦值給data1,然后輸入82,完成316-82的運算,最后數碼管顯示234。

wKgZoWcZ6YKAGHG3AADxt55glqA076.jpg

圖8 加減法仿真

連續乘法運算仿真,如圖所示,上次運算結果為234,然后按下乘號(11號按鍵),將上次運算結果賦值給data1,然后輸入乘數為13,按下等號后,mult_en拉高,使能乘法器模塊,乘法器計算結束(mult_vld為高),輸出運算結果3042。

wKgZoWcZ6YKAVLpNAADdr3hxsMs359.jpg

圖9 乘法仿真

連續除法運算仿真,如圖所示,生詞運算結果為3042,然后按下除號(15號按鍵),將上次運算結果賦值給data1,然后輸入除數為71,按下等號后,div_en拉高,使能除法器模塊,除法器計算結束(div_vld為高),輸出運算結果42。

wKgaoWcZ6YKAe-wHAACmItbVVrk438.jpg

圖10 除法仿真

05乘法器模塊

由于篇幅原因,本文不對乘法器的具體實現做講解。端口信號列表如下所示:

表4 乘法器模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
start I 1 開始計算,高電平有效。
multiplicand I 14 被乘數輸入。
multiplier I 14 乘數輸入。
product O 27 乘積輸出。
product_vld O 1 乘積有效指示信號。
rdy O 1 高電平表示模塊空閑,可以進行運算。

由于人使用計算器的頻率較低,所以沒有使用rdy信號,沒有影響。

乘法器模塊仿真如下所示,start為高電平時,被乘數為234,乘數為13,通過幾個時鐘周期后,product_v ld拉高,表示計算結束,輸出乘積為3042。仿真正常,這個模塊輸出延遲與乘數的值有關,最多不會超過乘數位寬那么多個時鐘周期,最少1個時鐘周期。

wKgaoWcZ6YKAEJm-AAEF5T1otbg683.jpg

pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg圖11 乘法器仿真

06除法器模塊

由于篇幅原因,本文不對除法器的具體實現做講解。端口信號列表如下所示:

表5 除法器模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
start I 1 開始計算,高電平有效。
dividend I 27 被除數輸入。
divisor I 27 除數輸入。
quotient O 27 商輸出。
remainder O 27 余數輸出。
quotient_vld O 1 商和余數除數有效指示信號。
ready O 1 高電平表示模塊空閑,可以進行運算。
error O 1 輸入除數為0。

該模塊沒有使用余數、error、ready信號,對應仿真結果如下圖所示。開始信號有效時,被除數為3042,除數為71,經過幾個時鐘周期后,quobient_vld拉高,表示除法計算結束,計算商為42,余數為60,經過驗算后沒有問題。

wKgZoWcZ6YKANWN-AAHY6pG6Wuo673.jpg

圖12 除法器仿真

07二進制轉BCD模塊

該模塊輸入27位二進制數據,輸出32位BCD碼,如果直接使用除法和取余操作,將消耗大量邏輯資源,并且時鐘頻率還不能提高。

所以就采用移位和加法的算法來實現轉換,本文不對該模塊具體實現方式進行講解,設計的時候考慮了參數化,直接修改輸入參數位寬即可實現任意位寬的轉換,端口列表如下:

表6 二進制轉BCD碼模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
din I 27 二進制輸入數據。
din_vld I 27 二進制輸入數據有效指示信號。
dout O 36 轉換后BCD碼
dout_vld O 1 轉換后的BCD碼有效指示信號。

該模塊的din_vld恒為高電平,需要對前文提到模塊稍作修改,修改后的代碼如下所示:

module hex2bcd #(
    parameter   IN_DATA_W       =           27              ,//輸入數據位寬;
    parameter   OUT_DATA_W      =           clogb2({{IN_DATA_W}{1'b1}})//自動計算輸出數據對應的十進制位數;
)(
    input                                   clk             ,//系統時鐘;
    input                                   rst_n           ,//系統復位,低電平有效;


    input       [IN_DATA_W-1:0]             din             ,//輸入二進制數據;
    input                                   din_vld         ,//輸入數據有效指示信號,高電平有效;


    output reg  [4*OUT_DATA_W-1:0]          dout            ,//輸出8421BCD碼;
    output reg                              dout_vld         //輸出數據有效指示信號,高電平有效;
    );


    localparam  CNT_W           =           clogb(IN_DATA_W-3);//根據輸入數據的位寬自動計算需要移動的輪數;
    //localparam  OUT_DATA_W      =           clogb2({{IN_DATA_W}{1'b1}});//自動計算輸出數據對應的十進制位數;


    reg         [IN_DATA_W-1:0]             din_ff0     ;
    reg                                     flag        ;
    reg         [CNT_W-1:0]                 cnt         ;
    reg         [IN_DATA_W+OUT_DATA_W*4-1:0]data_shift  ;
    reg                                     end_cnt_ff0 ;


    wire        [OUT_DATA_W*4-1:0]          data_compare;
    wire                                    add_cnt     ;
    wire                                    end_cnt     ;


    function integer clogb2(input integer depth);
        begin
            if(depth==0)
                clogb2 = 1;
            else if(depth!=0)
                for(clogb2=0;depth>0;clogb2=clogb2+1)
                    depth=depth/10;
        end
    endfunction


     //自動計算位寬
    function integer clogb(input integer depth);begin
        if(depth==0)
            clogb = 1;
        else if(depth!=0)
            for(clogb=0;depth>0;clogb=clogb+1)
                depth=depth>>1;
    end
    endfunction
    
    //當輸入數據有效并且此時該模塊空閑時保存輸入數據,否則不保存輸入數據,這樣可以保證本次轉換數據完全正確;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            din_ff0 <= 0;
        end
        else if(din_vld)begin
            din_ff0 <= din;
        end
    end


    //標志信號flag,當輸入數據有效時拉高,當計數器計數完成時清零;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            flag <= 1'b0;
        end
        else if(din_vld)begin
            flag <= 1'b1;
        end
        else if(end_cnt)begin
            flag <= 1'b0;
        end
    end


    //移位計數器,每次轉換需要移動IN_DATA_W-2次,初始值為0,加一條件flag信號有效,結束條件是計數到IN_DATA_W-2次;
    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 = flag;       
    assign end_cnt = add_cnt && cnt == IN_DATA_W-3;


    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            data_shift <= 0;
        end
        else if(add_cnt)begin
            if(cnt==0)begin//初始時將輸入數據左移三位保存;
                data_shift <= {{{OUT_DATA_W-3}{1'b0}},din_ff0,3'b0};
            end
            else begin//計數器加一條件有效時,將移位寄存器數據左移一位;
                data_shift <= {data_compare[OUT_DATA_W*4-2:0],data_shift[IN_DATA_W-1:0],1'b0};
            end
        end
    end


    //移位后大于等于5之后加3;
    generate
        genvar bit_num;
                for(bit_num = 0 ; bit_num < OUT_DATA_W ; bit_num = bit_num + 1)begin : DATA
                    assign data_compare[4*bit_num+3 : 4*bit_num] = data_shift[IN_DATA_W+4*bit_num+3 : IN_DATA_W+4*bit_num] + (data_shift[IN_DATA_W+4*bit_num+3 : IN_DATA_W+4*bit_num]>=5 ? 4'd3 : 4'd0);
        end
    endgenerate


    //將計數器延遲一拍,用于生成輸出信號;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            end_cnt_ff0 <= 1'b0;
        end
        else begin
            end_cnt_ff0 <= end_cnt;
        end
    end


    //通過計數器結束條件產生輸出信號;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            dout <= 0;
        end
        else if(end_cnt_ff0)begin
            dout <= data_shift[IN_DATA_W+OUT_DATA_W*4-1 : IN_DATA_W];
        end
    end


    //通過計數器結束條件生成輸出有效指示信號;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            dout_vld <= 1'b0;
        end
        else begin
            dout_vld <= end_cnt_ff0;
        end
    end


    endmodule

該模塊的端口仿真如下圖所示,輸入數據按照十進制顯示,輸出數據按照十六進制數據顯示,輸入數據為17’d29時,輸出數據為36’h000000029,轉換完成。

wKgZomcZ6eGANUCDAADFHfAl4as264.jpg

圖13 二進制轉BCD碼仿真

08數碼管刷新模塊

該模塊用的也比較多了,8個數碼管共用同一組數據線,那么這組數據線就只能通過時分復用的方式傳遞數據。

對應的端口列表如下所示:

表7 數碼管刷新模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
din I 32 需要顯示的32位BCD碼
segment O 8 數碼管的數據線
seg_sel O 8 數碼管位選信號
dout_vld O 1 數碼管刷新指示信號

該模塊后面還有一個74HC595驅動模塊,所以需要固定一段時間產生數據和位選信號,一般數碼管刷新時間采用20us,所以每隔20us產生一個刷新信號,輸出對應數碼管數據和位選信號給74HC595模塊的驅動,再生成相應輸出。

修改后的代碼如下所示:

module seg_disp #(
    parameter       TCLK                    =   20      ,//系統時鐘周期,單位ns。
    parameter       TIME_20US               =   20_000  ,//數碼管刷新時間,默認20us。
    parameter       SEG_NUM                 =   8        //需要顯示的數碼管個數。
)(
    //輸入信號定義
    input                                       clk     ,//系統時鐘,50MHz。
    input                                       rst_n   ,//系統復位,低電平有效。


    input           [(SEG_NUM * 4) - 1  :0]     din     ,//需要數碼管顯示的BCD碼數碼;
    //輸出信號定義
    output reg      [7 : 0]                     segment ,//數碼管的數據線;
    output reg      [SEG_NUM - 1 : 0]           seg_sel ,//數碼管的位選信號;
    output reg                                  dout_vld //為高電平時,表示段選和位選信號有效;
);
    //參數定義
    localparam      TIME                    =   TIME_20US/TCLK  ;
    localparam      TIME_W                  =   clogb2(TIME-1)  ;//計算數碼管掃描時間的時鐘數據位寬;
    localparam      SEG_W                   =   clogb2(SEG_NUM) ;
    localparam      ZERO                    =   8'h3F  ; //8'hC0;前面的數據是共陰數碼管使用的,后面數據是共陽數碼管使用的;
    localparam      ONE                     =   8'h06  ; //8'hF9;
    localparam      TWO                     =   8'h5B  ; //8'hA4;
    localparam      THREE                   =   8'h4F  ; //8'hB0;
    localparam      FOUR                    =   8'h66  ; //8'h99;
    localparam      FIVE                    =   8'h6D  ; //8'h92;
    localparam      SIX                     =   8'h7D  ; //8'h82;
    localparam      SEVEN                   =   8'h07  ; //8'hF8;
    localparam      EIGHT                   =   8'h7F  ; //8'h80;
    localparam      NINE                    =   8'h6F  ; //8'h90;
    localparam      ERR                     =   8'h77  ; //8'h86;


    //中間信號定義
    reg             [3 : 0]                     sel_result  ;
    reg             [SEG_W - 1 : 0]             sel         ;
    reg             [SEG_W - 1 : 0]             sel_ff0     ;
    reg             [TIME_W - 1 : 0]            cnt_20us    ;
    reg                                         add_sel_r   ;//


    wire                                        end_cnt_20us;
    wire                                        add_sel     ;
    wire                                        end_sel     ;


    //自動計算位寬的函數;
    function integer clogb2(input integer depth);
        begin
            if(depth==0)
                clogb2=1;
            else if(depth!=0)
                for(clogb2=0; depth>0;clogb2=clogb2+1)
                    depth=depth>>1;
        end
    endfunction


    //20us計數器,用于對一個數碼管點亮的持續時間進行計數,計數器初始值為0,對
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin//計數器初始值為0;
            cnt_20us <= 0;
        end
        else if(end_cnt_20us)begin//當計數器計數到20us時,表示一個數碼管已經被點亮20US了,將計數器清零;
            cnt_20us <= 0;
        end
        else begin//否則,計數器加一;
            cnt_20us <= cnt_20us + 1'b1;
        end
    end


    //計數器結束條件,當計數器計數到TIME-1時表示20US已經到了,將計數器清零;
    assign end_cnt_20us = cnt_20us == TIME - 1;


    //計數器sel,用于計數此時點亮的時第幾個數碼管,上電復位時點亮第零個數碼管,所以初始值為0,之后當計數器cnt_20us計數結束時,表示一個數碼管點亮時間已經到了,此時計數器sel加一,表示該點亮下一個計數器了,當點亮SEG_NUM-1個計數器完成(end_sel有效)時表示數碼管都被點亮了一次,此時計數器sel清零,又從第一個數碼管開始點亮;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值為0;
            sel <= 0;
        end
        else if(add_sel) begin
            if(end_sel)//當計數器sel計數結束時,計數器清零;
                sel <= 0;
            else
                sel <= sel + 1;
        end
    end
    assign add_sel = end_cnt_20us;//計數器sel的加一條件是,計數器cnt_20us計數器結束;
    assign end_sel = add_sel && sel == SEG_NUM - 1;//計數器sel計數到SEL_NUM-1時,計數器sel清零;


    //sel_result信號是當前被點亮數碼管需要顯示的數據,根據計數器sel的值確定此時應該將輸入信號的哪幾位數據譯碼輸出給數碼管進行顯示;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值為0;
            sel_result <= 4'd0;
        end
        else if(add_sel)begin//取輸入信號din[4*sel+3 : 4*sel]信號給譯碼部分進行譯碼,之后輸出給數碼管數據信號驅動數碼管顯示該數據;
            sel_result <= din[4*sel+3 -: 4];//{din[4*sel+3],din[4*sel+2],din[4*sel+1],din[4*sel]};
        end
    end


    //譯碼器部分,將sel_result十進制信號譯碼成數碼管顯示該數字對應的八位數據信號;
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始上電時,所有數碼管顯示數據0;
            segment <= ZERO;
        end
        else if(add_sel_r)begin
            case(sel_result)//將sel_result譯碼成對應的segment數據,segment數據驅動數碼管才能顯示sel_result代表的數字;
                0: segment <= ZERO ;//想要數碼管顯示0,就要給數碼管數據信號segment輸入ZERO數據,其余類似;
                1: segment <= ONE  ;
                2: segment <= TWO  ;
                3: segment <= THREE;
                4: segment <= FOUR ;
                5: segment <= FIVE ;
                6: segment <= SIX  ;
                7: segment <= SEVEN;
                8: segment <= EIGHT;
                9: segment <= NINE ;
                default: segment <= ERR;
            endcase
        end
    end


    //為了與段選動態掃描,保持同步,此時位選應該打一拍再賦給位選信號 seg_sel
    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            sel_ff0 <= 0;
        end
        else if(add_sel)begin
            sel_ff0 <= sel;
        end
    end


    always@(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值為0,全部數碼管被點亮;
            seg_sel <= {{SEG_NUM}{1'b0}};
        end
        else if(add_sel_r)begin//將1右移sel_ff0位之后取反,seg_sel的第sel_ff0輸出低電平,對應的第sel_ff0個數碼管被點亮了,其余位輸出高電平,對應的數碼管熄滅;
            seg_sel <= ~({1'b1,{{SEG_NUM-1}{1'b0}}} >> sel_ff0);//~(6'h1<

該模塊仿真比較簡單,如下圖所示,需要顯示的數據為32’h00000234,由于底板上位選最低位對應的是最左邊的數碼管,第1個十進制數需要與位選的最高位對齊,第8個十進制數需要與位選的最低位對齊,仿真結果如下所示。

位選信號為8’h7f是,此時數據段應該輸出8’h66,數碼管顯示4這個數字。pYYBAGORftaABrbPAAAAjgjvZ2U927.jpg

wKgZomcZ6f2AeJLoAAEvpxhHFbI888.jpg

圖14 數碼管刷新模塊仿真

0974H595模塊驅動

數碼管的驅動電路如下所示,通過兩片74HC595驅動8個數碼管,由于篇幅問題,本文只對該模塊進行仿真。

wKgaomcZ6f2ABqaxAAEszmgy4-g132.jpg

圖15 數碼管驅動電路

信號端口列表如下所示。

表8 74HC595驅動模塊信號列表

信號名 I/O 位寬 含義
clk I 1 系統時鐘頻率,默認12MHz。
rst_n I 1 系統復位信號,低電平有效。
segment I 8 數碼管的數據線
seg_sel I 8 數碼管位選信號
dout_vld I 1 數碼管刷新指示信號
ds O 1 74hc595芯片串行數據。
sclk O 1 74hc595芯片移位寄存器時鐘信號。
rclk O 1 74hc595芯片鎖存器時鐘信號。

該模塊整體仿真結果如下所示:

wKgZomcZ6f2AW56-AAF7ZBRUCSw162.jpg

圖16 整體仿真

該模塊細節仿真如下所示:

wKgaomcZ6f2APlj9AAE7G9eo_vU058.jpg

圖17 細節仿真

10上板實測

上述仿真了各個模塊,下文在開發板上對這個工程進行實測。

首先驗證正常的加減乘除運算,如下視頻所示,每次運算后使用清除按鍵清零運算結果,然后開始下次運算。

下面是進行連續運算的演示,使用該計算器實現以下運算((3320 + 2551) - 771) * 7 / 9,得到計算結果為3966,與真實結果一致。

同時在輸入運算符時,如果運算符輸入錯誤,則可以再次輸入運算符,以最后輸入的運算符為準,之后輸入第二個操作數,與一般的計算器機制一致。

來源:本文轉載自數字站公眾號

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FPGA
    +關注

    關注

    1635

    文章

    21837

    瀏覽量

    608330
  • 矩陣鍵盤
    +關注

    關注

    7

    文章

    207

    瀏覽量

    31631
  • 計算器
    +關注

    關注

    16

    文章

    438

    瀏覽量

    37606
  • 運算符
    +關注

    關注

    0

    文章

    172

    瀏覽量

    11176

原文標題:基于FPGA的計算器

文章出處:【微信號:FPGA研究院,微信公眾號:FPGA研究院】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    FPGA設計的計算器

    FPGA模擬設計的計算器
    發表于 05-04 20:27

    基于FPGA計算器設計:矩陣鍵盤模塊(含程序)

    做了一個計算器的實驗,目前可以正負實現數的加減乘除,以及定點小數的加減乘除,精度不高還在進一步完善,今天先把矩陣鍵盤掃描的模塊總結一下,明天繼續優化。這是一個大概的模塊劃分和數據流向圖,我們首先要
    發表于 08-09 17:46

    至芯科技昭哥帶你學FPGAFPGA_100天之旅_計算器設計

    本文屬于本人原創,和大家一起學習FPGA,交流FPGA,希望大家多多支持。來源:至芯科技昭哥帶你學FPGAFPGA_100天之旅_計算器
    發表于 10-18 11:25

    FPGA模擬設計的計算器

    計算器代碼不能實現一個功能: 3和7是不能同時按的, 也就是不能夠實現3+7和3-7等功能 可以自己試試看吧。。。 全部資料下載: fpga計算器.rar
    發表于 07-03 13:16

    8位二進制轉化個位,十位,百位的進程程序一直出錯

    我在做基于FPGA計算器設計時,用Quartus Ⅱ 13.1寫的程序,但寫完8位二進制轉化個位,十位,百位的進程程序后,編譯時,在ctrview:PROCESS(c,clk)BEGIN這一
    發表于 11-30 09:58

    基于FPGA計算器設計程序出錯

    我在做基于FPGA計算器設計時,用Quartus Ⅱ 13.1寫的程序,但寫完8位二進制轉化個位,十位,百位的進程程序后,編譯時,在ctrview:PROCESS(c,clk)BEGIN這一
    發表于 12-04 08:16

    FPGA計算器(矩陣按鍵,LCD1602)

    本帖最后由 溫家輝 于 2019-9-23 09:05 編輯 用FPGA實現了加減乘除,4*4矩陣按鍵作為輸入,LCD1602顯示,代碼寫了一個多星期,終于完成了所有功能,開心,,,,注意,矩陣按鍵需要設置上拉電阻
    發表于 09-23 09:03

    【工程源碼】基于FPGA計算器

    本文和設計代碼由FPGA愛好者小梅哥編寫,未經作者許可,本文僅允許網絡論壇復制轉載,且轉載時請標明原作者。
    發表于 02-20 14:10

    如何實現基于FPGA的電子計算器設計

    今天給大俠帶來基于FPGA的電子計算器設計,由于篇幅較長,分三篇。今天帶來第二篇,中篇,話不多說,上貨。導讀本篇介紹了一個簡單計算器的設計,基于 FPGA 硬件描述語言 Verilog
    發表于 11-11 08:31

    基于FPGA計算器設計

    發表于 03-21 16:52 ?36次下載

    基于FPGA計算器設計(源碼)

    這是一個基于FPGA設計的四則運算簡易計算器。可以實現定點小數運算和負數運算。
    發表于 08-23 16:23 ?31次下載

    FPGA設計計算器

    計算器是設計中經常用到的一個操作軟件,設計和學習計算器使我們親密的聯系所學的各模塊, 對我們的學習有很大的幫助和提升。希望大家來學習
    的頭像 發表于 09-15 08:48 ?9516次閱讀
    用<b class='flag-5'>FPGA</b>設計<b class='flag-5'>計算器</b>

    基于FPGA的電子計算器設計(中)

    今天給大俠帶來基于FPGA的電子計算器設計,由于篇幅較長,分三篇。今天帶來第二篇,中篇,話不多說,上貨。導讀本篇介紹了一個簡單計算器的設計,基于 FPGA 硬件描述語言 Verilog
    發表于 11-06 17:36 ?17次下載
    基于<b class='flag-5'>FPGA</b>的電子<b class='flag-5'>計算器</b>設計(中)

    基于FPGA的電子計算器設計

    在國外,電子計算器在集成電路發明后,只用短短幾年時間就完成了技術飛躍,經過激烈的市場競爭,現在的計算器技術己經相當成熟。
    的頭像 發表于 02-13 09:11 ?5640次閱讀

    基于FPGA的披薩切片角度計算器

    電子發燒友網站提供《基于FPGA的披薩切片角度計算器.zip》資料免費下載
    發表于 06-16 11:47 ?0次下載
    基于<b class='flag-5'>FPGA</b>的披薩切片角度<b class='flag-5'>計算器</b>
    主站蜘蛛池模板: 99久女女精品视频在线观看 | 视频一区二区中文字幕 | 国产啪精品视频网免费 | 学生无码AV一区二区三区 | 久久影院毛片一区二区 | 日韩一卡二卡三卡四卡免费观在线 | 欧美 另类 美腿 亚洲 无码 | 天龙八部慕容属性加点 | 爆乳啪啪无码成人二区亚洲欧美 | 窝窝色资源站 | 毛片手机在线看 | yellow视频免费观看高清在线 | 偷偷鲁青春草原视频分类 | 亚洲一区免费观看 | 色婷婷亚洲五月 | 妓女嫖客叫床粗话对白 | 国产精品卡1卡2卡三卡四 | 99国产精品久久人妻无码 | 成年美女黄网站色app | 男生插女生下体 | av狼新人开放注册区 | 国产电影无码午夜在线播放 | 中文字幕高清在线中文字幕 | 久久久久久久久久综合情日本 | 亚洲欧美国产视频 | 在线看片亚洲 | 99视频精品全部免费免费观 | AV天堂午夜精品一区 | 久久精品国产免费播放 | 国产亚洲精品久久久久久鸭绿欲 | 中文有码中文字幕免费视频 | 国产美女裸身网站免费观看视频 | 国产成人永久免费视频 | 欧美亚洲日韩欧洲不卡 | 怡春院院日本一区二区久久 | 中文字幕人成人乱码亚洲影视S | 色柚视频网站ww色 | 国产无遮挡色视频免费观看性色 | 一二三四韩国免费观看 | 国产精品成人自拍 | 国产精品A久久777777 |