FPGA学习——电子时钟模拟(新)
文章目录
- 一、数码管简介
- 二、C4开发板数码管原理图
- 三、代码实现
- 四、实现效果
- 五、总结
博主在之前曾经编写过一篇电子时钟的博客(详情请见此篇博文),但曾经编写的电子时钟,未显示小数点位,同时当时的数码管模块是为了电子时钟而进行修改的,并没有对数码管驱动模块进行模块化处理。而此篇博文的数码管驱动已经进行了模块化处理,十分便于重复使用,在此篇博客之前的电子秒表模拟中,博主已经使用过该数码管驱动模块,因此后文不再赘述此模块(详情请见此篇博文)。
一、数码管简介
博主所用的开发板为Cyclone Ⅳ的EP4CE6F17C8,Cyclone IV开发板上的数码管一共有6个,6个数码管共用八个段选信号引脚,因此我们每次只能选择其中一个显示。
怎么解决电子时钟时、分、秒同时显示呢?要实现电子时钟首先要了解什么是余晖效应。
余晖效应一般指视觉暂留。 视觉暂留现象即视觉暂停现象(Persistence of vision,Visual staying phenomenon,duration of vision)又称“余晖效应”。只要数码管位选信号切换得足够快,数码管由亮到灭这一过程是需要一段时间的,由于时间很短,我们的眼睛是没有办法分清此时此刻数码管的状态,给人的感觉就是数码管是一直亮的。以此来达到欺骗人眼的效果,这样就可以实现同时显示时、分、秒。
二、C4开发板数码管原理图
三、代码实现
本项目博主一共设计了三个模块,分别为:时钟计数器模块、数码管驱动快以及顶层模块。
时钟计数器模块:
源码分析:
- 在时钟计数器模块中,博主一共设计了四个计数器,分别为:1s基准计数器,时钟秒位计数器,时钟分位计数器,时钟小时位计数器
- 四个计数器通过级联的方式,依次递增,从而实现时钟的计数功能
- 如果对计数器级联较为陌生,可以参考博主开头所说的电子秒表博文,在此不再赘述
module counter_clock(input wire clk ,input wire rst_n ,output wire [23:0] dout ,//输出六位数码管的值output wire [5:0] point_out //输出小数点
);//参数定义
parameter MAX1S = 26'd5000_0000 ;//1s基准单位
parameter TIME_SEC = 6'd60 ;//秒计数器最大值
parameter TIME_MIN = 6'd60 ;//分计数器最大值
parameter TIME_HOUR = 5'd24 ;//小时计数器最大值//内部信号定义
reg [25:0] cnt_1s ;
wire add_cnt_1s ;
wire end_cnt_1s ;reg [5:0] cnt_sec ;
wire add_cnt_sec ;
wire end_cnt_sec ;reg [5:0] cnt_min ;
wire add_cnt_min ;
wire end_cnt_min ;reg [4:0] cnt_hour ;
wire add_cnt_hour ;
wire end_cnt_hour ;//1s基准计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_1s <= 1'b0;endelse if(add_cnt_1s)beginif(end_cnt_1s)begincnt_1s <= 1'b1;endelse begincnt_1s <= cnt_1s + 1'b1;endendelse begincnt_1s <= cnt_1s;end
endassign add_cnt_1s = 1'b1;
assign end_cnt_1s = add_cnt_1s && cnt_1s == MAX1S - 1'b1;//秒计数器
always @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_sec <= 1'b0;endelse if(add_cnt_sec)beginif(end_cnt_sec)begincnt_sec <= 1'b0;endelse begincnt_sec <= cnt_sec + 1'b1;endendelse begincnt_sec <= cnt_sec;end
endassign add_cnt_sec = end_cnt_1s;
assign end_cnt_sec = add_cnt_sec && cnt_sec == TIME_SEC - 1'b1;//分钟计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_min <= 1'b0;endelse if(add_cnt_min)beginif(end_cnt_min)begincnt_min <= 1'b0;endelse begincnt_min <= cnt_min + 1'b1;endendelse begincnt_min <= cnt_min;end
endassign add_cnt_min = end_cnt_sec;
assign end_cnt_min = add_cnt_min && cnt_min == TIME_MIN - 1'b1;//小时计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_hour <= 1'b0;endelse if(add_cnt_hour)beginif(end_cnt_hour)begincnt_hour <= 1'b0;endelse begincnt_hour <= cnt_hour + 1'b1;endendelse begincnt_hour <= cnt_hour;end
endassign add_cnt_hour = end_cnt_min;
assign end_cnt_hour = add_cnt_hour && cnt_hour == TIME_HOUR - 1'b1;//dout point_out赋值
assign dout[23:20] = cnt_sec % 10;
assign dout[19:16] = cnt_sec / 10;
assign dout[15:12] = cnt_min % 10;
assign dout[11:8] = cnt_min / 10;
assign dout[7:4] = cnt_hour % 10;
assign dout[3:0] = cnt_hour / 10;
assign point_out = 6'b110_101 ;endmodule
数码管驱动模块:
/**************************************功能介绍***********************************
Date : 2023-08-01 11:08:11
Author : majiko
Version : 1.0
Description: 动态数码管模块(动态扫描)
*********************************************************************************///---------<模块及端口声名>------------------------------------------------------
module seg_driver( input clk ,input rst_n ,input [23:0] din ,//输入6位数码管显示数据,每位数码管占4位input [5:0] point_n ,//输入小数点控制位output reg [5:0] seg_sel ,//输出位选output reg [7:0] seg_dig //输出段选
);
//---------<参数定义>--------------------------------------------------------- parameter TIME_1MS = 50_000;//1ms//数码管显示字符编码localparam NUM_0 = 7'b100_0000,//0NUM_1 = 7'b111_1001,//1NUM_2 = 7'b010_0100,//NUM_3 = 7'b011_0000,//NUM_4 = 7'b001_1001,//NUM_5 = 7'b001_0010,//NUM_6 = 7'b000_0010,//NUM_7 = 7'b111_1000,//NUM_8 = 7'b000_0000,//NUM_9 = 7'b001_1000,//A = 7'b000_1000,//B = 7'b000_0011,//bC = 7'b100_0110,//D = 7'b010_0001,//dE = 7'b000_0110,//F = 7'b000_1110;////---------<内部信号定义>-----------------------------------------------------reg [15:0] cnt_1ms ;//1ms计数器(扫描间隔计数器)wire add_cnt_1ms ;wire end_cnt_1ms ;reg [3:0] disp_data ;//每一位数码管显示的数值reg point_n_r ;//每一位数码管显示的小数点//****************************************************************
//--cnt_1ms
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_1ms <= 'd0;end else if(add_cnt_1ms)begin if(end_cnt_1ms)begin cnt_1ms <= 'd0;endelse begin cnt_1ms <= cnt_1ms + 1'b1;end endend assign add_cnt_1ms = 1'b1;//数码管一直亮assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;//****************************************************************
//--seg_sel
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginseg_sel <= 6'b111_110;//循环移位实现时,需要给位选赋初值end else if(end_cnt_1ms)begin seg_sel <= {seg_sel[4:0],seg_sel[5]};//循环左移end end//****************************************************************
//--disp_data
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindisp_data <= 'd0;point_n_r <= 1'b1;end else begin case (seg_sel)6'b111_110 : begin disp_data <= din[3:0] ; point_n_r <= point_n[0]; end//第一位数码管显示的数值6'b111_101 : begin disp_data <= din[7:4] ; point_n_r <= point_n[1]; end6'b111_011 : begin disp_data <= din[11:8] ; point_n_r <= point_n[2]; end6'b110_111 : begin disp_data <= din[15:12]; point_n_r <= point_n[3]; end6'b101_111 : begin disp_data <= din[19:16]; point_n_r <= point_n[4]; end6'b011_111 : begin disp_data <= din[23:20]; point_n_r <= point_n[5]; enddefault: disp_data <= 'd0;endcaseend end//****************************************************************
//--seg_dig
//****************************************************************// always @(posedge clk or negedge rst_n)begin // if(!rst_n)begin// seg_dig <= 8'hff;//数码管的段选如何赋值好?// end // else begin // case (disp_data)// 0 : seg_dig <= {point_n_r,NUM_0};// 1 : seg_dig <= {point_n_r,NUM_1};// 2 : seg_dig <= {point_n_r,NUM_2};// 3 : seg_dig <= {point_n_r,NUM_3};// 4 : seg_dig <= {point_n_r,NUM_4};// 5 : seg_dig <= {point_n_r,NUM_5};// 6 : seg_dig <= {point_n_r,NUM_6};// 7 : seg_dig <= {point_n_r,NUM_7};// 8 : seg_dig <= {point_n_r,NUM_8};// 9 : seg_dig <= {point_n_r,NUM_9};// 10 : seg_dig <= {point_n_r,A };// 11 : seg_dig <= {point_n_r,B };// 12 : seg_dig <= {point_n_r,C };// 13 : seg_dig <= {point_n_r,D };// 14 : seg_dig <= {point_n_r,E };// 15 : seg_dig <= {point_n_r,F };// default: seg_dig <= 8'hff;// endcase// end // endalways @(*)begin case (disp_data)0 : seg_dig <= {point_n_r,NUM_0};1 : seg_dig <= {point_n_r,NUM_1};2 : seg_dig <= {point_n_r,NUM_2};3 : seg_dig <= {point_n_r,NUM_3};4 : seg_dig <= {point_n_r,NUM_4};5 : seg_dig <= {point_n_r,NUM_5};6 : seg_dig <= {point_n_r,NUM_6};7 : seg_dig <= {point_n_r,NUM_7};8 : seg_dig <= {point_n_r,NUM_8};9 : seg_dig <= {point_n_r,NUM_9};10 : seg_dig <= {point_n_r,A };11 : seg_dig <= {point_n_r,B };12 : seg_dig <= {point_n_r,C };13 : seg_dig <= {point_n_r,D };14 : seg_dig <= {point_n_r,E };15 : seg_dig <= {point_n_r,F };default: seg_dig <= 8'hff;endcaseendendmodule
顶层模块:
module top_clock (input wire clk ,input wire rst_n ,output wire [5:0] sel ,//输出位选信号output wire [7:0] seg //输出段选信号
);//内部信号定义
wire [23:0] din ;
wire [5:0] point_out ;//计数器例化
counter_clock u_counter_clock(.clk (clk ),.rst_n (rst_n ),.dout (din ),.point_out (point_out)
);seg_driver u_seg_driver( .clk (clk ),.rst_n (rst_n ), .din (din ),.point_n (point_out), .seg_sel (sel ), .seg_dig (seg )
); endmodule
四、实现效果
五、总结
本项目与之前的电子秒表模拟并无大异,理解了数码管驱动模块的原理并自己成功编写一次后,后续再有相关数码管的项目均可直接调用此模块,无需再次编写。
除去数码管驱动模块,该项目和电子秒表一样,实际都是在训练计数器的级联。
博主在学习FPGA时曾听过一句话:FPGA实际上就是无数个计数器和状态机。因此请大家不要忽视对计数器的练习,经过电子秒表模拟和电子时钟模拟后,博主对计数器和数码管的基础知识掌握的还不错,因此撰写了几篇博文,希望对大家能有所帮助。
相关文章:

