VHDL/CPLD硬件描述语言:2022年做的万年历实验

之前接触过一些硬件描述语言以及VHDL/CPLD的单片机的设计实验,那时是2022年了

这里补写一篇笔记,以记录一下那十多个小时 万年历实验 研究中的心得体会:

说明解释都是个人理解,与标准描述有较大出入......

目录

输入输出器件的编写:

分频器点亮不同频率LED:

引用头文件:

实体(ENTITY)定义和架构(ARCHITECTURE)的开头部分:

主体:变量定义与时钟事件检查:

完整代码:

电路连接与效果描述:

级联分频/计时器实现时钟的效果:

四位计数器(10分频)的内部硬件描述代码:

接口与变量作用定义:

计数器过程:

数码管分频计数逻辑输出器:

​编辑

它的完整代码如下:

最后的输入输出总线连接:

编译下载效果如下:

回忆与感悟 :


 

输入输出器件的编写:

想必大家都听说过一些单片机外围电路上的芯片:

比如74HC138(三选八)芯片,74HC573扩展芯片等,这些芯片在硬件上定义一些输入与输出相联系的逻辑来实现他们的功能,

而这里的编程则是在软件上自己先使用硬件描述语言进行对输入输出逻辑的组合,自己编写出类似于分频器、片选芯片类似功能的器件模块,然后再将它们与具体的输入输出线引脚相组合,最后编译无报错下载进板子查看效果的过程。

分频器点亮不同频率LED:

这里展示了一个编写简单的分频器的硬件描述语言的逻辑代码,下面分布解释:

引用头文件:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

实体(ENTITY)定义和架构(ARCHITECTURE)的开头部分:

PORT 描述了一个具有时钟分离功能的模块,

它接收一个输入时钟信号CLK,并产生两个输出时钟信号:

CLK_1HZ(1赫兹的时钟)和CLK_1KHZ(1千赫兹的时钟)

ENTITY CLK_SEP IS
PORT(CLK:IN STD_LOGIC;
  CLK_1HZ:OUT STD_LOGIC;
  CLK_1KHZ:OUT STD_LOGIC);
END CLK_SEP;
ARCHITECTURE BHV OF CLK_SEP IS

主体:变量定义与时钟事件检查:

COUNT1:一个24位的STD_LOGIC_VECTOR,用于产生1Hz的时钟信号。

COUNT2:一个14位的STD_LOGIC_VECTOR,用于产生1kHz的时钟信号。

IF CLK'EVENT AND CLK = '1' THEN:这一行检查CLK信号是否有一个事件(上升沿0变为1)

COUNT1COUNT2 在每个CLK上升沿递增。

COUNT1的值在"010110111000110110000000"(二进制表示,对应于某个十进制数)和"101101110001101100000000"之间时,CLK_1HZ被设置为'1'。这个范围代表了从某个特定开始点到结束点的时间段,用于产生1Hz的时钟周期。

COUNT1的值小于或等于结束点且大于或等于"000000000000000000000000"(即开始点)时,CLK_1HZ被设置为'0'

COUNT1达到结束点"101101110001101100000000"时,它被重置为"000000000000000000000000",以便开始下一个1Hz周期。

COUNT2的值在"01011101110000""10111011100000"之间时,CLK_1KHZ被设置为'1'。这个范围代表了从某个特定开始点到结束点的时间段,用于产生1kHz的时钟周期。

COUNT2的值小于或等于结束点且大于或等于"00000000000000"时,CLK_1KHZ被设置为'0'

COUNT2达到结束点"10111011100000"时,它被重置为"00000000000000",以便开始下一个1kHz周期。

BEGIN
 PROCESS(CLK)
 VARIABLE COUNT1:STD_LOGIC_VECTOR(23 DOWNTO 0);
 VARIABLE COUNT2:STD_LOGIC_VECTOR(13 DOWNTO 0);
 
 BEGIN
  IF CLK'EVENT AND CLK = '1' THEN
   COUNT1:=COUNT1+1;
   COUNT2:=COUNT2+1;
   IF ((COUNT1>="010110111000110110000000") AND	(COUNT1<="101101110001101100000000") ) THEN
    CLK_1HZ<='1'; 
   ELSIF ((COUNT1<="010110111000110110000000") AND(COUNT1>="000000000000000000000000")) THEN
    CLK_1HZ<='0';
   IF COUNT1="101101110001101100000000" THEN
    COUNT1:="000000000000000000000000";
	END IF;
   END IF;
   
   IF ((COUNT2>="01011101110000") AND (COUNT2<="10111011100000")) THEN
	CLK_1KHZ<='1';
   ELSIF ((COUNT2<="01011101110000")AND(COUNT2>="00000000000000")) THEN
	CLK_1KHZ<='0';
	IF(COUNT2="10111011100000") THEN
    COUNT2:="00000000000000";
    
    END IF;
   END IF;
  END IF;
 END PROCESS;
END BHV;

完整代码:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CLK_SEP IS
PORT(CLK:IN STD_LOGIC;
  CLK_1HZ:OUT STD_LOGIC;
  CLK_1KHZ:OUT STD_LOGIC);
END CLK_SEP;
ARCHITECTURE BHV OF CLK_SEP IS
BEGIN
 PROCESS(CLK)
 VARIABLE COUNT1:STD_LOGIC_VECTOR(23 DOWNTO 0);
 VARIABLE COUNT2:STD_LOGIC_VECTOR(13 DOWNTO 0);
 
 BEGIN
  IF CLK'EVENT AND CLK = '1' THEN
   COUNT1:=COUNT1+1;
   COUNT2:=COUNT2+1;
   IF ((COUNT1>="010110111000110110000000") AND	(COUNT1<="101101110001101100000000") ) THEN
    CLK_1HZ<='1'; 
   ELSIF ((COUNT1<="010110111000110110000000") AND(COUNT1>="000000000000000000000000")) THEN
    CLK_1HZ<='0';
   IF COUNT1="101101110001101100000000" THEN
    COUNT1:="000000000000000000000000";
	END IF;
   END IF;
   
   IF ((COUNT2>="01011101110000") AND (COUNT2<="10111011100000")) THEN
	CLK_1KHZ<='1';
   ELSIF ((COUNT2<="01011101110000")AND(COUNT2>="00000000000000")) THEN
	CLK_1KHZ<='0';
	IF(COUNT2="10111011100000") THEN
    COUNT2:="00000000000000";
    
    END IF;
   END IF;
  END IF;
 END PROCESS;
END BHV;

电路连接与效果描述:

最终是实现了俩个LED不同频率的亮灭:

 

级联分频/计时器实现时钟的效果:

有人在设计时钟时想得十分麻烦,又是10分频器,又是6分频器,又是12分频,又是60分...

其实时间的进位关系是一目了然的,

我们只需要给每个分频器写一个到达某个特定时分进位的逻辑,然后将这个逻辑进行级联,即可只靠俩种分频器即可做到时钟的效果:

这里我明显当年没考虑到一天是有24小时的,毕竟是个大学的课设,并未想得做得完善

但其中器件的逻辑还是设计得清晰的:

Q_OUT                 用于分频器后的进位级联,计数到分频计数器逻辑定义的极限

                           (10或者6 )时就会在这个输出口产生一个进位信号,然后分频计数器

                             就会 重新开始计数

CNT[0]~CNT[3]    是一个提供当前分频/计数器 计数值的总线,(二进制表示0~9)

en                         是一个使能,直接连接了VCC,(其实可以不定义这个输入)

                             但定义了以后可以实现其他复杂的片选功能,实现想开就开,想关就关

                             的灵活分频计时功能把

    

四位计数器(10分频)的内部硬件描述代码:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY COUNT_10 IS
	PORT(clk,clr,en:IN STD_LOGIC;
		qa1,qb1,qc1,qd1,Q_OUT:OUT STD_LOGIC);
END COUNT_10;

ARCHITECTURE rtl OF COUNT_10 IS
SIGNAL count_4:STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
	qa1<=count_4(0);
	qb1<=count_4(1);
	qc1<=count_4(2);  
	qd1<=count_4(3);
