MicroPython-On-ESP8266——数码管的使用,2片HC595驱动四位数字管
1. 背景
在前面使用四位数字管时,需要12个GPIO接口,结果我们micropython能驱动的esp8266开发板只能用9个口,只能驱动两位数字管。幸运的是,使用了动态扫描机制。
回来学习第三个数字管,两个72HC四位数码管由595位移寄存器芯片驱动。
2. 74位移寄存器芯片HC595
先了解一下这74HC595芯片(以下简称595)是什么,是如何工作的。
2.1. 595原理图
符号 | 引脚 | 描述 |
---|---|---|
Q0–Q7 | 第15脚,第1-7脚 | 8位并行数据输出 (DS引脚发送的位数据可以依次推入这8个位,达到串入并出的效果。8个输出可用于控制数字管或位码(理论上可控制8位数字管)的8个段码 |
GND | 第8脚 | 地 |
Q7’ | 第9脚 | 串行数据输出(只有8个移位寄存器,当数据继续发送到内部时,引脚会溢出到初始位置。溢出位置可以发送到下一个595芯片,以实现无限串联。 |
/MR | 第10脚 | 主复位(低电平清零数据通常收到VCC防止数据被清除) |
SH_CP | 第11脚 | 时钟线的数据输入 |
ST_CP | 第12脚 | 输出存储器锁定时钟线 |
/OE | 第13脚 | 输出有效(低电平有效,数码管闪烁的效果可以通过控制这个引脚(一开一关就是闪烁) |
DS | 第14脚 | 串行数据输入(接收数据位的引脚,如果是串联,则按595芯片Q7’引脚) |
VCC | 第16脚 | 电源 |
2.2. 595使用流程
在上面的引脚说明中,除了上面的引脚说明之外,已经把除了 SH_CP 和 ST_CP 其他引脚注意事项写进去,核心两个单独说明:
首先,595芯片有两个寄存器:
- :负责将DS引脚的当前数据状态已推入Q同时,0数据位Q0->Q1->Q2–>Q3–>…–>Q7会依次向后推一个,可以想象成左轮手枪。如果你塞子弹,你必须轮子,然后塞子弹。每轮子弹的位置都会顺移。
- :锁定当前输出到位移寄存器的数据Qx保持引脚不变。就像你把左轮子弹装满或者不填满后,把轮子推到枪体上,位置就固定了。
问题来了,DS引脚用用来塞子弹的。什么时候转一轮(产生位移),什么时候推左轮(锁定)?这需要另外两个595芯片的引脚。
- :沿时数据寄存器的数据移位引脚上升。
- :沿时移位寄存器的数据上升到数据存储寄存器,沿时存储寄存器的数据保持不变。
也就是说,当我们需要移位时,我们通常会把这两个引脚的电平放低SH_CP拉需要锁定时,把它拉高ST_CP拉高引脚,只需要很长时间。短到微秒,我们在micropython没有微秒级的延迟函数,直接不延迟。
3. 两片595驱动数码管的原理
让我们从数字管模块的原理图中分析 1)首先,它对引脚名的定义与以上不同,没关系,仔细对比就能看清楚:
- SER引脚就是DS引脚,用来装子弹,在包装模块上引出弯头,丝印名称是DIO
- SCLK引脚用于控制位移
- RCLK引脚用于控制锁定Qx数据口
- QA-QH共有8个数据输出(跟上)Q0-Q7一样的)
2)上半部分数码管的引脚图不用理会,段码A到DP,从左到右分别有四个数字管DIG0到DIG3.下半部分有两片595片。从蓝色圈的部分可以看出,左边的第一片芯片只有四个位置来控制DIGx,指定的管道位置可以用高电平点亮;右侧的第二个芯片,用满8个位置输出,正好对应数字管的段码。
3)右边的595芯片的数据输入是由左边芯片溢出得到的(SER引脚接入左侧QH’输出)。那咱们要想驱动数码管点亮,就要先将显示数字的段码输出到DIO引脚,然后输出数字显示在哪个管位上的位码DIO引脚,然后一锁就一起工作。
4)还应强调,根据目前的驱动模式,为了在数字管的不同位置显示不同的数字,仍然需要使用动态扫描的原理,即利用人眼视觉暂留机制快速更改不同的位码。但我们不需要手动控制DIGx开关,直接输入不断变化的位码到595。
4. 实验:点亮数字管并显示自增长的数字
4.1. 接线图
4.2. 实验代码
from machine import Pin # import utime # 准备数据引脚 pin_sclk = Pin(4, Pin.OUT); pin_sclk.off() # 上升跳变时锁存
pin_rlck = Pin(0, Pin.OUT); pin_rlck.off() # 上升跳变时数据位移
pin_dio = Pin(2, Pin.OUT); pin_dio.off() # 待移入数据位
mapper = {
# 共阳方式段码对照
'0': 0xC0, '1': 0xF9, '2': 0xA4, '3': 0xB0,
'4': 0x99, '5': 0x92, '6': 0x82, '7': 0xF8,
'8': 0x80, '9': 0x90, 'A': 0x88, 'B': 0x83,
'C': 0xA7, 'D': 0xA1, 'E': 0x86, 'F': 0x8E,
}
def jump_up(pin):
pin.on() # 产生跳变
# utime.sleep_ms(1)
pin.off() # 保持一段时间后关闭
def send_data(num, is_position=False, has_point=False):
'向位称寄存器送数据'
if is_position: # 位码处理( 1~4位)
value = {
1:0x01, 2:0x02, 3:0x04, 4:0x08}.get(num, 0)
else: # 段码处理
value = mapper.get(str(num), 0)
value = value & 0x7F if has_point else value # 共阳方式
# value = (~value | 0x80) if has_point else ~value # 共阴方式
for i in range(8):
pin_dio.value(1 if (value << i) & 0x80 else 0) # 从最高位开始送数据
jump_up(pin_sclk) # 每送完一位后就让位移寄存器跳变一下
num = 0
step = 1
while True:
# 显示千位
send_data(int(num/1000))
send_data(4, is_position=True)
jump_up(pin_rlck)
# 显示百位
send_data(int(num / 100 % 10))
send_data(3, is_position=True)
jump_up(pin_rlck)
# 显示十位
send_data(int(num / 10 % 10))
send_data(2, is_position=True)
jump_up(pin_rlck)
# 显示个位
send_data(int(num % 10))
send_data(1, is_position=True)
jump_up(pin_rlck)
if step%100==0:
num += 1
if num>9999: num=0
step += 1
4.3. 效果
5. 后记
我手上有的数码管至此已经全部能点亮驱动了,也算能交个差了。当然了,数码管的驱动方式还有很多,网上也能查到一些专用的驱动芯片,每种芯片都有其独特的电路原理。 等实际遇到这些东西时再研究了,路漫漫其修远,共勉!