74HC595详细资料及使用方法参照以下文章:
https://vuko-wxh.blog.csdn.net/article/details/80500046
https://recclay.blog.csdn.net/article/details/78245642
https://snmplink.blog.csdn.net/article/details/102729009
https://blog.csdn.net/k1ang/article/details/80012686
https://blog.csdn.net/yangmolulu/article/details/79242973
https://blog.csdn.net/k1ang/article/details/80012463
https://blog.csdn.net/k1ang/article/details/80397173
项目使用8片74HC595级联控制64个继电器动作,每片74HC595控制8个继电器。
74HC595的级联电路如下:
74HC595的级联
继电器阵列
IO控制引脚
代码部分如下:
my_74hc595.h:
#ifndef APPLICATIONS_MY_74HC595_H_
#define APPLICATIONS_MY_74HC595_H_
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define HC595_RCLK GET_PIN(B,12)//存储寄存器时钟输入引脚
#define HC595_SCLK GET_PIN(B,13)//移位寄存器时钟引脚
#define HC595_DIO GET_PIN(B,15)//数据输入
#define HC595_NOE GET_PIN(C,6) //输出使能控制,低电平使能输出
#define RT_HC595_RCLK_HIGH(x) x?rt_pin_write(HC595_RCLK, PIN_HIGH):rt_pin_write(HC595_RCLK, PIN_LOW)
#define RT_HC595_SCLK(x) x?rt_pin_write(HC595_SCLK, PIN_HIGH):rt_pin_write(HC595_SCLK, PIN_LOW)
#define RT_HC595_DIO(x) x?rt_pin_write(HC595_DIO, PIN_HIGH):rt_pin_write(HC595_DIO, PIN_LOW)
#define RT_HC595_NOE(x) x?rt_pin_write(HC595_NOE, PIN_HIGH):rt_pin_write(HC595_NOE, PIN_LOW)
void my_hc595_pin_init(void);
void BK1_On_test(void);
void BK2_On_test(void);
void BK3_On_test(void);
void BK4_On_test(void);
void BK5_On_test(void);
void BK6_On_test(void);
void BK7_On_test(void);
void BK8_On_test(void);
void BK1_Off_test(void);
void BK2_Off_test(void);
void BK3_Off_test(void);
void BK4_Off_test(void);
void BK5_Off_test(void);
void BK6_Off_test(void);
void BK7_Off_test(void);
void BK8_Off_test(void);
void All_BK_On_test(void);
void All_BK_Off_test(void);
#endif /* APPLICATIONS_MY_74HC595_H_ */
my_74hc595.c:
#include "my_74hc595.h"
static rt_thread_t tid_hc595 = RT_NULL;
//hc595处理线程
//此线程实现64个继电器间隔1s吸合断开
static void thread_hc595_entry(void )
{
while (1)
{
All_BK_Off_test();
rt_thread_mdelay(1000);
All_BK_On_test();
rt_thread_mdelay(1000);
}
}
//初始化hc595,并创建hc595线程
int thread_hc595(void)
{
// 初始化HC595
// my_hc595_pin_init();//mian中已初始化
/* 创建线程 */
tid_hc595 = rt_thread_create("thread hc595",
thread_hc595_entry,
NULL,
512,
8, 5);
if (tid_hc595 != RT_NULL)
{
rt_thread_startup(tid_hc595);
rt_kprintf("tid_hc595 run!\r\n");
}
rt_thread_mdelay(100);
return 0;
}
INIT_APP_EXPORT(thread_hc595);
//控制继电器状态的buf,将64个继电器分为8组,每组8个继电器
//buf中存放向74HC595传送的数据,0xFF为继电器全部吸合,0x00为继电器全部断开
uint8_t BK1_on_buf[1] = {0xAA};
uint8_t BK2_on_buf[2] = {0xff};
uint8_t BK3_on_buf[3] = {0xff};
uint8_t BK4_on_buf[4] = {0xff};
uint8_t BK5_on_buf[5] = {0xff};
uint8_t BK6_on_buf[6] = {0xff};
uint8_t BK7_on_buf[7] = {0xff};
uint8_t BK8_on_buf[8] = {0xff};
uint8_t All_BK_on_buf[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
uint8_t BK1_off_buf[1] = {0x00};
uint8_t BK2_off_buf[2] = {0x00};
uint8_t BK3_off_buf[3] = {0x00};
uint8_t BK4_off_buf[4] = {0x00};
uint8_t BK5_off_buf[5] = {0x00};
uint8_t BK6_off_buf[6] = {0x00};
uint8_t BK7_off_buf[7] = {0x00};
uint8_t BK8_off_buf[8] = {0x00};
uint8_t All_BK_off_buf[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
/***
*74HC595 IO初始化
*时钟、数据、使能脚
*/
void my_hc595_pin_init(void)
{
rt_pin_mode(HC595_RCLK, PIN_MODE_OUTPUT);//初始化IO
rt_pin_mode(HC595_SCLK, PIN_MODE_OUTPUT);//初始化IO
rt_pin_mode(HC595_DIO, PIN_MODE_OUTPUT);//初始化IO
rt_pin_mode(HC595_NOE, PIN_MODE_OUTPUT);//初始化IO
//初始化输出低电平
rt_pin_write(HC595_RCLK, 0);
rt_pin_write(HC595_SCLK, 0);
rt_pin_write(HC595_NOE , 0);
//IO设置成输出模式
//此处使用8个引脚直接控制64个继电器之外的其他继电器
rt_pin_mode(AC1_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC2_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC3_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC4_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC5_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC6_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC7_Ctr,PIN_MODE_OUTPUT);
rt_pin_mode(AC8_Ctr,PIN_MODE_OUTPUT);
rt_kprintf("relay_IO_Init Success!\r\n");
}
/***
*74HC595 发送一个字节
*即往74HC595的DS引脚发送一个字节
*/
void HC595_Send_Byte(uint8_t byte)
{
uint8_t i;
for (i = 0; i < 8; i ++) //一个字节8位,传输8次,一次一位,循环8次,刚好移完8位
{
/**** 步骤1:将数据传到DS引脚 ****/
if (byte & 0x80) //先传输高位,通过与运算判断第八是否为1
{
RT_HC595_DIO(1); //如果第八位是1,则与 595 DS连接的引脚输出高电平
}
else
{
RT_HC595_DIO(0); //否则输出低电平
}
/*** 步骤2:SHCP每产生一个上升沿,当前的bit就被送入移位寄存器 ***/
RT_HC595_SCLK(0); // SHCP拉低
rt_thread_delay(1); // 适当延时
//rt_hw_us_delay(10);
RT_HC595_SCLK(1); // SHCP拉高, SHCP产生上升沿
rt_thread_delay(1); // RT_TICK_PER_SECOND 1000
//rt_hw_us_delay(10);
byte <<= 1; // 左移一位,将低位往高位移,通过 if (byte & 0x80)判断低位是否为1
}
}
/**
*74HC595输出锁存 使能
**/
void HC595_CS(void)
{
/** 步骤3:STCP产生一个上升沿,移位寄存器的数据移入存储寄存器 **/
RT_HC595_RCLK_HIGH(0); // 将STCP拉低
rt_thread_delay(1); // 适当延时
// rt_hw_us_delay(10);
RT_HC595_RCLK_HIGH(1); // 再将STCP拉高,STCP即可产生一个上升沿
rt_thread_delay(1);
// rt_hw_us_delay(10);
}
/**
*发送多个字节
*便于级联时数据的发送
*级联N级,就需要发送N个字节控制HC595
***/
void HC595_Send_Multi_Byte(uint8_t *data, uint16_t len)
{
uint8_t i;
for (i = 0; i < len; i ++ ) // len 个字节
{
HC595_Send_Byte(data[i]);
}
HC595_CS(); //先把所有字节发送完,再使能输出
}
//以下函数为测试每组继电器吸合状态
void BK1_On_test(void)
{
HC595_Send_Multi_Byte(BK1_on_buf,1);
}
MSH_CMD_EXPORT(BK1_On_test, bk1_relay_on_test);//自定义msh命令
//FINSH_FUNCTION_EXPORT_ALIAS(BK1_On_test, B1_on, bk1_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
//__cmd_
void BK2_On_test(void)
{
HC595_Send_Multi_Byte(BK2_on_buf,2);
}
MSH_CMD_EXPORT(BK2_On_test, bk2_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK2_On_test, B2_on, bk2_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK3_On_test(void)
{
HC595_Send_Multi_Byte(BK3_on_buf,3);
}
MSH_CMD_EXPORT(BK3_On_test, bk3_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK3_On_test, B3_on, bk3_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK4_On_test(void)
{
HC595_Send_Multi_Byte(BK4_on_buf,4);
}
MSH_CMD_EXPORT(BK4_On_test, bk4_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK4_On_test, B4_on, bk4_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK5_On_test(void)
{
HC595_Send_Multi_Byte(BK5_on_buf,5);
}
MSH_CMD_EXPORT(BK5_On_test, bk5_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK5_On_test, B5_on, bk5_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK6_On_test(void)
{
HC595_Send_Multi_Byte(BK6_on_buf,6);
}
MSH_CMD_EXPORT(BK6_On_test, bk6_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK6_On_test, B6_on, bk6_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK7_On_test(void)
{
HC595_Send_Multi_Byte(BK7_on_buf,7);
}
MSH_CMD_EXPORT(BK7_On_test, bk7_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK7_On_test, B7_on, bk7_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void BK8_On_test(void)
{
HC595_Send_Multi_Byte(BK8_on_buf,8);
}
MSH_CMD_EXPORT(BK8_On_test, bk8_relay_on_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK8_On_test, B8_on, bk8_relay_on_test);//自定义命令重命名,命令导出到 msh 模式
void All_BK_On_test(void)
{
HC595_Send_Multi_Byte(All_BK_on_buf,8);
}
MSH_CMD_EXPORT(All_BK_On_test, All_BK_On_test);
//以下函数为测试每组继电器断开状态
void BK1_Off_test(void)
{
HC595_Send_Multi_Byte(BK1_off_buf,1);
}
MSH_CMD_EXPORT(BK1_Off_test, bk1_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK1_Off_test, B1_on, bk1_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK2_Off_test(void)
{
HC595_Send_Multi_Byte(BK2_off_buf,2);
}
MSH_CMD_EXPORT(BK2_Off_test, bk2_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK2_Off_test, B2_on, bk2_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK3_Off_test(void)
{
HC595_Send_Multi_Byte(BK3_off_buf,3);
}
MSH_CMD_EXPORT(BK3_Off_test, bk3_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK3_Off_test, B3_on, bk3_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK4_Off_test(void)
{
HC595_Send_Multi_Byte(BK4_off_buf,4);
}
MSH_CMD_EXPORT(BK4_Off_test, bk4_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK4_Off_test, B4_on, bk4_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK5_Off_test(void)
{
HC595_Send_Multi_Byte(BK5_off_buf,5);
}
MSH_CMD_EXPORT(BK5_Off_test, bk5_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK5_Off_test, B5_on, bk5_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK6_Off_test(void)
{
HC595_Send_Multi_Byte(BK6_off_buf,6);
}
MSH_CMD_EXPORT(BK6_Off_test, bk6_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK6_Off_test, B6_on, bk6_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK7_Off_test(void)
{
HC595_Send_Multi_Byte(BK7_off_buf,7);
}
MSH_CMD_EXPORT(BK7_Off_test, bk7_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK7_Off_test, B7_on, bk7_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void BK8_Off_test(void)
{
HC595_Send_Multi_Byte(BK8_off_buf,8);
}
MSH_CMD_EXPORT(BK8_Off_test, bk8_relay_off_test);
//FINSH_FUNCTION_EXPORT_ALIAS(BK8_Off_test, B8_on, bk8_relay_off_test);//自定义命令重命名,命令导出到 msh 模式
void All_BK_Off_test(void)
{
HC595_Send_Multi_Byte(All_BK_off_buf,8);
}
MSH_CMD_EXPORT(All_BK_Off_test, All_BK_Off);
//IO口单独控制其他继电器
void BK10_All_On(void)
{
RT_AC1_Ctr(1);
RT_AC2_Ctr(1);
RT_AC3_Ctr(1);
RT_AC4_Ctr(1);
RT_AC5_Ctr(1);
RT_AC6_Ctr(1);
RT_AC7_Ctr(1);
RT_AC8_Ctr(1);
}
MSH_CMD_EXPORT(BK10_All_On, bk10_relay_on_test);
void BK10_All_Off(void)
{
RT_AC1_Ctr(0);
RT_AC2_Ctr(0);
RT_AC3_Ctr(0);
RT_AC4_Ctr(0);
RT_AC5_Ctr(0);
RT_AC6_Ctr(0);
RT_AC7_Ctr(0);
RT_AC8_Ctr(0);
}
MSH_CMD_EXPORT(BK10_All_Off, bk10_relay_off_test);
main.c
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "my_74hc595.h"
int main(void)
{
LOG_D("ChannelCard Test!");
LOG_D("System Clock information");
LOG_D("SYSCLK_Frequency = %d", HAL_RCC_GetSysClockFreq());
LOG_D("HCLK_Frequency = %d", HAL_RCC_GetHCLKFreq());
LOG_D("PCLK1_Frequency = %d", HAL_RCC_GetPCLK1Freq());
LOG_D("PCLK2_Frequency = %d", HAL_RCC_GetPCLK2Freq());
rt_kprintf("\r\n");
my_hc595_pin_init();//初始化IO
All_BK_Off_test();//关闭所有继电器
while (1)
{
//LOG_D("Hello RT-Thread!");
rt_thread_mdelay(1000);
}
return RT_EOK;
}
示波器查看74HC595通信波形如下:
CH1为数据管脚(DS),CH2为 数据输入时钟(SHCP)
向74HC595写入0xAA:
向两片74HC595分别写入0xFF和0x00:
向8片74HC595写入0xFF:
8片74HC595控制64个继电器以1s间隔吸合断开: