FPGA学习(10)-数码管
前3节视频目的是实现显示0~F的数码管仿真,后3节是用驱动芯片驱动数码管。
目录
1.数码管显示原理
2.代码过程
2.1仿真结果
3.串行移位寄存器原理
3.1原理
编辑 3.2 数据手册
3.3 先行设计思路
4.程序
4.1确定SRCLK的频率
4.2序列计数器
4.3 不同counter值干不同事
4.4仿真
5.实验结果
1.数码管显示原理
分为共阳极与共阴极驱动,共阴极:发光二极管负极都接地,a~dp接入端口引脚,输出高电平就点亮,共阳极与之相反。
不同位的高低电平可以组成不同的数字符号,后面查找表的值就看这张图。
在驱动多个数码管场景中,往往a~dg与每个数码管对应的二极管是互联的,而sel端的电压可以控制三极管是否导通,以此可以单独让某个数码管亮灭。
根据以上分析可以画出电路图:
1.首先SEL决定了点亮哪个数码管,需要用到38译码器进行选择,而38译码器的输出选择需要用到一个3位的状态计数切换器。
2.如何让状态切换,即加入一个计数器记到1ms,当大于1ms时,让状态加1,且计数器本身清0。
3.SEG决定了数码管哪段亮,在没有规律的情况下可以用查表法,把这些状态一一列出来,例如这里有16个状态,那么需要一个4位的变量来表示对应的索引号。
4.将状态计数切换器值传到8选1数据选择器,分别代表8个数码管对应的通道值,作为索引。
2.代码过程
1.实现1ms的计数器;
2.位切换逻辑,计数器每满一次,状态位切换+1;
3.38译码器实现连接哪个数码管;
4.位选择控制逻辑。
(1)查找表case,完成SEG的配置。
(2)位切换逻辑将8个数码管对应的值当作索引传进去。这里数组顺序错了。
代码:
module Shuma_guan(input clk,input reset,input[31:0] Dis_data,output reg[7:0] SEL,output reg[7:0] SEG);
reg[29:0] counter_1ms;
reg[2:0] which_sel;
reg[3:0] data_temp; //存放索引值
//数码管亮的时间
parameter Base_freq = 50_000_000;
parameter goal_freq = 1000;
parameter cworth = Base_freq/goal_freq-1;//计数器
always@(posedge clk or negedge reset)
beginif(!reset)counter_1ms <= 1'd0;else if(counter_1ms == cworth)counter_1ms <= 1'd0;elsecounter_1ms <= counter_1ms+1'd1;
end//数码管状态切换
always@(posedge clk or negedge reset)
beginif(reset)which_sel <= 1'd0;else if(counter_1ms == cworth)which_sel <= which_sel+1'd1;elsewhich_sel <= which_sel;
end//38译码器确定哪个数码管亮
always@(posedge clk)
begincase(which_sel)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
end//建立查找表,共阳
always@(posedge clk)
begincase(data_temp)0:SEG <= 8'b1100_0000;1:SEG <= 8'b1111_1001;2:SEG <= 8'b1010_0100;3:SEG <= 8'b1011_0000;4:SEG <= 8'b1001_1001;5:SEG <= 8'b1001_0010;6:SEG <= 8'b1000_0010;7:SEG <= 8'b1111_1000;8:SEG <= 8'b1000_0000;9:SEG <= 8'b1001_0000;10:SEG <= 8'b1000_1000;11:SEG <= 8'b1000_0011;12:SEG <= 8'b1100_0110;13:SEG <= 8'b1010_0001;14:SEG <= 8'b1000_0110;15:SEG <= 8'b1000_1110;endcase
end//将状态值传给索引
always@(*)
begincase(which_sel)0:data_temp <= Dis_data[3:0];1:data_temp <= Dis_data[7:4];2:data_temp <= Dis_data[11:8];3:data_temp <= Dis_data[15:12];4:data_temp <= Dis_data[19:16];5:data_temp <= Dis_data[23:20];6:data_temp <= Dis_data[27:24];7:data_temp <= Dis_data[31:28];endcase
endendmodule
仿真:
`timescale 1ns / 1nsmodule Shuma_guan_tb();
reg clk;
reg reset;
reg[31:0] Dis_data;
wire[7:0] SEL;
wire[7:0] SEG;Shuma_guan Shuma_guan_inst0(.clk(clk),.reset(reset),.Dis_data(Dis_data),.SEL(SEL),.SEG(SEG)
); initial clk = 1;
always #10 clk=~clk;initial beginreset = 0;Dis_data = 32'h12345678;#201;reset = 1;#20_000_000;Dis_data = 32'h9abcdef0;#20_000_000;$stop;
end
endmodule
2.1仿真结果
切换状态计数没有随着计数器增加到49999而加1,原因是在切换状态的时序逻辑中,复位没有取反,直接if(reset),相当于复位信号为1时,状态计数就会为0,所以一直不会增加。
修改后正确:
3.串行移位寄存器原理
3.1原理
为了节省IO口引脚,一般会使用驱动芯片74HC595驱动数码管,芯片的原理是基于串行移位寄存器。将上节输出的SEG与SEL传输到74HC595。
基本原理为,顺次连接4个D触发器,每经过一个时钟,就依次将DATA存储的值传送到下一个D触发器,例如,经过5个时钟周期后, 此时DFF3~DFF0存储的值为:1001。如果想在这个时刻把每个D触发器存储的值取出来,就在每个D触发器的输出端再接入一个D触发器,加的这4个D触发器单独用一个时钟进行控制,然后将输出分别接至各自端口。
当要移位8位,16位时,直接加更多的D触发器就行。
74HC595能够驱动8位移位寄存器,SER是数据传输口,SRCLK是第一路串联触发器的时钟,SRCLR非应该是复位信号,RCLK是是每个单独加入的触发器的时钟,OE非是输出的使能信号。第1个74HC595的输出串联到了第2个74HC595的输入,以此构成16位移位寄存器输出。
左边的74HC连接的是选择使能哪个数码管,右边的74HC是决定数码管哪段亮。DIO是数据输入,SRCLK是第一路串联触发器的时钟,RCLK是加入单个D触发器的时钟。
3.2 数据手册
从时序图可以看出,RCLK有方波时,才有输出。
移位寄存器时钟为上升沿时,其实也就是让移位寄存器存前一阶段的数据。RCLK为上升沿时,移位寄存器的数据存储在存储寄存器中。
时钟频率最低为5MHz,其中,SRCLK/RCLK的高低电平时间至少为时钟周期的一半,电平为2V时,SER在SRCLK上升沿前125ns必须稳定,SRCLK的上升沿必须RCLK上升沿之前的94ns。
3.3 先行设计思路
4.程序
4.1确定SRCLK的频率
取50MHz的乘除整数倍,取SRCLK的频率为12.5MHz,定义一个分频计数器,在SRCLK下降沿将数据传到DIO,上升沿DIO将数据传给驱动芯片的寄存器。计数器计数两次为1个SRCLK周期,可根据counter找到SRCLK的上升沿和下降沿。
4.2序列计数器
根据时序图与原理图,先传的SEG,后传的SEL。1ms切换了一次,人眼看不出来,实际上应该是闪烁的数字循环跳变。共32个状态,需要拉低拉高。
4.3 不同counter值干不同事
counter=0,拉低SRCLK,将SEG[7]的值传给DIO,将RCLK拉高。
counter=1,拉高SRCLK(驱动芯片自动会将DIO的数据传给寄存器),RCLK拉低。
......
counter=31,拉高SRCLK
4.4仿真
仿真的RCLK刚开始就拉高了,这是因为if后面没有跟else,直接按照每个时钟沿开始执行程序了。
正确的仿真波形:
此时代码:
module tube_active(input clk,input reset,input[7:0] SEL,input[7:0] SEG,output reg SRCLK,output reg RCLK,output reg DIO);reg[29:0] divi_counter;
reg[4:0] state_counter; //序列计数器
//确定SRCLK的频率为12.5MHz
parameter base_frequence = 50_000_000;
parameter goal_frequence = 12500_000;
parameter goal_counter = base_frequence/(goal_frequence*2)-1;//分频计数器
always@(posedge clk or negedge reset)
beginif(!reset)divi_counter <= 1'd0;else if(divi_counter == goal_counter)divi_counter <= 1'd0;elsedivi_counter <= divi_counter +1'd1;
end//序列计数器
always@(posedge clk or negedge reset)
beginif(!reset)state_counter <= 1'd0;else if(divi_counter == goal_counter)state_counter <= state_counter+1'd1;elsestate_counter <= state_counter;
end//不同counter值做不同事,下降沿将值传给DIO,上升沿将DIO的值送给驱动芯片
always@(posedge clk or negedge reset)
beginif(!reset)beginSRCLK <= 1'd0;RCLK <= 1'd0;DIO <= 1'd0;endelse begincase(state_counter)0:begin DIO <= SEG[7]; SRCLK <= 1'd0; RCLK <= 1'd1; end1:begin SRCLK <= 1'd1; RCLK <= 1'd0; end2:begin DIO <= SEG[6]; SRCLK <= 1'd0;end3:begin SRCLK <= 1'd1; end4:begin DIO <= SEG[5]; SRCLK <= 1'd0;end 5:begin SRCLK <= 1'd1; end6:begin DIO <= SEG[4]; SRCLK <= 1'd0;end7:begin SRCLK <= 1'd1; end8:begin DIO <= SEG[3]; SRCLK <= 1'd0;end 9:begin SRCLK <= 1'd1; end10:begin DIO <= SEG[2]; SRCLK <= 1'd0;end11:begin SRCLK <= 1'd1; end12:begin DIO <= SEG[1]; SRCLK <= 1'd0;end13:begin SRCLK <= 1'd1; end14:begin DIO <= SEG[0]; SRCLK <= 1'd0;end15:begin SRCLK <= 1'd1; end16:begin DIO <= SEL[7]; SRCLK <= 1'd0;end17:begin SRCLK <= 1'd1; end18:begin DIO <= SEL[6]; SRCLK <= 1'd0;end19:begin SRCLK <= 1'd1; end20:begin DIO <= SEL[5]; SRCLK <= 1'd0;end 21:begin SRCLK <= 1'd1; end22:begin DIO <= SEL[4]; SRCLK <= 1'd0;end23:begin SRCLK <= 1'd1; end24:begin DIO <= SEL[3]; SRCLK <= 1'd0;end 25:begin SRCLK <= 1'd1; end26:begin DIO <= SEL[2]; SRCLK <= 1'd0;end27:begin SRCLK <= 1'd1; end28:begin DIO <= SEL[1]; SRCLK <= 1'd0;end29:begin SRCLK <= 1'd1; end30:begin DIO <= SEL[0]; SRCLK <= 1'd0;end31:begin SRCLK <= 1'd1; endendcaseend
end
endmodule
仿真代码:
`timescale 1ns / 1nsmodule tube_active_tb();reg clk;reg reset;reg [7:0]SEL;reg [7:0]SEG;wire SRCLK;wire RCLK;wire DIO;tube_active tube_active_inst0(.clk(clk), .reset(reset), .SEL(SEL),.SEG(SEG),.SRCLK(SRCLK),.RCLK(RCLK), .DIO(DIO)
);initial clk = 1;
always #10 clk = ~clk;initial beginreset = 0;SEL = 8'b0000_0001;SEG = 8'b0101_0101;#201;reset = 1;#5000;SEL = 8'b0000_0010;SEG = 8'b1010_1010; #5000;SEL = 8'b1010_0101;SEG = 8'b0000_1101; #5000;$stop;
end
endmodule
在现有模块添加新的源文件,必须点击这里的+,左上角的会出错。新源文件调用了上面的两个模块,让这两个模块连在一起,然后引出三根线与驱动芯片连接。
module tube_shuma_test(input clk,input reset,output SRCLK,output RCLK,output DIO,input [1:0] SW
);wire[7:0] SEL,SEG; //将两个例化的端口连接起来
reg[31:0] Dis_data; //调用的并转串模块
tube_active tube_active_inst0(.clk(clk), .reset(reset), .SEL(SEL),.SEG(SEG),.SRCLK(SRCLK),.RCLK(RCLK), .DIO(DIO)
);//调用的hex8模块
Shuma_guan Shuma_guan_inst0(.clk(clk),.reset(reset),.Dis_data(Dis_data),.SEL(SEL),.SEG(SEG)
); always@(*)
begincase(SW)0:Dis_data <= 32'h01234567;1:Dis_data <= 32'h89abcdef;2:Dis_data <= 32'haaaabbbb;3:Dis_data <= 32'h66666666;endcase
endendmodule
5.实验结果
根据拨码开关的开关情况,数码管显示不同的数字。
相关文章:

