【模块学习】探索74HC595的使用指南与实践

74HC595

  • 1、74HC595简介
  • 2、代码演示
  • 2.1、驱动8位流水灯
  • 3、74HC595级联
  • 3.1、驱动16位流水灯
  • 3.2、驱动8位数码管
  • 3.3、驱动8×8点阵屏幕
  • 3.4、8×8点阵屏幕滚动显示
  • 1、74HC595简介

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

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

    引脚 功能
    VCC 供电正极(2v~6v)
    GND 供电负极
    DS 串行数据输入引脚(8位)
    Q7~Q0 并行数据输出引脚(Q7对应数据高位)
    SHCP 移位寄存器时钟输入
    STCP 输出寄存器时钟输入
    Q7S 8位数据串行输出引脚(常用于级联)
    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、驱动8×8点阵屏幕

    如图:点亮图上的小亮点,则阴极引脚输出的数据为: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、8×8点阵屏幕滚动显示

    从左向右滚动显示“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

    作者:浅陌pa

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【模块学习】探索74HC595的使用指南与实践

    发表回复