MicroPython-On-ESP8266——8x8LED点阵模块(2)使用74HC595驱动
1. 使用74HC595驱动的原理
1.1. 基础回顾
上篇我们学习了8x8LED点阵屏的电路基础知识和驱动的原理,见
8x8LED点阵模块(1)驱动原理
里面也提到了,咱们nodemcu开发板的GPIO引脚不够,只能借助74HC595、MAX7219此类IC来完成点阵屏的驱动。这里咱们先从74HC595开始来实验。
数码管的使用,2片HC595驱动四位数码管
595芯片的基础原理前面文章也有做过介绍,这里还是再笼统回顾一下。
1.2. 74HC595如何使用
595输入端只需要3个GPIO,却可以输出8个GPIO状态,其实咱们就理解为把GPIO管脚扩充了。
注:
1、这个动图不是我做的,也不清楚来源,但确实很形象。如有侵权请私信通知删除,感谢原作者!
2、图上有个错误的地方,大家要留意。DS串行口推入数据的次序是Q0 -> Q1 -> Q2 -> Q3 -> Q4 -> Q5 -> Q6 -> Q7 -> Q7S(溢出位) 。如果只送了8位过去,第一个移入的位状态最终会移到Q7,最后一个移入的状态位最终停留在Q0。也就是先送高位。上图锁存器对应的引脚顺序要反向过来才对。
实际使用时,时钟引脚每跳变(先变成高电平再变回低电平)一次,就把输入数据引脚DS的状态送入位移寄存器,想送入多少个随自己定。送完位移数据后,让锁存引脚跳变一下,把位移寄存器的值整体推入锁存器,从而让输出GPIO能保持住想要的状态。
仔细品味一下上面的动图,一定要理解串入并出是怎么实现的。^_^
1.3. 多个74HC595级联使用
既然一个595能有8个GPIO输出,那咱们要控制8x8LED点阵的16个引脚,就只需要两片595就行了。595还能无限级联实现串行,本片先入先出的状态值会经过Q0位移到Q7再溢出到Q7S管脚,把Q7S接入到下一片595的DS输入数据引脚作为输入端,就能实现级联了。
不管级联了多少片,把每片的时钟引脚和锁存引脚都短接在一起,这样咱们在开发板上指定两个GPIO引脚统一进行位移跳变和锁存跳变。
也就是说按照上图来说我想要一次性控制4片75HC595的输出状态,相对于在最左侧给DS(SER)引脚要输入4*8=32个状态位,每一个状态位输入完中间进行一下位移引脚跳变,所有状态位向右移动一次。
- 第一个状态位,经历过32次位移后,电平状态移动到了第4片74HC595的QH(Q7)位移寄存器
- 第二个状态位,经历过32-1次位移后,电平状态移动到了第4片74HC595的QG(Q6)位移寄存器
- 依次。。。
- 最后一个(第32个)状态位,位移1次,输出到了第1片74HC595的QA(Q0)位移寄存器
大家排队往前走,32个小伙伴都就位后,锁存引脚跳变一下。大家集体上位进入对应的锁存寄存器,
2. 74HC595实操接线
基本电路图:
电路图比较简洁,因为把led的16个管脚用标签的形式来对应了,没有直接进行连线,要不然会交叉的很厉害,没法看。
锁存GPIO位与点阵管脚连接顺序表:
锁存IC | 管脚 | 控制对象 | 点阵屏管脚 |
---|---|---|---|
U1 | QA | 行1 | ⑨ |
U1 | QB | 行2 | ⑭ |
U1 | QC | 行3 | ⑧ |
U1 | QD | 行4 | ⑫ |
U1 | QE | 行5 | ① |
U1 | QF | 行6 | ⑦ |
U1 | QG | 行7 | ② |
U1 | QH | 行8 | ⑤ |
U2 | QA | l列1 | ⑬ |
U2 | QB | 列2 | ③ |
U2 | QC | 列3 | ④ |
U2 | QD | 列4 | ⑩ |
U2 | QE | 列5 | ⑥ |
U2 | QF | 列6 | ⑪ |
U2 | QG | 列7 | ⑮ |
U2 | QH | 列8 | ⑯ |
来个模拟连接图:
实际的接线图有点乱,下面是我接的,光接线就搞了好久(欲哭无泪):
这里接线一定要有耐心,接完还要反复检查确保没有接错。
3. 代码逻辑
3.1. 图案取模
取模这里在上篇介绍点阵屏原理时已经介绍过了,这里只吧图放过来回顾。
3.2. 代码编写
关键点:
1). 连线方式是QA(Q0)连到了行/列1,QH(Q7)连接到了行/列8。
2). 取模方式是逆向(低位在前)
3). 前面说了向74HC595位移数据时,高位在先,也就是第1个移入的数据最终写入QH(Q7)
4). 也就是说控制74HC595寄存器时,我们取模出来的数据是要从低位到高位依次送入位移寄存器
有点绕,我也不知道有没有解释清楚,自己实操体会吧。。。 =_=!
看代码吧,基本上都配了注释,外面就不再过多解释了。
from machine import Pin
# 准备数据引脚
pin_sclk = Pin(4, Pin.OUT); pin_sclk.off() # D2,时钟,上升跳变时数据位移锁存
pin_rlck = Pin(0, Pin.OUT); pin_rlck.off() # D3,上升跳变时,数据全部推入锁存
pin_dio = Pin(2, Pin.OUT); pin_dio.off() # D4,待移入的数据
# 行引脚控制
row = [
0x7F, # 01111111
0xBF, # 10111111
0xDF, # 11011111
0xEF, # 11101111
0xF7, # 11110111
0xFB, # 11111011
0xFD, # 11111101
0xFE, # 11111110
]
col = [
0x00, # 00000000
0x66, # 01100110
0x99, # 10011001
0x81, # 10000001
0x42, # 01000010
0x24, # 00100100
0x18, # 00011000
0x00, # 00000000
]
def jump_up(pin):
pin.on() # 产生跳变
pin.off() # 保持一段时间后关闭
def send_data(position, is_row=True):
'向位移寄存器送数据'
value = row[position] if is_row else col[position] # 取行码或列码对应位置的值
for i in range(8):
pin_dio.value((value >> i) & 0x01) # 从最低位开始送数据
jump_up(pin_sclk) # 位移到下一个寄存器
while True:
for n in range(8):
send_data(n, is_row=False) # 先推列码
send_data(n, is_row=True) # 再推行码
jump_up(pin_rlck) # 全部数据推入锁存
3.2. 代码运行效果
4. 作业
借助74HC595用动态扫描的方式在8x8LED点阵屏上显示一个固定图案,这步已经实现了。这篇就介绍这么多吧。
最后来个拓展小作业,在点阵屏上切换显示多个图案,如下效果:
温馨提示:
1、轮换显示了3个图案,那对应取模时要取3个是必然的了;
2、定时切换多个图案,中间用time.sleep是行不通的,可以自行实验一下。