本文主要是学习按键消抖和数码管动态显示,秒表显示什么的,个人认为,拿FPGA做秒表真是嫌钱多。
感谢
感谢学校和至芯科技,笔者专业最近去北京至芯科技培训交流了一周。老师的经验还是可以的,优化了自己的代码也学习了新的知识。北京是个好地方,故宫没有想象中的那么大,但人真是多到密集恐惧症。至芯科技的最小开发板设计的一般般。。。
言归正传,本次主要实现数字数码管的主要功能,按键触发:秒表开始,暂停,记录,回显。一共四个按键,第一个按键控制全局复位,第二个按键控制秒表的开始与暂停,第三个按键控制秒表在运行中按下记录,第四个按键控制秒表在暂停的时候,记录的数据可以按一下则顺序回显一个数据,一共三个数据。
秒表可以从00:00:00 -- 59:59:99 , 6位数码管。
设计功能分解
至芯的板子数码管那位选采用了三八译码器,由于一般不用数码管,二进制转bcd的方法就不在这里阐述(不用也不会,至芯科技直接用了除号等不符合设计规范的编码实现)。直接采用到9则进1的方式,由于采用单时钟域,时序对齐还是需要特别注意的。
按键消抖模块设计
系统时钟50MHZ,采用时钟使能的方式10ms采样一个值,用一个8bit的移位寄存器实现消抖并只检测下降沿(按键按下为低电平),当按键按下产生1个系统时钟周期的高电平。
代码仅供学习使用:
//************************************************// Filename : debounce.v // Author : kingstacker // Company : School // Email : kingstacker_work@163.com // Device : Altera cyclone4 ep4ce6f17c8 // Description : when the key is pressed ,porduce 1 period clk high; //************************************************module debounce ( //use shift reg logic;/*i*/ input wire clk , input wire rst_n , input wire key_i ,/*o*/ output wire key_o );parameter CLK_MAX = 19'd49_9999; //0.01s;reg [18:0] cnt;reg clk_en;reg key_o_r;always @(posedge clk or negedge rst_n) begin //control cnt value; if (~rst_n) begin cnt <= 0; end //if else begin cnt <= (cnt == CLK_MAX) ? 19'd0 : cnt + 1'b1; end //elseend //alwaysalways @(posedge clk or negedge rst_n) begin //produce clk_en; if (~rst_n) begin clk_en <= 1'b0; end //if else begin clk_en <= (cnt == CLK_MAX) ? 1'b1 : 1'b0; end //elseend //alwaysreg [7:0] shift_val;always @(posedge clk or negedge rst_n) begin //shift logic; if (~rst_n) begin shift_val <= 8'hff; end //if else begin shift_val <= (clk_en) ? {shift_val[6:0],key_i} : shift_val; end //elseend //alwaysalways @(posedge clk or negedge rst_n) begin if (~rst_n) begin key_o_r <= 1'b0; end //if else begin if (shift_val == 8'b1000_0000 && clk_en) begin //check key pressed negedge; key_o_r <= 1'b1; end else begin key_o_r <= 1'b0; end end //elseend //alwaysassign key_o = key_o_r;endmodule
6位数码管动态显示模块设计
数码管由于dp位需要控制,本模块中dp位逻辑单独了出来,动态显示1ms刷新数码管,6个管需要6ms,注意间隔不要太大,否则会闪烁,太紧也没有必要,保证数据对齐,采用组合逻辑实现:
//************************************************// Filename : led_display.v // Author : kingstacker // Company : School // Email : kingstacker_work@163.com // Device : Altera cyclone4 ep4ce6f17c8 // Description :on board ok; one clk domain;input din is bcd ; //************************************************module led_display (/*i*/ input wire clk , input wire rst_n , input wire [23:0] din , //data from sco model;/*o*/ output wire [2:0] sel , //38 decoder; output wire [7:0] seg //segement; );parameter MS_MAX = 17'd4_9999; //1ms dynamic refresh;//4_9999;reg flag_1ms;reg [16:0] cnt_1ms;reg [2:0] sel_r;reg [6:0] seg_r;reg [3:0] seg_sel;reg [2:0] cnt_sel;wire dp; always @(posedge clk or negedge rst_n) begin //control cnt_1ms value; if (~rst_n) begin cnt_1ms <= 0; end //if else begin cnt_1ms <= (cnt_1ms == MS_MAX) ? 16'd0 : cnt_1ms + 1'b1; end //elseend //alwaysalways @(posedge clk or negedge rst_n) begin //produce the flag_1ms high level; if (~rst_n) begin flag_1ms <= 1'b0; end //if else begin flag_1ms <= (cnt_1ms == MS_MAX) ? 1'b1 : 1'b0; end //elseend //alwaysalways @(posedge clk or negedge rst_n) begin //cnt sel value control; if (~rst_n) begin cnt_sel <= 0; end //if else begin cnt_sel <= (cnt_sel == 3'd5 && flag_1ms) ? 3'd0 : (flag_1ms) ? cnt_sel + 1'b1 : cnt_sel; end //elseend //alwaysalways @(*) begin //sel logic exchange; case (cnt_sel) 3'd0: begin sel_r = 3'b101;seg_sel = din[3:0]; end 3'd1: begin sel_r = 3'b100;seg_sel = din[7:4]; end 3'd2: begin sel_r = 3'b011;seg_sel = din[11:8]; end 3'd3: begin sel_r = 3'b010;seg_sel = din[15:12]; end 3'd4: begin sel_r = 3'b001;seg_sel = din[19:16]; end 3'd5: begin sel_r = 3'b000;seg_sel = din[23:20]; end default: begin sel_r = 3'b111;seg_sel = 4'd10; end endcase //case end //alwaysalways @(*) begin //display coder; case (seg_sel) 4'd0: begin seg_r = 7'b100_0000; end //0; 4'd1: begin seg_r = 7'b111_1001; end //1; 4'd2: begin seg_r = 7'b010_0100; end //2; 4'd3: begin seg_r = 7'b011_0000; end //3; 4'd4: begin seg_r = 7'b001_1001; end //4; 4'd5: begin seg_r = 7'b001_0010; end //5; 4'd6: begin seg_r = 7'b000_0010; end //6; 4'd7: begin seg_r = 7'b111_1000; end //7; 4'd8: begin seg_r = 7'b000_0000; end //8; 4'd9: begin seg_r = 7'b001_0000; end //9; default: begin seg_r = 7'b100_0000; end //x; endcase //case end //alwaysassign dp = !(cnt_sel == 3'd2 || cnt_sel == 3'd4); //dp produce logic;assign seg = {dp,seg_r};assign sel = sel_r;endmodule
sco模块就没啥意思了,本文主要讲述按键消抖的实现方式以及数码管动态显示的问题。
如果需要整体源码,请上github查看:git@github.com:kingstacker/second_counter.git
再次感谢至芯科技公司以及李老师。
以上。