当前位置:   article > 正文

HC595级联原理及实例 - STM32

hc595

        74HC595的最重要的功能就是:串行输入,并行输出。其次,74HC595里面有2个8位寄存器:移位寄存器、存储寄存器。74HC595的数据来源只有一个口,一次只能输入一个位,那么连续输入8次,就可以积攒为一个字节了。

引脚图

14脚:DIN(SER),串行数据输入引脚

13脚:OE,  输出使能控制脚,它是低电才使能输出,所以接GND

12脚:RCK,存储寄存器时钟输入引脚。上升沿时,数据从移位寄存器转存带存储寄存器。

11脚:SCK,移位寄存器时钟引脚,上升沿时,移位寄存器中的bit 数据整体后移,并接受新的bit(从SER输入)。

10脚:SCLR,低电平时,清空移位寄存器中已有的bit数据,一般不用,接高电平即可。

9 脚 :串行数据出口引脚。当移位寄存器中的数据多于8bit时,会把已有的bit“挤出去”,就是从这里出去的。用于595的级联。

Qx:并行输出引脚

使用参数

VCC:2V~6V,5V最好

I Qn:+- 35mA

移位寄存器

74HC595的14脚:DIN,是串行数据输入口。595的数据来源只有这一个口,一次只能输入一个位,那么连续输入8次,就可以积攒为一个字节了。

74HC595的11脚,(shift register clock input) 移位寄存器时钟引脚。上升沿有效。
首先我们要介绍这个引脚的作用,当一个新的位数据要进来时,已经进入的位数据就在移位寄存器时钟脉冲的控制下,整体后移,让出位置。

上升沿:电平从低到高的那个过程。移位寄存器时钟在上升沿这个过程中才起作用。

存储寄存器

595是怎么将移位寄存器的数据转移到存储寄存器。存储寄存器是直接和8个输出引脚相通的,将移位寄存器的数据转移到存储寄存器后,Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 就可以接收到我们开始输入的一个字节的数据。所谓存储寄存器,就是数据可以存在这个寄存器中,并不会随着一次输出就消失,只要595不断电,也没有新的数据从移位寄存器中过来,数据就一直不变且有效。新的数据过来后,存储寄存器中的数据就会被覆盖更新。

74HC595的12脚: (storage register clock input ) 存储寄存器时钟
数据从位移寄存器转移到存储寄存器,也是需要时钟脉冲驱动的,这就是12脚的作用。它也是上升沿有效。

74HC595级联

通过上面的介绍,见识到595的厉害了吧。138译码器通过3个输入口控制8个输出口,而且还只能是特定的8个输出值,而595只用了一个输入口就可以输任意的8位数据,可谓短小精悍。

你觉的1位控制8位输出还不够?

在上面的程序中用到的9脚,没用起作用,如果要让2个595串联起来的话,就需要它了。想一下,我们将移位寄存器的8个位填满后,再往移位寄存器中塞一个会怎么样?也许你想到了。对!移位寄存器的最后一个位数据会被挤出去,从哪里出去?就是从9脚输出的。如果我们把第一个595的9脚连接到第二个的串行数据输入脚SER,那么,就形成了595的级联。这样,如果我们用2个595组合成了一个新的超级595,这个超级595的移位寄存器和存储寄存器的容量都翻倍了,1口控制16口,有木有!你还可以继续级联下去!

