购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第8章
扩展芯片静态驱动LED数码管

虽然使用动态扫描驱动方案在一定程度上可以降低需要的控制引脚数量,但对于电路设计有着较高要求的话,仍然还是不够的,在大多数场合下,我们不希望数码管这类简单显示功能占用更多引脚,因为这样做是非常不划算的,毕竟好钢还是要用到刀刃上。

例如,现在需要实现一个电子钟,它共有6位数码管,使用动态扫描方式需要约8+6=14个IO引脚(即便使用74LS47译码器芯片,需要的引脚也并不少),学习过程中使用这么多引脚用于数码管显示还是可以接受,但是作为产品开发却显得有些过于奢侈。要知道,AT89C1051可用的引脚只有15个,很多以出量为目标的产品为了降低成本,引脚的使用都是精打细算的,所选择的单片机的引脚数量可能还没这么多,把大部分引脚都用来做显示控制了,它还能完成其他什么功能呢?所以进一步探讨需要更少引脚数量的显示方案还是很有必要,我们想到的第一种方案便是使用扩展芯片,来看看如图8.1所示驱动共阳数码管的Proteus软件平台仿真电路。

图8.1 74HC595驱动共阳数码管仿真电路

该仿真电路使用了一款74HC595逻辑芯片来驱动数码管,它与单片机连接的引脚数量最少仅需要3个(除SH_CP、DS、ST_CP外,其他控制引脚不是必需的)。74HC595是8位串行输入、并行输出的缓存器,其内部逻辑如图8.2所示。

74HC595的逻辑电路非常简单,8个带高电平复位R的D触发器组成了8位移位寄存器(shift register),统一由低有效的复位引脚 控制,SH_CP(shift register clock input)引脚每到来一个时钟上升沿,移位寄存器将对DS(serial data input)引脚的串行输入数据完成一次移位操作。另外,每一个移位寄存器的数据输出Q都增加了一个锁存器,其意义与74HC573中锁存器是一致的(锁存器与触发器的区别在于:前者是 电平触发 ,后者是 边沿触发 ),一旦ST_CP(storage register clock input)引脚为高电平,移位寄存器的输出数据将被载入锁存器,当ST_CP引脚为低电平时,锁存器的输出处于保持状态。换句话说,如果要将移位寄存器的输出数据进行锁存,需要将ST_CP引脚电平 拉高后再拉低 (产生一个正脉冲)。另外,与74HC573类似,锁存器的数据也经过三态缓冲器再输出,统一通过低有效的使能 引脚控制。下面来看看它的功能表,见表8.1。

图8.2 逻辑功能框图

表8.1 74HC595功能表

表8.1中数据Q的符号上加一撇(例如 )表示内部移位寄存器的输出数据,没加一撇(如Q n )表示芯片(三态缓冲器)的输出数据。需要注意的是, 单独的复位操作只是把移位寄存器中的数据置0,并不影响锁存器的状态 。如果需要将芯片输出数据清零,需要先复位再从ST_CP引脚加入正脉冲,这能够将移位寄存器的全0输出数据载入锁存器。

现在需要图8.1所示共阳数码管显示数字“5”,首先请特别留意硬件电路的连接形式: 74HC595的数据输出Q 0 ~Q 6 分别与数码管的段a~f连接(Q 7 与段dp连接且编程使其置1,表示不点亮) ,所以驱动数码管的字型码应为0x92(共阴字型码0x5D的取反值),一共需要9个步骤,如图8.3所示。

图8.3 74HC595输出字型码0x92详细过程

为了正确驱动共阳数码管显示数字“5”,必须先将0x92(0b1001_0010)的 最高位 进行数据移入移位寄存器的操作,称为“高位(The Most Significant Bit,MSB)先行”。相应的,也有“低位(The Least Significant Bit,LSB)先行”的概念,这取决于字型码或硬件电路的具体形式。对于我们现在的硬件电路,如果以“低位先行”的方式将串行数据移入移位寄存器,74HC595的输出数据恰好是反过来的0x49(0b0100_1001),也就得不到正确的显示数字。

