一天一个设计实例-LED显示模块设计
LED显示模块设计
LED点阵模块指的是利用封装8*8的模块组合点元板形成模块,而LED模组应用中一般指两类产品:一种是用插灯或表贴封装做成的单元板,常用户外门头单红屏、户外全彩屏,室内全彩屏等;另外,用做夜间装饰的发光字串也被称为LED模组。LED点阵指用封装8*8的模块,再组合成单元板,这样的单元板称为点阵点元板,一般用于室内单色,双色显示屏用。LED点阵显示模块可显示汉字、图形、动画及英文字符等;显示方式有静态、横向滚动、垂直滚动和翻页显示等。单块模块控制驱动12块(最多可控制24块)8X8点阵,共16X48点阵(或32X48点阵),是单块MAX7219(或PS7219、HD7279、ZLG7289及8279等类似LED显示驱动模块)的12倍(或24倍)!可采用“级联”的方式组成任意点阵大显示屏。显示效果好,功耗小,且比采用MAX7219电路的成本更低。

图6‑1 LED点阵
点阵在使用的时候并不需要关心点阵式怎么工作的,因为点阵在使用时一般是配套驱动芯片(MAX7219等)使用的,只需要关注驱动芯片的工作时序即可,所以接下来详细介绍点阵驱动芯片的工作原理、时序等相关设计注意事项。
1.1.1MAX7219的简介
一、概述
MAX7219/MAX7221是一种集成化的串行输入/输出共阴极显示驱动器,它连接微处理器与8位数字的7段数字LED显示,也可以连接条线图显示器或者64个独立的LED。其上包括一个片上的B型BCD编码器、多路扫描回路,段字驱动器,而且还有一个8*8的静态RAM用来存储每一个数据。 只有一个外部寄存器用来设置各个LED的段电流。MAX7221与SPI™、 QSPI™以及 MICROWIRE™相兼容,同时它有限制回转电流的段驱动来减少EMI(电磁干扰)。
一个方便的四线串行接口可以联接所有通用的微处理器。 每个数据可以寻址在更新时不需要改写所有的显示。MAX7219/MAX7221同样允许用户对每一个数据选择编码或者不编码。
整个设备包含一个150μA的低功耗关闭模式,模拟和数字亮度控制,一个扫描限制寄存器允许用户显示1-8位数据,还有一个让所有LED发光的检测模式。
在应用时要求3V的操作电压或segment blinking,可以查阅MAX6951数据资料。
二、管脚配置

图6‑2 MAX7219 管脚配置
三、功能特点
10MHz 连续串行口;
独立的 LED 段控制;
数字的译码与非译码选择;
150μA 的低功耗关闭模式;
亮度的数字和模拟控制;
高电压中断显示;
共阴极 LED 显示驱动;
限制回转电流的段驱动来减少 EMI( MAX7221);
SPI, QSPI, MICROWIRE串行接口( MAX7221);
24 脚的 DIP 和 SO 封装。
四、典型应用电路

图6‑3 MAX7219 典型应用电路
五、管脚描述
表6‑1 MAX7219 管脚描述
|
管脚 |
名称 |
功能 |
|
1 |
DIN |
串行数据输入端口。在时钟上升沿时数据被载入内部的 16 位寄存器。 |
|
2,3,5-8,10,11 |
DIG 0–DIG7 |
八个数据驱动线路置显示器共阴极为低电平。关闭时7219 此管脚输出高电平,7221 呈现高阻抗。 |
|
4,9 |
GND |
地线(4 脚和 9 脚必须同时接地) |
|
LOAD(MAX7219) |
载入数据。连续数据的后 16 位在 LOAD 端的上升沿时被锁定。 |
12 |
|
CS(MAX7221) |
片选端。该端为低电平时串行数据被载入移位寄存器。连续数据的后 16 位在 cs 端的上升沿时被锁定。 |
|
|
13 |
CLK |
时钟序列输入端。最大速率为 10MHz.在时钟的上升沿,数据移入内部移位寄存器。下降沿时,数据从 DOUT端输出。对 MAX7221 来说,只有当 cs 端为低电平时时钟输入才有效。 |
|
14-17,20-23 |
SEGA–SEG G,DP |
7 段和小数点驱动,为显示器提供电流。当一个段驱动关闭时,7219 的此端呈低电平,7221 呈现高阻抗。 |
|
18 |
SET |
通过一个电阻连接到 VDD来提高段电流。 |
|
19 |
V+ |
正极电压输入,+5V |
|
24 |
DOUT |
串行数据输出端口,从 DIN 输入的数据在 16.5 个时钟周期后在此端有效。当使用多个 MAX7219/MAX7221时用此端方便扩展。 |
六、功能图表