PROCESS(clk,clr)
	BEGIN
		IF(clr='1')THEN
			count_4<="0000";
		ELSIF(clk'EVENT AND clk='1')THEN
		IF(en='1')THEN
   			IF(count_4="1001")THEN
				count_4<="0000";Q_OUT<='1';
			ELSE
				count_4<=count_4+'1';Q_OUT<='0';
			END IF;
		END IF;
	END IF;
END PROCESS;
END rtl;

接口与变量作用定义:

PORT(clk,clr,en:IN STD_LOGIC;qa1,qb1,qc1,qd1,Q_OUT:OUT STD_LOGIC);

定义器件包含的输入输出接口

SIGNAL count_4:STD_LOGIC_VECTOR(3 DOWNTO 0);

定义了一个四位的STD_LOGIC_VECTOR信号count_4,用于存储计数器的值。

qa1, qb1, qc1, qd1分别被赋值为count_4的四位

计数器过程:

clr为高电平时,计数器count_4被清零。

clk的上升沿到来(clk'EVENT AND clk='1')且en为高电平时,计数器进行更新:

如果count_4的值为1001(即十进制的10),则计数器被清零,并且Q_OUT输出高电平。否则,计数器加1,并且Q_OUT输出低电平。

数码管分频计数逻辑输出器:

最后还有个原始接收1khz信号,以及各级分频计时器的 CNT[0]~CNT[3]  计时输出来对数码管进行段选和位选的逻辑器件,它也是需要接收一个clk时钟信号进行段选位选的

它的完整代码如下:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY SMG_WXQ IS
	PORT(
		CLK:IN STD_LOGIC;
		
		C_IN1:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
		C_IN2:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
		C_IN3:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
		C_IN4:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
		
		C_OUNT:BUFFER STD_LOGIC_VECTOR(6 DOWNTO 0);
		
		SMG_WX:out std_logic_vector(7 downto 0)	
		);
END SMG_WXQ;

ARCHITECTURE rtl OF SMG_WXQ IS
BEGIN
process(clk)
variable cnt : integer range 0 to 3 := 0;
 BEGIN

 IF CLK'EVENT AND CLK = '1' THEN
	 
	 IF cnt=0 THEN 
	 SMG_WX<="00000001";
	 case C_IN1 is
	 when "0000"=>C_OUNT<="1111110";
	 when "0001"=>C_OUNT<="0000110";
	 when "0010"=>C_OUNT<="1101101";
	 when "0011"=>C_OUNT<="1111001";
	 when "0100"=>C_OUNT<="0110011";
	 when "0101"=>C_OUNT<="1011011";
	 when "0110"=>C_OUNT<="1011111";
	 when "0111"=>C_OUNT<="1110000";
	 when "1000"=>C_OUNT<="1111111";
	 when "1001"=>C_OUNT<="1111011";
	 when others=>C_OUNT<="0000000";
	 end case;
	 cnt:=cnt+1;
	 
	 ELSIF cnt=1 THEN 
	 SMG_WX<="00000010";
	 case C_IN2 is
	 when "0000"=>C_OUNT<="1111110";
	 when "0001"=>C_OUNT<="0110000";
	 when "0010"=>C_OUNT<="1101101";
	 when "0011"=>C_OUNT<="1111001";
	 when "0100"=>C_OUNT<="0110011";
	 when "0101"=>C_OUNT<="1011011";
	 when "0110"=>C_OUNT<="1011111";
	 when others=>C_OUNT<="0000000";
	 end case;
	 cnt:=cnt+1;
	 
	 ELSIF cnt=2 THEN 
	 SMG_WX<="00000100";
	 case C_IN3 is
	 when "0000"=>C_OUNT<="1111110";
	 when "0001"=>C_OUNT<="0000110";
	 when "0010"=>C_OUNT<="1101101";
	 when "0011"=>C_OUNT<="1111001";
	 when "0100"=>C_OUNT<="0110011";
	 when "0101"=>C_OUNT<="1011011";
	 when "0110"=>C_OUNT<="1011111";
	 when "0111"=>C_OUNT<="1110000";
	 when "1000"=>C_OUNT<="1111111";
	 when "1001"=>C_OUNT<="1111011";
	 when others=>C_OUNT<="0000000";
	 end case;
	 cnt:=cnt+1;	 
	 
	 ELSIF cnt=3 THEN 
	 SMG_WX<="00001000";
	 case C_IN4 is
	 when "0000"=>C_OUNT<="1111110";
	 when "0001"=>C_OUNT<="0110000";
	 when "0010"=>C_OUNT<="1101101";
	 when "0011"=>C_OUNT<="1111001";
	 when "0100"=>C_OUNT<="0110011";
	 when "0101"=>C_OUNT<="1011011";
	 when "0110"=>C_OUNT<="1011111";
	 when others=>C_OUNT<="0000000";
	 END CASE;
	 cnt:=0;
	 
	 END IF;
	END IF;
 END PROCESS;
END rtl;

 

最后的输入输出总线连接:

最后将讲到的四个主要器件、VCC、GND、数码管对应的引脚(并成总线形式),正确完整连接就组成了万年历:

但从这个设计我们发现,这个万年历并不完整!

从左往右看:只存在秒的个位、秒的十位、分的个位、分的十位,不存在小时!

 

编译下载效果如下:

这里的位选有些问题,而且我还没进编写空开 分与秒 位置的冒号显示:

 

 

回忆与感悟 :

这样接近底层对CLK时钟信号进行自由编程的感受释放奇妙,让我感觉任何信号的运转并不是凭空而来的,而是我自己靠着抽丝引线一点一点连接起来的!

现在许多STM32的编程都是调用库函数,导致很多人连寄存器都摸不到

而我从51、CPLD、汇编、硬件描述语言开始缓慢往上爬,到后来学习MSP432库函数残缺不完整,自己用库函数+寄存器混编,到STM32,操作系统,到现在学习树莓派、上位机

深刻感受到,编程的上限永远在底层,就像皇帝智力国家一样,只接触到大臣(调用库),只会在后面被底层人架空,然后终有一天被底层错误搅合得摸不着头脑。

如果底层都是直接可以被我摸清调遣的,那整个系统的逻辑就基本没有不明朗的地方了,出错也能比其余人更快找到病灶!

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

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

相关文章

年中汇报季?——一文教会你如何进行数据分析

一、常见的数据分析报告类型 数据分析报告通常可以分为三类&#xff1a;日常分析报告、专题型分析报告和综合性分析报告。前两者是以数据结论建议的格式去撰写&#xff0c;综合性分析报告则是&#xff1a;行业环境调研&#xff08;竞品类产品数据分析&#xff09;自身产品数据…

张大哥笔记:你卖什么,就反着来卖

普通人打工的一生&#xff0c;就是努力工作&#xff0c;买房&#xff0c;买车&#xff0c;送孩子上好的学校&#xff0c;为了孩子不要输在起跑线上&#xff0c;拼命报各种补习班等&#xff0c;这些都是普通人认为的主流价值观文化&#xff0c;也造就了一批批的赚钱机器&#xf…

深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

文章目录 一、CGLIB 代理简介1.1 CGLIB 代理的基本原理和特点1.2 分析 CGLIB 如何通过字节码技术创建代理类 二、深入分析 CglibAopProxy 类的结构2.1 CglibAopProxy 类结构2.2 CglibAopProxy 类源码 三、CGLIB 代理对象的创建过程3.1 配置 Enhancer 生成代理对象3.2 探讨如何通…

【斯坦福因果推断课程全集】1_随机对照试验1

目录 The average treatment effect Difference-in-means estimation IID Sampling and Population Asymptotics Example: The linear model Regression adjustments with a linear model 随机对照试验&#xff08;RCT&#xff09;是统计因果推论的基础。如果有的话&#…

Linux - 文件管理高级 sed

3.处理字符 sed ① sed 默认情况下不会修改原文件内容 ② sed 是一种非交互式的编辑器 3.1 工作原理 将原文件一行一行的进行处理&#xff0c;取出一行&#xff0c;放入“模式空间进行处理”&#xff0c;处理完成之后将结果输出到屏幕上&#xff0c;然后读取下一行&#xf…

瓦罗兰特国际服 外服游玩教程 瓦罗兰特外服下载注册游玩指南

瓦罗兰特国际服 外服游玩教程 瓦罗兰特外服下载注册游玩指南 瓦罗兰特作为当今游戏圈顶流的一款热门FPS。游戏&#xff0c;作为拳头游戏公司划时代的一款游戏。游戏不仅延续了传统FPS游戏的玩法&#xff0c;还添加许多新玩法&#xff0c;这也是游戏可以吸引大批量玩家的原因之…

vulnhub靶场之FunBox-10

一.环境搭建 1.靶场描述 As always, its a very easy box for beginners. This works better on VitualBox rather than VMware 2.靶场下载 Funbox: Under Construction! ~ VulnHub 3.靶场启动 靶场IP地址我们不知道&#xff0c;但是网段我们知道是192.168.2.0/24 二.信息…

Filter和ServletContext和Listener

目录 Filter案例 解决全站乱码问题 登录权限校验 ServletContext对象 Listener&#xff08;监听器&#xff09; Filter案例 解决全站乱码问题 我们每次访问每个servlet都要书写处理请求和响应乱码的代码&#xff0c;这样代码十分冗余&#xff0c;所以我们可以在过滤中 We…

python列表的进阶

小结&#xff1a; # 列表的删除小结&#xff1a; # 删除列表的最后一列 punished students.pop() print(被罚站的人是&#xff1a; punished &#xff0c;同学们引以为戒。)# 根据下标删除 del students[0]#根据名称删除 students.remove(王熙凤)在今天的课程里&#xff0c…

k8s自定义资源你会创建吗

创建自定义资源定义 CustomResourceDefinition 当你创建新的 CustomResourceDefinition&#xff08;CRD&#xff09;时&#xff0c;Kubernetes API 服务器会为你所 指定的每一个版本生成一个 RESTful 的 资源路径。CRD 可以是名字空间作用域的&#xff0c;也可以是集群作用域的…

短剧系统源码:构建互动娱乐的新平台

随着数字媒体的兴起&#xff0c;短剧成为了一种新兴的娱乐形式&#xff0c;它以紧凑的叙事和快速的节奏迎合了现代观众的观看习惯。短剧系统源码的开发&#xff0c;为短剧内容的创作、传播和消费提供了一个全面的技术解决方案。本文将探讨短剧系统源码的关键组成部分及其功能。…

如何让 LightRoom 每次导入照片后不自动弹出 SD 卡 LR

如何让 LightRoom 每次导入照片后不自动弹出 SD 卡 LR 在导入窗口左上角有个选项&#xff1a; 导入后弹出 把这个去掉就可以了

FreeRTOS同步互斥与通信

本章简介&#xff1a; 本章是概述性的内容。可以把多任务系统当做一个团队&#xff0c;里面的每一个任务就相当于团队里的一个人。团队成员之间要协调工作进度(同步)、争用会议室(互斥)、沟通(通信)。多任务系统中所涉及的概念&#xff0c;都可以在现实生活中找到例子。 各类RT…

Transformer从0到1的学习【还有2-10,别想太多】

1.高纬度介绍Transformer 1.分为编码Encoders和解码器Decoders&#xff1a;“我爱你”作为编码器Encoders的输入进行编码得到序列码后&#xff0c;作为解码器的输入得到输出即为&#xff0c;“I Love you”。 2.编码器和译码器的具体拆分&#xff1a; 左边的编码器Encoders的…

(函数)判断一句话中最长的单词(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明函数&#xff1b; int aiphabetic(char); int longest(char[]);int main() {//初始化变量值&#xff1b;int i;char line[100] { 0 };//获取用户输入字符…

每天写两道(五)合并两个有序链表、最长回文子串

21.合并两个有序链表 . - 力扣&#xff08;LeetCode&#xff09; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] (1)迭代法…

牛客网刷题 | BC105 菱形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

VMware Workstation中WinXP联网问题

我一直以为我的虚拟机上的XP没有联网 因为 蒙了半天&#xff0c;发现是因为这个网址打不开&#xff0c;不是没有网 太傻了 不如在cmd命令行中通过ping baidu.com来判断是否联网

Vue插槽与作用域插槽

title: Vue插槽与作用域插槽 date: 2024/6/1 下午9:07:52 updated: 2024/6/1 下午9:07:52 categories: 前端开发 tags:VueSlotScopeSlot组件通信Vue2/3插槽作用域API动态插槽插槽优化 第1章&#xff1a;插槽的概念与原理 插槽的定义 在Vue.js中&#xff0c;插槽&#xff08;…

【多目标跟踪】《FlowMOT: 3D Multi-Object Tracking by Scene Flow Association》论文阅读笔记

0.论文 论文地址链接:https://arxiv.org/pdf/2012.07541v1 通过流的方式跟踪是一个比较新颖的点,所以这里比较关注运动跟踪,是如果做到流的跟踪来预测目标的位置以及ID绑定的。 FlowMOT的框架结构如下所示,本中会主要关注下运动跟踪、数据关联、ID分配、新生/消亡…