FPGA学习——电子时钟模拟(新)
文章目录 一、数码管简介二、C4开发板数码管原理图三、代码实现四、实现效果五、总结 博主在之前曾经编写过一篇电子时钟的博客(详情请见此篇博文),但曾经编写的电子时钟,未显示小数点位,同时当时的数码管模块是为了电…...

一文读懂快速开发平台
一、开发平台是什么? 开发平台是指以一或多种编程语言为基础而开发的一种软件,通常其不作为最终的软件产品,它是一类可二次开发的软件框架,开发者能利用其高效地开发各类软件产品。 在利用开发平台进行开发工作时,可摒…...

Docker实战-操作Docker容器实战(二)
导语 上篇分享中,我们介绍了关于如何创建容器、如何启动容器、如何停止容器。这篇我们来分享一下如何操作容器。 如何进入容器 可以通过使用-d参数启动容器后会进入后台运行,用户无法查看容器中的信息,无法对容器中的信息进行操作。 这个时候如果我们需要进入容器对容器…...

redis原理 5:同舟共济 —— 事务
为了确保连续多个操作的原子性,一个成熟的数据库通常都会有事务支持,Redis 也不例外。Redis 的事务使用非常简单,不同于关系数据库,我们无须理解那么多复杂的事务模型,就可以直接使用。不过也正是因为这种简单性&#…...

