14【模块学习】74HC595:使用学习

1、74HC595简介

在51单片机中IO引脚资源十分的紧缺,所以常常需要使用75HC595芯片进行驱动那些需要占用多个IO引脚的芯片。那么75HC595是是什么喃?

74H595是一款串转并移位控制的芯片,通过移位的方式将串行输入的数据变为并行输出的数据

在这里插入图片描述

引脚功能
VCC供电正极(2v~6v)
GND供电负极
DS串行数据输入引脚(8位)
Q7~Q0并行数据输出引脚(Q7对应数据高位)
SHCP移位寄存器时钟输入
STCP输出寄存器时钟输入
Q7S8位数据串行输出引脚(常用于级联)
OE输出使能引脚

在这里插入图片描述

2、代码演示

在这里插入图片描述

2.1、驱动8位流水灯

①H595.c文件的代码如下:

#include "H595.h"

/**
 * 使用74HC595串行数据转换位并行数据
 * Byte:输入的串行数据
 */
void HC595_Driver(unsigned char Byte)
{
	unsigned char i = 0;
	HC595_ST = 0;
	for(i = 0; i < 8; i++)
	{
		HC595_SH = 0;					//SH低电平,将数据写入到输入数据引脚上
		HC595_DS = (Byte & (0x80 >> i));//高位先行
		HC595_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	HC595_ST = 1;		//595的ST引脚来一个上升沿将移位寄存器的数据移到存储寄存器
	HC595_OE = 0;		//输出使能
}

②H595.h文件的代码如下:

#ifndef __H595_H
#define __H595_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

sbit HC595_SH = P1^2;	//移位时钟引脚
sbit HC595_DS = P1^3;	//HC595输入数据口
sbit HC595_ST = P1^4;	//存储时钟引脚
sbit HC595_OE = P1^5;	//输出使能引脚

void HC595_Driver(unsigned char Byte);

#endif

③main.c文件的代码如下:

#include "H595.h"
#include "Delay.h"
#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	
	while(1)
	{
		unsigned char temp = 0x01;
		unsigned char i;
		for(i = 1; i < 8; i++)	//循环7次
		{
			HC595_Driver(temp);
			temp <<= 1;			//循环结束num = 1000 0000
			Delay_ms(500);		//延时500ms
		}
		
		for(i = 1; i < 8; i++)	//循环7次
		{
			HC595_Driver(temp);
			temp >>= 1;			//循环结束num = 0000 0001
			Delay_ms(500);		//延时500ms
		}
	}
}

3、74HC595级联

综上:74HC595的Q7S引脚是串行输出引脚,常常用于多片的75HC595芯片的级联。2片级联,则可看成一个2字节数据串转并移位寄存器,4片级联,则可看成4字节数据串转并移位寄存器

在这里插入图片描述
如图:2片74HC595进行级联,他们的SH引脚,ST引脚,OE引脚都连接在一起,第一片H595芯片的Q7S连接着第二片芯片的数据输入引脚。

3.1、驱动16位流水灯

在这里插入图片描述
①H595.c文件的代码如下:

/**
 * 2片74HC595的级联
 * HByte:高位字节的数据
 * LByte:低位字节的数据
 */
