学习笔记——数码管驱动实例(FPGA)

一、理论概念

1、数码管的分类

  • 数码管是一种半导体发光器件,其基本单元是发光二极管,常见的数码管有七段数码管(八段数码管少一个小数点位置)、八段数码管和其他类型数码管(下图中的“米”字管)如下图所示:

在这里插入图片描述

  • 本次实验使用的是八段数码管,八段数码管如下所示,是一个“8”型数码管,分为八段,对应abcdefg和小数点dp,每一段就是一个发光二极管,这样的八段我们称为段选,一位八段数码管常用十个管脚,没有一段对应一个管脚,另外两个管脚对应的是公共端(com端,内部导通)如下所示:

在这里插入图片描述

  • 八段数码管还分为共阴极数码管和共阳极数码管。对于共阴极数码管来说,八个发光二极管的阴极在数码管内部全部连接在一起,所以称为“共阴”,而阳极是独立的。而对于共阳极数码管来说,它的八个发光二极管的阳极在数码管内部是连接在一起的,而阴极是独立的,所以称为“共阳”

在这里插入图片描述

2、显示原理

  • 我们以共阳极数码管为例,下面这个表格是共阳极数码管对应的编码形式,我们的数码管可以显示数字0-9,字母A-F共16种字符,中间是对应的二级制段码,最后是对应的十六进制段码。例如,我们想要显示数字0,我们使用的是共阳极数码管,其阳极都是连在一块的,只有在每段对应的端口输入低电平,才能点亮对应的二极管,我们从数字0对应的二级制格式可以看出,abcdef这六段都是低电平,所以是点亮,所以在八字形的图中,周围六段就是点亮,g和dp是熄灭状态,所以就在数码管上显示了一个数字0.以此类推,同理,各种数字和字母都可以用这个方式将十六进制的段码写出来。(注意,当我们将二级制转换成十六进制的时候,最高位是dp最低位是a与下图中二级制段码的顺序正好相反)

在这里插入图片描述

  • 我们今天实验使用的是四位八段数码管,我们在使用多位数码管的时候,要尽量减少数码管使用的IO口,我们将段选连接在一起后引出来,而位选信号独立控制,这就是位选信号,这样我们就可以通过位选信号控制哪几个数码管点亮,而在同一时刻位选选通的数码管显示的数字都是一样的,因为他们的段选是引脚是连在一起的。现在把这个公共端连接到了FPGA 的 I/O 脚上,这便是数码管的片选信号。如果 FPGA 的这个 I/O 脚输出低电平0,那么这个数码管就能够显示数字;如果这个 I/O 输出高电平 1,那么无论数码管的 8 个段选端输出 0 还是 1,都无法将 8个发光二极管的任意一个点亮,这也达到了关闭数码管显示的效果。这样一来,这个数码管的公共端被我们当做了数码管片选引脚使用了。

二、功能概述

  • 我们这次实验的功能是让 4 个数码管每隔 1s 不断的递增计数显示,计数范围为 0-F。为了便于代码编写控制 7个用于段选(不包括小数点)的发光二极管显示不同的字符,这里只做了一个简单的对应表,把不同字符显示时的 7 个 I/O值进行编码,数码管显示字符与驱动编码映射表(共阴)如下图所示:

在这里插入图片描述

  • 本实例的功能框图如图下图所示。PLL 产生的 25MHz 时钟,分别供给两个子模块,秒计数器模块产生一个每秒递增的 16 位数据,这 16 位数据以 16 进制形式通过数码管显示驱动模块显示到数码管上。数码管显示驱动模块以分时复用的片选方式,将数据送到数码管的各个段选位上。
    在这里插入图片描述

三、代码解析

  • 从上次我们学习了FPGA的模块化设计之后,我们在之后的学习中尽量使用这个技巧,尽可能快的学习并掌握,首先第一个模块顶层模块

1、cy4.v模块代码解析

  • 顶层模块的作用就是定义接口信号以及对各个子模块进行互连。我们设计在pll_controller.v 模块中进行例化 PLL IP 核,产生 FPGA 内部其它逻辑工作所需的时钟信号 clk_25m 和复位信号 sys_rst_n;在counter.v 模块进行 1 秒的定时计数,产生数码管显示所需的每秒递增的 4 位 16 进制数据display_num;在seg7.v 模块中对需要显示到数码管的数据 display_num 进行译码,并驱动数码管显示。有了基本的逻辑之后,我们可以先看一下下图中的数码管模块互联接口,形象的将各个模块以及各个模块之间的关系表现出来了。
    在这里插入图片描述
  • 可以根据上图,依次将我们想要的信号、端口等编写到顶层模块中例程如下:
module cy4( 
				input ext_clk_25m, //外部输入 25MHz 时钟信号
				input ext_rst_n, //外部输入复位信号,低电平有效
				output[3:0] dtube_cs_n, //7 段数码管位选信号
				output[7:0] dtube_data //7 段数码管段选信号(包括小数点为 8 段)
		  );
