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

摸鱼记录 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  板级验证

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】75、如何理解深度学习中Dropout正则化技术?

一、Dropout正则化的原理是什么&#xff1f; Dropout&#xff08;随机失活&#xff09;正则化是一种用于减少神经网络中过拟合现象的技术。Dropout正则化的做法是&#xff1a; 在训练过程中的每次迭代中&#xff0c;随机将网络中的一部分权重临时"丢弃"&#xff08;即…

apache是什么

​Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上&#xff0c;由于其跨平台和安全性被广泛使用&#xff0c;是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩充&#xff0c;将Perl/Python等解释器编译…

Vue接收接口返回的mp3格式数据并支持在页面播放音频

一、背景简介 在实际工作中需要开发一个转音频工具&#xff0c;并且能够在平台页面点击播放按钮播放音频 二、相关知识介绍 2.1 JS内置对象Blob Blob对象通常用于处理大量的二进制数据&#xff0c;可以读取/写入/操作文件、音视频等二进制数据流。Blob表示了一段不可变的二…

SpringBoot(二)【整合第三方技术】

1、SpringBoot 整合第三方框架 1.1、整合 JUnit 我们先回顾一下在学习 SpringMVC 的时候&#xff0c;我们当时整合 Spring 和 JUnit 是这么整合的&#xff1a; 注意&#xff1a;如果测试类在 SpringBoot 启动类的包或者子包中&#xff0c;可以省略启动类的设置&#xff0c;也…

npm内部机制与核心原理

npm 的核心目标&#xff1a; Bring the best of open source to you, your team and your company. npm 最重要的任务是安装和维护开源库。 npm 安装机制与背后思想 npm 的安装机制非常值得探究。Ruby 的 Gem&#xff0c;Python的pip都是全局安装机制&#xff0c;但是npm的安装…

如何部署npm私有仓库以及在项目中如何使用

如何部署npm私有仓库以及在项目中如何使用 为什么要部署npm私有仓库&#xff1f; 安全性&#xff1a;私有仓库允许团队存放内部研发的、不宜公开发布的代码包&#xff0c;只对特定用户或者团队可见和可用&#xff0c;从而保护公司的知识产权和商业秘密。模块的复用性&#xf…

GUI02-在窗口上跟踪并输出鼠标位置(Win32版)

(1) 响应 WM_MOUSEMOVE 消息获得鼠标位置&#xff1b; (2) 响应 WM_PAINT 将鼠标位置输出到窗口中&#xff1b; (3) 学习二者之间的关键步骤&#xff1a;调用 InvalidateRect() 以通知窗口重绘。 零. 课堂视频 在窗口上跟踪输出鼠标位置-Win32版 一、关键知识点 1. BeginPaint…

安装Milvus的可视化工具Attu教程

提供两种方式来安装可视化工具Attu 一、docker安装 # 执行命令&#xff0c;加个 -d 在后台运行 docker run -d -p 8000:3000 -e MILVUS_URL127.0.0.1:19530 zilliz/attu:v2.2.8 至此安装完成&#xff01; 浏览器输入地址 http:127.0.0.1:8000即可访问 Attu主页 如果拉取最新…

windows下python opencv ffmpeg读取摄像头实现rtsp推流 拉流

windows下python opencv ffmpeg读取摄像头实现rtsp推流 拉流 整体流程1.下载所需文件1. 1下载rtsp推流服务器1.2 下载ffmpeg2. 开启RTSP服务器3. opencv 读取摄像头并调用ffmpeg进行推流4. opencv进行拉流整体流程 1.下载所需文件 1. 1下载rtsp推流服务器 下载 RTSP服务器 下…

Rust入门-所有权与借用

一、为什么、是什么、怎么用 1、为什么Rust要提出一个所有权和借用的概念 所有的程序都必须和计算机内存打交道&#xff0c;如何从内存中申请空间来存放程序的运行内容&#xff0c;如何在不需要的时候释放这些空间&#xff0c;成为所有编程语言设计的难点之一。 主要分为三种…