图6‑4 MAX7219 功能图表
七、时序图

图6‑5 MAX7219时序图
MAX7219 的串行口和 SPI 完全兼容。MAX7219 来说,串行数据在 DIN 输入 16 位数据包,无论 LOAD 端处于何种状态,在时钟的上升沿数据均移入到内部 16 位移位寄存器。对 MAX7221 来说,无论数据输入或输出 cs 必须为低电平。然后数据在 LOAD/ cs 的上升沿被载入数据寄存器或控制寄存器。LOAD/ cs 端在第 16 个时钟的上升沿同时或之后,下个时钟上升沿之前变为高电平,否则数据将会丢失。在 DIN 端的数据传输到移位寄存器在 16.5 个时钟周期之后出现在 DOUT 端。在时钟的下降沿数据将被输出。数据位标记为 D0-D15(如表6‑2表示)。D8-D11 为寄存器地址位。D0-D7 为数据位。D12-D15 为无效位。在传输过程中,首先接收到的是 D15 位,是非常重要的一位(MSB)。
表6‑2 串行数据格式
|
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
|
× |
× |
× |
× |
地址 |
MSB 数据 LSB |
||||||||||
表6‑3列出了 14 个可寻址的数据寄存器和控制寄存器。数据寄存器由一个在片上的8×8 的双向 SRAM 来实现。它们可以直接寻址所以只要在 V+大于 2V 的情况下每个数据都可以独立的修改或保存。控制寄存器包括编码模式、显示亮度、扫描限制、关闭模式以及显示检测五个寄存器。
表6‑3 数据寄存器和控制寄存器

掉电模式
MAX7219 掉电后,扫描震荡器关闭,所有段电流源和地连接,所要数字驱动与 V+相连,所以显示熄灭。MAX 7221 除了数字驱动呈现高阻抗以外其他都与 MAX7219 一样。在数据和控制寄存器里的数据是不变的。停机模式可以节省电源,当有一个连续的警报使显示器发光时,便能离开掉电模式。为了满足掉电模式最低的工作电流,逻辑输入应该在 GND 或 V+(CMOS 的逻辑电位)。
MAX7219 和 MAX7221 可以在小于 250μs 的时间内离开掉电模式。在掉电模式下,显示驱动是可以编程的,而且在显示检测的时候不用考虑他是否在掉电模式工作。
表6‑4 MAX7219 掉电模式

八、初始状态
在初始状态下,所有的控制寄存器将被重置,显示器熄灭,MAX7219/MAX7221 进入掉电模式。对显示驱动预先编程为以后显示而用。否则它将以最初的设置来扫描每一位数据,不对数据寄存器里的数据进行扫描,显示亮度寄存器设置为最小值。
九、译码模式寄存器
用来设置对每个数据进行 B 型 BCD 译码或者不译码。寄存器中的每一位对应一个数据。逻辑高电平用来选择译码低电平取消译码。表 4 举例说明了译码控制寄存器的格式。
表6‑5 译码模式寄存器(一)

当选择译码模式时,译码器只对数据的低四位进行译码(D3-D0),D4-D6 为无效位。D7 位用来设置小数点,不受译码器的控制且为高电平。表6‑6为 B 型译码的格式。
表6‑6 B 型译码的格式

十、亮度控制
MAX7219/MAX7221通过加在V+和ISET之间的一个外部电阻来控制显示亮度。段驱动电流一般是流入ISET端电流的100倍。这个电阻可以是固定的,也可以是可变电阻,通过前面板来控制以选择合适的亮度。其最小值为9.53KΩ,它设定段电流为40mA。显示亮度也可以通过亮度寄存器来控制。
数字控制显示亮度是通过亮度寄存器的低四位来控制的脉宽调制器来控制。调制器将段电流平均分为16个阶次,最大值为由RSET设置的最大电流的31/32,最小值为电流峰值的1/32( MAX7221为15/16到1/16)。表6‑7列出了亮度寄存器的格式。最小数据熄灭时间设置为时钟周期的1/32
表6‑7 亮度寄存器的格式

