此次的實驗來自於本人本科課程數電結課時的自選題目。因爲此次上傳是後知後覺,學校已將小腳丫板子回收,因此在這篇文章中無法貼出代碼結果的效果圖了,但最終效果已通過測試,可放心食用。那麼下面就貼上代碼並略加講解供你們參考。編程
咱們要實現一個秒錶,天然要將實驗板中的時鐘脈衝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
小腳丫板子上有兩個八位數碼管顯示,本實驗中用來顯示從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
在主模塊中除了要例化上述的兩個模塊以外,還需給這個秒錶添磚加瓦一下!標題中提到這是一個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
最後貼上主模塊的代碼,完成整個秒錶的實現。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
到這裏整個秒錶就完成啦。最後再次向讀者們道歉,不能貼上實驗效果圖了。身邊有實驗板的讀者們能夠將代碼燒錄進板子觀察現象。本人編程水平、時間有限,這篇文章到這裏就要結束啦,歡迎廣大讀者評論留言,更歡迎你們指出本人的不足,但願能經過交流自身獲得提升。最後感謝你們的耐心閱讀!資源