接下来看一下具体的显示过程。首先设置DS引脚的串行数据为“1”,SH_CP引脚的第一个移位时钟的上升沿将“1”移入到移位寄存器,然后依法炮制将0、0、1、0、0、1、0依次移入。经过8个移位时钟后,串行输入0x92的字型码已经并行送到了8位移位寄存器的数据输出。在移位的过程中,由于ST_CP引脚一直为低电平,所以芯片的输出数据是不会变化的。待移位寄存器完成数据移位后,我们给ST_CP引脚施加正脉冲,就可以将移位寄存器中的数据0x92载入到锁存器中,相应的时序如图8.4所示。

图8.4 写数据到74HC595的时序

在完整的8位串行输入数据完全移入移位寄存器之前,ST_CP引脚应该一直保持为低电平,如果想将移入移位寄存器的8位完整串行数据载入锁存器,则应该在第8个移位时钟上升沿 进行。值得一提的是,这 种给芯片载入数据的方法(先移位后锁存)是通用的,只要再加上地址信息,就可以给芯片内部多个寄存器载入需要的数据, 在第11章将会看到具体的应用案例。

我们同样实现数码管循环显示数字0~9的功能,相应的源代码如清单8.1所示。

清单8.1 共阳数码管循环显示数字

首先定义了一些引脚别名,后缀“_N”表示低电平有效。为了谨慎起见,硬件电路中使用单片机控制了 两个引脚,并且使用10kΩ电阻将 引脚上拉为高电平,表示我们希望电路刚上电时74HC595的数据输出是无效(高阻状态)的,相应的数码管也就处于不点亮状态,这可以避免上电瞬间出现随机数值显示(没意义的显示)。然而,10kΩ电阻只是一种 弱上拉 (weak pull-up),单片机刚上电时有可能会出现引脚输出电平不确定的现象,这可能会将74HC595的 引脚拉低,也就能够使74HC595输出有效,继而达不到10kΩ上拉电阻的应用目的。为此,在main主函数一开始就调用了init_74hc595函数,其中第一条语句就是将74HC595的 引脚初始化为高电平(OE_N=1)。

但是,74HC595的 引脚最终还是需要设置为低电平,因为正常驱动数码管显示时,需要将74HC595的数据缓冲器输出打开,但在此之前我们必须将芯片(三态缓冲门)输出设置为全0,把数码管初始化为不显示状态,这样也就避免了可能出现的随机数据显示现象。

具体的初始化思路是这样的: 引脚设置为有效(低电平)之前,先把移位寄存器清0,再把全0数据载入到锁存器即可。 首先把复位引脚 设置为低电平(MR_N=0),这可以让移位寄存器置为全0。然后将ST_CP引脚设置为高(ST_CP=1),表示允许移位寄存器的全0数据载入锁存器(尚未锁存),然后再把ST_CP引脚设置为低电平(ST_CP=0)即可把完成全0数据的锁存操作,最后将 引脚设置为低电平(OE_N=0)即可完成初始化工作。

在初始化的过程中,还顺便把SH_CP引脚初始化为了高电平(SH_CP=1)。有人可能会问:不能将其设置为低电平吗?可以,但最好设置为高电平,因为通常在上升沿有效的时钟信号中,高电平才是空闲状态。

然后开始循环显示数字。while循环语句比较简单,调用write_74hc595函数将变量i对应的字型码写入74HC595。每隔约1s就将变量i累加,然后循环执行即可。

下面重点分析一下write_74hc595函数。首先对DS引脚输入的串行数据进行移位操作,for语句中进行8次循环表示将要进行8次数据移位操作。由于移位时钟是上升沿有效的,所以在每次对串行数据进行移位前先将SH_CP引脚置0(SH_CP=0),只要把串行数据预先输入到DS引脚,再把ST_CP引脚置1即可产生一个时钟上升沿将数据移入移位寄存器。