十一、扫描控制寄存器
扫描控制寄存器用来设定扫描显示器的个数,从 1 个到 8 个.它们将以 800Hz 的扫描速率进行多路扫描显示。如果数据少的话,扫描速率为 8*fosc/N,N 是指需要扫描数字的个数。扫描数据的个数影响显示亮度,所以不能将扫描寄存器设置为空扫描。表6‑8列出了扫描寄存器的格式
表6‑8 扫描寄存器的格式

如果扫描寄存器被设置扫描 3 个数据或者更少,个别的数据驱动将损耗过多的能量。所以,RSET 的值必须根据显示数据的个数来确定,从而限制个别数据驱动对能里的浪费。表6‑9列出了不同个数字被扫描时所对应的最大需求段电流。
表6‑9 不同个数字被扫描时所对应的最大需求段电流

十二、显示检测寄存器
显示检测寄存器有正常和显示检测两种工作状态。显示检测状态在不改变所有其他控制和数据寄存器(包括关闭寄存器)的情况下将所有 LED 都点亮。在此状态下,8 个数据都会被扫描,工作周期为 31/32。表6‑10列出了显示检测寄存器的格式。
表6‑10 显示检测寄存器的格式

十三、不工作寄存器
当有多个MAX7219或MAX7221被串接使用时要用到不工作寄存器。把所有的芯片的LOAD/CS端联接在一起,把相邻的芯片的DOUT和DIN连接在一起。DOUT是一个CMOS逻辑电平的输出口,他可以很容易的驱动下一级的DIN口。例如,如果四个MAX7219被连接起来使用,然后向第四个芯片发送必要的16位数据,后面跟三组NO-OP代码(如表2所示,十六进制的0xXX0X)。然后使LOAD/CS端变为高电平,数据则被载入所有芯片。前三个芯片接收到 NO-OP 代码,第四个接收到有效数据。
1.1.2MAX7219的应用电路
首先看下单个点阵的电路:

图6‑6 单个点阵的应用电路
接下来是级联点阵的电路,如下:
3级联示例(多级联可依次类推)
其实也就是DOUT接到下一级的DIN, 所有的MAX7219的LOAD与CLK都共端.

图6‑7 级联点阵的应用电路
1.1.3MAX7219的工作流程
根据以上分析,得到整个点阵的工作流程:

图6‑8 MAX7219的工作流程
1.1.4取模软件的应用
下图是取模示例, 没接触过的同学可以参考一下:

图6‑9取模软件的应用
1.1.5单片LED点阵应用
根据以上分析,得到单片初始化设置的选项及对应的数值,如下:

图6‑10 MAX7219初始化设置的选项及对应的数值
对于时序方面,看着MAX7219的时序是不是很熟悉,是的,MAX7219的时序兼容SPI的时序,所以和实时时钟芯片DS1302的时序是相同的。

图6‑11 MAX7219的写操作时序图
上图是MAX7219芯片写操作的时序图。第一个字节是“访问寄存器的地址”,第二字节是“写数据”。在写操作的时候,都是“上升沿有效”,然而还有一个条件,就是 CE(/RST)信号必须拉高。(数据都是从 LSB 开始发送,亦即是最低位开始至最高位结束)。

图6‑12 MAX7219 写操作的理想时序(主机视角)(一)

图6‑13 MAX7219 写操作的理想时序(主机视角)(二)
图6‑12是写操作的理想时序图, SCLK 为串行时钟, CS 为拉高有效的片选(又名为RESET), DATA 是数据进出的 I/O。忘了说, MAX7219 由于使用 SPI 传输的关系,所以下降沿设置数据,上升沿锁存数据。
同理可以画出MAX7219的理想写操作的时序,如下:

