[FPGA]Verilog 60s秒錶計時器(最大可計時間長達9min)

[FPGA]Verilog 60s秒錶計時器

1.引述

    此次的實驗來自於本人本科課程數電結課時的自選題目。因爲此次上傳是後知後覺,學校已將小腳丫板子回收,因此在這篇文章中無法貼出代碼結果的效果圖了,但最終效果已通過測試,可放心食用。那麼下面就貼上代碼並略加講解供你們參考。編程

2.分頻模塊

咱們要實現一個秒錶,天然要將實驗板中的時鐘脈衝clk分頻爲一個週期爲1s的脈衝,已知小腳丫板子的晶振爲12MHz。下面貼上分頻模塊的代碼。ide

module divide #
(                            //parameter是verilog裏參數定義
parameter WIDTH    =    24,    //計數器的位數,計數的最大值爲 2**(WIDTH-1)
parameter N        =    12_000_000 //分頻係數,請確保 N<2**(WIDTH-1),不然計數會溢出
)
(
input clk, //clk頻率爲12MHz
input rst_n, //復位信號,低有效,
output clkout //輸出信號,能夠鏈接到LED觀察分頻的時鐘
);
reg    [WIDTH-1:0]    cnt_p,cnt_n; //cnt_p爲上升沿觸發時的計數器,cnt_n爲降低沿觸發時的計數器
reg    clk_p,clk_n; //clk_p爲上升沿觸發時分頻時鐘,clk_n爲降低沿觸發時分頻時鐘
//上升沿觸發時計數器的控制
always @(posedge clk or negedge rst_n)    
    begin        
        if(!rst_n)
            cnt_p <= 1'b0;
        else if(cnt_p == (N-1))
            cnt_p <= 1'b0;
        else 
            cnt_p <= cnt_p + 1'b1; //計數器一直計數,當計數到N-1的時候清零,這是一個模N的計數器
    end
//上升沿觸發的分頻時鐘輸出,若是N爲奇數獲得的時鐘佔空比不是50%;若是N爲偶數獲得的時鐘佔空比爲50%
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_p <= 1'b0;
        else if(cnt_p < (N>>1)) //N>>1表示右移一位,至關於除以2取商
            clk_p <= 1'b0;
        else 
            clk_p <= 1'b1; //獲得的分頻時鐘正週期比負週期多一個clk時鐘
    end
//降低沿觸發時計數器的控制            
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            cnt_n <= 1'b0;
        else if(cnt_n == (N-1))
            cnt_n <= 1'b0;
        else 
            cnt_n <= cnt_n + 1'b1;
    end
//降低沿觸發的分頻時鐘輸出,和clk_p相差半個clk時鐘
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_n <= 1'b0;
        else if(cnt_n < (N>>1))  
            clk_n <= 1'b0;
        else 
            clk_n <= 1'b1;    //獲得的分頻時鐘正週期比負週期多一個clk時鐘
    end
wire    clk1 = clk; //當N=1時,直接輸出clk
wire    clk2 = clk_p; //當N爲偶數也就是N的最低位爲0,N[0]=0,輸出clk_p
wire    clk3 = clk_p & clk_n; //當N爲奇數也就是N最低位爲1,N[0]=1,輸出clk_p&clk_n。正週期多因此是相與
assign clkout = (N==1)? clk1:(N[0]? clk3:clk2);    //條件判斷表達式
endmodule

3.八位數碼管顯示模塊 

小腳丫板子上有兩個八位數碼管顯示,本實驗中用來顯示從00s到59s的顯示。下面貼上數碼管顯示模塊的代碼。測試

