SPI接口的74HC595驱动数码管实现

news2024/11/15 6:55:39

摸鱼记录 Day_17      (((^-^)))

review

        前边已经学习了:

        数码管显示原理:数码管动态扫描显示-CSDN博客

        且挖了个SPI的坑坑

1.  今日份摸鱼任务

学习循环移位寄存器18 串行移位寄存器原理详解_哔哩哔哩_bilibili

学习SPI接口的74HC595驱动数码管19 SPI接口的74HC595驱动数码管实验_哔哩哔哩_bilibili

了解SPI协议:SPI协议详解(图文并茂+超详细) - 知乎 (zhihu.com)

                        SPI总线协议及SPI时序图详解 - Ady Lee - 博客园 (cnblogs.com)

2.  循环移位寄存器

        四位D触发器,输入信号1001,经过四次时钟上升沿,D0-D3 1001

        DATA是串行数据,在此结构下,每个上升沿到来,都会改变D0-D3的输出

        为了正确输出四位的串转并数据

        可在红色箭头处,添加一个使能信号,对输出进行控制

此时,使用CLK、DATA、LATCH三根信号线,即可完成将串行信号转为并行信号

3.  74HC959 循环移位寄存器

        一文搞懂74HC595芯片(附使用方法)_74hc595芯片引脚图及功能-CSDN博客

        74HC595的最重要的功能就是:串行输入,并行输出。

        其次,74HC595里面有2个8位寄存器:移位寄存器、存储寄存器。

    第一个从SER送入的bit将会从 Q7 出去

   本篇在草稿呆了很多天,因为上图SHCP  STCP的画法有一定理解上的问题

    SHCP       移位寄存器的时钟输出

    STCP       存储寄存器的时钟输出

    但是在例程中

        STCP是在数据都保存后,完成一次输出,这保证了输出数据是一个完整的

        ACZ702 配套 EDA 扩展板设计用到了芯片 74HC595,该芯片的作用是移位寄存器,通过移位的方式,节省 FPGA 的管脚。FPGA 只需要输出 3 个管脚,即可达到发送数码管数据的目的,与数码管动态扫描显示-CSDN博客的传统段选位选方式相比节省了 IO 设计资源。

        3.3V供电情况下,50MHz -----》25MHz-----》12.5MHz

4. VIO  Virtual Input/Output

        关于这个IP核可以看:Vivado中VIO IP核的使用_vivado vio-CSDN博客

        本次实验,用于设定数码管的显示内容,具体设置如下:

4.  SPI接口的74HC595驱动数码管实现 (((^-^)))

                SPI(Serial Peripheral Interface),串行外围设备接口。

                SPI是一个同步的数据总线,用单独的数据线一个单独的时钟信号来保证发送端和接收端的同步

                可以参考:SPI协议详解(图文并茂+超详细) - 知乎 (zhihu.com)

        对于74HC595,本次SPI协议,是学习SCK MOSI,无需MISO,片选默认选中

4.1   design sources

hex_8  

module hex_8(input clk,
                     input reset_n,
                     input [31:0]disp_data, 

                     //8个数码管进行显示,每个显示0~F,输入格式为disp_data = 32'h12345678
                     output reg [7:0]sel,
                     output reg [7:0]seg
                     );

        //[31:0]disp_data  16hex 4*8
        //[7:0]sel 位选信号
        //[7:0]seg 段选信号

// 1kHz分频时钟 
    reg [14:0]div_clk;
    always@(posedge clk or negedge reset_n)
    if(!reset_n) 
        div_clk <= 1'b0;
    else if(div_clk == 24999) 
        div_clk <= 1'b0;
    else 
        div_clk <= div_clk + 1'b1;
    reg disp_en;
   always@(posedge clk or negedge reset_n)
    if(!reset_n) 
        disp_en <= 1'b0;
    else if(div_clk == 24999) 
        disp_en <= 1'b1;
    else 
        disp_en <= 1'b0;    