//-------------------------------------
//PLL 例化
wire clk_12m5; //PLL 输出 12.5MHz 时钟
wire clk_25m; //PLL 输出 25MHz 时钟
wire clk_50m; //PLL 输出 50MHz 时钟
wire clk_100m; //PLL 输出 100MHz 时钟
wire sys_rst_n; //PLL 输出的 locked 信号,作为 FPGA 内部的复位信号,低电平复位,高
电平正常工作
pll_controller pll_controller_inst (
		.areset ( !ext_rst_n ),
		.inclk0 ( ext_clk_25m ),
		.c0 ( clk_12m5 ),
		.c1 ( clk_25m ),
		.c2 ( clk_50m ),
		.c3 ( clk_100m ),
		.locked ( sys_rst_n )
		);
//-------------------------------------
//25MHz 时钟进行分频,产生每秒递增的 16 位数据
wire[15:0] display_num; //数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百
位,[7:4]--数码管十位,[3:0]--数码管个位
counter  uut_counter(
			.clk(clk_25m), //时钟信号
			.rst_n(sys_rst_n), //复位信号,低电平有效
			.display_num(display_num) //LED 指示灯接口
			);
//-------------------------------------
//4 位数码管显示驱动
seg7 uut_seg7(
				.clk(clk_25m), //时钟信号
				.rst_n(sys_rst_n), //复位信号,低电平有效
				.display_num(display_num), //LED 指示灯接口
				.dtube_cs_n(dtube_cs_n),//7 段数码管位选信号
				.dtube_data(dtube_data) //7 段数码管段选信号(包括小数点为 8 段)
			 );
endmodule

2、counter.v模块代码解析

  • counter.v 模块接口定义如下,输入时钟信号 clk、复位信号 rst_n,输出递增的数码管显示数据寄存器 display_num。
module counter(
			input clk, //时钟信号,25MHz
			input rst_n, //复位信号,低电平有效
			output reg[15:0] display_num //数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
);
  • counter.v模块逻辑分为两个部分,为了解析方便,可以看清楚以及更好的理解,就一个方面一个方面的写,但是这两个方面都属于counter.v模块。
  • 第一部分如下,计数器 timer_cnt 不停递增,以 1 秒为周期循环计数,每个周期(即 1 秒)产生定时信号 timer_1s_flag,该信号每秒只有一个时钟周期为高电平。例程如下:
//-------------------------------------------------
//1s 定时产生逻辑
reg[24:0] timer_cnt; //1s 计数器,0-24999999
	//1s 定时计数
