【FPGA】高云FPGA之数字钟实验->HC595驱动数码管(一)https://developer.aliyun.com/article/1472641
2.2 74HC595驱动
为了节省IO引脚开发板数码管设计采用了74HC595来扩展IO,该芯片的作用是位移位寄存器,FPGA 只需要输出 3 个管脚,即可达到发
送数码管数据的目的,与传统段选、位选方式相比,大大节省了 IO 设计资源,在该原理图下,将第一片74HC595的Q7‘串行输出端接到第二片的数据输入端Ds,实现级联功能。经过14个时钟SHcp上升沿后,数据已经全部移位进入移位寄存器,一次共输入14位数据,那么第一位输入的串行数据会在第二片74HC595芯片的Q5输出,此时给一个上升沿的STcp信号就可以将信号移入存储寄存器,OE信号持续给低, 即可输出。这里贴一个博主做的很好理解的gif图。
设计代码如下
module hc595( input clk, input reset_n, input [15:0]data, input s_en, output reg sh_cp, output reg st_cp, output reg ds ); assign reset=~reset_n; /*******************时钟模块*********************************/ parameter CNT_MAX = 2; reg [15:0]r_data; //数据寄存器 reg [7:0]clk_count;//分频计数器; always@(posedge clk) begin if(s_en) r_data <= data; end always@(posedge clk or posedge reset) begin if(reset) clk_count <= 0; else if(clk_count == CNT_MAX - 1'b1) clk_count <= 0; else clk_count <= clk_count + 1'b1; end wire sck_plus; assign sck_plus = (clk_count == CNT_MAX - 1'b1); //对 sck_pluse进行计数, 用于查找表实现数据的串行输入以及移位时钟 sh_cp与存储时钟 st_cp 的产生 reg [5:0]SHCP_EDGE_CNT; always@(posedge clk or posedge reset) begin if(reset) SHCP_EDGE_CNT <= 0; else if(sck_plus)begin if(SHCP_EDGE_CNT == 6'd32) SHCP_EDGE_CNT <= 0; else SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1; end else SHCP_EDGE_CNT <= SHCP_EDGE_CNT; end // 查找表实现状态输出 always@(posedge clk or posedge reset) begin if(reset)begin st_cp <= 1'b0; ds <= 1'b0; sh_cp <= 1'd0; end else begin case(SHCP_EDGE_CNT) 0: begin sh_cp <= 0; st_cp <= 1'd0;ds <= r_data[15];end 1: begin sh_cp <= 1; st_cp <= 1'd0;end 2: begin sh_cp <= 0; ds <= r_data[14];end 3: begin sh_cp <= 1; end 4: begin sh_cp <= 0; ds <= r_data[13];end 5: begin sh_cp <= 1; end 6: begin sh_cp <= 0; ds <= r_data[12];end 7: begin sh_cp <= 1; end 8: begin sh_cp <= 0; ds <= r_data[11];end 9: begin sh_cp <= 1; end 10: begin sh_cp <= 0; ds <= r_data[10];end 11: begin sh_cp <= 1; end 12: begin sh_cp <= 0; ds <= r_data[9];end 13: begin sh_cp <= 1; end 14: begin sh_cp <= 0; ds <= r_data[8];end 15: begin sh_cp <= 1; end 16: begin sh_cp <= 0; ds <= r_data[7];end 17: begin sh_cp <= 1; end 18: begin sh_cp <= 0; ds <= r_data[6];end 19: begin sh_cp <= 1; end 20: begin sh_cp <= 0; ds <= r_data[5];end 21: begin sh_cp <= 1; end 22: begin sh_cp <= 0; ds <= r_data[4];end 23: begin sh_cp <= 1; end 24: begin sh_cp <= 0; ds <= r_data[3];end 25: begin sh_cp <= 1; end 26: begin sh_cp <= 0; ds <= r_data[2];end 27: begin sh_cp <= 1; end 28: begin sh_cp <= 0; ds <= r_data[1];end 29: begin sh_cp <= 1; end 30: begin sh_cp <= 0; ds <= r_data[0];end 31: begin sh_cp <= 1; end 32: st_cp <= 1'd1; default: begin st_cp <= 1'b0; ds <= 1'b0; sh_cp <= 1'd0; end endcase end end endmodule
2.3 主模块设计
完成了数码管驱动的显示,要实现计时的功能我们需要在主模块中设计一个1秒定时器来实现计数,然后在调用我们写好的模块将计数值传入到数码管中进行显示,为了更简单的理解这里没有添加更多的功能,但接口都是比较完整的可以自行DIY优化设计属于自己的数值钟,这里开拓一些功能(按键调整时间,设置定时器闹钟,轮流显示日期温度和时间,网络对时,掉点保存等等),好下面开始设计我们的简单计时代码;
module seg_top( input clk, // system 50m input reset_n, output sh_cp, output st_cp, output ds ); parameter MCNT = 49_999_999; // 一秒计数器 wire [31:0]disp_data; wire [7:0] sel;//数码管位选(选择当前要显示的数码管) wire [7:0] seg;//数码管段选(当前要显示的内容) wire [7:0]dp_data;//数码管小数点(某位点亮某位置1) reg dp_flag; // 小数点寄存器 reg [31:0]disp_data_reg = 32'h00000000; // 显示数据寄存器 reg [25:0]cnt; //定义计数器寄存器 reg[3:0] hour_reg_h; reg[3:0] hour_reg_l; reg[3:0] min_reg_h; reg[3:0] min_reg_l; reg[3:0] sec_reg_h; reg[3:0] sec_reg_l; //assign disp_data = disp_data_reg; assign disp_data = {4'h2,4'h4,hour_reg_h,hour_reg_l,min_reg_h,min_reg_l,sec_reg_h,sec_reg_l}; assign dp_data = (dp_flag) ? 8'b0101_0100 : 8'b0101_0000; hc595 hc595_mod( .clk(clk), .reset_n(reset_n), .data({seg,sel}), .s_en(1'b1), .sh_cp(sh_cp), .st_cp(st_cp), .ds(ds) ); hex8 hex8_mod( .clk(clk), .reset_n(reset_n), .en(1'b1), .disp_data(disp_data), .sel(sel), .seg(seg), .dp(dp_data) ); assign reset=~reset_n; //计数器计数进程 always@(posedge clk or posedge reset) begin if(reset) cnt <= 25'd0; else if(cnt == MCNT) cnt <= 25'd0; else cnt <= cnt + 1'b1; end //时钟 输出控制进程 always@(posedge clk or posedge reset) begin if(reset) begin dp_flag <= 1'b1; disp_data_reg = 0; end else if(cnt == 24_999_999) // 0.5反转一次dp点 dp_flag <= ~dp_flag; else if(cnt == MCNT) // 一秒计时 begin dp_flag <= ~dp_flag; disp_data_reg = disp_data_reg+1; sec_reg_l = sec_reg_l+1; if(sec_reg_l == 4'd9) begin sec_reg_l <= 0; sec_reg_h <= sec_reg_h + 1; if(sec_reg_h == 4'd5) begin sec_reg_h <= 0; min_reg_l <= min_reg_l + 1; if(min_reg_l == 4'd9) begin min_reg_l <= 0; min_reg_h <= min_reg_h + 1; if(min_reg_h == 4'd5) begin min_reg_h <= 0; hour_reg_l <= hour_reg_l + 1; if((hour_reg_h==4'd2) && (hour_reg_l==4'd3)) begin hour_reg_l <=0; hour_reg_h <=0; end if(hour_reg_l == 4'd9) begin hour_reg_l <= 0; hour_reg_h <= hour_reg_h + 1; end end end end end end else dp_flag <= dp_flag; end endmodule
【FPGA】高云FPGA之数字钟实验->HC595驱动数码管(三)https://developer.aliyun.com/article/1472644