上篇的代碼源於正點原子的的例程,但是經過對比,我還是更喜歡黑金版本的思路,因爲邏輯更加清晰。
實驗效果:與上篇相同,計時顯示。個位爲ms,十位爲s…
頂層結構圖:
1.seg_decoder.v
此模塊是數碼管位的解碼模塊,將數據轉換爲數碼管的段選碼輸出給後續的掃描模塊。對應是頂層結構圖中右側的六個,因爲有六個位,所以每個位都對應一個此模塊。
module seg_decoder
(
input[3:0] bin_data, // 數碼管一個位的數據
output reg[6:0] seg_data // 數碼管的段碼
);
always@(*)
begin
case(bin_data) //數字判斷(0~f)
4'd0:seg_data <= 7'b100_0000;
4'd1:seg_data <= 7'b111_1001;
4'd2:seg_data <= 7'b010_0100;
4'd3:seg_data <= 7'b011_0000;
4'd4:seg_data <= 7'b001_1001;
4'd5:seg_data <= 7'b001_0010;
4'd6:seg_data <= 7'b000_0010;
4'd7:seg_data <= 7'b111_1000;
4'd8:seg_data <= 7'b000_0000;
4'd9:seg_data <= 7'b001_0000;
4'ha:seg_data <= 7'b000_1000;
4'hb:seg_data <= 7'b000_0011;
4'hc:seg_data <= 7'b100_0110;
4'hd:seg_data <= 7'b010_0001;
4'he:seg_data <= 7'b000_0110;
4'hf:seg_data <= 7'b000_1110;
default:seg_data <= 7'b111_1111;
endcase
end
endmodule
2.seg_scan.v
此爲數碼管的掃描模塊,原理與之前相同,快速掃描六個位,利用視覺暫留現象形成動態。
module seg_scan
(
input clk, //系統時鐘
input rst_n, //復位信號
output reg[5:0] seg_sel, //數碼管位選輸出
output reg[7:0] seg_data, //當前顯示位的段選碼輸出
input[7:0] seg_data_0, //個位的段選碼,由上面的解碼器模塊得到
input[7:0] seg_data_1, //十位的段選碼
input[7:0] seg_data_2, //百位的段選碼
input[7:0] seg_data_3, //千位的段選碼
input[7:0] seg_data_4, //萬位位的段選碼
input[7:0] seg_data_5 //十萬位的段選碼
);
parameter SCAN_FREQ = 200; //掃描頻率,此處的掃描頻率是指6個位全部掃描一遍的頻率,200Hz->5ms
parameter CLK_FREQ = 50_000_000; //時鐘頻率
parameter SCAN_COUNT = CLK_FREQ /(SCAN_FREQ * 6) - 1;
reg[31:0] scan_timer; //動態掃描計數器
reg[3:0] scan_sel; //位選計數器
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
scan_timer <= 32'd0;
scan_sel <= 4'd0;
end
else if(scan_timer >= SCAN_COUNT) //按位進行掃描顯示
begin
scan_timer <= 32'd0;
if(scan_sel == 4'd5)
scan_sel <= 4'd0;
else
scan_sel <= scan_sel + 4'd1; //每計到1個週期位選數加1,用來顯示下一個位
end
else
begin
scan_timer <= scan_timer + 32'd1;
end
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
seg_sel <= 6'b111111;
seg_data <= 8'hff;
end
else
begin
case(scan_sel)
//個位數碼管顯示
4'd0:
begin
seg_sel <= 6'b11_1110; // 最低位爲0,即個位顯示
seg_data <= seg_data_0; // 個位的段選碼
end
//十位數碼管顯示
4'd1:
begin
seg_sel <= 6'b11_1101;
seg_data <= seg_data_1;
end
//...
4'd2:
begin
seg_sel <= 6'b11_1011;
seg_data <= seg_data_2;
end
4'd3:
begin
seg_sel <= 6'b11_0111;
seg_data <= seg_data_3;
end
4'd4:
begin
seg_sel <= 6'b10_1111;
seg_data <= seg_data_4;
end
4'd5:
begin
seg_sel <= 6'b01_1111;
seg_data <= seg_data_5;
end
default:
begin
seg_sel <= 6'b11_1111;
seg_data <= 8'hff;
end
endcase
end
end
endmodule
3.count_m10.v
10進制的計數模塊。此模塊的使用是實例化多個此模塊,然後將一個模塊的輸出t與下一個模塊輸入en相連,從而將多個此模塊串聯在一起。具體可看主模塊。
module count_m10
(
input clk,
input rst_n,
input en, // 此計數器使能信號,可看作前一個的進位信號
input clr, // 同步復位
output reg[3:0]data, // 計數器輸出值
output reg t // 給出下一個計數器的使能信號,可看作向下一個發出進位信號
);
always@(posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
data <= 4'd0;
t <= 1'd0;
end
else if(clr)
begin
data <= 4'd0;
t <= 1'd0;
end
else if(en) //如果使能,則開始計數
begin
if(data==4'd9)
begin
t<= 1'b1; //計到9後產生進位信號
data <= 4'd0;//計數器復位
end
else
begin
t <= 1'b0;
data <= data + 4'd1;
end
end
else
t <= 1'b0;
end
endmodule
4.seg_test.v
主模塊
module seg_test
(
input clk,
input rst_n,
output[5:0]seg_sel, // 位選輸出
output[7:0]seg_data // 段選輸出
);
reg[31:0] timer_cnt; // 計數器
reg en_1hz; //
//最底層計數器語句,每1ms給出一個信號
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
en_1hz <= 1'b0;
timer_cnt <= 32'd0;
end
else if(timer_cnt >= 32'd4_999_999) //1ms
begin
en_1hz <= 1'b1;
timer_cnt <= 32'd0;
end
else
begin
en_1hz <= 1'b0;
timer_cnt <= timer_cnt + 32'd1;
end
end
//實例化6個10進制計數器模塊並串聯,形成6位10進制計數器
wire[3:0] count0;
wire t0;
count_m10 count10_m0
(
.clk (clk),
.rst_n (rst_n),
.en (en_1hz),
.clr (1'b0),
.data (count0),
.t (t0)
);
wire[3:0] count1;
wire t1;
count_m10 count10_m1
(
.clk (clk),
.rst_n (rst_n),
.en (t0),
.clr (1'b0),
.data (count1),
.t (t1)
);
wire[3:0] count2;
wire t2;
count_m10 count10_m2
(
.clk (clk),
.rst_n (rst_n),
.en (t1),
.clr (1'b0),
.data (count2),
.t (t2)
);
wire[3:0] count3;
wire t3;
count_m10 count10_m3
(
.clk (clk),
.rst_n (rst_n),
.en (t2),
.clr (1'b0),
.data (count3),
.t (t3)
);
wire[3:0] count4;
wire t4;
count_m10 count10_m4
(
.clk (clk),
.rst_n (rst_n),
.en (t3),
.clr (1'b0),
.data (count4),
.t (t4)
);
wire[3:0] count5;
wire t5;
count_m10 count10_m5
(
.clk (clk),
.rst_n (rst_n),
.en (t4),
.clr (1'b0),
.data (count5),
.t (t5)
);
//實例化6個位的解碼模塊
wire[6:0] seg_data_0;
seg_decoder seg_decoder_m0
(
.bin_data (count5),
.seg_data (seg_data_0)
);
wire[6:0] seg_data_1;
seg_decoder seg_decoder_m1
(
.bin_data (count4),
.seg_data (seg_data_1)
);
wire[6:0] seg_data_2;
seg_decoder seg_decoder_m2
(
.bin_data (count3),
.seg_data (seg_data_2)
);
wire[6:0] seg_data_3;
seg_decoder seg_decoder_m3
(
.bin_data (count2),
.seg_data (seg_data_3)
);
wire[6:0] seg_data_4;
seg_decoder seg_decoder_m4
(
.bin_data (count1),
.seg_data (seg_data_4)
);
wire[6:0] seg_data_5;
seg_decoder seg_decoder_m5(
.bin_data (count0),
.seg_data (seg_data_5)
);
//實例化動態掃描模塊
seg_scan seg_scan_m0
(
.clk (clk),
.rst_n (rst_n),
.seg_sel (seg_sel),
.seg_data (seg_data),
.seg_data_0 ({1'b1,seg_data_0}), //小數點段控制,低電平有效
.seg_data_1 ({1'b1,seg_data_1}),
.seg_data_2 ({1'b1,seg_data_2}),
.seg_data_3 ({1'b1,seg_data_3}),
.seg_data_4 ({1'b1,seg_data_4}),
.seg_data_5 ({1'b1,seg_data_5})
);
endmodule
以上代碼的邏輯思路十分清晰使用起來也十分簡單。如果後續模塊過多,還可以把解碼器模塊和掃描模塊進行二次封裝。