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 针对数码管的管脚约束文件

相关推荐
LVerrrr28 分钟前
Missashe考研日记—Day37-Day43
学习·考研
TIF星空2 小时前
【使用 C# 获取 USB 设备信息及进行通信】
开发语言·经验分享·笔记·学习·microsoft·c#
AI算法工程师Moxi4 小时前
什么时候可以开始学习深度学习?
人工智能·深度学习·学习
jiedaodezhuti6 小时前
ElasticSearch重启之后shard未分配问题的解决
笔记·elasticsearch
z人间防沉迷k7 小时前
堆(Heap)
开发语言·数据结构·笔记·python·算法
z542968z7 小时前
Springboot3自定义starter笔记
笔记
丰锋ff8 小时前
操作系统学习笔记第3章 内存管理(灰灰题库)
笔记·学习
zly88653728 小时前
MMIO机制详解
fpga开发
jackson凌8 小时前
【Java学习笔记】equals方法
java·笔记·学习
虾球xz8 小时前
游戏引擎学习第282天:Z轴移动与摄像机运动
c++·学习·游戏引擎