18 19 SPI接口的74HC595驱动数码管实验

1. 串行移位寄存器原理(以四个移位寄存器为例)

  1. 通过移位寄存器实现串转并:一个数据输入端口可得到四位并行数据。

通过给data输送0101数据,那么在经过四个时钟周期后,与data相连的四个寄存器的输出端口得到了0101这样的数据,然后我们将latch信号拉高,在下一个时钟周期,D0, D1, D2, D3同时分别获得了这四个数据1010。(其中DFF指D触发器,LATCH信号也可连接锁存器来控制输出)

  1. 级联

级联:数据输出端口作为另外的移位寄存器数据端的输入。

2.使用74HC595驱动数码管

  1. ACX720上不是直接用fpga的管脚驱动数码管的,而是用74HC595这样的一种串转并的串行移位寄存器来驱动的

2.1 74HC595端口图:

2.2 74HC595时序图

2.3 74HC595时钟工作频率

取工作频率为12.5MHz。

3. 使用74HC595芯片驱动数码管的verilog代码实现

1.要完成的模块为hc595_driver,实现将16位并行数据转为串行数据发送至74HC595中,实现三线制控制数码管。因此我们的输入需遵循74HC595时序图。

3.1 设计代码

我们要输出shcp,stcp和ds,并满足它们的时序波形(照葫芦画瓢)。shcp的为最小的时间单元,我们可以根据它来作为其他信号波形的参照,但是切记不可将其作为门控时钟,我们只需要把这些要输出的信号当成普通信号,按照时序图上的时序输出即可。

  1. shcp的时钟频率为12.5MHz,即一个周期为80ns,我们取半个周期为最小时间单元进行变化,即40ns。

  2. 根据最小时间单元产生对应的节点,需要一个计数器记录这些节点(节点执行完开始下一次节点执行),产生节点后再在每个节点做相应的事情即可。

3. hc595_driver里的寄存器是从小到大排列的(0到15),即第0位数据最后进,放在最外面,第15位数据放在最里面。先进15,最后进0。

cpp 复制代码
module hex8_2(
    clk,
    rstn,
    disp_data,
    sel,
    led
);
    
    parameter times = 50000; // 1ms
    input clk;
    input rstn;
    input [31:0]disp_data;
    output reg [7:0] sel;
    output reg [7:0] led;
    
    reg [15:0]div_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(div_cnt >= times - 1)
        div_cnt <= 0;
    else
        div_cnt <= div_cnt + 1'd1;
     
    //使能时钟  
    reg clk_lk;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        clk_lk <= 0;
    else if(div_cnt == times - 1)
        clk_lk <= 1'd1;
    else
        clk_lk <= 0;
    
    //cnt累加器
    reg [2:0] num_cnt;    
    always@(posedge clk_lk or negedge rstn)
    if(!rstn)
        num_cnt <= 0;
    else if(clk_lk == 1)
        num_cnt <= num_cnt + 1'd1;
    
    //三八译码器    
    always@(posedge clk or negedge rstn)
    if(!rstn)
        sel <= 0;
    else case(num_cnt)
        0:sel = 8'b0000_0001;
        1:sel = 8'b0000_0010;
        2:sel = 8'b0000_0100;
        3:sel = 8'b0000_1000;
        4:sel = 8'b0001_0000;
        5:sel = 8'b0010_0000;
        6:sel = 8'b0100_0000;
        7:sel = 8'b1000_0000;
    endcase
   
    //八选一多路器
    reg [3:0]disp_tmp;
    always@(posedge clk)
    case(num_cnt)
        0:disp_tmp = disp_data[3:0];
        1:disp_tmp = disp_data[7:4];
        2:disp_tmp = disp_data[11:8];
        3:disp_tmp = disp_data[15:12];
        4:disp_tmp = disp_data[19:16];
        5:disp_tmp = disp_data[23:20];
        6:disp_tmp = disp_data[27:24];
        7:disp_tmp = disp_data[31:28];
    endcase
    
    //四十六译码器 
    always@(posedge clk)
    case(disp_tmp)
        0:led = 8'hc0;
        1:led = 8'hf9;
        2:led = 8'ha4;
        3:led = 8'hb0;
        4:led = 8'h99;
        5:led = 8'h92;
        6:led = 8'h82;
        7:led = 8'hf8;
        8:led = 8'h80;
        9:led = 8'h90;
        4'ha:led = 8'h88;
        4'hb:led = 8'h83;
        4'hc:led = 8'hc6;
        4'hd:led = 8'ha1;
        4'he:led = 8'h86;
        4'hf:led = 8'h8e;  
        default:led = 8'hc0;
    endcase

