虽然使用74HC595静态驱动数码管节省了单片机所需的控制引脚数量,但每多驱动一位数码管就需要相应增加一颗74HC595芯片,这就是钱呀,老板可不一定高兴呐!控制产品成本也是工程师必须考虑的一件事,所以进一步探索相对更优的方案很有意义。
前面已经提过,采用动态扫描驱动多位数码管可以节省单片机所需的控制引脚数量,那是否可以把74HC595的数据输出当作单片机的扩展引脚来实现数码管的动态扫描呢?这样既节省了单片机控制引脚还降低了成本,说不定老板高兴后一不小心给你加薪亦未可知。
嗯!加薪是头等大事,方案必须得拿出来,首先来看看两片74HC595驱动8位数码管的Proteus软件仿真电路,如图10.1所示。
图10.1 两片74HC595驱动8位共阴数码管
两片74HC595与AT89C1051之间的连接关系与图8.6是完全一样的,只不过串联的74HC595的数量不同而已,所以清单8.2所示源代码几乎可以直接使用,这样也就节省了重复的编程工作。这里需要注意的是,单片机输出的串行数据从U 2 (左侧)到U 3 (右侧)。我们把U 2 作为段驱动,U 3 作为位选通,那怎么样动态驱动数码管呢?关键还得先弄清明白两颗74HC595的数据输出状态。动态扫描原理其实与以前一样,只不过以往我们使用单片机引脚直接驱动,现在则把74HC595的数据输出当作单片机的引脚间接驱动数码管。
假设要使数码管从左到右分别显示“01234567”,当单片机动态扫描完成一帧时,两片74HC595的输出数据见表10.1。
表10.1 动态扫描时U 2 与U 3 的数据输出
按照Proteus软件平台中数码管器件的引脚定义约定: 最左侧数码管为第1位,最右侧数码管为第8位 。这里特别要注意U 3 输出数据的顺序,由于Q 7 与数码管的第8位(右侧)选通连接,所以我们必须把顺序反过来设置位选通,相应的源代码如清单10.1所示。
先来看一下write_multi_74hc595_ex函数,只要给它传入共阴字型码与位选通数据,单片机就可以把它们推送到74HC595的数据输出引脚。首先将位选通左移8位再与字型码进行位“或”运算,也就可以实现位选通与字型码的合并。由于位选通与字型码都是8位的,所以将合并后的数据赋给了临时的16位无符号整形变量data_tmp,这样就可以把16位数据一次性写入。接下来发生的事就很容易预料到,需要将16位数据移位16次,并且同样是高位先行。main主函数代码比较简单,只是在while语句中循环执行写入数据到74HC595的操作,每写入一位则延时约3ms。
方案确定了!老板就算不加薪,年终奖至少也会多一点吧!哪知道软件工程师在老板那里诉了苦:我们这个产品实现的功能已经很多,如果再这么使用动态刷新的方式控制显示,效果有时可能不太好,达不到客户的要求。这不,老板凌晨2点打电话给我:马上把新方案定下来,暂不考虑成本问题,必须在合同期限内完成交货。
行!我又是中气十足地给了肯定的答案,此情此景早已经非常习惯了,但内心却在呐喊着:瞎指挥个啥?本来之前使用8片74HC595驱动肯定没问题,你却舍不得一点点成本,后来我千辛万苦找了个方案,为你节省了6片74HC595,加薪的事都还没来得及跟你暗示,现在又要全部推翻说什么不考虑成本,再回过头使用之前8片74HC595方案吧,结构已经确定了,放不下这么多东西了,唉!
埋怨归埋怨,但问题还是要积极地解决!今晚注定是个不眠之夜,加个通宵把原理图及PCB弄出来,明天一大早发给PCB制造厂商应该来得及。先来看看新方案吧,相应的Proteus软件平台仿真电路如图10.2所示。
清单10.1 两片74HC595动态驱动8位数码管源代码
图10.2 新的驱动方案
新方案的硬件电路非常简洁,使用了一片可以驱动最多8位数码管( 只能是共阴极 )的MAX7219芯片,它内部集成了 动态扫描时序生成电路 ,仅需要3条信号线即可完全受控于单片机。MAX7219的段驱动输出引脚A~F与数码管的各段连接,位选通输出引脚DIG 0 ~DIG 7 则与数码管的位选通连接,而外围器件只需要一个用来调整流过数码管工作电流的电阻即可,选取的阻值与电流之间的关系见表10.2。
表10.2 限流电阻与工作电流I SEG 及正向导通电压降V LED 之间的关系
假设数码管中LED发光单元的正向导通电压降 V LED =3.0V,而需要设置的工作电流为40mA,则限流电阻的阻值应约为10kΩ。虽然对于Proteus软件仿真电路而言,限流电阻即使不连接也不影响正常的显示,但实际电路中却是必需的。
仍然使用单片机实现与图10.1相同的显示效果(即从左到右显示数字0~7),源代码如清单10.2所示。
目前暂时不必关注源代码的细节,先从宏观上与清单10.1比较,会发现它们是高度相似的,只是定义的引脚名不同而已,但同样有移位(CLK)与锁存时钟(LOAD),同样在main函数开始需要进行初始化。但是有一点需要注意,在清单10.2所示源代码中,main函数调用了8次write_max7219函数将需要显示的数字(0~7)写入到MAX7219之后,就不需要做更多的事情了。换句话说,单片机不需要再负责比较复杂的动态扫描时序,也就可以节省更多的单片机运算资源给其他需要执行的任务,这次大家都满足了吧!
清单10.2 MAX7219驱动8位数码管
下一章就会讲到MAX7219到底是如何工作的。