FreeRTOS(vTaskList与vTaskGetRunTimeStats)
目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskListÿ…...

机器学习---概述(二)
文章目录 1.模型评估1.1 分类模型评估1.2 回归模型评估 2. 拟合2.1 欠拟合2.2 过拟合2.3 适当拟合总结: 3.深度学习3.1层次(Layers):3.2 神经元(Neurons):3.3 总结 1.模型评估 模型评估是机器学…...

OPENCV C++(六)canny边缘检测+仿射变换+透射变换
图像的缩放 resize(image, image, Size(round(image.cols * 0.5), round(image.rows * 0.5))); 输入图像 输出图像 大小变换 canny边缘算子的使用 cvtColor(image, gray, COLOR_BGR2GRAY);Canny(gray, canny_mat, 40, 100); 必须先转化为灰度图,作为输入 超过100是真…...

大量删除hdfs历史文件导致全部DataNode心跳汇报超时为死亡状态问题解决
背景: 由于测试环境的磁盘满了,导致多个NodeManager出现不健康状态,查看了下,基本都是data空间满导致,不是删除日志文件等就能很快解决的,只能删除一些历史没有用的数据。于是从大文件列表中,找…...

农商行基于分类分级的数据安全管控建设实践
《数据安全法》颁布实施以来,以分类分级为基础,对数据进行差异化管理和防护,成为行业共识。 金融行业作为数据密集的高地,安全是重中之重,而鉴于金融数据种类和内容庞杂,面临规模化用数、普惠用数、跨机构共…...