图6‑14 MAX7219的理想写操作的时序
图6‑15 基于MAX7219 LED显示模块
代码6‑1 max7219_ctrlmod代码
|
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-19 20:55:44 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-23 03:47:46 7.//# Description: 8.//# @Modification History: 2019-05-19 20:58:05 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 20:58:05 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module max7219_ctrlmod 17.( 18. input CLOCK, RST_n, 19. input iCall, 20. output oDone, 21. input [63:0]iData, 22. output oCall, 23. input iDone, 24. output MAX7219_CS, MAX7219_SCLK, 25. output MAX7219_DATA, 26. output [7:0]oDATA0, oDATA1 27.); 28.//wire [7:0]oDATA0, oDATA1; 29.//wire oCall,iDone; 30.//wire oDone; 31. 32.// reg [63:0]iData; 33. reg [7:0]D1,D2; 34. reg [7:0]i; 35. reg isCall; 36. reg isDone; 37. always @ ( posedge CLOCK or negedge RST_n ) 38. if( !RST_n ) 39. begin 40. D1 <= 8'd0; 41. D2 <= 8'd0; 42. i <= 8'd0; 43. //iData <= 64'd0; 44. end 45. else if ( iCall ) 46. case( i ) 47.//************************初始化操作*********************************// 48. 0 : 49. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h09; D2 = 8'h00; end 50. else begin isCall <= 1'b1; end 51. 52. 1 : 53. begin isDone <= 1'b1; i <= i + 1'b1; end 54. 55. 2 : 56. begin isDone <= 1'b0; i <= i + 1'b1; end//译码方式:BCD码 57. 58. 3 : 59. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h0a; D2 = 8'h03; end 60. else begin isCall <= 1'b1; end 61. 62. 4 : 63. begin isDone <= 1'b1; i <= i + 1'b1; end 64. 65. 5 : 66. begin isDone <= 1'b0; i <= i + 1'b1; end//亮度 67. 68. 6 : 69. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h0b; D2 = 8'h07; end 70. else begin isCall <= 1'b1; end 71. 72. 7 : 73. begin isDone <= 1'b1; i <= i + 1'b1; end 74. 75. 8 : 76. begin isDone <= 1'b0; i <= i + 1'b1; end//扫描界限;8个数码管显示 77. 78. 9 : 79. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h0c; D2 = 8'h01; end 80. else begin isCall <= 1'b1; end 81. 82. 10 : 83. begin isDone <= 1'b1; i <= i + 1'b1; end 84. 85. 11 : 86. begin isDone <= 1'b0; i <= i + 1'b1; end//掉电模式:0,普通模式:1 87. 88. 12 : 89. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h0f; D2 = 8'h00; end 90. else begin isCall <= 1'b1; end 91. 92. 13 : 93. begin isDone <= 1'b1; i <= i + 1'b1; end 94. 95. 14 : 96. begin isDone <= 1'b0; i <= i + 1'b1; end//显示测试:1;测试结束,正常显示:0 97.//************************初始化操作结束******************************// 98.//************************显示第一位*********************************// 99. 15 : 100. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 =8'h01; D2 = iData[7:0]; end 101. else begin isCall <= 1'b1; end 102. 103. 16 : 104. begin isDone <= 1'b1; i <= i + 1'b1; end 105. 106. 17 : 107. begin isDone <= 1'b0; i <= i + 1'b1; end 108. 109. 18 : 110. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h02; D2 = iData[15:8]; end 111. else begin isCall <= 1'b1; end 112. 113. 19 : 114. begin isDone <= 1'b1; i <= i + 1'b1; end 115. 116. 20 : 117. begin isDone <= 1'b0; i <= i + 1'b1; end 118. 119. 21 : 120. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h03; D2 = iData[23:16]; end 121. else begin isCall <= 1'b1; end 122. 123. 22 : 124. begin isDone <= 1'b1; i <= i + 1'b1; end 125. 126. 23 : 127. begin isDone <= 1'b0; i <= i + 1'b1; end 128. 129. 24 : 130. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h04; D2 =iData[31:24]; end 131. else begin isCall <= 1'b1; end 132. 133. 25 : 134. begin isDone <= 1'b1; i <= i + 1'b1; end 135. 136. 26 : 137. begin isDone <= 1'b0; i <= i + 1'b1; end 138. 139. 27 : 140. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h05; D2 =iData[39:32]; end 141. else begin isCall <= 1'b1; end 142. 143. 28 : 144. begin isDone <= 1'b1; i <= i + 1'b1; end 145. 146. 29 : 147. begin isDone <= 1'b0; i <= i + 1'b1; end 148. 149. 30 : 150. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h06; D2 =iData[47:40]; end 151. else begin isCall <= 1'b1; end 152. 153. 31 : 154. begin isDone <= 1'b1; i <= i + 1'b1; end 155. 156. 32 : 157. begin isDone <= 1'b0; i <= i + 1'b1; end 158. 159. 33 : 160. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h07; D2 =iData[55:48]; end 161. else begin isCall <= 1'b1; end 162. 163. 34 : 164. begin isDone <= 1'b1; i <= i + 1'b1; end 165. 166. 35 : 167. begin isDone <= 1'b0; i <= i + 1'b1; end 168. 169. 36 : 170. if( iDone ) begin isCall <= 1'b0; i <= i + 1'b1;D1 = 8'h08; D2 =iData[63:56]; end 171. else begin isCall <= 1'b1; end 172. 173. 37 : 174. begin isDone <= 1'b1; i <= i + 1'b1; end 175. 176. 38 : 177. begin isDone <= 1'b0; i <= i + 1'b1; end 178. 179. 39: 180. i <= 8'd15; 181. endcase 182. 183. 184. assign oDone = isDone; 185. assign oCall = isCall; 186. assign oDATA0 = D1; 187. assign oDATA1 = D2; 188. 189.endmodule |
代码6‑2 max7219_funcmod
|
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-19 21:06:32 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-23 00:59:41 7.//# Description: 8.//# @Modification History: 2019-05-19 20:58:19 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 20:58:19 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module max7219_funcmod 17.( 18. input CLOCK, RST_n, 19. output MAX7219_CS,MAX7219_SCLK, 20. output MAX7219_DATA, 21. input iCall, 22. output oDone, 23. input [7:0]iDATA0,iDATA1 24. 25.); 26. parameter FCLK = 6'd25, FHALF = 6'd12; // 2Mhz,(1/2Mhz)/(1/50Mhz) FCLK 为一个周期 27. parameter FF_Write = 6'd16, FF_Read = 6'd32;//FHALF 为半周期 28. 29. reg [5:0]C1; 30. reg [5:0]i,Go; 31. reg [7:0]D1,T; //D1 为暂存读取结果 T 为伪函数的操作空间 32. reg rCS, rSCLK, rSIO; 33. reg isQ,isDone; //isQ 为 IO 的控制输出 34. 35. always @ ( posedge CLOCK or negedge RST_n ) 36. if( !RST_n ) 37. begin 38. C1 <= 6'd0; 39. { i,Go } <= { 6'd0,6'd0 }; 40. { D1,T } <= { 8'd0,8'd0 }; 41. { rCS, rSCLK, rSIO } <= 3'b000; 42. { isQ, isDone } <= 2'b00; 43. end 44./*********************************************************************** 45.下面步骤是写一个字节的伪函数。步骤 0 拉高片选,准备访问字节,并且进入伪函数。 46.步骤 1 准备写入数据并且进入伪函数。 47.步骤 2 拉低片选,步骤 3~4 则是用来产生完成信号。 48.***********************************************************************/ 49. else if( iCall ) 50. case( i ) 51. 52. 0: 53. begin { rCS,rSCLK } <= 2'b00; T <= iDATA0; i <= FF_Write; Go <= i + 1'b1; end 54. 55. 1: 56. begin T <= iDATA1; i <= FF_Write; Go <= i + 1'b1; end 57. 58. 2: 59. begin { rCS,rSCLK } <= 2'b10; i <= i + 1'b1; end 60. 61. 3: 62. begin isDone <= 1'b1; i <= i + 1'b1; end 63. 64. 4: 65. begin isDone <= 1'b0; i <= 6'd0; end 66. 67. /******************/ 68. 69. 16,17,18,19,20,21,22,23: 70. begin 71. isQ = 1'b1; 72. rSIO <= T[23-i];//i-16 73. 74. if( C1 == 0 ) rSCLK <= 1'b0; 75. else if( C1 == FHALF ) rSCLK <= 1'b1; 76. 77. if( C1 == FCLK -1) begin C1 <= 6'd0; i <= i + 1'b1; end 78. else C1 <= C1 + 1'b1; 79. end 80. 81. 24: 82. i <= Go; 83. 84. endcase 85. 86./*********************************************************************** 87.以下内容为相关输出驱动声明,其中 rSIO 驱动 MAX7219_DATA, D 驱动 oData 。 88.***********************************************************************/ 89. assign { MAX7219_CS,MAX7219_SCLK } = { rCS,rSCLK }; 90. assign MAX7219_DATA = rSIO ; //isQ ? rSIO : 1'bz; 91. assign oDone = isDone; 92. 93.endmodule |
编译后将程序下载到板子上,对应点阵就会滚动显示相关设置相关的数据。

