tag 标签: 74HC595

相关博文
  • 热度 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的单反相机还在这里,留一张渣图 :  
  • 热度 22
    2014-4-16 13:03
    1056 次阅读|
    0 个评论
       1  引言   LED显示屏是八十年代后期在全球迅速发展起来的新型信息显示媒体,应用越来越广泛。ATMEGA16L单片机是一款基于AVRRISC的、低功耗CMOS8位单片机,具有16K字节的自编程FLASH,512字节的EEPROM,1K字节的SRAM,通过AVR单片机来控制LED的显示,结构简单,应用灵活,并且易于扩展。    2  系统设计原理   2.1 编码设计   编码设计有两个过程:字库提取以及字模编码转换。字库的提取涉及到的两个字库文件,一个为ASC16,专门存放ASCII编码;另一个为HZK16,专门存放汉字字库编码。要提取字库,首先要对它进行初始化,即打开字库。然后根据一个字符提取它相应的编码。首先取出字符的内码后(注意区分单字节和双字节的内码),接着通过内码得到该字符对应的字模的编码在ASCII或HZK16内的偏移量,最后就可以根据这个地址在文件中的偏移位置找到相应的字模编码,我们可以通过这个公式来计算:   在ASCII中,偏移量=(内码的低地址)*16,   在HZK16中,偏移量=((内码的高地址-161)*94+(内码的低地址-161))*32。   此时得到的字模编码不是最终所要的编码(单字节数据如英文是按8*16点阵来显示,而双字节数据时如汉字是按16*16点阵来显示的)。这里统一使用8*16点阵,这就需要对其转换。下图左表格表示的是汉字编码在字库内存放顺序,右表格表示的是经过转换过后的编码存放顺序。编码转换过程如图1所示:    2.2 矩阵转换移位算法   矩阵转换移位算法目的就是如何把这些原始的数据转换成串行的数据,以便把数据正确的输入到74HC595。原始的数据必须要对其转换。下面为它的算法实现。    2.3 上位机和下位机的通信   上位机和下位机的通信主要是通过RS232接口进行的,对于上位机的数据传输的速率为9600bps。为了便于数据的传输和接收,可以给要传输数据的前后加上标志位。如果数据发送量大,在发送数据时还可采用线程的方式来发送。而下位机接收数据则可采用单片机内全双工的通用异步收发器(UART)来进行。   UART有中断和查询两种工作方式,UART初始化需要设置相关几个控制寄存器,我们可以把UART几个控制寄存器的初始化情况表示出来:   UCR=0x00设置波特率时要先关闭UARTUBRR=0x33设置波特率为9600bpsUCR=0xD8打开串口:   8位数据UART如何接收和发送数据通过数据寄存器UDR完成。UDR虽然只有一个地址,但是在物理上分离了二个寄存器,一个用于发送,一个用于接收。对于数据的接收和发送采用中断的方式,其中断向量为12,即是interrupthandleruart0_rx_isr:12。接收和发送方式如下:    2.4 显示原理   要想点亮LED,必须把LED的位选数据信号置成低电平。显示的过程如下:首先是将74HC595的RCK置0,再从相应数组中取出要显示的数据和位选信号,通过SCK的一个上升沿脉冲,就可以将这个数据输入74HC595,等所有的数据打入相应的芯片后最后再把RCK置1,这些数据就可以通过74HC595并行的传输出去。    3  系统的设计方案   硬件总体框架的设计是根据上位机与下位机通信原理以及LED点阵显示原理和74HC595的特性所构成的。下面的图2说明的是LED点阵显示驱动原理图及其连线方式。   PA、PB口将作为数据输出口,一共连接16个8*8的LED。端口的每一个引脚将作为74HC595的数据输入端。RCK、SCK的并联的使用的目的是为了实现LED字符的同步显示。在线路连接中Q`H必须连接SI,因为根据74HC595的特性,如果输入的数据数大于8,那么前面的数据将会被自动溢出,根据这一思想就将两个芯片连接起来,按照先输入8位数据信号,再输入8位位选信号的顺序,那么正好前一个74HC595存放位选信息,后一个74HC595存放数据信息。   根据系统的设计原理,得出如下图3的软件流程框架:    4  结论   对于目前这个显示系统,优点在于电路实现简单,成本较低。如果系统需要更大的字符显示库,只需要扩展AVR的RAM或EEPROM,并且用多个AVR单片机进行的通讯即可。当然,这时需要注意数据写入时间问题,以免造成显示的不同步。
  • 热度 33
    2013-12-25 13:55
    2034 次阅读|
    3 个评论
    74HC595 是 8 位串行输入串 / 并行输出的锁存移位寄存器。它的串入并出特性,非常适合驱动 8 段数码管;串入串出特性,非常适合首尾串联。 8 段数码管采用共阴或者共阳都可以,因为 74HC595 的吸入 / 灌出电流均达到 20mA 。   它的使用方法也很简单,并行输出接到 8 段数码管上,串行输入与输出首尾连接即可。单片机提供第一个 74HC595 的串行输入,最后一个 74HC595 的串行输出悬空。所有的 74HC595 共用 2 根时钟线、 1 根使能线。因此,理想情况下单片机提供 4 个 IO 口,能驱动后续的无限多个 74HC595 。原理图如下: 但实际情况下, 74HC595 驱动 8 段数码管,究竟最多能串联多少呢? 换一种问法: 第一,电路板能够通过的最大电流? 第二,推挽输出能够驱动多少个高阻态输入? 第三,推挽输出可以传输多长距离? 第四,末端位置的电源线和地线的变化? (图中左端的数码管比右端稍暗,是因为限流电阻的关系。所处的位置对它的亮度影响不大。)   第一,电路板能够通过的最大电流? 限流电阻取 1K 时,每个 74HC595 大概需要 8mA 的电流。 电源线宽度为 25mil ,铜层厚度为 1oz ,则最大电流为 1.7A 。 因此,可以串联的 74HC595 个数为 1.7A/8mA=212 个。   第二,推挽输出能够驱动多少个高阻态输入? 典型的推挽输出,驱动能力为 4mA 。 74HC595 的技术手册上,高阻态输入的漏电流为 1uA 。 一个推挽输出至少可以驱动 4000 个高阻态输入引脚。换言之,单片机的 IO 口可以驱动 4000 个 74HC595 。 (实际上,IO口在电平快速切换的时候,比静态驱动的情况要稍微复杂一些,需要进行阻抗匹配的处理。)   第三,推挽输出可以传输多长距离? 推挽输出为 TTL 电平,使用它传输信号,距离一般不会超过 2 米。这不是受 TTL 电平的驱动能力限制,而是因为它需要对地参考,所以很容易受到环境的干扰。 如果工作环境的干扰很小,传输距离可以更长一些。   第四,末端位置的电源线和地线的变化? 串联 60 个 74HC595 小电路板,以 500ms 时间间隔显示 0 ~ 9 ,实测发现:当数码管熄灭时,最末端供电电压为 +5V ;当数码管点亮时,最末端供电电压为 +2.91V 。 74HC595 的最低工作电压在 +2V 。因此设每个 74HC595 电路板的线上电阻为 R ,串联个数为 x ,则有: x*R*(8mA*x/2)=U x=60 , U=2.1 ,因此 R=0.146 。 U=3V 时, x=71 。 因此,受线上压降和 74HC595 的最低工作电压限制,能够串联的个数限制为 71 。   结论: 综合以上四点,可以知道 74HC595 驱动 8 段数码管最大串联数量为 71 。 而实际上,通过将电源和地线变得更加短粗,并且选择更好的接插件,可以减少线上电阻,从而降低线上压降;而且由于很少会同时点亮数码管的所有段,所以实际上能够串联的个数比 71 还要更多一些。
  • 热度 27
    2013-12-25 13:21
    1929 次阅读|
    0 个评论
    问题:当多个数码管串联时,不论是多少个,为什么处在末端的几个模块都有可能出现乱码?   使用示波器的 10x 探头观察,会发现高速时钟线上会出现驻波 (standing wave) 。其实信号线上都会有驻波,只是强度大小不同而已。 驻波强度和信号的 0/1 频率没有关系,而是上升下降斜率的频率有关。   一般来说,判断电平值的信号线,驻波并没有多大的影响;但是读取边沿的信号线,驻波的影响就不可忽视。驻波造成了 74HC595 的时钟线读取错误:本来应该读取 1 个数据的时候,它读取了多个进去。   解决方法有两个: 第一,降低信号线频率,将 GPIO_Speed_50MHz 改为 GPIO_Speed_2MHz 。 降低了信号线频率,驻波的强度也会随之降低,原来会出现的乱码现象也会消失。 从前我一直不明白,既然 STM 能够做到 50MH 的 IO 口操作频率,为什么还要提供 2M 、 10M 、 50M 三种选项呢?直接都设置成 50MHz 不就可以了吗? 原来它们是有各自适用的范围的。     第二,使用 GPIO_Speed_50MHz 频率,但是在信号线上串联一个 68 ohms 的电阻。 在高速时钟线上,阻抗不匹配会导致信号的反射,也就是信号的振铃效应。一般来说,对直流通路而言,输出引脚的阻抗较低,而输入引脚的阻抗较高;在高速频率下,传输线还具备大概认为是 50 ohms 的特征阻抗。信号遇到高阻抗的输入引脚,反射回来叠加到输出引脚上,形成驻波。因此,通常在高速时钟线上串联一个 47 ohms ~ 100 ohms 的电阻,吸收掉它。 如果串联的电阻为 10 欧姆 ,信号振铃几乎没有改善; 如果串联的电阻为 120 欧姆 ,信号变形严重; 如果串联的电阻为 68 欧姆 ,信号振铃得道很好的改善。      
  • 热度 21
    2013-12-24 14:58
    2907 次阅读|
    0 个评论
    //使用STM32F103作为MCU,74HC595驱动8段数码管的程序 #define HC595_OE_H()          GPIO_SetBits(GPIOB, GPIO_Pin_12) #define HC595_OE_L()        GPIO_ResetBits(GPIOB, GPIO_Pin_12) #define HC595_RCK_H()          GPIO_SetBits(GPIOB, GPIO_Pin_13) #define HC595_RCK_L()        GPIO_ResetBits(GPIOB, GPIO_Pin_13) #define HC595_SCK_H()          GPIO_SetBits(GPIOB, GPIO_Pin_14) #define HC595_SCK_L()        GPIO_ResetBits(GPIOB, GPIO_Pin_14) #define HC595_DAT_H()          GPIO_SetBits(GPIOB, GPIO_Pin_15) #define HC595_DAT_L()        GPIO_ResetBits(GPIOB, GPIO_Pin_15) void HC595_init(void) {     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     GPIO_Init(GPIOB, GPIO_InitStructure);     GPIO_SetBits(GPIOB, GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);     HC595_start(); } void HC595_start(void) {     HC595_OE_L(); } void HC595_stop(void) {     HC595_OE_H(); } void HC595_shift_byte(uint8_t byte) {     uint8_t i = 0;     for (i = 0; i 8; i++) {         if (byte 0x80)             HC595_DAT_L();         else             HC595_DAT_H();         HC595_SCK_L();         HC595_SCK_H();         byte = byte 1;     }     HC595_RCK_L();     HC595_RCK_H(); } //每执行HC595_shift_byte一次,串联起来的8段数码管就会将自己的显示往左边推移一位; //HC595_shift_byte的入口参数byte的二进制,刚好对应8段数码管的段位; //是不是很森破呢~
相关资源