always @(posedge clk or negedge rst_n)
	if(!rst_n) timer_cnt <= 25'd0;
	else if(timer_cnt < 25'd24_999_999) timer_cnt <= timer_cnt+1'b1;
	else timer_cnt <= 25'd0;
wire timer_1s_flag = (timer_cnt == 25'd24_999_999); //1s 定时到标位,高有效一个时钟周期
  • 第二部分代码如下,秒定时信号 timer_1s_flag 拉高时,display_num 递增。
//-------------------------------------------------
//递增数据产生逻辑
//显示数据每秒递增
always @(posedge clk or negedge rst_n)
	if(!rst_n) display_num <= 16'd0;
	else if(timer_1s_flag) display_num <= display_num+1'b1;
endmodule

3、seg7.v模块代码解析

  • seg7.v 模块对数据 display_num 进行译码,驱动数码管显示。该模块的接口如下,其中 输入信号 display_num 由counter.v 模块产生,输出信号 dtube_cs_n[3:0]是数码管的位选信号, dtube_data[7:0]为数码管的段选信号。
module seg7(
		input clk, //时钟信号,25MHz
		input rst_n, //复位信号,低电平有效
		input[15:0] display_num, //数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
		output reg[3:0] dtube_cs_n, //7 段数码管位选信号
		output reg[7:0] dtube_data //7 段数码管段选信号(包括小数点为 8 段)
);
  • 以下参数定义数码管显示 0~F 对应的数码管段选输出值,即“译码”。
//参数定义
//数码管显示 0~F 对应段选输出
parameter   NUM0 = 8'h3f,//c0,
			NUM1 = 8'h06,//f9,
			NUM2 = 8'h5b,//a4,
			NUM3 = 8'h4f,//b0,
			NUM4 = 8'h66,//99,
			NUM5 = 8'h6d,//92,
			NUM6 = 8'h7d,//82,
			NUM7 = 8'h07,//F8,
			NUM8 = 8'h7f,//80,
			NUM9 = 8'h6f,//90,
			NUMA = 8'h77,//88,
			NUMB = 8'h7c,//83,
			NUMC = 8'h39,//c6,
			NUMD = 8'h5e,//a1,
			NUME = 8'h79,//86,
			NUMF = 8'h71,//8e;
			NDOT = 8'h80; //小数点显示
  • 以下参数则定义数码管不同位选单独选中的位选输出值,由于数码管的段选是 4 个位显 示复用的,因此我们采用了“4个位分时点亮”的办法来驱动数码管。实际上,4 位数码管 在同一时刻,只有 1 位是点亮的,4位轮流循环被点亮。点亮某一位时,则“译码”对应的 输出数据。虽然数码管时分时点亮的,但是当它的频率控制在一定的范围内,人眼很容易被“欺骗”,看到 4 位一直点亮的显示数据。
//数码管位选 0~3 对应输出
parameter CSN = 4'b1111,
		  CS0 = 4'b1110,
		  CS1 = 4'b1101,
		  CS2 = 4'b1011,
		  CS3 = 4'b0111;
  • 分时计数器 div_cnt 就是用来计数分频,产生 4 个不同的时间段,分别点亮 4 位的数码管,并且在单独选中某个位的数码管时current_display_num[3:0]寄存器则用于缓存相应的位要显示的数据(display_num[15:0]的其中 4 位)。
//分时显示数据控制单元
reg[3:0] current_display_num; //当前显示数据
reg[7:0] div_cnt; //分时计数器
	//分时计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) div_cnt <= 8'd0;
	else div_cnt <= div_cnt+1'b1;
//显示数据
always @(posedge clk or negedge rst_n)
	if(!rst_n) current_display_num <= 4'h0;
	else begin
		case(div_cnt)
			8'hff: current_display_num <= display_num[3:0];
			8'h3f: current_display_num <= display_num[7:4];
			8'h7f: current_display_num <= display_num[11:8];
			8'hbf: current_display_num <= display_num[15:12];
			default: ;
		endcase
	end
  • 接下来的 always 语句中进行数据段选“译码”逻辑的实现,根据显示数据 current_display_num 获得段选输出 dtube_data。
//段选数据译码
always @(posedge clk or negedge rst_n)
	if(!rst_n) dtube_data <= NUM0;
	else begin
		case(current_display_num) 
			4'h0: dtube_data <= NUM0;
			4'h1: dtube_data <= NUM1;
			4'h2: dtube_data <= NUM2;
			4'h3: dtube_data <= NUM3;
			4'h4: dtube_data <= NUM4;
			4'h5: dtube_data <= NUM5;
			4'h6: dtube_data <= NUM6;
			4'h7: dtube_data <= NUM7;
			4'h8: dtube_data <= NUM8;
			4'h9: dtube_data <= NUM9;
			4'ha: dtube_data <= NUMA;
			4'hb: dtube_data <= NUMB;
			4'hc: dtube_data <= NUMC;
			4'hd: dtube_data <= NUMD;
			4'he: dtube_data <= NUME;
			4'hf: dtube_data <= NUMF;
			default: ;
		endcase
	end
  • 最后一个 always 语句则对位选信号进行分时“开启”的处理。
//位选译码
always @(posedge clk or negedge rst_n)
	if(!rst_n) dtube_cs_n <= CSN;
	else begin
		case(div_cnt[7:6])
			2'b00: dtube_cs_n <= CS0;
			2'b01: dtube_cs_n <= CS1;
			2'b10: dtube_cs_n <= CS2;
			2'b11: dtube_cs_n <= CS3;
			default: dtube_cs_n <= CSN;
		endcase
	end
endmodule

四、实验现象

  • 我们可以看到数码管从最低位开始依次从 0 到 F(16 进制形式)不断的递增。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考链接

https://www.bilibili.com/video/BV17z411i7er?p=31
https://www.bilibili.com/video/BV17z411i7er?p=32
https://www.bilibili.com/video/BV17z411i7er?p=33
https://www.bilibili.com/video/BV17z411i7er?p=34
https://www.bilibili.com/video/BV17z411i7er?p=35

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,关于 jQuery 的事件,我可以和你分享一些学习笔记。 1. 绑定事件 在 jQuery 中,我们可以通过以下方式来绑定事件: ``` $(selector).event(function(){ // 事件处理程序 }) ``` 其中,`selector` 表示要绑定事件的元素,`event` 表示要绑定的事件类型,比如 `click`、`mouseover` 等等。事件处理程序则是在事件触发时要执行的代码块。 2. 多个事件绑定 我们可以通过 `on()` 方法来同时绑定多个事件: ``` $(selector).on({ event1: function(){ // 事件处理程序1 }, event2: function(){ // 事件处理程序2 } }) ``` 这样,当 `event1` 或 `event2` 中任意一个事件触发时,对应的处理程序都会被执行。 3. 解除事件 如果需要解除某个元素的事件处理程序,可以使用 `off()` 方法: ``` $(selector).off(event); ``` 其中,`event` 表示要解除的事件类型。如果不指定事件类型,则会解除该元素上所有的事件处理程序。 4. 事件委托 在 jQuery 中,我们可以使用事件委托来提高性能。事件委托是指将事件绑定到父元素上,而不是绑定到子元素上,然后通过事件冒泡来判断是哪个子元素触发了该事件。这样,当子元素数量较多时,只需要绑定一次事件,就可以监听到所有子元素的事件。 ``` $(selector).on(event, childSelector, function(){ // 事件处理程序 }) ``` 其中,`selector` 表示父元素,`event` 表示要绑定的事件类型,`childSelector` 表示要委托的子元素的选择器,事件处理程序则是在子元素触发事件时要执行的代码块。 以上是 jQuery 中事件的一些基本操作,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱生活的fuyao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值