【opencv】dnn示例-vit_tracker.cpp 使用OpenCV库和ViTTrack模型实现的视频追踪程序

这段代码是一个使用OpenCV库和ViTTrack模型实现的视频追踪程序。程序通过摄像头或视频文件获取图像序列&#xff0c;并对选定的目标对象进行实时追踪。 代码主要分为以下几个部分&#xff1a; 导入必要的库&#xff1a;程序开始时先导入了iostream&#xff0c;cmath以及相关Ope…

Hive进阶(4)----MapReduce的计算过程(赋图助君理解)

MapReduce的计算过程 MapReduce是一种编程模型和处理大规模数据集的方法。它通常用于分布式计算环境中&#xff0c;能够将数据处理任务分解成独立的部分&#xff0c;分配给多台计算机进行并行处理。这个模型由Google提出&#xff0c;并在开源领域中得到了广泛的应用和实现。Map…

CSS显示模式

目录 CSS显示模式简介 CSS显示模式的分类 块元素 行元素 行内块元素 元素显示模式的转换 使块内文字垂直居中的方法 设计简单小米侧边栏&#xff08;实践&#xff09; CSS显示模式简介 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0…

前端三剑客 HTML+CSS+JavaScript ② HTML相关概念

他们这样形容我 是暴雨浇不灭的火 —— 24.4.18 学习目标 理解 HTML的概念 HTML的分类 HTML的关系 HTML的语义化 应用 HTML骨架格式 sublime基本使用 一、HTML初识 HTML指的是超文本标记语言&#xff0c;是用来描述网页的一种语言 超文本&#xff1a;暂且理解为“超级的文本”&…

BNB链融合

BNB Chain融合 BNB Chain目前有BNB智能链&#xff08;BSC&#xff09;&#xff0c;BNB信标链 BNB信标链&#xff1a;用作质押和投票的治理层&#xff0c;采用BEP-2代币标准BNB智能链(BSC)&#xff1a;用作EVM兼容层&#xff0c;提供DApp、DeFi服务、共识层、多链支持和其他Web3…

【AI工具之Prezo如何自动生成PPT操作步骤】

先说优缺点&#xff1a; 最大的优点就是免费&#xff08;但说实话功能和体验方面很弱&#xff09;支持中文提问&#xff08;最好用英文&#xff09;&#xff0c;智能生成图文&#xff08;但是只能生成英文内容&#xff09;可以AI生成图片&#xff0c;图片很精美酷炫&#xff0…

Hive进阶(2)----HDFS写入数据流程(赋图助君理解)

HDFS写入数据流程 一、写入流程 1、 Client向NameNode发起RPC请求&#xff0c;来确定请求文件block所在的位置&#xff1b; 2、 NameNode会视情况返回文件的部分或者全部block列表&#xff0c;对于每个block&#xff0c;NameNode都会返回含有该block副本的DataNode地址&…

三、fpga对完成过滤和校验的有效包数据进行有效像素提取、MATLAB对数据源进行处理与下发(完整实现pc机→显示器通信链路)

前言:上篇文章实现了MATLAB模拟发送UDP以太网协议数据包到fpga,能实现双沿数据→单沿数据转换,并将转换后的数据进行包过滤和crc校验,本篇内容要实现真正的从pc机下发视频数据,经过千兆以太网传输存储到fpga 的ddr3中,然后通过hdmi读出到显示屏上。 文章目录 一、模块设…

Tensorflow小技巧01:检测本地Tensorflow的版本

前言&#xff1a; 以Pycharm为例&#xff0c;Windwos10系统&#xff0c;检测本地环境的Tensorflow的版本&#xff1a; 1 打开Pycharm窗口 2 在窗口中输入&#xff1a; pythonPython 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win…

面试经典算法系列之二叉树17 -- 验证二叉树

面试经典算法32 - 验证二叉树 LeetCode.98 公众号&#xff1a;阿Q技术站 问题描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当…