【FPGA】数码管电子时钟
目錄
- 一丶數(shù)碼管介紹
- 二丶任務(wù)描述
- 三丶系統(tǒng)框圖
- 四丶模塊調(diào)用
- 五丶模塊原理圖
- 六丶工程源碼
- 1.計數(shù)器模塊
- 2.數(shù)碼管驅(qū)動模塊
- 3.頂層模塊
- 七丶仿真測試
- 1.TestBench
- 2.仿真結(jié)果
- 八丶管腳信息
- 九丶上板驗證
- 十丶源碼
一丶數(shù)碼管介紹
Cyclone IV開發(fā)板上的數(shù)碼管一共有6個,我們每次只能選擇其中一個顯示,怎么解決電子時鐘時、分、秒同時顯示呢?要實現(xiàn)電子時鐘首先要了解什么是余暉效應(yīng)。
余暉效應(yīng)一般指視覺暫留。 視覺暫留現(xiàn)象即視覺暫停現(xiàn)象(Persistence of vision,Visual staying phenomenon,duration of vision)又稱“余暉效應(yīng)”。只要數(shù)碼管位選信號切換得足夠快,數(shù)碼管由亮到滅這一過程是需要一段時間的,由于時間很短,我們的眼睛是沒有辦法分清此時此刻數(shù)碼管的狀態(tài),給人的感覺就是數(shù)碼管是一直亮的。以此來達到欺騙人眼的效果,這樣就可以實現(xiàn)同時顯示時、分、秒。
二丶任務(wù)描述
使用數(shù)碼管設(shè)計電子時鐘,計數(shù)器部分有3種實現(xiàn)方法:
①.采用1個計數(shù)器,模為24x60x60;
②.采用3個計數(shù)器,模分別為24、60、60;
③.采用6個計數(shù)器分別計數(shù)時、分、秒的個位、十位;
方法1:計數(shù)器個數(shù)少,設(shè)計簡單,但是后面數(shù)碼管譯碼時,需要對計數(shù)值取余、取整,分離出時、分、秒的個位和十位,比較耗費組合邏輯資源;
方法2:3個計數(shù)器,后面數(shù)碼管譯碼時,需要對時、分、秒計數(shù)值取余、取整,分離出時、分、秒的個位和十位,比較耗費組合邏輯資源;
方法3:6個計數(shù)器,相對復雜一點,但是計數(shù)值直接就是時、分、秒的個位和十位值,不需要除法器進行取余、取整操作,使用的觸發(fā)器資源略多,但節(jié)省組合邏輯資源。
因為本文是作為作者stark-lin: 數(shù)碼管電子時鐘的拓展,原文使用了方法1,我們這里選擇方法3
最后實現(xiàn)上面圖片的效果,由于開發(fā)板上的數(shù)碼管沒有冒號(:),所以我們用小數(shù)點代替
三丶系統(tǒng)框圖
計時器模塊:我們把計時器模塊細分了6個計時器,分別計時整個時間的小時的十位和個位,分鐘的十位和個位,秒的十位和個位,最后將6個計時器拼接成一個dout_time輸出給數(shù)碼管驅(qū)動。與stark-lin的方法1不同,我們不需要對計數(shù)器做運算,只需要在數(shù)碼管驅(qū)動中取出對應(yīng)數(shù)碼管的數(shù)字
(對應(yīng)dout_time[x:y]的第x到y(tǒng)位,比如我要取出小時的十位,由于小時的十位最大能計時到2,換算成2進制數(shù)就是2位的,因為一天24小時嘛,所以應(yīng)該對應(yīng)dout_time[19:18])
數(shù)碼管驅(qū)動:對dout_time數(shù)值進行譯碼,產(chǎn)生驅(qū)動數(shù)碼管動態(tài)顯示數(shù)字的位選信號和段選信號。
四丶模塊調(diào)用
五丶模塊原理圖
六丶工程源碼
1.計數(shù)器模塊
counter.v:
module counter (input wire clk ,input wire rst_n ,output reg [19:0] dout_time //輸出時間 HH:MM:SS ); //計數(shù)器 reg [25:0] cnt ; wire add_cnt; wire end_cnt; //S計時器//個位 (0~9) reg [3:0] cnt_s_bit; wire add_cnt_s_bit; wire end_cnt_s_bit; //十位 (0~5) reg [2:0] cnt_s_ten; wire add_cnt_s_ten; wire end_cnt_s_ten;//M計時器//個位 (0~9) reg [3:0] cnt_m_bit; wire add_cnt_m_bit; wire end_cnt_m_bit; //十位 (0~5) reg [2:0] cnt_m_ten; wire add_cnt_m_ten; wire end_cnt_m_ten;//H計時器//個位 (0~9) reg [3:0] cnt_h_bit; wire add_cnt_h_bit; wire end_cnt_h_bit; //十位 (0~2) reg [1:0] cnt_h_ten; wire add_cnt_h_ten; wire end_cnt_h_ten;reg [3:0] flag; parameter MAX_CNT=26'd50_000_000;//計數(shù)器 always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt<=0;endelse if (add_cnt) beginif (end_cnt) begincnt<=0;endelsecnt<=cnt+1;end endassign add_cnt=1'b1; assign end_cnt=add_cnt&&cnt==MAX_CNT-1;//秒計時器---個位(0~9) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_s_bit<=0;endelse if (add_cnt_s_bit) beginif (end_cnt_s_bit) begincnt_s_bit<=0;endelsecnt_s_bit<=cnt_s_bit+1;endendassign add_cnt_s_bit=end_cnt; assign end_cnt_s_bit=add_cnt_s_bit&&cnt_s_bit==9;//秒計時器---十位(0~5) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_s_ten<=0;endelse if (add_cnt_s_ten) beginif (end_cnt_s_ten) begincnt_s_ten<=0;endelsecnt_s_ten<=cnt_s_ten+1;endendassign add_cnt_s_ten=end_cnt_s_bit; assign end_cnt_s_ten=add_cnt_s_ten&&cnt_s_ten==5;//分計時器---個位(0~9) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_m_bit<=9;endelse if (add_cnt_m_bit) beginif (end_cnt_m_bit) begincnt_m_bit<=0;endelsecnt_m_bit<=cnt_m_bit+1;endendassign add_cnt_m_bit=end_cnt_s_ten; assign end_cnt_m_bit=add_cnt_m_bit&&cnt_m_bit==9;//分計時器---十位(0~5) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_m_ten<=5;endelse if (add_cnt_m_ten) beginif (end_cnt_m_ten) begincnt_m_ten<=0;endelsecnt_m_ten<=cnt_m_ten+1;endendassign add_cnt_m_ten=end_cnt_m_bit; assign end_cnt_m_ten=add_cnt_m_ten&&cnt_m_ten==5;//時計時器---個位(0~9) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_h_bit<=3;endelse if (add_cnt_h_bit) beginif (end_cnt_h_bit) begincnt_h_bit<=0;endelsecnt_h_bit<=cnt_h_bit+1;endendassign add_cnt_h_bit=end_cnt_m_ten; assign end_cnt_h_bit=add_cnt_h_bit&&cnt_h_bit==flag;//時計時器---十位(0~2) always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_h_ten<=2;endelse if (add_cnt_h_ten) beginif (end_cnt_h_ten) begincnt_h_ten<=0;endelsecnt_h_ten<=cnt_h_ten+1;endendassign add_cnt_h_ten=end_cnt_h_bit; assign end_cnt_h_ten=add_cnt_h_ten&&cnt_h_ten==2;//判斷小時計時器十位是否記到 2 always @(*) beginif (cnt_h_ten==2) beginflag=4'd3;endelseflag=4'd9; end//dout_time輸出 always @(posedge clk or negedge rst_n) beginif (!rst_n) begindout_time<=20'b0;endelsedout_time<={cnt_h_ten,cnt_h_bit,cnt_m_ten,cnt_m_bit,cnt_s_ten,cnt_s_bit}; //拼接成 HH:MM:SS end endmodule //counter2.數(shù)碼管驅(qū)動模塊
seg_driver:
module seg_driver (input wire clk,input wire rst_n,input wire [19:0] dout_time,output reg [5:0] sel,output reg [7:0] seg ); reg [3:0] seg_flag; reg dot; //小數(shù)點 用來顯示 HH.MM.SS 這樣的格式//10ms計時器---用來切換數(shù)碼管位選,以達到輪流顯示時間的各位(肉眼可以看到動態(tài)的時間計數(shù)) reg [15:0] cnt; wire add_cnt; wire end_cnt;parameter MAX_CNT =50_000 ,ZERO =7'b100_0000,ONE =7'b111_1001,TWO =7'b010_0100,THREE =7'b011_0000,FOUR =7'b001_1001,FIVE =7'b001_0010,SIX =7'b000_0010,SEVEN =7'b111_1000,EIGHT =7'b000_0000,NINE =7'b001_0000;//計時器 always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt<=0;endelse if(add_cnt) beginif (end_cnt) begincnt<=0;endelsecnt<=cnt+1;end end assign add_cnt=1'b1; assign end_cnt=add_cnt&&cnt==MAX_CNT-1;//切換數(shù)碼管位選 always @(posedge clk or negedge rst_n) beginif (!rst_n) beginsel<=6'b111_110;endelse if(cnt==MAX_CNT-1) beginsel<={sel[4:0],sel[5]};end end //切換數(shù)碼管段選 always @(posedge clk or negedge rst_n) beginif (!rst_n) beginseg_flag<=0;endelse begincase (sel)6'b111_110: begin seg_flag<=dout_time[19:18]; dot<=1'b1;end //小時 十位6'b111_101: begin seg_flag<=dout_time[17:14]; dot<=1'b0;end //小時 個位6'b111_011: begin seg_flag<=dout_time[13:11]; dot<=1'b1;end //分鐘 十位6'b110_111: begin seg_flag<=dout_time[10:7]; dot<=1'b0;end //分鐘 個位6'b101_111: begin seg_flag<=dout_time[6:4]; dot<=1'b1;end //秒 十位6'b011_111: begin seg_flag<=dout_time[3:0]; dot<=1'b1;end //秒 個位default :seg_flag<=0;endcaseend end//段選譯碼 always @(posedge clk or negedge rst_n) beginif (!rst_n) beginseg<=8'b1111_1111;endelse begincase (seg_flag) 0: seg<={dot,ZERO} ;1: seg<={dot,ONE} ;2: seg<={dot,TWO} ;3: seg<={dot,THREE} ;4: seg<={dot,FOUR} ;5: seg<={dot,FIVE} ;6: seg<={dot,SIX} ;7: seg<={dot,SEVEN} ;8: seg<={dot,EIGHT} ;9: seg<={dot,NINE} ;default: seg<=8'b1111_1111;endcaseend endendmodule //seg_driver3.頂層模塊
top.v:
module top (input wire clk , //系統(tǒng)時鐘input wire rst_n , //復位信號output wire [5:0] sel , //數(shù)碼管位選output wire [7:0] seg //數(shù)碼管段選 ); wire [19:0] dout_time;//例化計時模塊 counter u_counter(.clk (clk) ,.rst_n (rst_n) ,.dout_time (dout_time) //輸出時間 HH:MM:SS );//例化數(shù)碼管驅(qū)動 seg_driver u_seg_driver(.clk (clk) ,.rst_n (rst_n) ,.sel (sel) ,.seg (seg) ,.dout_time (dout_time) ); endmodule //top七丶仿真測試
1.TestBench
`timescale 1ns/1ns module tb_top(); reg clk; reg rst_n; wire [5:0] sel; wire [7:0] seg; top u_top(.clk (clk) ,.rst_n (rst_n) ,.sel (sel) ,.seg (seg) ); defparam u_top.u_counter.MAX_CNT=2; defparam u_top.u_seg_driver.MAX_CNT=2;always #10 clk=~clk; initial beginclk=1;rst_n=1;#100;rst_n=0;#100;rst_n=1;#200000;$stop; endendmodule2.仿真結(jié)果
八丶管腳信息
九丶上板驗證
數(shù)碼管電子時鐘
十丶源碼
https://github.com/xuranww/digital_clock.git
總結(jié)
以上是生活随笔為你收集整理的【FPGA】数码管电子时钟的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FPGA 十进制 转化为二进制
- 下一篇: 分享一篇写的非常好的文章《如何掌握企业级