module segment
(
input  wire [3:0] seg_data_1, //四位輸入數據信號
input  wire [3:0] seg_data_2, //四位輸入數據信號
output wire [8:0] segment_led_1, //數碼管1,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
output wire [8:0] segment_led_2  //數碼管2,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
reg[8:0] seg [15:0]; //存儲7段數碼管譯碼數據
initial 
    begin
        seg[0] = 9'h3f;   //  0
        seg[1] = 9'h06;   //  1
        seg[2] = 9'h5b;   //  2
        seg[3] = 9'h4f;   //  3
        seg[4] = 9'h66;   //  4
        seg[5] = 9'h6d;   //  5
        seg[6] = 9'h7d;   //  6
        seg[7] = 9'h07;   //  7
        seg[8] = 9'h7f;   //  8
        seg[9] = 9'h6f;   //  9
        seg[10]= 9'h77;   //  A
        seg[11]= 9'h7C;   //  b
        seg[12]= 9'h39;   //  C
        seg[13]= 9'h5e;   //  d
        seg[14]= 9'h79;   //  E
        seg[15]= 9'h71;   //  F
    end
   assign segment_led_1 = seg[seg_data_1];
   assign segment_led_2 = seg[seg_data_2];
endmodule

4.功能講解

    在主模塊中除了要例化上述的兩個模塊以外,還需給這個秒錶添磚加瓦一下!標題中提到這是一個60s秒錶,而咱們數碼管顯示只從00到59,但最大計時量程卻達到了9min,這是怎麼辦到的呢?這裏咱們就用到了小腳丫上的一排八位LED燈,每當計到59s時,下一秒數碼管顯示回到00,點亮八位LED燈中的一個,達到表示已計過了1min的做用。一共有八位LED燈,當八個燈都被點亮後,數碼管還有一次從00到59的顯示機會,這樣咱們就的獲得了一個最大計時量程爲9min的秒錶啦!下面貼上八位LED燈顯示部分的代碼。spa

always@(posedge clk)
     if(cnt1==4'b0)
         LED[7:0]<=8'b11111111;
     else if(cnt1==4'b0001)
         LED[7:0]<=8'b11111110;
     else if(cnt1==4'b0010)
         LED[7:0]<=8'b11111100;
     else if(cnt1==4'b0011)
         LED[7:0]<=8'b11111000;
     else if(cnt1==4'b0100)
         LED[7:0]<=8'b11110000;
     else if(cnt1==4'b0101)
         LED[7:0]<=8'b11100000;
     else if(cnt1==4'b0110)
         LED[7:0]<=8'b11000000;
     else if(cnt1<=4'b0111)
         LED[7:0]<=8'b10000000;
     else if(cnt1<=4'b1000)
         LED[7:0]<=8'b00000000;

        此外做爲一個秒錶天然就要有暫停和開始計時的功能(固然清零功能也是有噠!主模塊中就用rst復位鍵來實現,這裏很少贅述。)暫停和開始計時這裏我就用同一個按鍵實現。小腳丫板子上還有兩個RGB三色燈,既然有這麼好的資源存在,咱們就要物盡其用!按下開始計時鍵時,秒錶開始計時,數碼管顯示開始變化,此處咱們讓RGB三色燈中的一個等亮綠燈,表示處於正常計時狀態中;當再次按鍵開啓鍵時,秒錶暫停,咱們讓另外一個RGB三色燈亮紅色,表示處於暫停狀態。下面貼上包含三色燈點亮的部分代碼。code

always @(posedge clk1h or negedge rst) //產生60進制計數器
    begin    //數碼管顯示要按照十進制的方式顯示
        if(!rst)
        begin
            cnt <= 8'h00; //復位初值顯示00
            cnt1<=4'b0;
        end
        else if(flag)
            begin
            G_LED2<=1'b0;
            R_LED1<=1'b1;
                if(cnt[3:0] == 4'd9) //個位滿九?
                    begin
                        cnt[3:0] <= 4'd0; //個位清零
                        if(cnt[7:4] == 4'd5 ) //十位滿五?
                        begin
                            cnt[7:4] <= 4'd0; //十位清零
                            cnt1<=cnt1+1;
                        end
                        else
                        begin
                            cnt[7:4] <= cnt[7:4] + 1'b1; //十位加一
                            cnt1<=cnt1;
                        end
                    end
                else cnt[3:0] <= cnt[3:0] + 1'b1; //個位加一
            end
        else
        begin
            cnt <= cnt;
            G_LED2<=1'b1;
            R_LED1<=1'b0;
        end
    end

5.主模塊

最後貼上主模塊的代碼,完成整個秒錶的實現。blog

module counter60 
(
input clk,rst, //時鐘和復位輸入
input key, //啓動暫停按鍵
output wire [8:0] segment_led_1,segment_led_2, //數碼管輸出
output reg [7:0] LED, //八位LED燈
output reg R_LED1,G_LED2 //RGB三色燈,此處用紅色表示處於暫停狀態中,綠色表示處於正常計時中
);
wire clk1h;    //1秒時鐘
reg    [7:0] cnt;    //計時計數器
reg [3:0] cnt1; //分鐘計數器
reg    flag; //啓動暫停標誌
 
divide #  //例化分頻器產生1秒時鐘信號
(
.WIDTH(24),
.N(12_000_000)
) u1
(
.clk(clk),
.rst_n(rst),
.clkout(clk1h)
);
segment u2 //例化數碼管顯示模塊
(
.seg_data_1        (cnt[7:4]),  //seg_data input
.seg_data_2        (cnt[3:0]),  //seg_data input
.segment_led_1    (segment_led_1),  //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
.segment_led_2    (segment_led_2)   //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
always @(posedge clk or negedge rst) //產生標誌信號
begin
if(!rst)
        flag = 1'b0;
else if(!key)
     begin
        flag = ~flag;
        end
    else
    begin
        flag = flag;
        end
end
always @(posedge clk1h or negedge rst) //產生60進制計數器
    begin    //數碼管顯示要按照十進制的方式顯示
        if(!rst)
        begin
            cnt <= 8'h00; //復位初值顯示00
            cnt1<=4'b0;
        end
        else if(flag)
            begin
            G_LED2<=1'b0;
            R_LED1<=1'b1;
                if(cnt[3:0] == 4'd9) //個位滿九?
                    begin
                        cnt[3:0] <= 4'd0; //個位清零
                        if(cnt[7:4] == 4'd5 ) //十位滿五?
                        begin
                            cnt[7:4] <= 4'd0; //十位清零
                            cnt1<=cnt1+1;
                        end
                        else
                        begin
                            cnt[7:4] <= cnt[7:4] + 1'b1; //十位加一
                            cnt1<=cnt1;
                        end
                    end
                else cnt[3:0] <= cnt[3:0] + 1'b1; //個位加一
            end
        else
        begin
            cnt <= cnt;
            G_LED2<=1'b1;
            R_LED1<=1'b0;
        end
    end
always@(posedge clk)
     if(cnt1==4'b0)
         LED[7:0]<=8'b11111111;
     else if(cnt1==4'b0001)
         LED[7:0]<=8'b11111110;
     else if(cnt1==4'b0010)
         LED[7:0]<=8'b11111100;
     else if(cnt1==4'b0011)
         LED[7:0]<=8'b11111000;
     else if(cnt1==4'b0100)
         LED[7:0]<=8'b11110000;
     else if(cnt1==4'b0101)
         LED[7:0]<=8'b11100000;
     else if(cnt1==4'b0110)
         LED[7:0]<=8'b11000000;
     else if(cnt1<=4'b0111)
         LED[7:0]<=8'b10000000;
     else if(cnt1<=4'b1000)
         LED[7:0]<=8'b00000000;
endmodule

module divide #
(                            //parameter是verilog裏參數定義
parameter WIDTH    =    24,    //計數器的位數,計數的最大值爲 2**(WIDTH-1)
parameter N        =    12_000_000 //分頻係數,請確保 N<2**(WIDTH-1),不然計數會溢出
)
(
input clk, //clk頻率爲12MHz
input rst_n, //復位信號,低有效,
output clkout //輸出信號,能夠鏈接到LED觀察分頻的時鐘
);
reg    [WIDTH-1:0]    cnt_p,cnt_n; //cnt_p爲上升沿觸發時的計數器,cnt_n爲降低沿觸發時的計數器
reg    clk_p,clk_n; //clk_p爲上升沿觸發時分頻時鐘,clk_n爲降低沿觸發時分頻時鐘
//上升沿觸發時計數器的控制
always @(posedge clk or negedge rst_n)    
    begin        
        if(!rst_n)
            cnt_p <= 1'b0;
        else if(cnt_p == (N-1))
            cnt_p <= 1'b0;
        else 
            cnt_p <= cnt_p + 1'b1; //計數器一直計數,當計數到N-1的時候清零,這是一個模N的計數器
    end
//上升沿觸發的分頻時鐘輸出,若是N爲奇數獲得的時鐘佔空比不是50%;若是N爲偶數獲得的時鐘佔空比爲50%
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_p <= 1'b0;
        else if(cnt_p < (N>>1)) //N>>1表示右移一位,至關於除以2取商
            clk_p <= 1'b0;
        else 
            clk_p <= 1'b1; //獲得的分頻時鐘正週期比負週期多一個clk時鐘
    end
//降低沿觸發時計數器的控制            
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            cnt_n <= 1'b0;
        else if(cnt_n == (N-1))
            cnt_n <= 1'b0;
        else 
            cnt_n <= cnt_n + 1'b1;
    end
//降低沿觸發的分頻時鐘輸出,和clk_p相差半個clk時鐘
always @(negedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_n <= 1'b0;
        else if(cnt_n < (N>>1))  
            clk_n <= 1'b0;
        else 
            clk_n <= 1'b1;    //獲得的分頻時鐘正週期比負週期多一個clk時鐘
    end
wire    clk1 = clk; //當N=1時,直接輸出clk
wire    clk2 = clk_p; //當N爲偶數也就是N的最低位爲0,N[0]=0,輸出clk_p
wire    clk3 = clk_p & clk_n; //當N爲奇數也就是N最低位爲1,N[0]=1,輸出clk_p&clk_n。正週期多因此是相與
assign clkout = (N==1)? clk1:(N[0]? clk3:clk2);    //條件判斷表達式
endmodule

module segment
(
input  wire [3:0] seg_data_1, //四位輸入數據信號
input  wire [3:0] seg_data_2, //四位輸入數據信號
output wire [8:0] segment_led_1, //數碼管1,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
output wire [8:0] segment_led_2  //數碼管2,MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
reg[8:0] seg [15:0]; //存儲7段數碼管譯碼數據
initial 
    begin
        seg[0] = 9'h3f;   //  0
        seg[1] = 9'h06;   //  1
        seg[2] = 9'h5b;   //  2
        seg[3] = 9'h4f;   //  3
        seg[4] = 9'h66;   //  4
        seg[5] = 9'h6d;   //  5
        seg[6] = 9'h7d;   //  6
        seg[7] = 9'h07;   //  7
        seg[8] = 9'h7f;   //  8
        seg[9] = 9'h6f;   //  9
        seg[10]= 9'h77;   //  A
        seg[11]= 9'h7C;   //  b
        seg[12]= 9'h39;   //  C
        seg[13]= 9'h5e;   //  d
        seg[14]= 9'h79;   //  E
        seg[15]= 9'h71;   //  F
    end
   assign segment_led_1 = seg[seg_data_1];
   assign segment_led_2 = seg[seg_data_2];
endmodule

 

6.總結

       到這裏整個秒錶就完成啦。最後再次向讀者們道歉,不能貼上實驗效果圖了。身邊有實驗板的讀者們能夠將代碼燒錄進板子觀察現象。本人編程水平、時間有限,這篇文章到這裏就要結束啦,歡迎廣大讀者評論留言,更歡迎你們指出本人的不足,但願能經過交流自身獲得提升。最後感謝你們的耐心閱讀!資源

相關文章
相關標籤/搜索