数码管的驱动实验在实验七有干过了是吗,但是驱动和封装时不同一回事(5.1章说过了)。在实验七中,我曾经针对“十进制”“两位数码管”,去完成驱动的设计,但是在建模的过程中,我也付出许多昂贵的逻辑资源,因为在设计中涉及了“求余和除法”的数学符号。
实验七也仅是当当针对“两位数码管”而已,如果换做是“六位数码管”的封装动作,实验七的设计思路当然是行不通的。所以在这一章的实验,我们要充分的使用Verilog HDL语言强大的“位操作”来简化数码管的封装工作。
数码管位数 | 6位 |
每一位停留时间 | 1 ms |
一次性扫描时间 | 6 ms |
扫描频率 | 166.67 Hz |
(具体的内容请回顾3.1章)
上表是封装数码管所要求的属性。下面是封装的“图形”。
在上图的组合模块数码管接口smg_interface.v中,输入信号 Number_Sig 占了24位宽,然而Number_Sig 的位分配如下表:
Number_Sig[23:0] | 位代表(从左边数起) |
Number_Sig[23:20] | 第一位 数字 | 数码管 |
Number_Sig[19:16] | 第二位 数字 | 数码管 |
Number_Sig[15:12] | 第三位 数字 | 数码管 |
Number_Sig[11:8] | 第四位 数字 | 数码管 |
Number_Sig[7:4] | 第五位 数字 | 数码管 |
Number_Sig[3:0] | 第六位 数字 | 数码管 |
为什么每一位数字|数码管,都用4位位宽来代表呢?其实这个idea是来至ds1302芯片。
我们知道每数码管可以支持显示0~F,正是因为如此,如果我们用每4位位宽来代表某一个数码管显示的信号,那么我们就可以“免得使用除法或者求余运算符”。一来方便设计,而来减少资源。
举个例子:24'h123456, 亦即0001 0010 0011 0100 0101 0110。
smg_encode_module.v在这里的功能就是就是将数字 0~F加码为数码管码 。然而,比较特别的是,smg_control_module.v 和 smg_scan_module.v 有并行操作的性质。smg_interface.v 的大致操作如下:
假设我往 Number_Sig 输入 24'h123456
在T1,smg_control_module.v 会将 Number_Sig[23:20] 送往至 smg_encode_module.v加码并且送往数码管。在同一时间smg_scan_module.v 会扫描第一位数码管(使能)。
在T2,smg_control_module.v 会将 Number_Sig[19:16] 送往至 smg_encode_module.v加码并且送往数码管,在同一时间smg_scan_module.v 会扫描第二位数码管(使能)。
在T3,smg_control_module.v 会将 Number_Sig[15:12] 送往至 smg_encode_module.v加码并且送往数码管,在同一时间smg_scan_module.v 会扫描第三位数码管(使能)。
在T4,smg_control_module.v 会将 Number_Sig[11:8] 送往至 smg_encode_module.v加码并且送往数码管,在同一时间smg_scan_module.v 会扫描第四位数码管(使能)。
在T5,smg_control_module.v 会将 Number_Sig[7:4] 送往至 smg_encode_module.v加码并且送往数码管,在同一时间smg_scan_module.v 会扫描第五位数码管(使能)。
在T6,smg_control_module.v 会将 Number_Sig[3:0] 送往至 smg_encode_module.v加码并且送往数码管,在同一时间smg_scan_module.v 会扫描第六位数码管(使能)。
在T1的时候第一位数码管会显示1。在T2的时候第二位数码管会显示2,其他的依此类推。最后在T6的时候,第六位数码管会显示6。
就这样一次性的扫描(六位数码管全扫描)就完成。啊,别忘了!每位数码管扫描停留的时间(使能的时间)大约是1ms。所以一次性扫描所需要的时间大约是6ms,亦即在每一秒内,数码管会扫描166次左右。
smg_interface.v 具体的操作还是直接看源码吧。
在11行是1ms的常量,第15~23行则是 1ms 的定时器。第27~63行是该控制模块的核心部分。rNumber是每一位数字的暂存器(28行)用来驱动 Number_Data(67行)。(39~61行)每隔1ms 该控制模块就会将不同位的数字往 Number_Data 输出。
smg_encode_module.v 和实验七相比更简化了许多。
同样和实验七相比,数码管扫描模块没有了“行扫描”的概念,而是简化至仅有“列扫描”。第10行是1ms的常量申明,在14~22行是1ms的定时器。该模块和smg_control_module.v 一样,都是每隔1ms都有一个动作。smg_scan_module.v 每隔1ms就会使能不同的拉(38~60行)。然而数码管实际的扫描顺序是自左向右。在位操作上,亦即,逻辑0从最高位到最低位移位。
这个实验也没有什么特别的,最重要还是得搞懂 Number_Sig 的位分配。
完成扩展图:
实验十五和实验十四不同的是,实验十四的 key_interface.v 我们要考虑如何调用它的输出,然而实验十五的 smg_interface.v 我们则要考虑输入调用它的输入。
上图是实验十五演示要实现的“图形”。演示中会建立一个名为 demo_control_module.v
输出24'h000000 ~ 24'h999999 用来驱动 smg_interface.v 的输入。具体的内容还是直接看代码:
第8~24行之间,包含了100ms定时的常量(10行)和100ms的定时器(14~22行)。
在26~74就是该模块的核心部分。寄存器rNum 是用于24'h000000 位处理(27),然而rNumber是用于驱动 Number_Sig(78行)。每隔100ms 的定时都会是 rNum 递增(41行),43~69行之间就会执行“4位宽”数字之间的“进位操作”。
我们假设一个情况,当rNum的值是24'h000009,然后在下一个100ms的定时钟,rNum的值就会 +1 操作。在44行,if条件就会成立 rNum[3:0]就会被赋值位零,然后rNum[7:4]就会执行 +1 操作,rNum的值成为 24'h000010。接下来的几个步骤也会执行类似的操作。
在67~69行表示了当rNum的值超过 24'h999999的时候,就会恢复为 24'h000000。
在这里我们有一个问题?为什么不使用 rNum 驱动 Number_Sig 而是选择使用 rNumber寄存器来驱动 Number_Sig。如果我们把 rNum 当着 Number_Sig 的驱动对象,在i步骤 1~7之间,由于“进位操作”的关系,会使得 Number_Sig 的输出产生许多毛刺。因为如此,才使用而外的寄存器,当rNum完成“进位操作”以后,再赋值与rNumber,由rNumber驱动 Number_Sig ( 72行)。
在同一个时间 i步骤会被复原,以期待另一次操作的开始。
上面的内容就是组合模块 smg_interface _demo.v ,基本上和“图形”是一样的。自己看着办吧。
这个演示说明了如何调用 smg_interface.v 的输入,而且还说明了, 善用位操作会简化设计和建模。
完成的扩展图:
数码管接口的调用演示。
文章评论(0条评论)
登录后参与讨论