FPGA和外围接口-第一章 爱上FPGA(1.8 FPGA和数码管)(二)

2020-06-30 10:19:04 浏览数 (1)

1.1.2 FPGA和数码管驱动

使用动态扫描还是静态扫描其实取决于硬件设计,并不取决于驱动程序,一旦硬件确定下来那么就需要驱动去适配硬件,本次设计使用的数码管为共阳极数码管,动态扫描加载,电路如下:

图1‑97动态数码管设计电路

表1‑39数码管设计资源

设计的资源如表1‑39所示,其中扫描频率是1s扫描的频率,即1/6ms≈166.67Hz。

数码管主要考虑输入调用它的输入。即如何充分减少资源以实现数码管动态显示。

图1‑98数码管电路设计建模图

表 1‑40建模图中模块间信号定义

整个系统设计结构如图1‑98所示,上图的组合模块-数码管接口 smg_interface.v 中,输入信号 Number_Sig 占了 24 位宽,然而 Number_Sig 的位分配如下表:

表1‑42 Number_Sig的位分配表

整个系统设计结构如图1‑98所示,上图的组合模块-数码管接口smg_interface.v 中,输入信号 Number_Sig 占了 24 位宽,然而Number_Sig 的位分配如下表:

为什么每一位数字数码管,都用 4 位位宽来代表呢?

每数码管可以支持显示 0~F,正是因为如此,如果我们用每 4 位位宽来代表某一个数码管显示的信号,那么可以避免“使用除法或者求余运算符执行十进制的取位操作”。一来方便设计,而来减少资源。

举个例子: 24'h123456, 亦即 00010010 0011 0100 0101 0110。

smg_encode_module.v 在这里的功能就是就是将数字 0~F 加码为数码管码。然而,比较特别的是, smg_control_module.v 和 smg_scan_module.v 有并行操作的性质。

smg_interface.v 的大致操作如下:

在 T1 的时候第一位数码管会显示1。在 T2 的时候第二位数码管会显示 2,其他的依此类推。最后在 T6 的时候,第六位数码管会显示 6。就这样一次性的扫描(六位数码管全扫描)就完成。啊,别忘了!每位数码管扫描停留的时间(使能的时间)大约是 1ms。所以一次性扫描所需要的时间大约是6ms,亦即在每一秒内,一组 6 位的数码管会扫描 166 次左右。

图1‑100数码管控制模块结构图

代码1‑13数码管控制模块(smg_control_module)代码

rNumber 是每一位数字的暂存器(43 行)用来驱动Number_Data(82 行)。(52~78 行)每隔 1ms该控制模块就会将不同位的数字往 Number_Data 输出

图1‑101数码管加码模块结构图

代码 1‑14数码管加码模块(smg_encode_module)代码

第 26~31 行声明了 SMG 码的常量。第 43~62 行是针对“十位数”的加码操作,但是是针对“个位数”(58 行)。第66 行是输出。

代码 1‑15数码管扫描模块(smg_scan_module)代码

//****************************************************************************// 2. //# @Author: 碎碎思 3. //# @Date: 2019-05-18 23:59:39 4. //# @Last Modified by: zlk 5. //# @WeChat Official Account: OpenFPGA 6. //# @Last Modified time: 2019-07-21 03:37:17 7. //# Description: 8. //# @Modification History: 2019-05-19 01:41:56 9. //# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 01:41:56 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module smg_scan_module 17.( 18. input CLOCK, 19. input RST_n, 20. output [5:0]Scan_Sig 21.); 22. 23. /*****************************/ 24. 25. parameter T1MS = 16'd49999; 26. 27. /*****************************/ 28. 29. reg [15:0]C1; 30. 31. always @ ( posedge CLOCK or negedge RST_n ) 32. if( !RST_n ) 33. C1 <= 16'd0; 34. else if( C1 == T1MS ) 35. C1 <= 16'd0; 36. else 37. C1 <= C1 1'b1; 38. 39. /*******************************/ 40. 41. reg [3:0]i; 42. reg [5:0]rScan; 43. 44. always @ ( posedge CLOCK or negedge RST_n ) 45. if( !RST_n ) 46. begin 47. i <= 4'd0; 48. rScan <= 6'b100_000; 49. end 50. else 51. case( i ) 52. 53. 0: 54. if( C1 == T1MS ) i <= i 1'b1; 55. else rScan <= 6'b011_111; 56. 57. 1: 58. if( C1 == T1MS ) i <= i 1'b1; 59. else rScan <= 6'b101_111; 60. 61. 2: 62. if( C1 == T1MS ) i <= i 1'b1; 63. else rScan <= 6'b110_111; 64. 65. 3: 66. if( C1 == T1MS ) i <= i 1'b1; 67. else rScan <= 6'b111_011; 68. 69. 4: 70. if( C1 == T1MS ) i <= i 1'b1; 71. else rScan <= 6'b111_101; 72. 73. 5: 74. if( C1 == T1MS ) i <= 4'd0; 75. else rScan <= 6'b111_110; 76. 77. 78. endcase 79. 80. /******************************/ 81. 82. assign Scan_Sig = rScan; 83. 84. /******************************/ 85. 86. 87.endmodule

