MicroPython-On-ESP8266——8x8LED点阵模块(3)使用MAX7219驱动
1. 新主角登场
手上有块8x8LED点阵屏,咱们已经了解了点阵屏的基础电路与驱动原理,并用两片74HC595锁存IC成功驱动点阵屏显示需要的图案。
MicroPython-On-ESP8266——8x8LED点阵模块(1)驱动原理
MicroPython-On-ESP8266——8x8LED点阵模块(2)使用74HC595驱动
今天咱们来试试用一片MAX7219来驱动这个LED点阵屏。
2. MAX7219介绍与驱动点阵屏原理
2.1. MAX7219驱动IC介绍
MAX7219驱动IC,官方文档:
https://datasheets.maximintegrated.com/cn/ds/MAX7219-MAX7221_cn.pdf
文档里面有引脚图和典型驱动数码管的接线示例(这就给力了)
且不论芯片如何工作起来,单单从标注的引脚和典型驱动数码管的接线图来看,要用本芯片来驱动8x8
LED,需要先建立一个基础的对照关系:
- DIG_0 ~ DIG_7,这8个引脚用来控制数码管的位码,我们驱动8x8点阵屏就对应来控制8个
行
管脚; - SEG_A ~ SEG_G,SEG_DP,这8个段码的引脚,驱动8x8点阵屏就对应来控制8个
列
管脚。
2.2. 驱动原理
这里我们不用按官方文档详细去解读,我只把核心知识点罗列出来。
2.2.1. 知识点1:这是一个串入并出的芯片
MAX7219内部有一个16位的寄存器,外部数据通过串行的方式逐个输入,按照D0 -> D1 -> D2 -> … -> D15的顺序依次填入到该16为寄存器内。
如果外部串行输入16次状态值,第一个输入的值最终被位移至D15位,第二个位移到D14,最后一个进入的状态值只位移到D0位。
串入位移的动作和最终并出的结果是通过以下管脚实现的:
引脚 | 作用 |
---|---|
DIN | 输入引脚。外部给定的电平状态,等待被移入到寄存器内部的D0位 |
CLK | 时钟引脚。初始是低电平,当时钟引脚电平向上跳变一次时,DIN输入引脚的状态值可以位移进寄存器了 |
LOAD | 锁存引脚。初始低电平。输入引脚和时钟引脚配合,完成想要位移的状态值后,本锁存引脚需要向上跳变一次,完成寄存器所有位状态的锁定,也就是具备了对外输出指定结果的能力 |
DOUT | 溢出数据引脚。状态值位移到寄存器D15后,如果还有DIN值位移进寄存器,那D15的值就溢出到DOUT引脚。这样把DOUT引脚接入到下一片MAX7219的DIN脚,就实现了多个芯片级联的效果了 |
位移逻辑图:
注:
LOAD锁存引脚进行跳变以确认最终值时,必须在两个时钟引脚跳变之间进行,否则最终值可能有误
2.2.2. 知识点2:寄存器两个字节 = 地址 + 数据
MAX7219内部16位的寄存器,被串行输入数据后,并不是直接一一对应地并出到了DIG0 ~ 7、SEGA ~ DP这16个输出管脚上的,而是内部还有一层业务转换。
寄存器数据状态如下图:
- 第1个字节的高4位用不到,所以标注了XXXX
- 第1个字节的低4位为ADDRESS地址,作为寻址位寄存器或控制寄存器使用,按官方文档共用14种类型
- 第2个字节的8位为DATA数据,不同的ADDRESS下的数据有不同的含义
2.2.4. 知识点3:ADDRESS地址位的使用方式
寄存器第一个字节的地址表我从手册上截图下来:
ADDRESS高4位用不到,所以标注成X,咱们实际使用时终归还是要传值过去的,这里我们统一高4位给0,相对于地址0我们就传0x00,后面都用该方式。
不同ADDRESS地址代表的含义和对应的DATA用法:
类型 | 寄存器地址 | 说明 | DATA用法 |
---|---|---|---|
No-Op | 0x00 | 不显示 | DATA=1 ?? |
Digit 0 ~ 7 | 0x01 ~ 0x08 | LED屏的位码 | 也就是控制哪一个行要显示 例如,我们控制的是DIG 0(0x01),也就是led屏的第1行,依次类推到第8行。 DATA的8个字节就是对应控制该行下8列led的亮灭情况。 |
Decode Mode | 0x09 | 译码模式 | DATA=0x00 不译码(我们连接LED点阵屏用这个) DATA=0x01 BCD译码只取digit 0 DATA=0x0F BCD译码取digit 0到3 DATA=0xFF BCD译码取digit 0到7 (BCD码不在我们这篇里介绍了) |
Intensity | 0x0A | 亮度 | DATA取值范围0 ~ 15,数值越大亮度越高 |
Scan Limit | 0x0B | 扫描限制 |
2.2.5. 知识点4:数据字节控制led的顺序
需要注意的是,控制顺序不是A/B/C/D/E/F/G/DP,而是DP在最高位,见下图所示。也就是说我们在控制段码时,按位串入状态值时,要按DP、A、B、C、D、E、F、G
的次序依次位移进入寄存器。
2.2.6. 知识点5:初始化模块
因为我们不是拿这模块来玩花活儿的,目的就是点亮led矩阵,所以需要在使用模块进行图案控制前,把位码控制外的其他Address都设置好,具体的有:
- 模块不测试LED,向模块发送:[0x0F, 0x00]
- 需要扫描所有位码,向模块发送:[0x0B, 0x07]
- 模块亮度,向模块发送:[0x0A, 0x07] (半亮,根据自己需要调整亮度)
- 不译码,向模块发送:[0x09, 0x00]
- 关断控制器处于显示状态,向模块发送:[0x0C, 0x01]
3. 接线电路图与实操
3.1. 接线方式说明
3.1.1. 芯片与点阵屏接线方式
根据上面我们分析MAX7219的驱动原理,位码连接到点阵屏的行引脚、段码连接到点阵屏的列引脚。
行码通过地址位输出了DIG 0 到7,对应接到LED点阵的行1到行8管脚即可;
段码部分,由于最高位是 SEG DP,所以我们接线顺序是 DP端接列1的管脚(⑬管脚),其次才是SEG A、B。。。
具体对应关系表:
管脚 | 控制点阵屏对象 | 点阵屏管脚 |
---|---|---|
DIG 0 | 行1 | ⑨ |
DIG 1 | 行2 | ⑭ |
DIG 2 | 行3 | ⑧ |
DIG 3 | 行4 | ⑫ |
DIG 4 | 行5 | ① |
DIG 5 | 行6 | ⑦ |
DIG 6 | 行7 | ② |
DIG 7 | 行8 | ⑤ |
SEG DP | 列1 | ⑬ |
SEG A | 列2 | ③ |
SEG B | 列3 | ④ |
SEG C | 列4 | ⑩ |
SEG D | 列5 | ⑥ |
SEG E | 列6 | ⑪ |
SEG F | 列7 | ⑮ |
SEG G | 列8 | ⑯ |
3.1.2. 芯片与nodemcu开发板进行串行输入的连线
管脚 | nodemcu针脚 | GPIO |
---|---|---|
DIN | D7 | Pin(13) |
CS | D8 | Pin(15) |
CLK | D5 | Pin(14) |
V+ | 3V3 | - |
GND | GND | - |
3.1.3. max7219还需连接的地方
- 两个GND需要串起来
- V+与ISET之间用一个10K电阻串接
3.2. 接线模拟与实物展示
3.2.1. 模拟接线图
3.2.2. 实物效果
有点乱,简直没眼看,这个接线接了半个多小时(汗!)
4. 取模方式与控制代码
4.1. 取模原理
线已经接好了,在实际编写代码之前,咱们还是要考虑清楚图案该怎样实际展示到led点阵屏上面。
由于我们把DIG0~7都对应到了点阵屏的行1-8管脚上,且行控制已经交由MAX7219来控制了,我们只需要关心在具体的某一行上对应的8个led我们要以怎样的组合来控制亮灭。一行上的8个led又通过了SEG系列的管脚进行了对应。
我们按照按照前面“知识点4”说明,最先送入的位是DP,就是控制第1列led灯珠的亮灭的。那么我们就直观的按照当前行的亮灭状态从左到右取值,左边是最高位,再按从左到右的顺序移入位状态给max7219芯片即可对应起来。比如:第4行,应该取模为0x81。
4.2. 实际取模
取模的软件比较多,也有专门针对8x8点阵屏的取模工具,不过我用多了PCtoLCD2002,觉得顺手就好用这个取模了。
用法不多说,配置按图标注的来即可,其实心里面想想把点阵屏刚好跟取模窗1:1对应就好。
手动建立一个8*8的图形区域,自己标注爱心图案,按照选项配置下来,生成的字模就得到了:
{0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00}
好了,下面就要进入编码部分实际控制一下看看效果了。
5. 实验效果
5.1. 实验代码
from machine import Pin
import time
# 准备数据引脚
pin_clk = Pin(14, Pin.OUT, value=1) #D5,时钟,上升跳变时数据位移锁存
pin_cs = Pin(15, Pin.OUT, value=1) #D8,上升跳变时,数据全部推入锁存
pin_din = Pin(13, Pin.OUT, value=1) #D7,待移入的数据
def write_byte(data):
"按位移入数据"
for i in range(8):
pin_clk.off()
pin_din.value(1 if ((data << i) & 0x80) else 0) # 从高位开始送数据
pin_clk.on()
def write_data(addr, data):
"向模块写入地址与数据"
pin_cs.off()
write_byte(addr)
write_byte(data)
time.sleep_us(5)
pin_cs.on()
def init_max7219():
"初始化模块"
write_data(0x0c, 0x00) #关断处于关闭状态
write_data(0x0f, 0x00) #不测试
write_data(0x0b, 0x07) #扫描所有位码
write_data(0x0a, 0x0F) #亮度0x07,半亮
write_data(0x09, 0x00) #不译码
write_data(0x0c, 0x01) #关断处于显示状态
# 定义数据与初始模块
time.sleep_ms(50)
col = [0x00, 0x66, 0x99, 0x81, 0x42, 0x24, 0x18, 0x00]
init_max7219()
# 写图案
for n in range(8):
write_data(n+1, col[n])
注意,程序的最后没有写while死循环,只刷了一次各行数据进入max7219芯片。也就是没有用到前面学习595芯片时的动态扫描机制,你可以理解为动态扫描功能由max7219内部来处理,不需要我们关心。
5.2. 刷入代码效果展示
至此,咱们的爱心终于出来了,达到了预期的目的。
如果要展示其他图案,自行按照取模部分进行配置即可。
6. 后记
前面罗里吧嗦的介绍了一堆原理分析,写得比较随意,希望不会给大家造成越看越不明白的困境吧。
其实那个MAX7219和8x8LED点阵屏,都是我从一个封装好的成品模块中拆下来的。直接用这个模块就不用像我上面接线接到吐了。
MAX7219是一种集成化的串行输入/输出共阴极显示驱动器,它连接微处理器与8位数字的7段数字LED显示,也可以连接条线图显示器或者64个独立的LED。其上包括一个片上的B型BCD编码器、多路扫描回路,段字驱动器,而且还有一个8*8的静态RAM用来存储每一个数据。只有一个外部寄存器用来设置各个LED的段电流
模块参数:
1.单个模块可以驱动一个8*8共阴点阵
2.模块工作电压:5V
3.模块带输入输出接口,支持多个模块级联
接线说明:
1.模块左边为输入端口,右边为输出端口。
2.控制单个模块时,只需要将输入端口接到CPU
3.多个模块级联时,第1个模块的输入端接CPU,输出端接第2个模块的输入端,第2个模块的输出端接第3个模块的输入端,以此类推…
6.1. 接线方式
接线以我们上面接的nodemcu开发机为例:
MAX7219点阵模块引脚 | NodeMCU管脚 |
---|---|
VCC | 3V3 |
GND | GND |
DIN | D7 (Pin13) |
CS | D8 (Pin15) |
CLK | D5 (Pin14) |
6.2. 实验效果
代码不用改,开发板直接通电看效果即可:
6.3. 其他
这个模块是支持用SPI进行数据传输的,其实我的代码里面的write_byte
和write_data
这个机制就跟SPI是差不多的了。这些内容我们后面有机会再补充学习吧。