void HC595_Cascade(unsigned char HByte, unsigned char LByte)
{	
	unsigned char i = 0;
	HC595_ST = 0;
	
	/* 将高位字节的数据移位到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		HC595_SH = 0;
		HC595_DS = (HByte & (0x80 >> i));//高位先行
		HC595_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	
	/* 将高位字节的数据移位到第二个595里面,低位字节的数据移到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		HC595_SH = 0;
		HC595_DS = (LByte & (0x80 >> i));//高位先行
		HC595_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	HC595_ST = 1;		//595的ST引脚来一个上升沿将移位寄存器的数据移到存储寄存器
	HC595_OE = 0;		//输出使能
}

②H595.h文件的代码如下:

#ifndef __H595_H
#define __H595_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

sbit HC595_SH = P1^2;	//移位时钟引脚
sbit HC595_DS = P1^3;	//HC595输入数据口
sbit HC595_ST = P1^4;	//存储时钟引脚
sbit HC595_OE = P1^5;	//输出使能引脚

void HC595_Cascade(unsigned char HByte, unsigned char LByte);;

#endif

③main.c文件的代码如下:

#include "H595.h"
#include "Delay.h"
#include "Timer.h"

void main(void)
{
	Time0_Intrrupt_Init();
	
	while(1)
	{
		unsigned char Htemp = 0x01;
		unsigned char Ltemp = 0x01;
		unsigned char i;
		for(i = 1; i < 8; i++)		//循环7次
		{
			HC595_Cascade(0x00, Ltemp);
			Ltemp <<= 1;			//S循环结束num = 1000 0000
			Delay_ms(500);			//延时500ms
		}
		HC595_Cascade(0x00, Ltemp);
		Delay_ms(500);				//延时500ms
		for(i = 1; i < 8; i++)		//循环7次
		{
			HC595_Cascade(Htemp, 0x00);
			Htemp <<= 1;			//S循环结束num = 1000 0000
			Delay_ms(500);			//延时500ms
		}		
		for(i = 1; i < 8; i++)		//循环7次
		{
			HC595_Cascade(Htemp, 0x00);
			Htemp >>= 1;			//循环结束num = 0000 0001
			Delay_ms(500);			//延时500ms
		}
		HC595_Cascade(Htemp, 0x00);
		Delay_ms(500);				//延时500ms
		for(i = 1; i < 8; i++)		//循环7次
		{
			HC595_Cascade(0x00, Ltemp);
			Ltemp >>= 1;			//循环结束num = 0000 0001
			Delay_ms(500);			//延时500ms
		}
	}
}

3.2、驱动8位数码管

在这里插入图片描述

①H595_SEG8.c文件的代码如下:

/***********************************/
/*    8位数码管的底层驱动文件      */
/***********************************/

#include "H595_Seg8.h"

/**
 * 2片74HC595的级联
 * PossData:位选数据
 * SegData:段码数字据
 */
void HC595_Cascade_Seg8(unsigned char PossData, unsigned char SegData)
{	
	unsigned char i = 0;
	SEG_ST = 0;
	
	/* 将高位字节的数据移位到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		SEG_SH = 0;
		SEG_DS = (PossData & (0x80 >> i));//高位先行
		SEG_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	
	/* 将高位字节的数据移位到第二个595里面,低位字节的数据移到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		SEG_SH = 0;
		SEG_DS = (SegData & (0x80 >> i));//高位先行
		SEG_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	SEG_ST = 1;		//595的ST引脚来一个上升沿将移位寄存器的数据移到存储寄存器
	SEG_OE = 0;		//输出使能
}

/* 字符数据 */
static unsigned char code Data8[] ={
		0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, //共阳极显示的数值0 ~ 9
		0xBF,0x7F,0x8C									   //-,.P
};

/* 显示的数值缓存区 */
unsigned char SegNum8[] = {0,0,0,0,0,0,0,0};

/**
 * 选择哪个数码管显示数据的函数
 * Position:哪一个数码管
 * Data:需要显示的数据
 */
void Seg8_Choice(unsigned char Position, unsigned char DisplayData)
{
	HC595_Cascade_Seg8(0x00,0x00);												//让8个数码管先熄灭
	HC595_Cascade_Seg8((0x01 << (Position - 1)), Data8[SegNum8[DisplayData]]);	//传入段码数据进行显示
}

②H595_SEG8.h文件的代码如下:

#ifndef __H595_Seg8_H
#define __H595_Seg8_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

sbit SEG_SH = P0^0;	//移位时钟引脚
sbit SEG_DS = P0^1;	//HC595输入数据口
sbit SEG_ST = P0^2;	//存储时钟引脚
sbit SEG_OE = P0^3;	//输出使能引脚

extern unsigned char SegNum8[];

void HC595_Cascade_Seg8(unsigned char PossData, unsigned char SegData);
void Seg8_Choice(unsigned char Position, unsigned char DisplayData);

#endif

③User_SEG8.c文件的代码如下:

/********************************************/
/*    使用74HC595驱动8位数码管的应用层       */
/********************************************/
#include "User_Seg8.h"

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

/**
 * X的Y次方函数
 */
unsigned int SEG8_Pow(unsigned char X,unsigned char Y)
{
	unsigned int Result = 1;
	while(Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
 * 数码管显示正整数
 * Number:0~65535
 * Length:有效数值长度
 * Lie:   显示的位置1~8
 */
void SEG8_Show_Num(unsigned char Lie,unsigned short Number,unsigned char Length)
{
	/* 将整数进行拆分在通过数码管显示 */
	unsigned char i,Data;
	for(i = 0; i < Length; i++)
	{
		Data = (Number / SEG8_Pow(10,Length - i -1)) % 10;//将整数进行拆分
		SegNum8[0] = Data;
		Seg8_Choice(i+Lie,0);//显示
		Delay1ms();
	}
}

/**
 * 数码管显示有符号整数
 * Number:-32768~32767
 * Length:有效数值长度
 * Lie:   显示的位置1~8
 */
void SEG8_Show_SignedNum(unsigned char Lie, short Number,unsigned char Length)
{
	/* 若为负数,先显示负号 */
	if(Number < 0)
	{
		Number = -Number;
		SegNum8[0] = 10;
		Seg8_Choice(Lie,0);		//显示"-"
		Lie++;
	}
	SEG8_Show_Num(Lie, Number, Length);
}

/**
 * 数码管显示有符号小数
 * Number:小数数值
 * Floatcount:显示几位小数
 * Lie:   显示的位置1~8
 * IntCount:  整数的个数
 * Floatcount:小数的个数
 */
void SEG8_Show_FloatNum(unsigned char Lie, float Number, unsigned char IntCount, unsigned char Floatcount)
{
	short Num = 0;	
	
	if(Number < 0)
	{
		Number = -Number;	//将负数转换为正数,例如:3.14 = -(-3.14)
		SegNum8[0] = 10;
		Seg8_Choice(Lie,0);	//显示"-"
		Lie++;
	}
	
	/* 先提取整数部分 */
	Num = Number;			//此时Num保存的是小数的整数部分,例如:Number = 3.14,Num = 3
	SEG8_Show_Num(Lie,Num,IntCount);
	
	/* 将小数换算为整数 */
	Number -= Num;					//Number = Number - Num,Number = 3.14 - 3 = 0.14
	Num = (short)(Number * SEG8_Pow(10,Floatcount));//将小数将小数换算为整数,0.14 * 100 = 14
	SegNum8[0] = 11;				//先添加数据的索引
	Seg8_Choice(Lie+IntCount-1,0);	//显示小数点
	
	/* 显示小数部分 */
	SEG8_Show_Num(Lie+IntCount, Num, Floatcount);
}

④User_SEG8.h文件的代码如下:

#ifndef __User_Seg8_H
#define __User_Seg8_H
#include "H595_Seg8.h"

unsigned int SEG8_Pow(unsigned char X,unsigned char Y);
void SEG8_Show_Num(unsigned char Lie,unsigned short Number,unsigned char Length);
void SEG8_Show_SignedNum(unsigned char Lie, short Number,unsigned char Length);
void SEG8_Show_FloatNum(unsigned char Lie, float Number, unsigned char IntCount, unsigned char Floatcount);

#endif

⑤main.c文件的代码如下:

#include "User_Seg8.h"

void main(void)
{		
	while(1)
	{
		SEG8_Show_FloatNum(1,-200.123,3,3);
	}
}

在这里插入图片描述

3.3、驱动8x8点阵屏幕

在这里插入图片描述

如图:点亮图上的小亮点,则阴极引脚输出的数据为:1011 1111(0xBF)选中第2列;阳引脚输出的数据为:1000 0000(0x80)表示点亮选中的列的最上的LED

LED显示字符的方法:
例如想要显示如下图是字符:

在这里插入图片描述

列扫描:从左到右依次从第一列进行扫描,即(共阴极的引脚数据依次为:0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE)。而共阳极字模数据Data[8] = {0x00,0x00,0xFF,0x08,0x08,0xFF,0x00,0x00};当进行第一列扫描时,共阳极引脚输出Data[0]

在这里插入图片描述

①Dot_LED.c文件的代码如下:

#include "Dot_LED.h"

/**
 * 2片74HC595的级联
 * LEDNode:	阳极数据,选择选定的列中哪几个LED点亮
 * LEDCathode: 阴极数据,选择哪一列点亮
 */
void HC595_Cascade_8x8LED(unsigned char LEDNode, unsigned char LEDCathode)
{	
	unsigned char i = 0;
	LED_ST = 0;
	
	/* 将高位字节的数据移位到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		LED_SH = 0;
		LED_DS = (LEDNode & (0x80 >> i));//高位先行
		LED_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	
	/* 将高位字节的数据移位到第二个595里面,低位字节的数据移到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		LED_SH = 0;
		LED_DS = (LEDCathode & (0x80 >> i));//高位先行
		LED_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	LED_ST = 1;		//595的ST引脚来一个上升沿将移位寄存器的数据移到存储寄存器
	LED_OE = 0;		//输出使能
}

/**
 * 8x8点阵显示函数(列扫描显示)
 */
void Show_8X8Dot_ColumnScan(unsigned char *Data)
{
	unsigned char i = 0;
	for(i = 0; i < 8; i++)
	{
		HC595_Cascade_8x8LED(Data[i],~(0x80 >> i));	//依次选择从左到右第1列行开始(行扫描显示)
	}
}

②Dot_LED.h文件的代码如下:

#ifndef __Dot_LED_H
#define __Dot_LED_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

sbit LED_SH = P2^0;
sbit LED_DS = P2^1;	
sbit LED_ST = P2^2;
sbit LED_OE = P2^3;

void HC595_Cascade_8x8LED(unsigned char LEDNode, unsigned char LEDCathode);
void Show_8X8Dot_ColumnScan(unsigned char *Data);

#endif

③main.c文件的代码如下:

#include "Dot_LED.h"

/* 显示H */
static unsigned char H_Data[] = {0x00,0x00,0xFF,0x08,0x08,0xFF,0x00,0x00};//纵向,上为最高位(纵向取模)
void main(void)
{		
	while(1)
	{
		Show_8X8Dot_ColumnScan(H_Data);	//列扫描显示
	}
}

在这里插入图片描述

3.4、8x8点阵屏幕滚动显示

从左向右滚动显示“HELLO”

滚动显示的原理: 点阵屏幕一直显示8个字节Buffer中的数据,我们只需要将HELLO的字模数据依次放入到Buffer中。如下图所示:
在这里插入图片描述
取模软件的使用:

在这里插入图片描述

①Dot_LED.c文件的代码如下:

#include "Dot_LED.h"
#include "Timer.h"

/**
 * 2片74HC595的级联
 * LEDNode:	阳极数据,选择选定的列中哪几个LED点亮
 * LEDCathode: 阴极数据,选择哪一列点亮
 */
void HC595_Cascade_8x8LED(unsigned char LEDNode, unsigned char LEDCathode)
{	
	unsigned char i = 0;
	LED_ST = 0;
	
	/* 将高位字节的数据移位到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		LED_SH = 0;
		LED_DS = (LEDNode & (0x80 >> i));//高位先行
		LED_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	
	/* 将高位字节的数据移位到第二个595里面,低位字节的数据移到第一个595里面 */
	for(i = 0; i < 8; i++)
	{
		LED_SH = 0;
		LED_DS = (LEDCathode & (0x80 >> i));//高位先行
		LED_SH = 1;	//595的SH引脚来一个上升沿将数据进行移位
	}	
	LED_ST = 1;		//595的ST引脚来一个上升沿将移位寄存器的数据移到存储寄存器
	LED_OE = 0;		//输出使能
}

/**
 * 8x8点阵显示函数(列扫描显示)
 */
void Show_8X8Dot_ColumnScan(unsigned char *Data)
{
	unsigned char i = 0;
	for(i = 0; i < 8; i++)
	{
		HC595_Cascade_8x8LED(0x00,~(0x80 >> i));	//向将扫描的一列的灯全部熄灭
		HC595_Cascade_8x8LED(Data[i],~(0x80 >> i));	//依次选择从左到右第1列行开始(行扫描显示)
	}
}

/**
 * 8x8点阵数据滚动函数
 */
unsigned char LEDBuffer[8] = {0x00,0x00,0xFF,0x08,0x08,0xFF,0x00,0x00};
void LEDData_Move(unsigned char *Data,unsigned char Len)
{
	static unsigned char j = 0;
	static unsigned char i = 0;
	static unsigned char Status = 1;
	static unsigned char TimeStatus = 1;
	static unsigned long GET_Time = 0;
	switch(TimeStatus)
	{
		case 1:
			GET_Time = Get_TimeCount();				//获取TimeCount的值
			TimeStatus = 2;
		case 2:
			if(Get_TimeCount() - GET_Time > 500)	//每隔500ms进行向Buff里面放入数据
			{
				GET_Time = Get_TimeCount();
				for (j = 0; j < 7; j++) 			//先移动
				{
					LEDBuffer[j] = LEDBuffer[j+1];
				}
				switch(Status)
				{
					case 1:
						LEDBuffer[7] = Data[i+8];	//第一轮放入从Data[8]开始取出放入
						if(i == Len - 8)			
						{
							i = Len - 1;
							Status = 2;
						}
						break;
					case 2:							//第二轮放入从Data[0]开始取出放入
						LEDBuffer[7] = Data[i];	
						break;
				}
				i = (i + 1) % Len;  				//循环遍历 i:0~31
			}
			break;
	}
}

②Dot_LED.h文件的代码如下:

#ifndef __Dot_LED_H
#define __Dot_LED_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址

sbit LED_SH = P2^0;
sbit LED_DS = P2^1;	
sbit LED_ST = P2^2;
sbit LED_OE = P2^3;

extern unsigned char LEDBuffer[8];

void HC595_Cascade_8x8LED(unsigned char LEDNode, unsigned char LEDCathode);
void Show_8X8Dot_ColumnScan(unsigned char *Data);
void LEDData_Move(unsigned char *Data,unsigned char Len);

#endif

③main.c文件的代码如下:

#include "Dot_LED.h"
#include "Timer.h"

/* 显示HELLO */
static unsigned char HELLO_Data[] = {
	0x00,0x00,0xFF,0x08,0x08,0xFF,0x00,0x00,
	0xFF,0x89,0x89,0x89,0x00,0x00,0xFF,0x01,
	0x01,0x01,0x00,0x00,0xFF,0x01,0x01,0x01,
	0x00,0x00,0x7E,0x81,0x81,0x7E,0x00,0x00	 
};//纵向,上为最高位(纵向取模)

void main(void)
{		
	Time0_Intrrupt_Init();	//启动定时器中断
	
	while(1)
	{
		LEDData_Move(HELLO_Data,32);
		Show_8X8Dot_ColumnScan(LEDBuffer);	//列扫描显示
	}
}

QQ2025427-13511

### 关于74HC595芯片的详细介绍 #### 芯片概述 74HC595是一种具有8位串行输入/并行输出移位寄存器的集成电路,常用于扩展微控制器的I/O端口数量。它通过将串行数据转换为并行输出的方式,能够显著减少所需的GPIO引脚数[^3]。 --- #### 管脚定义与功能 以下是74HC595的主要管脚及其功能: | **管脚名称** | **描述** | |--------------|--------------------------------------------------------------------------| | DS | 数据输入引脚(Serial Data Input),接收来自外部设备的串行数据流 | | SH_CP | 移位时钟信号(Shift Register Clock),上升沿触发数据移动 | | ST_CP | 存储时钟信号(Storage Register Clock),上升沿锁存当前移位寄存器中的数据 | | MR | 主复位引脚(Master Reset),低电平有效,用于清零内部存储器 | | OE | 输出使能引脚(Output Enable),低电平允许输出 | | Q0-Q7 | 并行输出引脚,提供最终的8位数据 | 这些管脚的功能使得该芯片非常适合应用于多LED显示、矩阵屏幕以及其他需要大量数字输出接口的场景[^1]。 --- #### 工作原理 74HC595的工作流程分为两个阶段: 1. **数据加载阶段**:在SH_CP的作用下,DS上的每一位数据依次被送入移位寄存器中。 2. **数据锁存阶段**:当ST_CP接收到一个脉冲时,已装载好的数据会从移位寄存器传输至存储寄存器,并由Q0~Q7输出。 此机制允许多个74HC595器件串联起来形成更大的输出能力,从而满足复杂系统的硬件需求[^2]。 --- #### 驱动代码示例 下面是一个基于STM32平台控制74HC595的C语言程序片段,展示了如何向其发送数据: ```c #include "stm32f1xx_hal.h" #define SRCLK_PIN GPIO_PIN_x // 定义SH_CP对应的GPIO引脚 #define RCLK_PIN GPIO_PIN_y // 定义ST_CP对应的GPIO引脚 #define SER_Pin GPIO_PIN_z // 定义DS对应的GPIO引脚 void send_data_to_74HC595(uint8_t data){ uint8_t i; HAL_GPIO_WritePin(GPIOA, RCLK_PIN, GPIO_PIN_RESET); // 清除RCLK for(i=0;i<8;i++){ if(data & 0x01){ // 判断最低位是否为1 HAL_GPIO_WritePin(GPIOA, SER_Pin, GPIO_PIN_SET); } else{ HAL_GPIO_WritePin(GPIOA, SER_Pin, GPIO_PIN_RESET); } HAL_GPIO_WritePin(GPIOA, SRCLK_PIN, GPIO_PIN_SET); // 上升沿传递数据 HAL_Delay(1); // 微延迟确保稳定 HAL_GPIO_WritePin(GPIOA, SRCLK_PIN, GPIO_PIN_RESET); data >>= 1; // 右移一位处理下一个bit } HAL_GPIO_WritePin(GPIOA, RCLK_PIN, GPIO_PIN_SET); // 锁定新数据到输出缓冲区 } ``` 上述函数接受一个字节作为参数并通过指定的GPIO操作完成一次完整的写入过程。 --- #### 应用实例 除了基本的LED驱动外,74HC595还经常出现在以下领域: - 数码管动态扫描显示; - 控制继电器模块切换状态; - 构建大型点阵显示屏控制系统等实际项目当中。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值