第 25 行是 1ms 的常量声明,在 31~37 行是 1ms 的定时器。该模块和smg_control_module.v一样,都是每隔 1ms 都有一个动作。smg_scan_module.v 每隔 1ms就会使能不同的数码管(56~78行)。然而数码管实际的扫描顺序是自左向右。在位操作的角度上,逻辑 0 从最高位到最低位交替移位。

接下来将上诉模块进行封装,详细的连接图见图1‑98,综合后的RTL电路图如下:

图1‑103数码管电路综合后的RTL图

结果和图1‑98一样。

下面进行模块的调用和验证,验证结构图如下:

图1‑104数码管电路验证结构图

验证中会建立一个名为demo_control_module.v输出 24'h000000 ~ 24'h999999 用来驱动smg_interface.v 的输入。具体的内容还是直接看代码:

代码 1‑16数码管电路验证代码

//****************************************************************************// 2. //# @Author: 碎碎思 3. //# @Date: 2019-05-18 23:59:39 4. //# @Last Modified by: zlk 5. //# @WeChat Official Account: OpenFPGA 6. //# @Last Modified time: 2019-07-20 21:04:39 7. //# Description: 8. //# @Modification History: 2019-07-20 21:04:39 9. //# Date By Version Change Description: 10. //# ========================================================================= # 11. //# 2019-07-20 21:04:39 12. //# ========================================================================= # 13. //# | | # 14. //# | OpenFPGA | # 15. //****************************************************************************// 16. module demo_control_module 17. ( 18. input CLOCK, 19. input RST_n, 20. output [23:0]Number_Sig 21. ); 22. 23. /******************************/ 24. 25. parameter T100MS = 23'd4_999_999; 26. 27. /******************************/ 28. 29. reg [22:0]C1; 30. 31. always @ ( posedge CLOCK or negedge RST_n ) 32. if( !RST_n ) 33. C1 <= 23'd0; 34. else if( C1 == T100MS ) 35. C1 <= 23'd0; 36. else 37. C1 <= C1 1'b1; 38. 39. /*******************************************************/ 40. 41. reg [3:0]i; 42. reg [23:0]rNum; 43. reg [23:0]rNumber; 44. 45. always @ ( posedge CLOCK or negedge RST_n ) 46. if( !RST_n ) 47. begin 48. i <= 4'd0; 49. rNum <= 24'd0; 50. rNumber <= 24'd0; 51. end 52. else 53. case( i ) 54. 55. 0: 56. if( C1 == T100MS ) begin rNum[3:0] <= rNum[3:0] 1'b1; i <= i 1'b1; end 57. 58. 1: 59. if( rNum[3:0] > 4'd14 ) begin rNum[7:4] <= rNum[7:4] 1'b1; rNum[3:0] <= 4'd0; i <= i 1'b1; end 60. else i <= i 1'b1; 61. 62. 2: 63. if( rNum[7:4] > 4'd14 ) begin rNum[11:8] <= rNum[11:8] 1'b1; rNum[7:4] <= 4'd0; i <= i 1'b1; end 64. else i <= i 1'b1; 65. 66. 3: 67. if( rNum[11:8] > 4'd14 ) begin rNum[15:12] <= rNum[15:12] 1'b1; rNum[11:8] <= 4'd0; i <= i 1'b1; end 68. else i <= i 1'b1; 69. 70. 4: 71. if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] 1'b1; rNum[15:12] <= 4'd0; i <= i 1'b1; end 72. else i <= i 1'b1; 73. 74. 5: 75. if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] 1'b1; rNum[15:12] <= 4'd0; i <= i 1'b1; end 76. else i <= i 1'b1; 77. 78. 6: 79. if( rNum[19:16] > 4'd14 ) begin rNum[23:20] <= rNum[23:20] 1'b1; rNum[19:16] <= 4'd0; end 80. else i <= i 1'b1; 81. 82. 7: 83. if( rNum[23:20] > 4'd14 ) begin rNum <= 24'd0; i <= i 1'b1; end 84. else i <= i 1'b1; 85. 86. 8: 87. begin rNumber <= rNum; i <= 4'd0; end 88. 89. endcase 90. 91. /*******************************************************/ 92. 93. assign Number_Sig = rNumber; 94. 95. /*******************************************************/ 96. 97. endmodule

