原创 两个74HC595驱动共阳4位段选数码管

2015-4-22 16:03 6862 24 24 分类: MCU/ 嵌入式 文集: 硬件小模块

74HC595-串入/串并出带锁存的移位寄存器,实在是灰常magic,用它来驱动8段数码管太方便了!除了静态驱动可无限串联的一位数码管,它还能用来动态驱动4位段选数码管,而且由于串入/串并出和锁存特性,动态驱动也能够不闪烁显示并无限串联。

原理图
(两个74HC595分别做段选和段显,数码管为0.56寸4位共阳SR410561N)

screenshot from 2014-04-29 10:45:32.jpg


PCB板

screenshot from 2014-04-29 10:41:21.jpg

需不需要使用三极管?
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], 0);Delay100Us_xt2_8MHz(1);
        HC595_single_4BIT(LGBH38_HC595_DEC_4BIT[1], 1);Delay100Us_xt2_8MHz(1);
        HC595_single_4BIT(LGBH38_HC595_DEC_4BIT[2], 2);Delay100Us_xt2_8MHz(1);
        HC595_single_4BIT(LGBH38_HC595_DEC_4BIT[3], 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], 0);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT[1], 1);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT[2], 2);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 0, LGBH38_HC595_DEC_4BIT[3], 3);Delay100Us_xt2_8MHz(1);

        HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT[4], 0);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT[5], 1);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT[6], 2);Delay100Us_xt2_8MHz(1);
        HC595_multi_4BIT(2, 1, LGBH38_HC595_DEC_4BIT[7], 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[2], LGBH38_HC595_DEC_4BIT[1],
                LGBH38_HC595_DEC_4BIT[0]);
        DelayMs_xt2_8MHz(1);

        HC595_multi_dis_4BIT(3, 1, LGBH38_HC595_DEC_4BIT[2], LGBH38_HC595_DEC_4BIT[1],
                LGBH38_HC595_DEC_4BIT[0]);
        DelayMs_xt2_8MHz(1);

        HC595_multi_dis_4BIT(3, 2, LGBH38_HC595_DEC_4BIT[2], LGBH38_HC595_DEC_4BIT[1],
                LGBH38_HC595_DEC_4BIT[0]);
        DelayMs_xt2_8MHz(1);

        HC595_multi_dis_4BIT(3, 3, LGBH38_HC595_DEC_4BIT[2], LGBH38_HC595_DEC_4BIT[1],
                LGBH38_HC595_DEC_4BIT[0]);
        DelayMs_xt2_8MHz(1);
    }    
    return 0;
}

最后,趁着某xx的单反相机还在这里,留一张渣图

img_7930_.jpg



 

文章评论0条评论)

登录后参与讨论
我要评论
0
24
关闭 站长推荐上一条 /2 下一条