endmodule
cpp 复制代码
module hc595_driver(
    clk,
    rstn,
    data,
    s_en,
    shcp,
    stcp,
    ds  
);

    parameter times = 2; //40ns为最小刻度
    input clk;
    input rstn;
    input [15:0] data;
    input s_en;
    output reg shcp;
    output reg stcp;
    output reg ds;
    
    reg [15:0] r_data;
    always@(posedge clk)
    if(s_en)
        r_data <= data;
    
    //1.产生74hc595时序所需的最小时间单元
    reg [7:0] div_cnt;
    
    //脉冲信号
    wire sck_plus;
    assign sck_plus = div_cnt >= times - 1;
    
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(div_cnt >= times - 1)
        div_cnt <= 0;
    else
        div_cnt <= div_cnt + 1'd1;
    
    //2.依据sck_plus做相对应的事情
    //先记录sck_plus的节点数
    reg [5:0]shcp_edge_counter;    
    always@(posedge clk or negedge rstn)
    if(!rstn)   
        shcp_edge_counter <= 0;
    else if(sck_plus) //sck_plus不是寄存器的输出    
        if(shcp_edge_counter == 32)            
            shcp_edge_counter <= 0;
        else
            shcp_edge_counter <=  shcp_edge_counter + 1'd1;
        
    always@(posedge clk or negedge rstn)
    if(!rstn) begin
        shcp <= 0;
        stcp <= 0;
        ds <= 0;
    end
    else case(shcp_edge_counter)
        0: begin shcp <= 0; ds <= r_data[15]; stcp <= 0; end
        1: shcp <= 1'd1;
        2: begin shcp <= 0; ds <= r_data[14]; end
        3: shcp <= 1'd1;
        4: begin shcp <= 0; ds <= r_data[13]; end
        5: shcp <= 1'd1;
        6: begin shcp <= 0; ds <= r_data[12]; end
        7: shcp <= 1'd1;
        8: begin shcp <= 0; ds <= r_data[11]; end
        9: shcp <= 1'd1;
        10: begin shcp <= 0; ds <= r_data[10]; end
        11: shcp <= 1'd1;
        12: begin shcp <= 0; ds <= r_data[9]; end
        13: shcp <= 1'd1;
        14: begin shcp <= 0; ds <= r_data[8]; end
        15: shcp <= 1'd1;
        16: begin shcp <= 0; ds <= r_data[7]; end
        17: shcp <= 1'd1;
        18: begin shcp <= 0; ds <= r_data[6]; end
        19: shcp <= 1'd1;
        20: begin shcp <= 0; ds <= r_data[5]; end
        21: shcp <= 1'd1;
        22: begin shcp <= 0; ds <= r_data[4]; end
        23: shcp <= 1'd1;
        24: begin shcp <= 0; ds <= r_data[3]; end
        25: shcp <= 1'd1;
        26: begin shcp <= 0; ds <= r_data[2]; end
        27: shcp <= 1'd1;
        28: begin shcp <= 0; ds <= r_data[1]; end
        29: shcp <= 1'd1;
        30: begin shcp <= 0; ds <= r_data[0]; end
        31: shcp <= 1'd1;
        32: begin shcp <= 0; stcp <= 1; ds <= 0; end
        default : begin
            shcp <= 0;
            stcp <= 0;
            ds <= 0;
        end
    endcase
        
endmodule

3.2 针对hc595_driver的仿真代码

cpp 复制代码
`timescale 1ns / 1ps

module hc595_driver_tb();
    
    reg clk;
    reg rstn;
    reg [15:0] data;
    reg s_en;
    wire shcp;
    wire stcp;
    wire ds;

    hc595_driver hc595_driver_inst(
        clk,
        rstn,
        data,
        s_en,
        shcp,
        stcp,
        ds  
    );
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        #201;
        rstn = 1;
        #200;
        s_en = 1;
        data = 16'h1357;
        #4000;
        s_en = 0;
        #200;
        s_en = 1;
        data = 16'h2468;
        #4000;
        s_en = 0;
        $stop;
    end
    
endmodule

3.3 针对hc595_driver的仿真波形

3.4 针对数码管的管脚约束文件

相关推荐
努力变厉害的小超超2 小时前
ArkTS中的组件基础、状态管理、样式处理、class语法以及界面渲染
笔记·鸿蒙
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
dayouziei4 小时前
java的类加载机制的学习
java·学习
aloha_7896 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
dsywws7 小时前
Linux学习笔记之vim入门
linux·笔记·学习
晨曦_子画8 小时前
3种最难学习和最容易学习的 3 种编程语言
学习
城南vision8 小时前
Docker学习—Docker核心概念总结
java·学习·docker
ctrey_9 小时前
2024-11-1 学习人工智能的Day20 openCV(2)
人工智能·opencv·学习
十年之少9 小时前
由中文乱码引来的一系列学习——Qt
学习
A-超9 小时前
vue3展示pag格式动态图
笔记