再来看一下串行数据的设置语句。前面已经提过,在图8.1所示硬件电路中,字型码写入74HC595时必须高位先行,所以使用了一个条件运算符。当字型码与0x80进行“与”运算的结果为1时(也就代表着字型码的最高位为1,关键字 bit 的作用是强制类型转换,如果转换值为非0则结果为1,否则为0),则将串行数据DS引脚设置为1(DS=1);运算结果为0时,则将串行数据DS引脚设置为0(DS=0)。

将串行数据设置后,把字型码左移了一位(byte_data <<=1),这样可以预备将次高位与0x80进行下一次判断,然后把ST_CP引脚置1就产生上升沿将串行数据移入到了移位寄存器。

write_74hc595函数中将8位串行数据载入到锁存器的时序如图8.5所示。

如果需要驱动更多数码管,可以使用多片74HC595进行级联。下面来看看驱动4位 共阴 数码管的Proteus仿真电路,如图8.6所示。

图8.5 将串行数据载入锁存器的时序

图8.6 74HC595驱动4位共阴数码管

在硬件电路上,把所有74HC595的SH_CP、ST_CP、 引脚都连接在一起,并交由单片机统一控制。另外,还把上一片74HC595的 (内部移位寄存器的最后一位输出)与下一片74HC595的串行数据输入DS引脚连接。

下面来看看相应的源代码,如清单8.2所示(仅截取添加或更新后的部分代码,其他与清单8.1相同)

清单8.2 多片74HC595级联驱动多位数码管

为了节省代码篇幅,决定仅显示“9527”四个数字(不进行累加),为此定义了一个display_char数组,里面包含了数字“9527”各数字对应的共阴数码管字型码。这里特别要注意单片机是如何将串行输入数据载入到锁存器的,虽然也可以像清单8.1所示那样调用四次write_74hc595函数将display_char数组中的字型码依次写入,但是这样带来了一个问题,即每写入一个字型码(8位),就会进行一次载入锁存器的操作。例如,要在最右侧数码管显示数字“9”,则数字“9”会像流水灯一样从左刷到右,如图8.7所示。

现在只是静态显示四个数字,由此带来的影响还不是很明显,动态显示则不然,而且串联的数码管越多,显示效果也会越差。也就是说,得把4个字节的字型码全部移入移位寄存器后,再一次性载入到锁存器才行。为此把“对移位寄存器进行移位”的操作单独包装成了一个shift_data_74hc595函数,它仅处理将串行数据移入到移位寄存器的操作,而write_multi_74hc595函数中则完成将数据载入锁存器的操作,期间如果数组中的数据不为0,则调用shift_data_74hc595函数(清单8.2中调用了4次)。

如果对C语言比较了解的话,也可以将write_multi_74hc595函数稍微改写一下,给它传入一个指针,如清单8.3所示。

图8.7 显示数字逐位刷新

清单8.3 另一种写数据函数

虽然新改写的write_multi_74hc595函数的形参是一个指针,但它与清单8.2所示同名函数的功能完全一样,因为在C语言中, 指针 就是 地址 (一维数组的数组名就是数组的首地址)。但是这两个函数的使用有一个限制:数组里面的数据不能为0(也就是 共阳 数码管显示数字“8”时对应的字形码,这也是使用 共阴 字形码的好处),否则当while语句中找到它时就停止循环了,也就不会对数组剩下的数据继续查找。如果使用的数组中确实存在数据0,则需要修改一下函数(例如,增加一个表示数组长度的形参),后面我们会看到这种方式。

值得一提的是,main主函数中最后的无限循环while语句是非常必要的。如果没有它,虽然后续并没有更多的语句,但程序却可能会乱跑,控制引脚的状态也就可能是不稳定的。

最后请大家思考一下,清单8.2中的init_74hc595函数应该怎么写呢?也就是如何使复位后的输出为全1呢? T4zByg8OR51+0cB5RCdGE7l5bjlAn1qeXYR3fj9tgvpEztUzghRt654cIQwLa9WT

点击中间区域
呼出菜单
上一章
目录
下一章
×