學(xué)弟加油!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?———來自科大焯人
最近剛好學(xué)習(xí)了數(shù)電有關(guān)知識,就做了這個項目(鬧鐘過于繁瑣就沒有做了)
希望給還在學(xué)習(xí)的大伙一點參考,完整代碼在最后
在這里先附上兩串代碼分別是debounce(按鍵消抖)和divide(分頻)
這兩個在小腳丫的示例中都可以找到,但我還是先附在這
//按鍵消抖
module debounce (clk,rst,key,key_pulse);
parameter N = 1; //要消除的按鍵的數(shù)量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵
output [N-1:0] key_pulse; //按鍵動作產(chǎn)生的脈沖
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發(fā)時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當(dāng)前時刻觸發(fā)的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產(chǎn)生一個高脈沖
//利用非阻塞賦值特點,將兩個時鐘觸發(fā)時按鍵狀態(tài)存儲在兩個寄存器變量中
always @(posedge clk or negedge rst)
begin
if (!rst) begin
key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全為1,{}中表示N個1
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //第一個時鐘上升沿觸發(fā)之后key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
key_rst_pre <= key_rst; //非阻塞賦值。相當(dāng)于經(jīng)過兩個時鐘觸發(fā),key_rst存儲的是當(dāng)前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
end
end
assign key_edge = key_rst_pre & (~key_rst);//脈沖邊沿檢測。當(dāng)key檢測到下降沿時,key_edge產(chǎn)生一個時鐘周期的高電平
reg [17:0] cnt; //產(chǎn)生延時所用的計數(shù)器,系統(tǒng)時鐘12MHz,要延時20ms左右時間,至少需要18位計數(shù)器
//產(chǎn)生20ms延時,當(dāng)檢測到key_edge有效是計數(shù)器清零開始計數(shù)
always @(posedge clk or negedge rst)
begin
if(!rst)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //延時后檢測電平寄存器變量
reg [N-1:0] key_sec;
//延時后檢測key,如果按鍵狀態(tài)變低產(chǎn)生一個時鐘的高脈沖。如果按鍵狀態(tài)是高的話說明按鍵無效
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec <= {N{1'b1}};
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
assign key_pulse = key_sec_pre & (~key_sec);
endmodule
//分頻
module divide ( clk,rst_n,clkout);
input clk,rst_n; //輸入信號,其中clk連接到FPGA的C1腳,頻率為12MHz
output clkout; //輸出信號,可以連接到LED觀察分頻的時鐘
//parameter是verilog里常數(shù)語句
parameter WIDTH = 24; //計數(shù)器的位數(shù),計數(shù)的最大值為 2**WIDTH-1
parameter N = 12_000_000; //分頻系數(shù),請確保 N < 2**WIDTH-1,否則計數(shù)會溢出
reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p為上升沿觸發(fā)時的計數(shù)器,cnt_n為下降沿觸發(fā)時的計數(shù)器
reg clk_p,clk_n; //clk_p為上升沿觸發(fā)時分頻時鐘,clk_n為下降沿觸發(fā)時分頻時鐘
//上升沿觸發(fā)時計數(shù)器的控制
always @ (posedge clk or negedge rst_n ) //posedge和negedge是verilog表示信號上升沿和下降沿
//當(dāng)clk上升沿來臨或者rst_n變低的時候執(zhí)行一次always里的語句
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1; //計數(shù)器一直計數(shù),當(dāng)計數(shù)到N-1的時候清零,這是一個模N的計數(shù)器
end
//上升沿觸發(fā)的分頻時鐘輸出,如果N為奇數(shù)得到的時鐘占空比不是50%;如果N為偶數(shù)得到的時鐘占空比為50%
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1)) //N>>1表示右移一位,相當(dāng)于除以2去掉余數(shù)
clk_p<=0;
else
clk_p<=1; //得到的分頻時鐘正周期比負(fù)周期多一個clk時鐘
end
//下降沿觸發(fā)時計數(shù)器的控制
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_n<=0;
else if (cnt_n==(N-1))
cnt_n<=0;
else cnt_n<=cnt_n+1;
end
//下降沿觸發(fā)的分頻時鐘輸出,和clk_p相差半個時鐘
always @ (negedge clk)
begin
if(!rst_n)
clk_n<=0;
else if (cnt_n<(N>>1))
clk_n<=0;
else
clk_n<=1; //得到的分頻時鐘正周期比負(fù)周期多一個clk時鐘
end
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p; //條件判斷表達(dá)式
//當(dāng)N=1時,直接輸出clk
//當(dāng)N為偶數(shù)也就是N的最低位為0,N(0)=0,輸出clk_p
//當(dāng)N為奇數(shù)也就是N最低位為1,N(0)=1,輸出clk_p&clk_n。正周期多所以是相與
endmodule
將上述兩個代碼文件準(zhǔn)備好就可以開始編寫我們的主體程序了。
既然是數(shù)字時鐘,首先我們應(yīng)當(dāng)通過分頻器產(chǎn)生1s中的信號,代碼中的clk1h是我用分頻器產(chǎn)生的1Hz信號。每經(jīng)過1Hz的上升沿秒鐘進(jìn)一位,在秒鐘個位為9且十位不為5時,下一次進(jìn)位個位要變?yōu)?,十位要加1,若是59秒時則應(yīng)當(dāng)都置零。同時,分鐘的計時和時鐘的計時實現(xiàn)思路與秒鐘計時類似,但需要額外進(jìn)行判斷,比如說分鐘進(jìn)位要在秒鐘均為0時才產(chǎn)生進(jìn)位,時鐘進(jìn)位要在分鐘秒鐘全為0時才進(jìn)位。
時鐘和分鐘的敏感信號為clk,這是小腳丫自帶的時鐘信號12MHz。由于clk頻率極高,在1s中內(nèi)能夠觸發(fā)分鐘和時鐘計時模塊多次,為實現(xiàn)分鐘和時鐘在進(jìn)位時僅進(jìn)位一次,我額外增加了兩個判斷位,分別為pan(分鐘)和pan1(時鐘)。在pan和pan1為0時才能分別觸發(fā)分鐘和時鐘進(jìn)位,并且觸發(fā)一次進(jìn)位后pan或pan1置1,直到秒鐘或者分鐘不全為0時pan或pan1才會重新置0。
具體代碼如下
//60秒計時控制
always @ (posedge clk1h ) begin
if(cnt_shi==5 && cnt_ge==9) begin
cnt_shi <= 0;
cnt_ge <= 0;
end
else if(cnt_shi==0 && cnt_ge==0) begin
cnt_shi <= 0;
cnt_ge <= 1;
end
else if(cnt_ge==9)begin
cnt_ge <= 0;
cnt_shi <= cnt_shi+1;
end
else
cnt_ge <= cnt_ge +1;
end
//60分計時控制
always @ (posedge clk)begin
if((cnt_ge==0)&&(cnt_shi==0)&&(pan==0))begin
pan<=1;
if(minute_shi==5 && minute_ge==9) begin
minute_shi <= 0;
minute_ge <= 0;
end
else if(minute_ge==9)begin
minute_ge <= 4'd0;
minute_shi <= minute_shi+1;
end
else
minute_ge <= minute_ge +1;
end
else
if((cnt_ge!=0) || (cnt_shi!=0))begin
pan<=0;
end
//24小時計時與加減法模塊
always @ (posedge clk ) begin
if ((minute_ge==0)&&(minute_shi==0)&&(pan1==0)&&(cnt_shi==0)&&(cnt_ge==0)) begin
pan1<=1;
if(hour_shi==2 && hour_ge==3) begin
hour_shi <= 0;
hour_ge <= 0;
end
else if(hour_ge==9)begin
hour_ge <= 4'd0;
hour_shi <= hour_shi+1;
end
else
hour_ge <= hour_ge +1;
end
else
if((minute_ge!=0) || (minute_shi!=0))begin
pan1<=0;
end
至此,我們已經(jīng)基本實現(xiàn)了數(shù)字時鐘的運行,但還需要將時鐘信息顯示在數(shù)碼管上。
我將按鍵觸發(fā)的信號位記作change和change2。當(dāng)change2為高電平且change為低電平時,數(shù)碼管顯示分鐘;當(dāng)change2為高電平且change為高電平時,數(shù)碼管顯示時鐘;而當(dāng)change2為低電平時,不論change電平,均顯示秒鐘。
//數(shù)碼管顯示數(shù)字
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
//選擇顯示
always @ (posedge clk)begin
if((change==0)&&(change2==1))begin
seg_led_1<= seg[hour_ge];
seg_led_2<= seg[hour_shi];
end
else if((change==1)&&(change2==1))
begin
seg_led_1<= seg[minute_ge];
seg_led_2<= seg[minute_shi];
end
else if(change2==0)begin
seg_led_1<= seg[cnt_ge];
seg_led_2<= seg[cnt_shi];
end
end
所有基礎(chǔ)功能都已經(jīng)實現(xiàn),現(xiàn)在還需要進(jìn)階,也就是調(diào)時和整點報時的實現(xiàn)。
我們先講調(diào)時的實現(xiàn)
我在實現(xiàn)加減法的時候均用了兩個計數(shù)器add、add1和jian、jian1。
add和jian分別為按鍵按下次數(shù)的計數(shù)器,而add1和jian1則分別為add和jian的匹配計數(shù)器。
當(dāng)add1不等于add時,將時鐘進(jìn)一位,并且add1+1;當(dāng)jian1不等于jian時,將時鐘退一位,再將jian1+1,便可以實現(xiàn)調(diào)時的功能。
我在寫代碼的時候是將調(diào)時模塊整合在計時模塊中的,時鐘調(diào)時與分鐘調(diào)時的原理是相同的。
if((add1!=add)&&(change==1)) begin
add1<=add1+1;
if(minute_shi==5 && minute_ge==9) begin
minute_shi <= 0;
minute_ge <= 0;
end
else if(minute_ge==9)begin
minute_ge <= 4'd0;
minute_shi <= minute_shi+1;
end
else
minute_ge <= minute_ge +1;
end
if((jian1!=jian)&&(change==1))begin
jian1<=jian1+1;
if(minute_shi==0 && minute_ge==0)begin
minute_ge<=9;
minute_shi<=5;
end
else if(minute_ge==0)begin
minute_ge<=9;
minute_shi<=minute_shi-1;
end
else
minute_ge<=minute_ge-1;
end
最后便是整點報時的實現(xiàn)
整點報時需要讓燈光按照1Hz的頻率閃爍,那么我們便可以用一個2Hz的時鐘信號作為敏感信號來觸發(fā)燈光閃爍。
首先只有在秒鐘分鐘均為0時才算作整點,在這個條件下我又用到了兩個計數(shù)器cnt、cnt1與加減法的計數(shù)器功能一致,cnt1為當(dāng)前整點數(shù)的兩倍(因為一亮一滅需要燈光反轉(zhuǎn)兩次),cnt為匹配計數(shù)器,當(dāng)cnt不等于cnt1時燈光反轉(zhuǎn)一次,并且cnt+1。具體代碼實現(xiàn)如下。
//整點報時
always @(posedge clk0)begin
led=~led;
if((minute_shi==0)&&(minute_ge==0)&&(cnt_shi==0)&&(cnt_ge==0))begin
cnt1=2*(10*hour_shi+hour_ge);
end
if(cnt!=cnt1)begin
rgb=~rgb;
cnt<=cnt+1;
end
else if(cnt==cnt1)begin
cnt<=0;
cnt1<=0;
end
end
以上就是整個數(shù)字時鐘的設(shè)計,最后附上數(shù)字時鐘的全部代碼(記得把文首的debounce和devide兩個代碼也裝上,不然數(shù)字時鐘代碼無法成功編譯)
module counter
(
clk , //時鐘
rst , //復(fù)位
plus , //加法為
cut , //減法位
change , //顯示轉(zhuǎn)化按鍵1
change2 , //顯示轉(zhuǎn)化按鍵2
seg_led_1 , //數(shù)碼管1
seg_led_2 , //數(shù)碼管2
rgb , //rgb燈光
led //led
);
input clk,rst;
input change;
input change2;
input plus,cut;
output reg [8:0] seg_led_1,seg_led_2;
output reg [7:0] led;
output reg [5:0] rgb;
wire clk0; //0.5秒時鐘
wire clk1h; //1秒鐘時鐘
wire change_pulse; //轉(zhuǎn)換按鍵消抖后信號
wire plus_pulse; //加法按鍵消抖后信號
wire cut_pulse; //減法按鍵消抖后信號
reg change_flag; //轉(zhuǎn)換按鍵標(biāo)志位
reg plus_flag; //加法按鍵標(biāo)志位
reg cut_flag; //減法按鍵標(biāo)志位
reg pan; //判斷分鐘進(jìn)位
reg pan1; //判斷時鐘進(jìn)位
reg add; //分鐘加法按鍵按下計數(shù)
reg add1; //分鐘加法按鍵匹配計數(shù)
reg add10; //時鐘加法按鍵按下計數(shù)
reg add11; //時鐘加法按鍵匹配計數(shù)
reg jian10; //時鐘減法按鍵按下計數(shù)
reg jian11; //時鐘減法按鍵匹配計數(shù)
reg jian; //分鐘減法按鍵按下計數(shù)
reg jian1; //分鐘減法按鍵匹配計數(shù)
reg [5:0] cnt=0; //記錄當(dāng)前小時數(shù)
reg [5:0] cnt1=0; //小時匹配數(shù)
reg [6:0] seg [9:0]; //數(shù)碼管
reg [3:0] cnt_ge; //秒鐘個位
reg [3:0] cnt_shi; //秒鐘十位
reg [3:0] minute_ge; //分鐘個位
reg [3:0] minute_shi; //分鐘十位
reg [3:0] hour_ge; //小時個位
reg [3:0] hour_shi; //小時十位
initial
begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
end
// 啟動/暫停按鍵進(jìn)行消抖
debounce U2 (
.clk(clk),
.rst(rst),
.key(change),
.key_pulse(change_pulse)
);
debounce U6 (
.clk(clk),
.rst(rst),
.key(plus),
.key_pulse(plus_pulse)
);
debounce U7 (
.clk(clk),
.rst(rst),
.key(cut),
.key_pulse(cut_pulse)
);
// 用于分出一個1Hz的頻率
divide #(.WIDTH(32),.N(12000000)) U1 (
.clk(clk),
.rst_n(rst),
.clkout(clk1h)
);
// 用于分出一個2Hz的頻率
divide #(.WIDTH(32),.N(6000000)) U5 (
.clk(clk),
.rst_n(rst),
.clkout(clk0)
);
//按鍵動作標(biāo)志信號產(chǎn)生
always @ (posedge change_pulse)begin
if(!rst==1)
change_flag <= 0;
else
change_flag <= ~change_flag;
end
always @ (posedge plus_pulse)begin
if(!rst==1)
plus_flag <= 0;
else
plus_flag <= ~plus_flag;
if(change==1)begin
add<=add+1;
end
else if(change==0)begin
add10<=add10+1;
end
end
always @ (posedge cut_pulse)begin
if(!rst==1)
cut_flag <= 0;
else
cut_flag <= ~cut_flag;
if(change==1)begin
jian<=jian+1;
end
else if(change==0)begin
jian10<=jian10+1;
end
end
//60秒計時控制
always @ (posedge clk1h ) begin
if(cnt_shi==5 && cnt_ge==9) begin
cnt_shi <= 0;
cnt_ge <= 0;
end
else if(cnt_shi==0 && cnt_ge==0) begin
cnt_shi <= 0;
cnt_ge <= 1;
end
else if(cnt_ge==9)begin
cnt_ge <= 0;
cnt_shi <= cnt_shi+1;
end
else
cnt_ge <= cnt_ge +1;
end
//60分鐘計時與加減法模塊
always @ (posedge clk)begin
if((cnt_ge==0)&&(cnt_shi==0)&&(pan==0))begin
pan<=1;
if(minute_shi==5 && minute_ge==9) begin
minute_shi <= 0;
minute_ge <= 0;
end
else if(minute_ge==9)begin
minute_ge <= 4'd0;
minute_shi <= minute_shi+1;
end
else
minute_ge <= minute_ge +1;
end
else
if((cnt_ge!=0) || (cnt_shi!=0))begin
pan<=0;
end
if((add1!=add)&&(change==1)) begin
add1<=add1+1;
if(minute_shi==5 && minute_ge==9) begin
minute_shi <= 0;
minute_ge <= 0;
end
else if(minute_ge==9)begin
minute_ge <= 4'd0;
minute_shi <= minute_shi+1;
end
else
minute_ge <= minute_ge +1;
end
if((jian1!=jian)&&(change==1))begin
jian1<=jian1+1;
if(minute_shi==0 && minute_ge==0)begin
minute_ge<=9;
minute_shi<=5;
end
else if(minute_ge==0)begin
minute_ge<=9;
minute_shi<=minute_shi-1;
end
else
minute_ge<=minute_ge-1;
end
end
//24小時計時與加減法模塊
always @ (posedge clk ) begin
if ((minute_ge==0)&&(minute_shi==0)&&(pan1==0)&&(cnt_shi==0)&&(cnt_ge==0)) begin
pan1<=1;
if(hour_shi==2 && hour_ge==3) begin
hour_shi <= 0;
hour_ge <= 0;
end
else if(hour_ge==9)begin
hour_ge <= 4'd0;
hour_shi <= hour_shi+1;
end
else
hour_ge <= hour_ge +1;
end
else
if((minute_ge!=0) || (minute_shi!=0))begin
pan1<=0;
end
if((add11!=add10)&&(change==0))begin
add11<=add11+1;
if(hour_shi==2 && hour_ge==3) begin
hour_shi <= 0;
hour_ge <= 0;
end
else if(hour_ge==9)begin
hour_ge <= 4'd0;
hour_shi <= hour_shi+1;
end
else
hour_ge <= hour_ge +1;
end
if((jian11!=jian10)&&(change==0))begin
jian11<=jian11+1;
if(hour_shi==0 && hour_ge==0)begin
hour_ge<=3;
hour_shi<=2;
end
else if(hour_ge==0)begin
hour_ge<=9;
hour_shi<=hour_shi-1;
end
else
hour_ge<=hour_ge-1;
end
end
//選擇顯示
always @ (posedge clk)begin
if((change==0)&&(change2==1))begin
seg_led_1<= seg[hour_ge];
seg_led_2<= seg[hour_shi];
end
else if((change==1)&&(change2==1))
begin
seg_led_1<= seg[minute_ge];
seg_led_2<= seg[minute_shi];
end
else if(change2==0)begin
seg_led_1<= seg[cnt_ge];
seg_led_2<= seg[cnt_shi];
end
end
//整點報時
always @(posedge clk0)begin
led=~led;
if((minute_shi==0)&&(minute_ge==0)&&(cnt_shi==0)&&(cnt_ge==0))begin
cnt1=2*(10*hour_shi+hour_ge);
end
if(cnt!=cnt1)begin
rgb=~rgb;
cnt<=cnt+1;
end
else if(cnt==cnt1)begin
cnt<=0;
cnt1<=0;
end
end
endmodule
管腳分配如下
?文章來源:http://www.zghlxwxcb.cn/news/detail-726935.html
有疑惑或者問題歡迎一起討論文章來源地址http://www.zghlxwxcb.cn/news/detail-726935.html
到了這里,關(guān)于Verilog語言fpga小腳丫數(shù)字時鐘(整點報時,調(diào)時,顯示秒鐘等功能)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!