【FPGA】高云FPGA之数字钟实验->HC595驱动数码管(二)

简介: 【FPGA】高云FPGA之数字钟实验->HC595驱动数码管

【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

相关文章
|
7月前
|
芯片 异构计算
【FPGA】高云FPGA之数字钟实验->HC595驱动数码管(三)
【FPGA】高云FPGA之数字钟实验->HC595驱动数码管
164 4
|
5月前
|
异构计算
FPGA入门(6):数码管静态/动态显示(二)
FPGA入门(6):数码管静态/动态显示(二)
71 10
|
5月前
|
数据采集 计算机视觉 异构计算
FPGA进阶(2):基于I2C协议的EEPROM驱动控制
FPGA进阶(2):基于I2C协议的EEPROM驱动控制
78 0
|
5月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(二)
FPGA进阶(1):基于SPI协议的Flash驱动控制
65 0
|
5月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
215 0
|
5月前
|
异构计算
FPGA强化(9):TFT_LCD液晶屏驱动
FPGA强化(9):TFT_LCD液晶屏驱动
65 0
|
5月前
|
异构计算
FPGA强化(8):HDMI显示器驱动
FPGA入门(8):VGA显示器驱动
47 0
|
5月前
|
异构计算
FPGA入门(6):数码管静态/动态显示(一)
FPGA入门(6):数码管静态/动态显示
54 0
|
13天前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
113 69
|
17天前
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
53 26