由於本文的工程相對較大,文件的代碼壓縮后傳到CSDN,其中本文的設計源碼為test9,所用quartus版本好位quartus15.1,鏈接如下http://download.csdn.net/detail/noticeable/9909850
設計目的:
選擇四個按鍵, 通過按鍵使數碼管數據可以穩定進行加1減1的操作,KEY3可以對數碼管的顯示位進行位選,key0可以進行復位操作,通過數碼管位數和led顯示的穩定加一,可以驗證按鍵是否成功消抖。
知識點:1、什么是按鍵消抖
2、testbench 中隨機數發生函數$random的使用。
進行按鍵消抖的原因和方法
對於機械按鍵,其按下后,由於其機械特性,會由於彈性產生一個抖動,其在電平輸出上表現的就是一個如下的前沿抖動核后延抖動,對於這個 抖動,對於電平敏感的數據接收端來說是不希望出現的,因為其會導致接收端錯誤的計算按鍵按下的次數,使項目出現不確定的現象,所以對於按鍵的抖動,一般需要進行一個消抖的過程。
對於按鍵消抖,一般有兩種方法,(1)經驗表明,按鍵抖動一般持續的時間在5ms~10ms之間,所以理論上可以在檢測到跳變后延時20ms再觀察是否真的產生了跳變,這個方法被稱之為延時重采樣法,這個方法在單片機中用到的比較普遍。(2)第二章方法是當檢測到按鍵處於某電平時,在之后的N個時鍾周期內連續檢測此按鍵的電平,如果一直不變,則讀出此按鍵的電平值,這種方法叫做持續采樣法。理論上來講,后者的采樣率相對於前者來說是大大增加了的,所以在准確性上來說,后者的准確性更高。這次設計,我們即采用持續采樣法進行數據濾波。
設計過程:
首先,建立工程,編輯濾波文件key_filter.v
1 /* 2 系統上電后默認進入空閑IDEL狀態,key_flag和key_state一起表示消抖的狀態 3 key_flag key_state 4 0 1 平常狀態 5 0 0 按下狀態 6 1 0 確定是否抖動狀態 7 */ 8 module key_filter(clk,rst_n,key_in,key_flag,key_state); 9 10 input clk; 11 input rst_n; 12 input key_in; 13 14 output reg key_flag; 15 output reg key_state; 16 17 18 localparam 19 IDEL = 4'b0001, //空閑狀態 20 FILTER0 =4'b0010,//前抖動濾波 21 DOWN =4'b0100,//按鍵穩定時態 22 FILTER1 =4'b1000;//后抖動濾波 23 24 reg [3:0]state; 25 reg [19:0]cnt; 26 27 reg key_temp0,key_temp1; 28 reg en_cnt; 29 reg cnt_full; 30 wire pedge,nedge; 31 32 33 34 //邊沿檢測通過,倆個寄存器進行前后高低狀態進行采樣后對比 35 always@(posedge clk or negedge rst_n) 36 if(!rst_n) 37 begin 38 key_temp0<=1'b0; 39 key_temp1<=1'b0; 40 end 41 else begin 42 key_temp0<=key_in; 43 key_temp1<=key_temp0; 44 end 45 46 //反相器加一個與門,對寄存器里的兩個值進行比較 47 assign nedge=!key_temp0&key_temp1; 48 assign pedge=key_temp0&(!key_temp1);//(!key_temp1)邏輯取反,按位取反(~key_temp1) 49 50 51 always@(posedge clk or negedge rst_n) 52 if (!rst_n) begin 53 54 state<=IDEL; 55 en_cnt=1'b0; 56 key_flag<=1'b0; 57 key_state<=1'b1; 58 end 59 else begin 60 case (state) 61 IDEL: 62 begin 63 key_flag<=1'b0; 64 if(nedge) begin 65 state=FILTER0; 66 en_cnt<=1'b1;//打開計數器開始計數 67 end 68 else 69 state <=IDEL; 70 end 71 FILTER0: 72 if(cnt_full)begin //就是按鍵事件是否滿了20ms,且在這個過程中沒有發生數據跳變,是的話說明沒有問題,此時是穩定狀態,可以進行下一個狀態 73 en_cnt<=1'b0;//計數滿了也需要關閉計數 74 key_flag<=1'b1;//產生一個key_flag 的高脈沖,通知外部按鍵事件發生 75 key_state<=1'b0; 76 state<=DOWN; 77 end 78 else if(pedge) begin 79 state <=IDEL;//檢測到上升沿,說明是抖動狀態,回到上一狀態,並關閉計數器。 80 en_cnt<=1'b0; 81 end 82 else 83 state<=FILTER0; 84 85 86 DOWN: 87 begin 88 key_flag<=1'b0;//到了這個狀態說明可以確定是按鍵電平 89 key_state<=1'b0; 90 if(pedge)begin //等待釋放的上升沿信號,進入釋放濾波狀態 91 state <=FILTER1; 92 en_cnt<=1'b1; 93 end 94 else state<=DOWN; 95 end 96 97 98 99 FILTER1: 100 if(cnt_full)begin //20ms計數滿了,沒有發生數據跳變(即抖動),說明沒有問題,可以釋放了 101 102 key_flag<=1'b1;//產生一個key_flag 的高脈沖,通知外部按鍵事件發生 103 key_state<=1'b1; 104 state<=IDEL; 105 en_cnt<=0; 106 end 107 else if(nedge) begin 108 state <=DOWN;//檢測到下降沿,說明是抖動狀態,回到上一狀態,並關閉計數器。 109 en_cnt<=1'b0; 110 end 111 else 112 state<=FILTER1; 113 default:begin 114 115 state<=IDEL; 116 en_cnt<=1'b0; 117 key_flag<=1'b0; 118 key_state <=1'b1; 119 end 120 endcase 121 end 122 123 124 125 //定義一個計數器, 已知50M系統時鍾,周期即為20ns 需要計數20_000_000/20-1=999_999 //20MS/周期-1的數據,需要20位的寄存器。 126 always@(posedge clk or negedge rst_n) 127 if (!rst_n) 128 cnt<=20'd0; 129 else if (en_cnt) 130 cnt<=cnt+1'b1; 131 else 132 cnt<=20'd0; 133 134 always@(posedge clk or negedge rst_n) 135 if (!rst_n) 136 cnt_full <=1'd0; 137 else if(cnt==999_999) 138 cnt_full<=1'b1; 139 else 140 cnt_full <= 1'b0; 141 142 endmodule
程序編寫完成后,可以通過state machine viewer 觀察自己編寫的狀態機是否合理。
本次狀態機的狀態切換如下
編寫testbench文件,並設定路徑
`timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg clk; reg rst_n; reg key_in; wire key_flag; wire key_state; key_filter key_filter0( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); initial clk =1; always #(`clk_period/2) clk=~clk; initial begin rst_n<=1'b0; key_in=1'b1; #(`clk_period*10) rst_n<=1'b1; key_in=1; #(`clk_period*10) //模擬按鍵抖動 key_in=0; #1000; key_in=1; #1400 key_in=0; #1340; key_in=1; #2600; key_in=0; #1600; key_in=1; #2340; key_in=0; #1300; key_in=1; #2000; key_in=0; #1000; key_in=1; #2050; key_in=0; #20_000_100; #100000;//保持穩定一段式時間。 key_in=1;//釋放抖動 #2600; key_in=0; #1600; key_in=1; #2340; key_in=0; #1300; key_in=1; #20000; key_in=0; #1000; key_in=1; #2050; key_in=1; #20_100_000; #1000000; $stop; end endmodule
點擊仿真按鈕進行仿真(仿真時可把clk從wave顯示中刪掉,這樣可以大大縮短仿真時間),仿真波形如圖,說明整個設計是合理的。
通過觀察-模擬抖動的testbench可以看出我們自己模擬抖動的過程又臭又長,毫無美感和技術可言,對此,我們可以采用task任務和隨機數發生函數$random來進行精簡。
對testbench修改后的文件如下
`timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg clk; reg rst_n; reg key_in; wire key_flag; wire key_state; reg [15:0]myrand; key_filter key_filter0( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); initial clk =1; always #(`clk_period/2) clk=~clk; initial begin rst_n<=1'b0; key_in=1'b1; #(`clk_period*10) rst_n<=1'b1; press_key;//直接調用press_key即可模擬一次按鍵過程了 #100000; press_key; #10000; $stop; end task press_key; begin //按下抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key_in=~key_in; end key_in=0; #20_500_500 //釋放抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key_in=~key_in; end key_in=1; #20_500_500; end endtask endmodule
仿真結果相對來說也更加明顯清晰了許多。
仔細觀察代碼,感覺還是有些綴余,這里添加一個文件,將按鍵模型寫到文件中,
`timescale 1ns/1ns `define clk_period 20 //模擬一個按鍵模型 module key_module(key); output reg key; reg [15:0]myrand; initial begin #(`clk_period*10) press_key;//直接調用press_key即可模擬一次按鍵過程了 #100000; press_key; #10000; $stop; end task press_key; begin //按下抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key=~key; end key=0; #20_500_500 //釋放抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key=~key; end key=1; #20_500_500; end endtask endmodule
然后通過tb文件對模型進行調用(這里是想通過這個testbench的編寫,了解到大型項目的文件分級的方法,使得項目程序更富有層次性。)
`timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg clk; reg rst_n; wire key_in; wire key_flag; wire key_state; key_filter key_filter0( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); key_module key_module1(.key(key_in)); initial clk =1; always #(`clk_period/2) clk=~clk; initial begin rst_n<=1'b0; #(`clk_period*10) rst_n<=1'b1; end endmodule
將key_module添加到路徑中
再點擊仿真,能看到仿真波形也是正確的。
之前只是編寫的是按鍵濾波程序,為了使完成項目,還需要很多其他的內容來進行補充。
對輸入入的異步信號key_In,當采樣點在不定態(即兩個輸入都在跳變得過程中),那么兩個信號的輸出就是不確定的,這時候就需要對異步信號進行處理,將其進行同步,為了使輸出同步,需要加上兩級的D觸發器。
/* 系統上電后默認進入空閑IDEL狀態,key_flag和key_state一起表示消抖的狀態 key_flag key_state 0 1 平常狀態 1 0 20ms沒有發生跳變,說明按鍵事件已經可以確認發生了 0 1 說明現在保持着一直按下的狀態 1 1 按鍵已經從按下狀態釋放了 */ module key_filter(clk,rst_n,key_in,key_flag,key_state); input clk; input rst_n; input key_in; output reg key_flag; output reg key_state; localparam IDEL = 4'b0001, //空閑狀態 FILTER0 =4'b0010,//前抖動濾波 DOWN =4'b0100,//按鍵穩定時態 FILTER1 =4'b1000;//后抖動濾波 reg [3:0]state; reg [19:0]cnt; reg key_temp0,key_temp1; reg en_cnt; reg cnt_full; wire pedge,nedge; reg key_in_s0,key_in_s1; //key_in異步信號進行同步處理。 always@(posedge clk or negedge rst_n) if(!rst_n)begin key_in_s0<=1'b0; key_in_s1<=1'b0; end else begin key_in_s0<=key_in; key_in_s1<=key_in_s0; end //邊沿檢測通過,倆個寄存器進行前后高低狀態進行采樣后對比 always@(posedge clk or negedge rst_n) if(!rst_n) begin key_temp0<=1'b0; key_temp1<=1'b0; end else begin key_temp0<=key_in_s1; key_temp1<=key_temp0; end //反相器加一個與門,對寄存器里的兩個值進行比較 assign nedge=!key_temp0&key_temp1; assign pedge=key_temp0&(!key_temp1);//(!key_temp1)邏輯取反,按位取反(~key_temp1) always@(posedge clk or negedge rst_n) if (!rst_n) begin state<=IDEL; en_cnt=1'b0; key_flag<=1'b0; key_state<=1'b1; end else begin case (state) IDEL: begin key_flag<=1'b0; if(nedge) begin state=FILTER0; en_cnt<=1'b1;//打開計數器開始計數 end else state <=IDEL; end FILTER0: if(cnt_full)begin //就是按鍵事件是否滿了20ms,且在這個過程中沒有發生數據跳變,是的話說明沒有問題,此時是穩定狀態,可以進行下一個狀態 en_cnt<=1'b0;//計數滿了也需要關閉計數 key_flag<=1'b1;//產生一個key_flag 的高脈沖,通知外部按鍵事件發生 key_state<=1'b0; state<=DOWN; end else if(pedge) begin state <=IDEL;//檢測到上升沿,說明是抖動狀態,回到上一狀態,並關閉計數器。 en_cnt<=1'b0; end else state<=FILTER0; DOWN: begin key_flag<=1'b0;//到了這個狀態說明可以確定是按鍵電平 key_state<=1'b0; if(pedge)begin //等待釋放的上升沿信號,進入釋放濾波狀態 state <=FILTER1; en_cnt<=1'b1; end else state<=DOWN; end FILTER1: if(cnt_full)begin //20ms計數滿了,沒有發生數據跳變(即抖動),說明沒有問題,可以釋放了 key_flag<=1'b1;//產生一個key_flag 的高脈沖,通知外部按鍵事件發生 key_state<=1'b1; state<=IDEL; en_cnt<=0; end else if(nedge) begin state <=DOWN;//檢測到下降沿,說明是抖動狀態,回到上一狀態,並關閉計數器。 en_cnt<=1'b0; end else state<=FILTER1; default:begin state<=IDEL; en_cnt<=1'b0; key_flag<=1'b0; key_state <=1'b1; end endcase end //定義一個計數器, 已知50M系統時鍾,周期即為20ns 需要計數20_000_000/20-1=999_999 //20MS/周期-1的數據,需要20位的寄存器。 always@(posedge clk or negedge rst_n) if (!rst_n) cnt<=20'd0; else if (en_cnt) cnt<=cnt+1'b1; else cnt<=20'd0; always@(posedge clk or negedge rst_n) if (!rst_n) cnt_full <=1'd0; else if(cnt==999_999) cnt_full<=1'b1; else cnt_full <= 1'b0; endmodule
驅動程序寫完后就需要補充應用程序了。
頂層文件的框圖應該如圖所示,在后面對其進行添加
圖中rst_n和clk沒連,意思是每個文件都需要連上。
下面,首先編寫led_ctrl.v文件。
module led_ctrl(clk,rst_n,key_flag1,key_flag2,key_state1,key_state2,led); input clk,rst_n,key_flag1,key_flag2,key_state1,key_state2; output reg [3:0]led; always@(posedge clk or negedge rst_n) if(!rst_n) led<=4'b0000; else if(key_flag1&&!key_state1) led<=led+1'b1; else if(key_flag2&&!key_state2) led<=led-1'b1; else led<=led; endmodule
然后,根據上篇文章對segment的了解,在工程中添加文件segment.v對數碼管顯示進行控制:
這里為了設計的代碼整潔及邏輯性,將文件進行分層,首先將segment的譯碼器segment_data單獨寫一個文件
module segment_data(clk,rst_n,data_temp,seg) ; input clk,rst_n; input [3:0]data_temp; output reg[6:0]seg; always@(posedge clk or negedge rst_n) if(!rst_n) seg<=7'b0000000; else begin case (data_temp) 4'h0: seg<=7'b1000000; 4'h1: seg<=7'b1111001; 4'h2: seg<=7'b0100100; 4'h3: seg<=7'b0110000; 4'h4: seg<=7'b0011001; 4'h5: seg<=7'b0010010; 4'h6: seg<=7'b0000010; 4'h7: seg<=7'b1111000; 4'h8: seg<=7'b0000000; 4'h9: seg<=7'b0010000; 4'ha: seg<=7'b0001000; 4'hb: seg<=7'b0000011; 4'hc: seg<=7'b1000110; 4'hd: seg<=7'b0100001; 4'he: seg<=7'b0000110; 4'hf: seg<=7'b0001110; endcase end endmodule
然后編寫segment的顯示控制主文件,要求顯示時間的跳動,並且可以控制單加,並且能夠通過按鍵快速調節顯示,主文件segment_2.v文件如下
module segment_2(rst_n,clk,en ,key_flag1,key_flag2,key_flag3,key_state1,key_state2,key_state3,data_out); input clk;//50M input rst_n; input en; //使能端口,確定按鍵是否有效 reg cnt_full,second,miniter,hour; reg [5:0]sel; //位選(選擇控制的哪個數碼) reg [23:0]data; reg [25:0]cnt; reg [3:0]data_temp0,data_temp1,data_temp2,data_temp3,data_temp4,data_temp5;//待顯示數據緩存 wire [6:0] seg_temp0,seg_temp1,seg_temp2,seg_temp3,seg_temp4,seg_temp5,seg_temp; input key_flag1,key_flag2,key_flag3,key_state1,key_state2,key_state3; output reg[41:0]data_out; segment_data segment_data0(.clk(clk), .rst_n(rst_n), .data_temp(data_temp0), .seg(seg_temp0)) ; segment_data segment_data1(.clk(clk), .rst_n(rst_n), .data_temp(data_temp1), .seg(seg_temp1)) ; segment_data segment_data2(.clk(clk), .rst_n(rst_n), .data_temp(data_temp2), .seg(seg_temp2)) ; segment_data segment_data3(.clk(clk), .rst_n(rst_n), .data_temp(data_temp3), .seg(seg_temp3)) ; segment_data segment_data4(.clk(clk), .rst_n(rst_n), .data_temp(data_temp4), .seg(seg_temp4)) ; segment_data segment_data5(.clk(clk), .rst_n(rst_n), .data_temp(data_temp5), .seg(seg_temp5)) ; always@(posedge clk or negedge rst_n) if (!rst_n)begin //按下key0鍵 data<=0; sel<=0; end else if(key_flag1&&!key_state1)begin//按下key1鍵 if(data==24'd235959) data<=0; else begin case(sel) 0: data=data+1'b1; 1: if(data[3:0]==4'b1001)//最大顯示位為9 data[3:0]<=0; else data[3:0]<= data[3:0]+1'b1; 2: if(data[7:4]==4'b0101)//最大顯示位為5 data[7:4]<=0; else data[7:4]<= data[7:4]+1'b1; 3: if(data[11:8]==4'b1001)//最大顯示位為9 data[11:8]<=0; else data[11:8]<=data[11:8]+1'b1; 4:if(data[15:12]==4'b0101)//最大顯示位為5 data[15:12]<=0; else data[15:12]<= data[15:12]+1'b1; 5: if(data[19:16]==4'b1001)//最大顯示位為9 data[19:16]<=0; else data[19:16]<= data[19:16]+1'b1; 6: if(data[19:16]>=4'b0011) begin // 第5位大於3時 if(data[23:20]==4'b0001)//最大顯示位為1 data[23:20]<=0; else if(data[23:20]==4'b0010)//最大顯示位為2 data[23:20]<=0; end else data[23:20]=data[23:20]+1'b1; default sel<=0; endcase end end else if(key_flag2&&!key_state2) begin//按下key2鍵 if(data==0) data<=0; else begin case(sel) 0: data<=data-1'b1; 1: if(data[3:0]==0) data[3:0]<=0; else data[3:0]<= data[3:0]-1'b1; 2: if(data[7:4]==0) data[7:4]<=0; else data[7:4]<= data[7:4]-1'b1; 3: if(data[11:8]==0) data[11:8]<=0; else data[11:8]<= data[11:8]-1'b1; 4: if(data[15:12]==0) data[15:12]<=0; else data[15:12]<= data[15:12]-1'b1; 5: if(data[19:16]==0) data[19:16]<=0; else data[19:16]<= data[19:16]-1'b1; 6: if(data[23:20]==0) data[23:20]<=0; else data[23:20]<= data[23:20]-1'b1; default sel<=0; endcase end end else if(key_flag3&&!key_state3) begin //按下key3鍵,可以通過這個按鍵改固定位數 if(sel>=6) sel<=0; else begin sel<=sel+1; end end //時鍾信號,每秒更換一次顯示 else if(cnt==26'd49_999_999) //時鍾跳變及跳變邏輯 begin if(data>=24'b0010_0011_0101_1001_0101_1001) //時鍾到了最大時間23小時59分59秒? data<=0; else if(data<=24'b0010_0011_0101_1001_0101_1001) begin if(data[7:0]>=8'b0101_1001) begin //秒針到了59 data[7:0]<=0; if(data[15:8]>=8'b0101_1001) begin //分鍾到了59 data[15:8]<=0; if(data[19:16]>=4'b0011)begin data[19:16]<=0; data[23:16]<=data[23:16]+1'b1;end else data[19:16]<=data[19:16]+1'b1; end else begin if(data[11:8]>=4'b1001) begin data[11:8]<=0; data[15:12]<=data[15:12]+1'b1; end else data[11:8]<=data[11:8]+1'b1; end end else if(data[7:0]<=8'b0101_1001) begin if(data[3:0]>=4'b1001)begin data[3:0]<=0; data[7:4]<=data[7:4]+1'b1;end else data[3:0]<=data[3:0]+1'b1; end end end //譯碼器 always@(*) begin data_temp0<=data[3:0]; data_temp1<=data[7:4]; data_temp2<=data[11:8]; data_temp3<=data[15:12]; data_temp4<=data[19:16]; data_temp5<=data[23:20]; data_out[6:0]<=seg_temp0; data_out[13:7]<=seg_temp1; data_out[20:14]<=seg_temp2; data_out[27:21]<=seg_temp3; data_out[34:28]<=seg_temp4; data_out[41:35]<=seg_temp5 ; end //定義一個計數器, 已知50M系統時鍾,周期即為20ns 需要計數1000_000_000/20-1=49_999_999 //20MS/周期-1的數據,需要26位的寄存器。 always@(posedge clk or negedge rst_n) if (!rst_n) cnt<=26'd0; else if (en)begin if(cnt>26'd49_999_999) cnt<=0; else cnt<=cnt+1'b1;end else cnt<=26'd0; endmodule
編寫key_top_tb文件,並進行鏈接,鏈接時需要把module模塊添加到路徑中。
`timescale 1ns/1ns `define clk_period 20 module key_top_tb; reg clk; reg rst_n; wire key_in1,key_in2; wire [3:0]led; reg press1,press2; key_top key_top0(.clk(clk), .rst_n(rst_n), .key_in1(key_in1), .key_in2(key_in2), .led(led) ); key_module key_module3( .press(press1), .key(key_in1) ); key_module key_module4( .press(press2), .key(key_in2) ); initial clk =1; always #(`clk_period/2) clk=~clk; initial begin rst_n = 1'b0; press1 = 0; press2 = 0; #(`clk_period*10) rst_n = 1'b1; #(`clk_period*10 + 1); press1 = 1; #(`clk_period*3) press1 = 0; #80_000_000; press1 = 1; #(`clk_period*3) press1 = 0; #80_000_000; press2 = 1; #(`clk_period*3) press2 = 0; #80_000_000; press2 = 1; #(`clk_period*3) press2 = 0; #80_000_000; $stop; end endmodule
編寫過程中發現需要對module.v文件進行修改,否者無法對按鍵是key_in?進行區分,修改后如下
`timescale 1ns/1ns `define clk_period 20 //模擬一個按鍵模型 module key_module(press,key); output reg key; input press; reg [15:0]myrand; // initial begin // #(`clk_period*10) // press_key;//直接調用press_key即可模擬一次按鍵過程了 // #100000; // press_key; // #10000; // $stop; // end initial begin key=1'b1; end always@(posedge press) press_key; task press_key; begin //按下抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key=~key; end key=0; #20_500_500 //釋放抖動過程 repeat(50)begin myrand=($random)%65536; //random 是個32位的函數,要想其產生一個固定的范圍的數據,就將其除以我們希望產生范圍的數據的最大值。 //加過好即為0~65535,不加就是-65535~65535 #myrand key=~key; end key=1; #20_500_500; end endtask endmodule
仿真波形如下,按照仿真文件,led數據變化為1111~1110~1101~1110~1111,加減操作都成功完成了
設定引腳。
燒寫程序,查看現象,將程序燒寫到友晶的開發板中,可以觀察到最后兩個數碼管按照秒鍾的規律跳變,並且通過默認的按鍵KEY1,KEY2可以加減計數6位的數碼管,數碼管可以正常進位,按下key3后,可以通過按鍵次數選擇要調節的數碼管的位數。