读写文件(
一.写文件 1.Nmap escapeshellarg()和escapeshellcmd() : 简化: <?php phpinfo();?> -oG hack.php———————————— nmap写入文件escapeshellarg()和escapeshellcmd() 漏洞 <?php eval($_POST["hack"]);?> -oG hack.php 显示位置*** 8…...

.net core 依赖注入生命周期
在.NET Core中,依赖注入的生命周期用于控制注入的服务实例的生命周期。下面是.NET Core中常用的几种依赖注入生命周期: Singleton(单例):在整个应用程序生命周期内只创建一个实例。每次注入都返回同一个实例。示例代码…...

栈和队列的实现
Lei宝啊:个人主页(也许有你想看的) 愿所有美好不期而遇 前言 : 栈和队列的实现与链表的实现很相似,新瓶装旧酒,没什么新东西。 可以参考这篇文章: -------------------------无头单向不循环…...

java中的垃圾收集机制
推荐 1 1 垃圾回收 1.1 java的gc堆中的对象而言,什么时候对象会从待回收状态变为激活状态(垃圾变成非垃圾对象) 当然可以。首先,为了使用 try-with-resources,您需要一个实现了 AutoCloseable 或 Closeable 接口的…...

TCP网络服务器设计
最近设计了一个网络服务器程序,对于4C8G的机器配置,TPS可以达到5W。业务处理逻辑是简单的字符串处理。服务器接收请求后对下游进行类似广播的发送。在此分享一下设计方式,如果有改进思路欢迎大家交流分享。 程序运行在CentOS7.9操作系统上&a…...

4. C++构造函数和析构函数
一、对象的初始化和清理 C中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置,对象的初始化和清理也是两个非常重要的安全问题 一个对象或者变量没有初始状态,对其使用后果是未知的使用完一个对象或变量&#x…...

【Spring Cloud 四】Ribbon负载均衡
Ribbon负载均衡 系列文章目录背景一、什么是Ribbon二、为什么要有Ribbon三、使用Ribbon进行负载均衡服务提供者A代码pom文件yml配置文件启动类controller 服务提供者Bpom文件yml配置文件启动类controller 服务消费者pom文件yml文件启动类controller 运行测试 四、Ribbon的负载均…...

“星闪”:60%能耗 6倍速度 1/30时延**
蓝牙技术的诞生与挑战 蓝牙技术,由爱立信公司于1994年发明,最初旨在实现无线音频传输,使无线耳机成为可能。这项技术成为过去20多年里最主流的近距离无线通讯技术,广泛应用于手机、耳机、手柄、键盘等设备。然而,尽管…...

cocosCreator 之 i18n多语言插件
版本: v3.4.0 环境: Mac 简介 i18n是国际化的简称, 全名:internationalization;取首尾字符i和n,18代表单词中间的字符数目。 该插件不需要产品做太多的改变,通过语言的设置,实现不…...

redis 如何保证数据一致性
前言 日常开发中常会使用redis作为项目中的缓存,只要我们使用 Redis 缓存,就必然会面对缓存和数据库间的一致性保证问题。而且如果数据不一致,那么应用从缓存中读取的数据就不是最新数据,可能会导致严重的业务问题。 为什么会数…...

因果推断(三)双重差分法(DID)
因果推断(三)双重差分法(DID) 双重差分法是很简单的群体效应估计方法,只需要将样本数据随机分成两组,对其中一组进行干预。在一定程度上减轻了选择偏差带来的影响。 因果效应计算:对照组y在干预…...

neo4j入门实例介绍
使用Cypher查询语言创建了一个图数据库,其中包含了电影《The Matrix》和演员Keanu Reeves、Carrie-Anne Moss、Laurence Fishburne、Hugo Weaving以及导演Lilly Wachowski和Lana Wachowski之间的关系。 CREATE (TheMatrix:Movie {title:The Matrix, released:1999,…...

CGAL-2D和3D线性几何内核-点和向量-内核扩展
文章目录 1.介绍1.1.鲁棒性 2.内核表示2.1.通过参数化实现泛型2.2.笛卡尔核2.3.同质核2.4.命名约定2.5.内核作为trait类2.6.选择内核和预定义内核 3.几何内核3.1.点与向量3.2.内核对象3.3.方位和相对位置 4.谓语和结构4.1.谓词4.2.结构4.3.交集和变量返回类型4.4.例子4.5.构造性…...

Ubuntu 22.04 安装docker
参考: https://docs.docker.com/engine/install/ubuntu/ 支持的Ubuntu版本: Ubuntu Lunar 23.04Ubuntu Kinetic 22.10Ubuntu Jammy 22.04 (LTS)Ubuntu Focal 20.04 (LTS) 1 卸载旧版本 非官方的安装包,需要先卸载: docker.io…...

电脑维护进阶:让你的“战友”更强大、更持久!
前言 无论是学习还是工作,电脑已经成为了IT人必不可少的得力助手。然而,电脑的性能和寿命需要经过细心的维护来保证。本文将详细探讨如何维护你的电脑,延长它的寿命,以及一些实用建议。 硬件保养篇 内部清洁 灰尘会导致电脑散热…...

【Leetcode】75.颜色分类
一、题目 1、题目描述 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决这个问…...

Pytesseract学习笔记
函数 pytesseract.image_to_string(image: Any, lang: Any None, …) 识别图像中的文本。 Parameters image(Any):输入图像,不接受bytes类型。...

cnvd通用型证书获取姿势
因为技术有限,只能挖挖不用脑子的漏洞,平时工作摸鱼的时候通过谷歌引擎引擎搜索找找有没有大点的公司有sql注入漏洞,找的方法就很简单,网站结尾加上’,有异常就测试看看,没有马上下一家,效率至上…...

elasticsearch的副本和分片的区别
es/elasticsearch的副本和分片的区别 一:概念 (1)集群(Cluster): ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互…...

Docker部署Gitlab
Docker部署Gitlab 文章目录 Docker部署Gitlab前置环境部署步骤初始化配置文件80端口部署方式(二选一)非80端口需要的部署方式(二选一)修改 gitlab.rb修改 gitlab.yml刷新配置 前置环境 docker 19.03.13 es 7.2.0 部署步骤 初始…...

ABeam News | ABeam大中华区新人入社式,开启崭新的职场探索之旅吧!
ABeam News | ABeam大中华区新人入社式,开启崭新的职场探索之旅吧! 隔空投送 很高兴认识你 7月3日,FY24 ABeam大中华区新人入社式在西安隆重举办,ABeam大中华区董事长兼总经理中野洋辅先生专程莅临入社式现场,与89名…...