1. 74HC595介绍
74HC595是一个8位串行输入、并行输出的位移缓存器:并行输出为三态输出。在SCK 的上升沿,串行数据由数据脚(A)输入到内部的8位位移缓存器,并由Q7’输出,而并行输出则是在LCK的上升沿将在8位位移缓存器的数据存入到8位并行输出缓存器。当串行数据输入端OE的控制信号为低使能时,并行输出端的输出值等于并行输出缓存器所存储的值
2.思路分析
由真值表可知:
-
当移位寄存器时钟线为上升沿时,会获取当时数据引脚的数据 ,即SDA当时的电平状态,存入移位寄存器中,若 SER引脚为低电平,则从低位开始存储。如果SER引脚为高电平,则从高位开始存储。
-
由于移位寄存器只有八位,当数据超过8位时,多出的位数将会通过SQH串行输出,一般将SQH与下一级的数据输入引脚(SDA)相连,实现级联。
-
当存储寄存器时钟线为上升沿时,会将移位寄存器中的数据存储到存储寄存器当中。
-
当OE脚为低电平时,会将存储寄存器中的数据输出至总线,通过存储寄存器的数据实现对 QA-QH的控制。
根据以上信息,可以得出结论,如果要控制QA-QH的电平输出,控制分为四个步骤:
-
SDA输入数据。
-
SCK给一个上升沿,将SDA的一个数据移入移位寄存器,循环八次。
-
RCK给一个上升沿,将移位寄存器的数据转存到存储寄存器。
-
OE脚置低,将存储寄存器中的数据输入至数据总线
3. 代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "mdf_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "gpio.h"
#include "stdint.h"
#define SCK_GPIO_PIN 13 // 移位寄存器时钟线引脚
#define RCK_GPIO_PIN 15 //存储寄存器时钟线引脚
#define SDA_GPIO_PIN 2 //数据引脚
#define _74HC595_LEVEL 2 //74HC595级联数
#define HC595_SCK_Low() gpio_set_level(SCK_GPIO_PIN,0) //SCK置低
#define HC595_SCK_High() gpio_set_level(SCK_GPIO_PIN,1) //SCK置高
#define HC595_RCK_Low() gpio_set_level(RCK_GPIO_PIN,0) //RCK置低
#define HC595_RCK_High() gpio_set_level(RCK_GPIO_PIN,1) //RCK置高
#define HC595_Data_Low() gpio_set_level(SDA_GPIO_PIN,0) //输入低电平
#define HC595_Data_High() gpio_set_level(SDA_GPIO_PIN,1) //输入高电平
/*
@brief : 74HC595控制引脚初始化
*/
void _74hc595_init(void)
{
gpio_config_t io_conf = {
.intr_type = GPIO_PIN_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL<<SCK_GPIO_PIN)|(1ULL<<RCK_GPIO_PIN)|(1ULL<<SDA_GPIO_PIN),
.pull_down_en = 1,
.pull_up_en = 0,
};
gpio_config(&io_conf);
HC595_SCK_Low();
HC595_RCK_Low();
HC595_Data_Low();
}
/*
@brief:74HC595输出锁存 使能
*/
void HC595_Save(void)
{
/** 步骤3:RCK产生一个上升沿,移位寄存器的数据移入存储寄存器 **/
HC595_RCK_Low(); // 将RCK拉低
delay(10);
HC595_RCK_High(); // 再将RCK拉高,RCK即可产生一个上升沿
}
/***
*74HC595 发送一个字节
*即往74HC595的SDA引脚发送一个字节
*/
void HC595_Send_Byte(uint8_t byte)
{
uint8_t i;
for (i = 0; i < 8; i ++) //一个字节8位,传输8次,一次一位,循环8次,刚好移完8位
{
/**** 步骤1:将数据传到DS引脚 ****/
HC595_SCK_Low(); // SCK拉低
if (byte & 0x80){ //先传输高位,通过与运算判断第八是否为1
HC595_Data_High(); //如果第八位是1,则与 595 DS连接的引脚输出高电平
}else{ //否则输出低电平
HC595_Data_Low();
}
/*** 步骤2:SCK每产生一个上升沿,当前的bit就被送入移位寄存器 ***/
byte <<= 1; // 左移一位,将低位往高位移,通过 if (byte & 0x80)判断低位是否为1
HC595_SCK_High(); // SHCP拉高, SHCP产生上升沿
}
}
/**
*发送多个字节
*便于级联时数据的发送
*级联N级,就需要发送N个字节控制HC595
***/
void HC595_Send_Multi_Byte(uint8_t *data, uint16_t len)
{
uint8_t i;
// len 个字节
for (i = 0; i < len; i ++ ) {
HC595_Send_Byte(data[i]);
debug_i("leve :%d data:%x",i,data[i]);
}
HC595_Save();
}
/*
@brief: 测试函数
*/
void _74hc595_test(void)
{
uint8_t place=0; //级联位置
uint8_t floor=0; //级联层级
uint8_t ele[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
while(1)
{
uint8_t led_Pos_Buf[_74HC595_LEVEL];
int max_num=_74HC595_LEVEL*8;
for (int i=1;i<num+1;i++){
vTaskDelay(300); //延时500毫秒
memset(ele_Pos_Buf,0,_74HC595_LEVEL);
floor=(i-1)/8;
if(0!=i%8){
place=i%8-1;
}else{
place=7;
}
ele_Pos_Buf[_74HC595_LEVEL-floor-1] = ele[7-place];
debug_w("floor :%d place:%d ",floor+1,place);
HC595_Send_Multi_Byte(ele_Pos_Buf,_74HC595_LEVEL);//将当前数据发送到595
}
}
}