第 20~37 行之间,包含了100ms 定时的常量(25 行)和 100ms 的定时器(31~37行)。

在 41~89 就是该模块的核心部分。寄存器rNum 操作空间(42 行),然而 rNumber 是用于驱动Number_Sig(93 行)。每隔 100ms 的定时都会是rNum 递增(56 行), 58~84行之间就会执行“4 位宽”数字之间的“进位操作”。

我们假设一个情况,当rNum 的值是 24'h000009,然后在下一个100ms 的定时钟, rNum的值就会 1 操作。在59行, if条件就会成立,rNum[3:0]就会被赋值位零,然后rNum[7:4]就会执行 1 操作,rNum 的值成为 24'h000010。接下来的几个步骤也会执行类似的操作。

在 72~84 行表示了当 rNum 的值超过24'h999999 的时候,就会恢复为 24'h000000。

在这里我们有一个问题?为什么不直接使用rNum 驱动 Number_Sig 而是选择使用rNumber寄存器来驱动Number_Sig。如果我们把 rNum 当着 Number_Sig 的驱动对象,在 i 步骤 16~22 之间,由于“进位操作”的关系,会使得Number_Sig 的输出产生许多毛刺,因此才使用 rNumber 驱动Number_Sig。当 rNum 完成“进位操作”以后,再赋值与rNumber,由 rNumber 驱动 Number_Sig ( 87行)。

然后按照图1‑104进行顶层模块的设计,代码如下:

代码 1‑17数码管电路验证代码的顶层代码

//****************************************************************************// 2. //# @Author: 碎碎思 3. //# @Date: 2019-05-18 23:59:39 4. //# @Last Modified by: zlk 5. //# @WeChat Official Account: OpenFPGA 6. //# @Last Modified time: 2019-07-21 03:49:46 7. //# Description: 8. //# @Modification History: 2019-05-19 01:41:52 9. //# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 01:41:52 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module smg_interface_demo 17.( 18. input CLOCK, 19. input RST_n, 20. output [7:0]SMG_Data, 21. output [5:0]Scan_Sig 22.); 23. 24. /******************************/ 25. 26. wire [23:0]Number_Sig; 27. 28. demo_control_module U1 29. ( 30. .CLOCK( CLOCK ), 31. .RST_n( RST_n ), 32. .Number_Sig( Number_Sig ) // output - to U2 33. ); 34. 35. /******************************/ 36. 37. smg_interface U2 38. ( 39. .CLOCK( CLOCK ), 40. .RST_n( RST_n ), 41. .Number_Sig( Number_Sig ), // input - from U1 42. .SMG_Data( SMG_Data ), // output - to top 43. .Scan_Sig( Scan_Sig ) // output - to top 44. ); 45. 46. /******************************/ 47. 48.endmodule

综合后的RTL如下:

图1‑105数码管电路验证代码的顶层代码综合后RTL图

基本和图1‑104一样,将代码下载到目标板上就可以看到数码管已经变成了一个“秒表”了。

代码地址

github

你笑一次,我就可以高兴好几天;可看你哭一次,我就难过了好几年...

https://github.com/suisuisi/FPGA/tree/master/FPGAandPI

0 人点赞