//  位选sel
    reg[2:0]sel_num;
    always@(posedge clk or negedge reset_n)
    if(!reset_n) 
        sel_num <= 3'b000;
    else if(disp_en) 
        sel_num <= sel_num + 1'b1;
        
    always@(posedge clk or negedge reset_n)
    if(!reset_n) 
        sel <= 8'b0000_0000;
    else case(sel_num) 
         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   
   
// 段选seg   [31:0]disp_data  16hex 4*8
    reg [3:0] dis_tmp;
    always@(posedge clk )
    case(sel_num) //高位放前面
         0:dis_tmp <= disp_data[31:28];
         1:dis_tmp <= disp_data[27:24];
         2:dis_tmp <= disp_data[23:20];
         3:dis_tmp <= disp_data[19:16];
         4:dis_tmp <= disp_data[15:12];
         5:dis_tmp <= disp_data[11:8];
         6:dis_tmp <= disp_data[7:4];
         7:dis_tmp <= disp_data[3:0];
    endcase 
    
    always@(posedge clk )
    case(dis_tmp) 
         0:seg <= 8'hc0;
         1:seg <= 8'hf9;
         2:seg <= 8'ha4;
         3:seg <= 8'hb0;
         4:seg <= 8'h99;
         5:seg <= 8'h92;
         6:seg <= 8'h82;
         7:seg <= 8'hf8;
         8:seg <= 8'h80;
         9:seg <= 8'h90;
         4'ha:seg <= 8'h88;
         4'hb:seg <= 8'h83;
         4'hc:seg <= 8'hc6;
         4'hd:seg <= 8'ha1;
         4'he:seg <= 8'h86;
         4'hf:seg <= 8'h8e;
    endcase 

endmodule

hc595_driver   //在Verilog中,不能使用数字开头命名