FPGA学习(10)-数码管
前3节视频目的是实现显示0~F的数码管仿真,后3节是用驱动芯片驱动数码管。 目录 1.数码管显示原理 2.代码过程 2.1仿真结果 3.串行移位寄存器原理 3.1原理 编辑 3.2 数据手册 3.3 先行设计思路 4.程序 4.1确定SRCLK的频率 4.2序列计数器 4.3 不同coun…...

C++(继承)
继承的语法 继承的好处:减少重复代码 语法: class 子类 : 继承方法 父类 子类 也称为 派生类 父类 也成为 基类 继承方式 公共继承 保护继承 私有继承 结论:父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后…...
华为OD机试 - RSA加密算法(Java 2024 E卷 100分)
long n (long) Math.sqrt(num); 与long n (long) Math.floor(Math.sqrt(num)); 这两行代码的目的都是计算 num 的平方根,并将结果转换为 long 类型的整数。然而,它们在处理方式上有一些微小的差别。 long n (long) Math.sqrt(num);long n (long) M…...

分组校验在Spring中的应用详解
目录 前言1. 什么是分组校验2. 分组校验的基本原理3. 分组校验的实现步骤3.1 定义分组接口3.2 在校验项中指定分组3.3 校验时指定要校验的分组3.4 默认分组和分组的继承 4. 分组校验的优势和适用场景4.1 优势4.2 适用场景 5. 常见问题与解决方案5.1 校验未生效5.2 无法识别默认…...
torch.nn.**和torch.nn.functional.**的区别
torch.nn.** torch.nn.**是一个继承了torch.nn.Module的类,使用前必须先构造对象,然后再调用。如果直接使用则会报错 例如 a torch.randn(3,4) print(a) sigmoid torch.nn.Sigmoid() a sigmoid(a) print(a) a torch.nn.Sigmoid(a)tensor([[ 0.2462…...

Air780E基于LuatOS编程开发
Air780E基于LuatOS编程开发 Air780E开发板下载固件版本开发板刷机开发调试源码编译下载源码编译工具编译工具链 Air780E开发板 合宙通信推出的 LTE Cat.1 bis通信模块,采用移芯EC618平台,支持4G全网通, 包括的模组有: Air780E – 4G Cat.1Air780EG – …...

贪心算法-汽车加油
这道题目描述了一个汽车旅行场景,需要设计一个有效的算法来决定在哪几个加油站停车加油,以便最小化加油次数。题目给出了汽车加满油后的行驶距离n公里,以及沿途若干个加油站的位置。我们需要找出一个方案,使得汽车能够完成整个旅程…...

Qt信号和槽-->day04
Qt信号和槽 标准的信号和槽函数Qt中的槽函数Qt中的信号 connect案例 自定义信号和槽案例分析 信号槽的拓展信号连接信号案例 信号槽的两种连接方式Qt5中的处理方式Qt4中的处理方式Qt5处理信号槽重载问题案例 lambda表达式简单案例Qt中的应用 补充知识点 标准的信号和槽函数 QW…...

【Linux】为终端命令自定义快件键并弹窗提醒 设置快捷键切换网络代理(Network Proxy)Disabled/Manual 并弹窗提醒
【Linux】为终端命令自定义快件键并弹窗提醒 设置快捷键切换网络代理(Network Proxy)Disabled/Manual 并弹窗提醒 可以自定义快捷键执行终端命令,执行完毕会有弹窗提醒。下面给一个例子,设置快捷键切换网络代理(Netwo…...
十六:Spring Boot依赖 (1)-- spring-boot-starter 依赖详解
1. 简介: spring-boot-starter 是 Spring Boot 项目中的基础启动器依赖,它为开发者提供了 Spring Boot 应用所需的核心功能和自动配置 spring-boot-starter 不是一个具体的功能模块,而是一个基础的启动器。 Spring Boot 提供了一系列的 sta…...

讲讲关于SNMP与智能PDU插座
什么是SNMP 简单网络管理协议 (SNMP) 是一种应用层协议,主要用于网络管理中的设备监控和控制。通过 SNMP,网络管理员可以从管理站远程访问网络中的设备,获取设备的状态信息、配置参数,甚至控制设备的行为。SNMP 被广泛应用于 TCP/…...
在CentOS下安装RabbitMQ
在CentOS下安装RabbitMQ 在CentOS下安装RabbitMQ可以按照以下步骤进行:步骤 1: 更新系统步骤 2: 安装Erlang步骤 3: 添加RabbitMQ仓库步骤 4: 安装RabbitMQ步骤 5: 启动RabbitMQ服务步骤 6: 检查RabbitMQ状态步骤 7: 启用RabbitMQ管理插件(可选ÿ…...

前端使用Canvas实现网页电子签名(兼容移动端和PC端)
实现效果: 要使用Canvas实现移动端网页电子签名,可以按照以下步骤: 在HTML文件中创建一个Canvas元素,并设置其宽度和高度,以适配移动设备的屏幕大小。 // 创建一个canvas元素 let canvas document.createElement(&q…...
什么是OSTRPT报文?
OSTRPT(Order Status Report)是一种 EDI(电子数据交换)报文,用于在供应链管理中向采购商报告订单状态。这种报文通常由供应商发送给采购商,目的是告知订单的当前处理状态、预期交货时间、订单中的变化等信息…...

PICO+Unity MR空间锚点
官方链接:空间锚点 | PICO 开发者平台 注意:该功能只能打包成APK在PICO 4 Ultra上真机运行,无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。 在 Inspector 窗口中的 PXR_Manager (Script) 面板上,勾选…...
electron 中 contextBridge 作用
1. 安全地实现渲染进程和主进程之间的通信 在 Electron 应用中,主进程和渲染进程是相互隔离的,这是为了安全和稳定性考虑。 然而,在很多情况下,渲染进程需要访问主进程中的某些功能,例如系统级别的操作或者一些应用级…...
15分钟学 Go 第 42 天:RESTful API设计
第42天:RESTful API设计 目标:理解RESTful API的设计原则 在现代Web开发中,RESTful API(Representational State Transfer)已经成为了标准的架构风格,用于实现客户端与服务器之间的通信。通过遵循REST的设…...

如何安全的中断一个运行中的线程?
文心快码进入3.0时代, 最新发布的代码问答、编码、Debug、单测、安全智能体, 分别在开发的设计、编码、构建、测试验证全流程通过AI赋能,让效率更高、效果更好。可以通过自然语言对话,独立为你完成一项编码任务。 👉点…...
【121. 买卖股票的最佳时机】——贪心算法/动态规划
121. 买卖股票的最佳时机 一、题目难度 简单 三、题目描述 给定一个数组 prices,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获…...
LLMs之Calculate:利用大语言模型技术基于文本内容实现数字计算能力的简介、常用方法、代码实现之详细攻略
LLMs之Calculate:利用大语言模型技术基于文本内容实现数字计算能力的简介、常用方法、代码实现之详细攻略 导读:在基于大语言模型(LLM)技术实现数字计算能力的背景下,文本内容的理解和计算过程涉及多个领域的交叉技术,包括自然语言处理(NLP)、机器学习、以及数值计算。…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...