热度 24
2015-4-22 16:03
6868 次阅读|
0 个评论
74HC595-串入/串并出带锁存的移位寄存器,实在是灰常magic,用它来驱动8段数码管太方便了!除了静态驱动可无限串联的一位数码管,它还能用来动态驱动4位段选数码管,而且由于串入/串并出和锁存特性,动态驱动也能够不闪烁显示并无限串联。 原理图 (两个74HC595分别做段选和段显,数码管为0.56寸4位共阳SR410561N) PCB板 需不需要使用三极管? 74HC595的IO输出电流推荐为20mA,而数码管的限流电阻为1K欧姆时大概工作电流为10mA左右,因此使用74HC595的IO作为段选,提供数码管的阳极电流,不用再增加三极管了。74HC595的总电流不能超过70mA,所以这里只使用它驱动4个段选信号。(好吧,由于动态显示是扫描的,每一时刻只点亮一个,所以驱动8个段选信号是没有问题的,74HC595的资源就全部利用了) 程序设计 动态驱动4位段选数码管的原理是,每个小间隔,使能一个段选信号,点亮它对应的段显。循环使能这4个段选信号,由于人的视觉停留,看起来就好像是同时点亮的。所以程序设计的时候,就需要每个小间隔对它操作一次,在主函数或者定时器中断里面都可以。 调试这个小模块的时候,我遇到了两个问题: 第一,显示有闪烁;第二,没用到的段会轻微点亮。 第一个问题解决的很快,闪烁是因为显示速度太慢,提高显示速度就可以;第二个问题是因为我的初版程序里面,每移位8bit就锁存一次,因此段选信号会在瞬间作为段显信号,所以明明不该亮起来的段会看起来微微亮,解决方法是段选和段显信号都发送完成之后再锁存。 /************************************************* 驱动4位段选数码管的方法: HC595_single_4BIT的功能是,将段显信号byte发送,然后使能第byte_position个的段选信号; 发送完毕之后更新锁存(而不是每个for分别更新),实际显示的效果非常漂亮; 当多个这样的模块串联时,会发现它们都会显示“3210”; get_square的功能是,计算2的3-byte_position次幂,并存放在byte_position_t中。 **************************************************/ void HC595_single_4BIT(int8_t byte, int8_t byte_position) { int8_t i = 0, j = 0; uint32_t byte_position_t = 0; get_square(2, 3 - byte_position, byte_position_t); for (i = 0; i 8; i++) { if (byte 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); byte = byte 1; } for (i = 0; i 8; i++) { if (byte_position_t 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); byte_position_t = byte_position_t 1; } HC595_RCK_L_4BIT(); HC595_RCK_H_4BIT(); } int main (void) { /* functions(); */ while (1) { HC595_single_4BIT(LGBH38_HC595_DEC_4BIT , 0);Delay100Us_xt2_8MHz(1); HC595_single_4BIT(LGBH38_HC595_DEC_4BIT , 1);Delay100Us_xt2_8MHz(1); HC595_single_4BIT(LGBH38_HC595_DEC_4BIT , 2);Delay100Us_xt2_8MHz(1); HC595_single_4BIT(LGBH38_HC595_DEC_4BIT , 3);Delay100Us_xt2_8MHz(1); } return 0; } /************************************************* 驱动多个4位段选数码管的方法一: 由于74HC595的串入/串并出特性,这个模块可以无限串联使用,但是使用上面的程序,没有办法单独点亮后级,因为后级的数据始终是前级推出来的,而前级始终处在4位段选的循环中。 所以程序需要做一些修改,使得在每个小间隔里面,能够对后级单独操作。 基本原理是,假如有len个模块串联使用,先将要显示的len_position之前的熄灭,然后发送byte和byte_position,再将len_position之后的熄灭;完成之后更新锁存器。 虽然它可以单独对任何段选和段显进行操作,但是随着串联个数的增加,亮度也会降低。 **************************************************/ void HC595_multi_4BIT(int8_t len, int8_t len_position, int8_t byte, int8_t byte_position) { int8_t i = 0, j = 0; uint32_t byte_position_t = 0; get_square(2, 3 - byte_position, byte_position_t); for (j = 0; j len - len_position - 1; j++) { HC595_DAT_H_4BIT(); for (i = 0; i 8; i++) { HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); } HC595_DAT_L_4BIT(); for (i = 0; i 8; i++) { HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); } } for (i = 0; i 8; i++) { if (byte 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); byte = byte 1; } for (i = 0; i 8; i++) { if (byte_position_t 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); byte_position_t = byte_position_t 1; } for (j = 0; j len_position; j++) { HC595_DAT_H_4BIT(); for (i = 0; i 8; i++) { HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); } HC595_DAT_L_4BIT(); for (i = 0; i 8; i++) { HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); } } HC595_RCK_L_4BIT(); HC595_RCK_H_4BIT(); } int main (void) { /* functions(); */ while (1) { /*****会显示 76543210 *********/ HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT , 0);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT , 1);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT , 2);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT , 3);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT , 0);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT , 1);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT , 2);Delay100Us_xt2_8MHz(1); HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT , 3);Delay100Us_xt2_8MHz(1); } return 0; } /************************************************* 驱动多个4位段选数码管的方法二: 动态驱动,每个小间隔只能使能其中一个段选,上面的程序是假如有12个段选,就在12个小间隔里面循环使能,优点是可以单独操作任何一段,缺点是亮度会降低。 其实可以仍然使用4个小间隔,在每个小间隔里面,发送bytex段显信号,使能所有串联模块的第byte_position个段选。 因为后级串联的模块个数不确定,所以使用变参函数会方便一些。 **************************************************/ void HC595_multi_dis_4BIT(int8_t len, int8_t byte_position, int8_t byte_h, ...) { int8_t i = 0, j = 0, bytex = 0; uint32_t byte_position_t = 0, byte_position_save = 0; get_square(2, 3 - byte_position, byte_position_save); char *p = (char *)byte_h; for (j = 0; j len; j++) { bytex = *((int *)p); p += sizeof(int); for (i = 0; i 8; i++) { if (bytex 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); bytex = bytex 1; } byte_position_t = byte_position_save; for (i = 0; i 8; i++) { if (byte_position_t 0x80) HC595_DAT_H_4BIT(); else HC595_DAT_L_4BIT(); HC595_SCK_L_4BIT(); HC595_SCK_H_4BIT(); byte_position_t = byte_position_t 1; } } HC595_RCK_L_4BIT(); HC595_RCK_H_4BIT(); } int main (void) { /* functions(); */ while (1) { /******会显示 222211110000 ***********/ HC595_multi_dis_4BIT(3, 0, LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT ); DelayMs_xt2_8MHz(1); HC595_multi_dis_4BIT(3, 1, LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT ); DelayMs_xt2_8MHz(1); HC595_multi_dis_4BIT(3, 2, LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT ); DelayMs_xt2_8MHz(1); HC595_multi_dis_4BIT(3, 3, LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT , LGBH38_HC595_DEC_4BIT ); DelayMs_xt2_8MHz(1); } return 0; } 最后,趁着某xx的单反相机还在这里,留一张渣图 :