74HC595级联实例

  1. #ifndef __HC_595_H__
  2. #define __HC_595_H__
  3. #include "stm32h7xx_hal.h"
  4. // HC595的16位输出端定义位号
  5. // Q0 - 0 .....Q15 -15
  6. #define DIO_HC_A0 (6)
  7. #define DIO_HC_B0 (7)
  8. #define DIO_HC_C0 (0)
  9. #define DIO_HC_A1 (15)
  10. #define DIO_HC_B1 (8)
  11. #define DIO_HC_C1 (1)
  12. #define DIO_HC_A2 (10)
  13. #define DIO_HC_B2 (13)
  14. #define DIO_HC_C2 (14)
  15. /* RLY */
  16. #define DIO_RLY0 (5)
  17. #define DIO_RLY1 (2)
  18. /* S1 S2 */
  19. #define DIO_S1 (11)
  20. #define DIO_S2 (12)
  21. /* CUR */
  22. #define DIO_CUR (9)
  23. /*----------------------------------------- HC595 引脚配置宏 -----------------------------------------------*/
  24. #define HC_595_DIN_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE() // 使能DIN引脚时钟
  25. #define HC_595_DIN_PORT GPIOE // DIN引脚端口
  26. #define HC_595_DIN_PIN GPIO_PIN_1 // DIN引脚
  27. #define HC_595_RCK_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE() // 使能RCK引脚时钟
  28. #define HC_595_RCK_PORT GPIOE // RCK引脚端口
  29. #define HC_595_RCK_PIN GPIO_PIN_3 // RCK引脚
  30. #define HC_595_SCK_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE() // 使能SCK引脚时钟
  31. #define HC_595_SCK_PORT GPIOE // SCK引脚端口
  32. #define HC_595_SCK_PIN GPIO_PIN_2 // SCK引脚
  33. #define HC_595_SCLR_ENABLE __HAL_RCC_GPIOC_CLK_ENABLE() // 使能SCLR引脚时钟
  34. #define HC_595_SCLR_PORT GPIOC // SCLR引脚端口
  35. #define HC_595_SCLR_PIN GPIO_PIN_4 // SCLR引脚
  36. /*-------------------------------------------- IO口操作 ---------------------------------------------------*/
  37. #define HC_595_DIN(a) if (a) \
  38. HAL_GPIO_WritePin(HC_595_DIN_PORT, HC_595_DIN_PIN, GPIO_PIN_SET); \
  39. else \
  40. HAL_GPIO_WritePin(HC_595_DIN_PORT, HC_595_DIN_PIN, GPIO_PIN_RESET)
  41. #define HC_595_RCK(a) if (a) \
  42. HAL_GPIO_WritePin(HC_595_RCK_PORT, HC_595_RCK_PIN, GPIO_PIN_SET); \
  43. else \
  44. HAL_GPIO_WritePin(HC_595_RCK_PORT, HC_595_RCK_PIN, GPIO_PIN_RESET)
  45. #define HC_595_SCK(a) if (a) \
  46. HAL_GPIO_WritePin(HC_595_SCK_PORT, HC_595_SCK_PIN, GPIO_PIN_SET); \
  47. else \
  48. HAL_GPIO_WritePin(HC_595_SCK_PORT, HC_595_SCK_PIN, GPIO_PIN_RESET)
  49. #define HC_595_SCLR(a) if (a) \
  50. HAL_GPIO_WritePin(HC_595_SCLR_PORT, HC_595_SCLR_PIN, GPIO_PIN_SET); \
  51. else \
  52. HAL_GPIO_WritePin(HC_595_SCLR_PORT, HC_595_SCLR_PIN, GPIO_PIN_RESET)
  53. // 函数声明
  54. void HC_595_GPIO_Config(void);
  55. void HC_595_Send_Byte(unsigned short Q15_Q0);
  56. void HC595_Write_QX(unsigned short index,unsigned short sta);
  57. #endif
  1. #include "hc595.h"
  2. static unsigned short Q0_Q15_S = 0;
  3. /*****************************************************************************************
  4. * 函数名: HC595_Delay
  5. * 入口参数: t - 延时时间,以时钟周期数为单位
  6. * 返回值: 无
  7. * 函数功能: 简单延时函数
  8. * 说明: 为了移植的简便性且对延时精度要求不高,所以不需要使用定时器做延时
  9. ******************************************************************************************/
  10. void HC595_Delay(unsigned int t)
  11. {
  12. while(t--); // 简单的循环延时,延时时间由入口参数 t 决定
  13. }
  14. /*****************************************************************************************
  15. * 函数名: HC_595_GPIO_Config
  16. * 入口参数: 无
  17. * 返回值: 无
  18. * 函数功能: 初始化移位寄存器的 GPIO 口
  19. * 说明: 配置数据输入引脚(DIN)、存储寄存器时钟引脚(RCK)、移位寄存器时钟引脚(SCK)和清除引脚(SCLR)为推挽输出模式,
  20. * 不带上下拉,速度配置为低速。然后将这些引脚初始化,并设置初始电平状态。
  21. ******************************************************************************************/
  22. void HC_595_GPIO_Config(void)
  23. {
  24. GPIO_InitTypeDef GPIO_InitStruct = {0};
  25. // 初始化IO口时钟
  26. HC_595_DIN_ENABLE;
  27. HC_595_RCK_ENABLE;
  28. HC_595_SCK_ENABLE;
  29. HC_595_SCLR_ENABLE;
  30. // 配置DIN引脚
  31. GPIO_InitStruct.Pin = HC_595_DIN_PIN;
  32. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
  33. GPIO_InitStruct.Pull = GPIO_NOPULL; // 不带上下拉
  34. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
  35. HAL_GPIO_Init(HC_595_DIN_PORT, &GPIO_InitStruct);
  36. // 配置RCK引脚
  37. GPIO_InitStruct.Pin = HC_595_RCK_PIN;
  38. HAL_GPIO_Init(HC_595_RCK_PORT, &GPIO_InitStruct);
  39. // 配置SCK引脚
  40. GPIO_InitStruct.Pin = HC_595_SCK_PIN;
  41. HAL_GPIO_Init(HC_595_SCK_PORT, &GPIO_InitStruct);
  42. // 配置SCLR引脚
  43. GPIO_InitStruct.Pin = HC_595_SCLR_PIN;
  44. GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
  45. HAL_GPIO_Init(HC_595_SCLR_PORT, &GPIO_InitStruct);
  46. // 初始化引脚状态
  47. HAL_GPIO_WritePin(HC_595_RCK_PORT, HC_595_RCK_PIN, GPIO_PIN_RESET); // RCK输出低电平
  48. HAL_GPIO_WritePin(HC_595_SCK_PORT, HC_595_SCK_PIN, GPIO_PIN_RESET); // SCK输出低电平
  49. HAL_GPIO_WritePin(HC_595_SCLR_PORT, HC_595_SCLR_PIN, GPIO_PIN_SET); // SCLR输出高电平
  50. }
  51. /*****************************************************************************************
  52. * 函数名: HC_595_Send_Byte
  53. * 入口参数: Q15_Q0 - 要发送的16位数据
  54. * 返回值: 无
  55. * 函数功能: 向移位寄存器发送一个16位数据
  56. * 说明: 首先设置移位寄存器的控制引脚为初始状态,然后逐位发送数据,最后将数据加载到移位寄存器中。
  57. ******************************************************************************************/
  58. void HC_595_Send_Byte(unsigned short Q15_Q0)
  59. {
  60. // 设置移位寄存器的控制引脚初始状态
  61. HC_595_DIN(0);
  62. HC_595_RCK(0);
  63. HC_595_SCK(0);
  64. HC595_Delay(10);
  65. for( int i = 0 ; i < 16 ; i ++ )
  66. {
  67. // 逐位发送数据
  68. HC_595_SCK(0);
  69. if( Q15_Q0 & 0x8000 )
  70. {
  71. HC_595_DIN(1);
  72. }
  73. else
  74. {
  75. HC_595_DIN(0);
  76. }
  77. HC595_Delay(1);
  78. HC_595_SCK(1); // 移位寄存器输入一个字节数据
  79. HC595_Delay(1);
  80. Q15_Q0 <<= 1;
  81. }
  82. // 将数据加载到移位寄存器中
  83. HC_595_RCK(1); // 存储寄存器输入,Q1-Q15输出
  84. HC595_Delay(1);
  85. HC_595_RCK(0);
  86. HC_595_SCK(0);
  87. }
  88. /* w_gpio_sem */
  89. static unsigned char w_gpio_sem = 0;
  90. /*****************************************************************************************
  91. * 函数名: HC595_Write_QX
  92. * 入口参数: index - 输出引脚的编号,sta - 输出状态,1为高电平,0为低电平
  93. * 返回值: 无
  94. * 函数功能: 控制移位寄存器的输出引脚状态
  95. * 说明: 根据输入的输出引脚编号和状态,更新移位寄存器中对应引脚的状态,并发送更新后的状态到移位寄存器。
  96. ******************************************************************************************/
  97. void HC595_Write_QX(unsigned short index,unsigned short sta)
  98. {
  99. // 检查是否有其他操作在进行
  100. if( w_gpio_sem )
  101. {
  102. return;
  103. }
  104. // 锁定
  105. w_gpio_sem = 1;
  106. // 检查输出引脚编号是否合法
  107. if( index > 16 )
  108. {
  109. return;
  110. }
  111. // 根据状态设置输出引脚状态
  112. if( sta )
  113. {
  114. Q0_Q15_S |= ( 1 << index );
  115. }
  116. else
  117. {
  118. Q0_Q15_S &=~ ( 1 << index );
  119. }
  120. // 更新移位寄存器中的输出状态
  121. HC_595_Send_Byte(Q0_Q15_S);
  122. // 解锁
  123. w_gpio_sem = 0;
  124. }
  1. HC_595_GPIO_Config: 初始化HC595芯片的GPIO口,包括数据输入引脚(DIN)、存储寄存器时钟引脚(RCK)、移位寄存器时钟引脚(SCK)和清除引脚(SCLR)。配置这些引脚为推挽输出模式,并设置初始电平状态。

  2. HC_595_Send_Byte: 向HC595芯片发送一个16位数据,通过移位寄存器将数据加载到芯片中,控制输出引脚的状态。

  3. HC595_Write_QX: 根据输入的输出引脚编号和状态,更新移位寄存器中对应引脚的状态,并通过 HC_595_Send_Byte 函数发送更新后的状态到移位寄存器。

  4. HC595_Delay: 简单的延时函数,用于产生一定时间的延时,以时钟周期数为单位。

注意 

HC595的DIN输入端输出的数据会从Q0开始,继续输入,之前的Q0数据会移位至Q1,一直循环。

完整的输入一个字节数据,假设输入的是unsigned char Q_byte =(高)1011 1111(低) 。因为HC_595_Send_Byte程序中是从高位开始发送的,最高位的数据再经过完整的一个字节循环后就被push到了Q7(很像队列)

Q0 = 1;Q1=1;Q2=1;Q3=1;Q4=1;Q5=1;Q6=0;Q7=1;

我们要改变Q0-Q7中的某个位做法:

假设改变Q3位,则把 (高)1011 0111(低)重新写入HC595中。

Q_byte = Q_byte & ~ (1<<3)  = 1011 1111 & 1111 0111 = 1011 0111;

这是一个595的情况,级联情况下同理,代码,处理的就是级联情况。所以应用HC595_Write_QX函数就可以操作Q0-Q15任意一个输出位的电平了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/801779
推荐阅读
相关标签
  

闽ICP备14008679号