MicroPython-On-ESP8266——8x8LED点阵模块(4)基于MAX7219滚动显示字符/图案
1. 继续折腾点阵模块
咱们已经学习了点阵屏基础电路与驱动原理,并用74HC595和MAX7219都成功地驱动点阵屏显示了爱心图案。
MicroPython-On-ESP8266——8x8LED点阵模块(1)驱动原理
MicroPython-On-ESP8266——8x8LED点阵模块(2)使用74HC595驱动
MicroPython-On-ESP8266——8x8LED点阵模块(3)使用MAX7219驱动
使用max7219只显示一个爱心未免单调了一些,该整些小花活儿了。
我们来实现展示多个图案,并逐步深入到能整体切换、按行动态滚动、按列动态滚动,滚动还能带首位衔接的效果。
2. 显示多个图案
2.1. 先对图案取模
这次咱们来三个图案:I♡U
照旧在PCtoLCD里取模,建立24x8的图案,手动编辑出需要的图案出来。按行列式、顺向、十六进制的方式来取模。如下图所示(当然也可以自己发挥其他想要的图案):
得到取模结果:
{0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00},
{0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00},
{0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00},
2.2. 先分析一下前面的单图案显示代码
from machine import Pin,freq
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):
"按位移入数据到模块"
pin_cs.off()
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)
init_max7219()
## 显示逻辑:单图案
## 多图案只需要更换此部分逻辑即可
col = [0x00, 0x66, 0x99, 0x81, 0x42, 0x24, 0x18, 0x00] # 爱心
for n in range(8):
write_data(n+1, col[n])
前面的导入Pin库、定义管脚、定义写入数据方案以及初始化模块,这些通过的东西不能省。
最后的显示逻辑部分,单图案时就是直接按行刷入对应的数据一次即可。那我们要显示多个图案其实也简单,遍历每个字符/图案(就是上面取模到的三组值),依次刷入即可。
2.3. 多个图案的显示逻辑
# 显示逻辑:多图案逐个切换
col1 = [
0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00, # I
0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00, # ♡
0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00 # U
]
while 1:
for i in range(3): # 遍历三个图案
for n in range(8): # 每个图案刷入取模的值
write_data(n+1, col1[i*8+n])
time.sleep(1)
三个图案取模得到的所有值都放入col1数组,取某个图案时计算一下下标即可,不需要弄二元数组。
写个while循环不断重复显示I♡U
,每个图案之间sleep一下,让图案在屏幕上保持一段时间。
2.4. 实验效果
3. 按行(纵向)滚动显示多图案
3.1. 按行滚动原理
上面实现了切换多个图案进行显示的效果,但这样切换的效果太生硬了,现在改进一下,用动态逐行滚动的形式来显示。
原理就是显示的时候,一个图案整体是8行,我们递进1行来展示,当前图案显示一部分,再跨到下个图案显示一部分;
再让每行递进的时间缩小到秒级以内,停留时间变短,这样人眼观察就会形成滚动的效果了。
三个图案取模的结果都在一个大数组内,我们直接从首位开始递进取8个字节来展示,直到取到数组的最后。
3.2. 显示逻辑代码
# 显示逻辑:逐行递进切换
col2 = [
0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00, # I
0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00, # ♡
0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00 # U
]
while 1:
for i in range(len(col2)-8): # 取到最后图案的第一个字节,避免截取后面8个字节时越界
for n in range(8):
write_data(n+1, col2[i+n])
time.sleep_ms(150)
3.3. 实验效果
4. 按行(纵向)滚动显示多图案,增加首尾衔接过渡效果
4.1. 首尾衔接过渡处理
上一步实现了按行滚动,但首尾图案的处理还是不理想,第一个图案直接出现,最后一个图案没有逐步退出的效果。
要解决这个问题,我们需要在取模数组前部增加一个空白图案,最开始先显示空白图案再逐步滚入后续图案。
而最后一个图案,前面为了避免数组越界,当图案全部出现时就结束了。那我们需要故意越界一下,只不过是越到了整个数组的头部(用求模的算法)。这样尾部滚入完成后就又过渡到了头部图案的展示了。实现了衔接效果。
4.2. 实操代码与效果
# 显示逻辑:逐行递进切换,首位衔接起来
col3 = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, # 空
0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00, # I
0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00, # ♡
0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00 # U
]
while 1:
for i in range(len(col3)): # 取完所有的字节
for n in range(8):
idx = (i+n) % len(col3) # 最后图案不完整时,用求模的方式去取数组最前面的值
write_data(n+1, col3[idx])
time.sleep_ms(150)
5. 按列(横向)滚动显示多图案
5.1. 横向滚动原理
从取模软件那边的效果来看,我们led点阵屏始终显示8*8的区域,如下图红框部分。该区域内显示的图案,如果整体做一个左移动作,相对红框做一个右移动作。红框右移后,框起来的部分是当前图案的右n列以及下一个图案左8-n列。
也就是说,我们当前显示区域的每一行数值,左移一次,左移后空出来的最低位,需要把下一个图案的对应行数值的最高位放置过来。然后重复做左移与补位的动作。
需要特别注意的地方:
- python数值没有char类型,我们定义取模的数组时,默认都是int,那么左移1次需要先把当前的最高位清空,否则int数会被乘2后变得非常大
- 为了向左滚动移入的效果更好看,我们一样在前面补一个空白图案。
5.2. 代码与效果
# 显示逻辑:横向递进切换
col4 = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, # 空
0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00, # I
0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00, # ♡
0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00 # U
]
while 1:
for n in range(8):
write_data(n+1, col4[n])
# 所有数据左移,并在当前最低位补充下一字符对应位置的最高位
for ch in range(len(col4)/8): # 遍历所有图案
for i in range(8): # 每个图案的8行
col4[ch*8+i] = (col4[ch*8+i] & 0x7F) << 1 # (python类型限制)清空最高位再左移1位
if ch != len(col4)/8 - 1: # 非最后一个图案,要把下个图案对应行的最高位补到当前最低位
col4[ch*8+i] += (col4[ch*8+i+8]>>7)
time.sleep_ms(150)
有个问题是,当所有的有效数据都左移完后,数组的所有元素都变成 0x00
了,也就是后面不会再显示任何内容了。这就要求我们像前面处理按行滚动的首位衔接过渡的效果一样,把列滚动也衔接起来。
6. 按列滚动图案并具体首位衔接过渡效果
6.1. 过渡原理
上一步,我们不断的左移,相当于红框一直右移,直到移出到待显示图案的外部(无尽虚空),导致后续屏幕空白。
既然知道红框移动到的区域超出了最后一个图案的边界,那我们可以就可以把超出的边界的部分再用第一个图案的头部列区域来填补,这样就实现了过渡的衔接。
由于位移动作不像前面按行滚动时那样直接数组取对应的值就能解决,我们需要另外定义一个缓存区,把需要额外补位部分的各行数值缓存起来。或者说把第一个图案的第1列缓存起来,所有图案的各列都做了一次左移动作,再把刚才缓存的首图案的首列补位到最后一个图案的最后一列。这样就实现了一个无尽循环,从而我们各个图案就能一直循环地滚动下去了。
6.2. 代码与效果
# 显示逻辑:横向递进切换,首位接起来
col5 = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, # 空
0x00,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00, # I
0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00, # ♡
0x00,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00 # U
]
tmp = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] # 缓存区
while 1:
for n in range(8):
write_data(n+1, col5[n])
# 所有数据左移,并在当前最低位补充下一字符对应位置的最高位
for ch in range(len(col5)/8):
for i in range(8):
if ch == 0:
tmp[i] = col5[ch*8+i] >> 7 #缓存下来当前显示内容的最高位
col5[ch*8+i] = (col5[ch*8+i] & 0x7F) << 1 # 清空最高位再左移1位
if ch != len(col5)/8 - 1: #非最后一个图案,要补下个字符对应位
col5[ch*8+i] += (col5[ch*8+i+8]>>7)
else: #最后一个图案,补缓存下来的最高位到后面
col5[ch*8+i] += tmp[i]
time.sleep_ms(150)
为了左移效果好看,第一个图案是空白,所以动图上不能直观的看出这是一个无尽的循环。自己实操体会一下就明白了,代码部分我加了注释,这里也不过去解释,多理解一下就好。
7. 后记
至此,咱们实现了多图案的按行滚动、按列滚动的效果。并且都能实现首位衔接过渡。人眼观察看效果都还比较平顺。
那本节就到此,后续再继续深入。