(原創) 如何設計電子鐘(I)? (SOC) (Verilog) (DE2)
Abstract
學會計數器與除頻電路後,就能以這兩個電路為基礎,設計一個電子鐘,並可自行調整目前時間。
Introduction
使用環境:Quartus II 7.2 SP3 + DE2(Cyclone II EP2C35F627C6)
在(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)討論過計數器,接著在(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)討論過萬用除頻器,利用這兩個基礎,就可以設計一個簡易的電子鐘,如同電子錶一樣顯示時間。
Specification
SW17 | enable |
SW16 | reset |
SW15 | load (設定時間) |
SW[3:0] | 設定分的個位數 |
SW[6:4] | 設定分的十位數 |
SW[10:7] | 設定時的個位數 |
SW[13:11] | 設定時的十位數 |
HEX[2] | 顯示秒的個位數 |
HEX[3] | 顯示秒的十位數 |
HEX[4] | 顯示分的個位數 |
HEX[5] | 顯示分的十位數 |
HEX[6] | 顯示時的個位數 |
HEX[7] | 顯示時的十位數 |
Block Diagram
這是一個簡化的系統方塊圖,因為寬度的關係,我將input與output都與以省略,上圖的divn是個除頻器,負責將DE2提供的50MHz clock除頻程1 Hz,seg7_lut則是負責將數字顯示在7段顯示器上。clock的細部實現為下圖,由兩個60計數器負責秒和分,24計數器負責時。
digi_clock.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : digi_clock.v
5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6 Description : Demo how to write digital clock top module
7 Release : 07/27/2008 1.0
8 */
9
10 module digi_clock (
11 input CLOCK_50,
12 input [17:0] SW,
13 input [3:0] KEY,
14 output [6:0] HEX2,
15 output [6:0] HEX3,
16 output [6:0] HEX4,
17 output [6:0] HEX5,
18 output [6:0] HEX6,
19 output [6:0] HEX7
20 );
21
22 wire clk_1;
23 wire [3:0] w_sq0;
24 wire [2:0] w_sq1;
25 wire [3:0] w_mq0;
26 wire [2:0] w_mq1;
27 wire [3:0] w_hq0;
28 wire [2:0] w_hq1;
29
30 // 1Hz clock
31 divn # (.WIDTH(26), .N(50000000))
32 u0 (
33 .clk(CLOCK_50),
34 .rst_n(KEY[0]),
35 .o_clk(clk_1)
36 );
37
38 clock u1 (
39 .clk(clk_1),
40 .en(SW[17]), // input enable
41 .clr(SW[16]), // input clear
42 .load(SW[15]), // input load
43 .sd0(4'h0), // input second digit 0
44 .sd1(3'h0), // input second digit 1
45 .md0(SW[3:0]), // input minute digit 0
46 .md1(SW[6:4]), // input minute digit 1
47 .hd0(SW[10:7]), // input hour digit 0
48 .hd1(SW[13:11]), // input hour digit 1
49 .sq0(w_sq0), // output second digit 0
50 .sq1(w_sq1), // output second digit 1
51 .mq0(w_mq0), // output minute digit 0
52 .mq1(w_mq1), // output minute digit 1
53 .hq0(w_hq0), // output minute digit 0
54 .hq1(w_hq1) // output minute digit 1
55 );
56
57 // sec. dig0 to seg7
58 seg7_lut u2 (
59 .i_dig(w_sq0),
60 .o_seg(HEX2)
61 );
62
63 // sec. dig1 to seg7
64 seg7_lut u3 (
65 .i_dig({1'b0, w_sq1}),
66 .o_seg(HEX3)
67 );
68
69 // min. dig0 to seg7
70 seg7_lut u4 (
71 .i_dig(w_mq0),
72 .o_seg(HEX4)
73 );
74
75 // min. dig1 to seg7
76 seg7_lut u5 (
77 .i_dig({1'b0, w_mq1}),
78 .o_seg(HEX5)
79 );
80
81 // hour dig0 to seg7
82 seg7_lut u6 (
83 .i_dig(w_hq0),
84 .o_seg(HEX6)
85 );
86
87 // hour dig1 to seg7
88 seg7_lut u7 (
89 .i_dig({1'b0, w_hq1}),
90 .o_seg(HEX7)
91 );
92
93 endmodule
這是整個project的top module,為了簡化pin assignment的動作,所以port的命名方式和DE2_pin_assignments.csv相同,或許你跟我一樣不喜歡port用大寫的coding style,但為了pin assignment方便,只好在top module配合一下大寫,其他module的port一樣可以用小寫。
30行
divn # (.WIDTH(26), .N(50000000))
u0 (
.clk(CLOCK_50),
.rst_n(KEY[0]),
.o_clk(clk_1)
);
divn為(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)所寫過的萬用除頻器,由於DE2提供的clock是50MHz,但電子鐘只希望每秒變化一次,所以要除頻剩下1Hz,所以要將50MHz除50M,經過計算,這樣需26位才夠,所以傳進26與50000000。
divn.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : divn.v
5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6 Description : Demo how to write frequency divider by n
7 Release : 07/16/2008 1.0
8 */
9
10 module divn (
11 input clk,
12 input rst_n,
13 output o_clk
14 );
15
16 parameter WIDTH = 3;
17 parameter N = 6;
18
19 reg [WIDTH-1:0] cnt_p;
20 reg [WIDTH-1:0] cnt_n;
21 reg clk_p;
22 reg clk_n;
23
24 assign o_clk = (N == 1) ? clk :
25 (N[0]) ? (clk_p | clk_n) : (clk_p);
26
27 always@(posedge clk or negedge rst_n) begin
28 if (!rst_n)
29 cnt_p <= 0;
30 else if (cnt_p == (N-1))
31 cnt_p <= 0;
32 else
33 cnt_p <= cnt_p + 1;
34 end
35
36 always@(posedge clk or negedge rst_n) begin
37 if (!rst_n)
38 clk_p <= 1;
39 else if (cnt_p < (N>>1))
40 clk_p = 1;
41 else
42 clk_p = 0;
43 end
44
45 always@(negedge clk or negedge rst_n) begin
46 if (!rst_n)
47 cnt_n <= 0;
48 else if (cnt_n == (N-1))
49 cnt_n <= 0;
50 else
51 cnt_n <= cnt_n + 1;
52 end
53
54 always@(negedge clk or negedge rst_n) begin
55 if (!rst_n)
56 clk_n <= 1;
57 else if (cnt_n < (N>>1))
58 clk_n = 1;
59 else
60 clk_n = 0;
61 end
62
63 endmodule
64
dinn.v請參考(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)中的討論。
seg7_lut.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : seg7_lut.V
5 Compiler : Quartus II 7.2 SP3
6 Description : Demo how to use 8 bit 7 segment display decimal
7 Release : 07/20/2008 1.0
8 */
9 module seg7_lut (
10 input [3:0] i_dig,
11 output reg [6:0] o_seg
12 );
13
14 always@(i_dig) begin
15 case(i_dig)
16 4'h1: o_seg = 7'b111_1001; // ---t----
17 4'h2: o_seg = 7'b010_0100; // | |
18 4'h3: o_seg = 7'b011_0000; // lt rt
19 4'h4: o_seg = 7'b001_1001; // | |
20 4'h5: o_seg = 7'b001_0010; // ---m----
21 4'h6: o_seg = 7'b000_0010; // | |
22 4'h7: o_seg = 7'b111_1000; // lb rb
23 4'h8: o_seg = 7'b000_0000; // | |
24 4'h9: o_seg = 7'b001_1000; // ---b----
25 4'ha: o_seg = 7'b000_1000;
26 4'hb: o_seg = 7'b000_0011;
27 4'hc: o_seg = 7'b100_0110;
28 4'hd: o_seg = 7'b010_0001;
29 4'he: o_seg = 7'b000_0110;
30 4'hf: o_seg = 7'b000_1110;
31 4'h0: o_seg = 7'b100_0000;
32 endcase
33 end
34
35 endmodule
這是一個7段顯示器的lookup table,請參考(原創) 如何以16進位顯示8位數的七段顯示器? (SOC) (Verilog) (DE2)。
clock.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : clock.v
5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6 Description : Demo how to write clock counter
7 Release : 07/28/2008 1.0
8 */
9
10 module clock (
11 input clk,
12 input en,
13 input clr,
14 input load,
15 input [3:0] sd0,
16 input [2:0] sd1,
17 input [3:0] md0,
18 input [2:0] md1,
19 input [3:0] hd0,
20 input [2:0] hd1,
21 output [3:0] sq0,
22 output [2:0] sq1,
23 output [3:0] mq0,
24 output [2:0] mq1,
25 output [3:0] hq0,
26 output [1:0] hq1,
27 output co
28 );
29
30 wire w_clr;
31 wire [3:0] w_md0;
32 wire [2:0] w_md1;
33 reg [3:0] w_hd0;
34 reg [2:0] w_hd1;
35 wire w_sco; // second carry
36 wire w_mco; // minute carry
37 wire w_hco; // hour carry
38
39 counter60 sec (
40 .clk(clk),
41 .load(load),
42 .clr(w_clr),
43 .en(en),
44 .d0(sd0),
45 .d1(sd1),
46 .q0(sq0),
47 .q1(sq1),
48 .co(w_sco)
49 );
50
51 counter60 min (
52 .clk(clk),
53 .load(load),
54 .clr(w_clr),
55 .en(en & w_sco),
56 .d0(w_md0),
57 .d1(w_md1),
58 .q0(mq0),
59 .q1(mq1),
60 .co(w_mco)
61 );
62
63 counter24 hour (
64 .clk(clk),
65 .load(load),
66 .clr(w_clr),
67 .en(en & w_mco & w_sco),
68 .d0(w_hd0),
69 .d1(w_hd1),
70 .q0(hq0),
71 .q1(hq1),
72 .co(w_hco)
73 );
74
75 assign w_clr = clr | co;
76 assign co = w_hco & w_mco & w_sco;
77 assign w_md0 = (!load) ? 0 :
78 (md0 < 10) ? md0 : 9;
79 assign w_md1 = (!load) ? 0 :
80 (md1 < 6) ? md1 : 5;
81
82 always@(load or hd0 or hd1) begin
83 if (!load) begin
84 w_hd0 = 0;
85 w_hd1 = 0;
86 end
87 else begin
88 if (hd1 <= 1) begin // 0 1
89 w_hd1 = hd1;
90
91 if (hd0 < 10)
92 w_hd0 = hd0;
93 else
94 w_hd0 = 9;
95 end
96 else begin // >= 2
97 w_hd1 = 2;
98
99 if (hd0 < 4)
100 w_hd0 = hd0;
101 else
102 w_hd0 = 3;
103 end
104 end
105 end
106
107 endmodule
clock.v事實上已經是一個完整功能的電子鐘,可以單獨用testbench測試,但為了要和DE2周邊搭配,所以才又包了一個digi_clock.v。
clock.v主要有兩個功能:
1.例化時、分、秒3個instance。
2.對輸入做防呆的動作。
75行
assign co = w_hco & w_mco & w_sco;
co為整個電子鐘的進位功能,也就是當時、分、秒皆有進位時才進位,也就是23:59:59時才進位。
w_clr為整個電子鐘歸0的連線,除了手動clear外,當co為1,也就是23:59:59時, 整個電子鐘也會歸0。
下面都是對輸入防呆的程式,也就是分的輸入的的部分,最多只能輸入到59,時的部分最多只能輸入到23。
77行
(md0 < 10) ? md0 : 9;
assign w_md1 = (!load) ? 0 :
(md1 < 6) ? md1 : 5;
分的部分比較單純,所以用 ?: 寫法即可,因為分最多只能到59分,個位數最多只能到9,超過9的部分一率只能輸入9。十位部分最多只能到5,超過5的部分一率只能輸入5。
81行
if (!load) begin
w_hd0 = 0;
w_hd1 = 0;
end
else begin
if (hd1 <= 1) begin // 0 1
w_hd1 = hd1;
if (hd0 < 10)
w_hd0 = hd0;
else
w_hd0 = 9;
end
else begin // >= 2
w_hd1 = 2;
if (hd0 < 4)
w_hd0 = hd0;
else
w_hd0 = 3;
end
end
end
時的部分比較複雜,所以用always block來寫。因為時最多只能到23,所以個位數能輸入的數字還要看當時的十位數而定。若十位數為0或1,則個位數做多到9;若十位數大於2,個位數最多只能到3,超過3的部分一率只能輸3。
counter60.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : counter60.v
5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6 Description : Demo how to write 60 counter
7 Release : 07/27/2008 1.0
8 */
9
10 module counter60 (
11 input clk,
12 input clr,
13 input load,
14 input en,
15 input [3:0] d0,
16 input [3:0] d1,
17 output reg [3:0] q0,
18 output reg [2:0] q1,
19 output co
20 );
21
22 assign co = q1[2] & q1[0] & q0[3] & q0[0]; // 101 1001 = 59
23
24 always@(posedge clk) begin
25 if (clr) begin
26 q0 <= 0;
27 q1 <= 0;
28 end
29 else if (load) begin
30 q0 <= d0;
31 q1 <= d1;
32 end
33 else if (en) begin
34 if (q0 == 9) begin
35 q0 <= 0;
36
37 if (q1 == 5)
38 q1 <= 0;
39 else
40 q1 <= q1 + 1;
41 end
42 else
43 q0 <= q0 + 1;
44 end
45 else begin
46 q0 <= q0;
47 q1 <= q1;
48 end
49 end
50
51 endmodule
由於分和秒為60進制,所以需要一個60計數器,並且能進位,請參考(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)。
22行
進位的地方較特別,當59時要送出進位carry,若用2進位表示就是101 1001,所以只要將q1[2] & q1[0] & q0[3] & q0[0]即可,這就是2進位好用的地方。
counter24.v / Verilog
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : counter24.v
5 Compiler : Quartus II 7.2 SP3 + ModelSim-Altera 6.1g
6 Description : Demo how to write 24 counter
7 Release : 07/27/2008 1.0
8 */
9
10 module counter24 (
11 input clk,
12 input clr,
13 input load,
14 input en,
15 input [3:0] d0,
16 input [1:0] d1,
17 output reg [3:0] q0,
18 output reg [1:0] q1,
19 output co
20 );
21
22 assign co = q0[1] & q0[0] & q1[1]; // 010 11 = 23
23
24 always@(posedge clk) begin
25 if (clr) begin
26 q0 <= 0;
27 q1 <= 0;
28 end
29 else if (load) begin
30 q0 <= d0;
31 q1 <= d1;
32 end
33 else if (en) begin
34 if (q0 == 9) begin
35 q0 <= 0;
36 q1 <= q1 + 1;
37 end
38 else if (q1 == 2 & q0 == 3) begin // 23
39 q1 <= 0;
40 q0 <= 0;
41 end
42 else
43 q0 <= q0 + 1;
44 end
45 else begin
46 q0 <= q0;
47 q1 <= q1;
48 end
49 end
50
51 endmodule
原理和60計數器一樣,我就不在多做解釋。
完整程式碼下載
digi_clock.7z
Conclusion
原來每天帶的電子錶,就是以clock為基礎,搭配計數器做出來的,透過FPGA,我們也可以自己設計一個簡易的電子鐘。
See Also
(原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)
(原創) 如何以16進位顯示8位數的七段顯示器? (SOC) (Verilog) (DE2)
(筆記) 如何設計計數器? (SOC) (Verilog) (MegaCore)
(原創) 如何設計電子鐘(II)? (SOC) (Verilog) (MegaCore) (DE2)
Reference
陸自強 2007,數位系統實習 Quartus II,儒林圖書公司
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· Vite CVE-2025-30208 安全漏洞
· 《HelloGitHub》第 108 期
· MQ 如何保证数据一致性?
· 一个基于 .NET 开源免费的异地组网和内网穿透工具
2007-07-27 (轉貼)《程序员》推荐C++ 图书三人谈 (C/C++)