module hc595_driver(
                    input clk,
                    input reset_n,
                    input [15:0]data,
                    input s_en,
                    
                    output reg sh_cp,
                    output reg st_cp,
                    output reg ds
                 );

                /启动信号s_en时,保存当前data

             reg [15:0]r_data;
            always@(posedge clk)
            if(s_en)
                r_data <= data;


    parameter CNT_MAX = 2;
   // 3.3V 状态下工作于 12.5MHz   

    reg [7:0]divider_cnt;//分频计数器
    always@(posedge clk or negedge reset_n)
    if(!reset_n)
        divider_cnt <= 0;
    else if(divider_cnt == CNT_MAX - 1'b1)
        divider_cnt <= 0;
    else
        divider_cnt <= divider_cnt + 1'b1;
        
    wire sck_plus;
    assign sck_plus = (divider_cnt == CNT_MAX - 1'b1);
        
    reg [5:0]SHCP_EDGE_CNT;
    
    always@(posedge clk or negedge reset_n)
    if(!reset_n)
        SHCP_EDGE_CNT <= 0;
    else if(sck_plus)
        begin
            if(SHCP_EDGE_CNT == 6'd32) //32 16个数据,按照SH_CP上升沿、下降沿
                SHCP_EDGE_CNT <= 0;
            else
                SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
        end
    else
        SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
        
    always@(posedge clk or negedge reset_n)
    if(!reset_n)
        begin
            st_cp <= 1'b0;
            ds <= 1'b0;
            sh_cp <= 1'd0;
        end 
    else begin
        case(SHCP_EDGE_CNT)//重点就是线性序列机这部分分析啦

                        //SH_CP 移位寄存器的时钟

                        //在SH_CP上升沿  0->1 输出数据

                        //在SH_CP下降沿  1->0 改变数据
            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;//最后拉高一下st_cp锁存器输出
            default:        
                begin
                    st_cp <= 1'b0;
                    ds <= 1'b0;
                    sh_cp <= 1'd0;
                end
        endcase
    end

endmodule

hex_top

module hex_top(
                clk,
                reset_n,
                sh_cp,
                st_cp,
                ds
                 );

    input clk;    //50M
    input reset_n;
    
    output sh_cp;
    output st_cp;
    output ds;
    
    wire [31:0]disp_data;
    wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
    wire [7:0] seg;//数码管段选(当前要显示的内容)
    
    vio_0 vio_0 (
        .clk(clk), 
        .probe_out0(disp_data)  
    );
    
    hc595_driver hc595_driver(
        .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(
        .clk(clk),
        .reset_n(reset_n),
        .en(1'b1),
        .disp_data(disp_data),
        .sel(sel),
        .seg(seg)
    );
    
endmodule

4.2  板级验证

//好啦, (((^-^)))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1612703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【编程Tool】VS code安装与使用配置保姆级教程

目录 1.软件介绍 2.软件下载&#xff1a; 3.安装 3.1. 双击可执行文件 3.2. 同意协议 3.3. 选择安装路径&#xff0c;默认在C盘 3.4. 点击下一步 3.5. 可选择所有附加任务 3.6. 点击安装 3.7. 等待安装 3.8. 点击完成 3.9. 安装成功 4.下载MinGW64 4.1. MinGW-64下载地址 &…

电脑缺失d3dcompiler_43.dll如何修复?多种修复dll问题的有效方法分享

当用户尝试在个人计算机上运行特定的软件游戏时&#xff0c;系统弹出了一条错误提示信息&#xff0c;明确指出“d3dcompiler_43.dll”文件缺失。这个动态链接库文件(dll)是Direct3D编译器的重要组成部分&#xff0c;对于许多基于Windows操作系统的应用程序&#xff0c;尤其是那…

LeetCode 热题 100 题解:普通数组部分

文章目录 题目一&#xff1a;最大子数组和&#xff08;No. 53&#xff09;题解 题目二&#xff1a;合并区间&#xff08;No. 56&#xff09;题解 题目三&#xff1a;轮转数组&#xff08;No. 189&#xff09;题解 题目四&#xff1a;除自身以外数组的乘积&#xff08;No. 238&a…

从例题出发,提高离散数学兴趣(一)集合关系

关系的性质&#xff1a;(反)自反性&#xff0c;&#xff08;反&#xff09;对称性&#xff0c;可传递性&#xff01; 例题一&#xff1a; 复合关系与逆关系&#xff1a; 例题二&#xff1a; 覆盖与划分与等价关系&#xff1a; 重要的证明&#xff1a; 偏序关系&#xff08;自反…

【高阶数据结构】并查集 -- 详解

一、并查集的原理 1、并查集的本质和概念 &#xff08;1&#xff09;本质 并查集的本质&#xff1a;森林。 &#xff08;2&#xff09;概念 在一些应用问题中&#xff0c;需要将 n 个不同的元素划分成一些不相交的集合。 开始时&#xff0c;每个元素自成一个单元素集合&…

Spring Cloud 运维篇1——Jenkins CI/CD 持续集成部署

Jenkins 1、Jenkins是什么&#xff1f; Jenkins 是一款开源 CI/CD 软件&#xff0c;用于自动化各种任务&#xff0c;包括构建、测试和部署软件。 Jenkins 支持各种运行方式&#xff0c;可通过系统包、Docker 或者一个独立的 Java 程序。 Jenkins Docker Compose持续集成流…

linux中/etc/hosts文件的内容和功能

更准确的说是主机和ip地址映射绑定配置文件 用于主机名解析成ip地址的 转换配置 效果&#xff1a; 这个东西是局域网下面的解析&#xff0c;老师说是本地局域网解析 windows对应的就是

开源项目-汽车租赁管理系统

哈喽,大家好,今天主要给大家带来一个开源项目-汽车租赁管理系统 汽车租赁管理系统的主要功能包括汽车管理,新闻管理,用户管理,订单管理,数据展示等模块 注:后续文章都会附上安装教程,有问题也欢迎大家评论私信。 登录 汽车管理 汽车管理可以查看所有汽车进行线上汽…

python爬虫笔记1

1 爬虫介绍 爬虫概述&#xff1a; 获取网页并提取和保存信息的自动化程序 1.获取网页 2.提取信息 css选择器 xpath 3.保存数据&#xff08;大数据时代&#xff09; 4.自动化 爬虫&#xff08;资产收集&#xff0c;信息收集&#xff09; 漏扫&#xff08;帮我发现漏洞&#xff…

【机器学习】《ChatGPT速通手册》笔记

文章目录 第0章 前言第1章 ChatGPT的由来&#xff08;一&#xff09;自然语言处理任务&#xff08;二&#xff09;ChatGPT所用数据数据大小&#xff08;三&#xff09;ChatGPT的神经网络模型有175亿个参数&#xff08;四&#xff09;模型压缩 方案 第2章 ChatGPT页面功能介绍&a…

每日算法4/21

LCR 073. 爱吃香蕉的狒狒 题目 狒狒喜欢吃香蕉。这里有 N 堆香蕉&#xff0c;第 i 堆中有 piles[i] 根香蕉。警卫已经离开了&#xff0c;将在 H 小时后回来。 狒狒可以决定她吃香蕉的速度 K &#xff08;单位&#xff1a;根/小时&#xff09;。每个小时&#xff0c;她将会选…

Json三方库介绍

目录 Json是干什么的Json序列化代码Json反序列化代码 Json是干什么的 Json是一种轻量级的数据交换格式&#xff0c;也叫做数据序列化方式。Json完全独立于编程语言的文本格式来存储和表述数据。易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#xff0c;并有效地提升…

MATLAB求和函数

语法 S sum(A) S sum(A,“all”) S sum(A,dim) S sum(A,vecdim) S sum(,outtype) S sum(,nanflag) 说明 示例 S sum(A) 返回沿大小大于 1 的第一个数组维度计算的元素之和。 如果 A 是向量&#xff0c;则 sum(A) 返回元素之和。 如果 A 是矩阵&#xff0c;则 sum(A) 将…

2023年网络安全行业:机遇与挑战并存

2023年全球网络安全人才概况 根据ISC2的《2023年全球网络安全人才调查报告》&#xff0c;全球的网络安全专业人才数量达到了550万&#xff0c;同比增长了8.7%。然而&#xff0c;这一年也见证了网络安全人才短缺达到了历史新高&#xff0c;缺口数量接近400万。尤其是亚太地区&am…

【题解】NC40链表相加(二)(链表 + 高精度加法)

https://www.nowcoder.com/practice/c56f6c70fb3f4849bc56e33ff2a50b6b?tpId196&tqId37147&ru/exam/oj class Solution {public:// 逆序链表ListNode* reverse(ListNode* head) {// 创建一个新节点作为逆序后链表的头节点ListNode* newHead new ListNode(0);// 当前…

Spring Boot中接收各种各样的参数

一、接收json参数&#xff0c;封装为Map 1.1、核心代码 /*** 接收json参数&#xff0c;封装为Map* param servletRequest* return* throws Exception*/ PostMapping("/getParam") public R getParam(HttpServletRequest servletRequest) throws Exception {Map<…

MyCat 分片

一、垂直拆分 1、场景概述&#xff1a; 在业务系统中&#xff0c;由于用户与订单每天都会产生大量的数据&#xff0c;单台服务器的数据存储及处理能力是有限的&#xff0c;可以对数据库表进行进行垂直分库操作。将商品相关的表拆分到一个数据库服务器&#xff0c;订单表拆分到…

Spring(下)

接上篇&#xff0c;从第八个问题讲起 八.Spring工厂创建复杂对象 1.什么是复杂对象 简单对象就是可以直接new出来的&#xff0c;也就是直接调用构造方法创建 所以复杂对象就是不能直接通过调用构造方法创建。就比如JDBC中的Connection 2.三种方法 &#xff08;1&#xff…

【LeetCode刷题记录】206. 反转链表

206 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例…

毕业设计——基于ESP32的智能家居系统(语音识别、APP控制)

ESP32嵌入式单片机实战项目 一、功能演示二、项目介绍1、功能演示2、外设介绍 三、资料获取 一、功能演示 多种控制方式 ① 语音控制 ②APP控制 ③本地按键控制 ESP32嵌入式单片机实战项目演示 二、项目介绍 1、功能演示 这一个基于esp32c3的智能家居控制系统&#xff0c;能实…