74系列595芯片是一种常用的芯片,常被用来驱动数码管或是继电器等。该芯片价格便宜,使用简单,但是如果使用中不注意也会掉到很多坑里,本文就针对该芯片的使用做个详细的说明。
74系列595芯片是具有三态输出寄存器的 8 位移位寄存器,很多厂家都有生产该类芯片(比如TI、NXP等),各种细分类别的74lv595、74ls595、74hc595等各种也很齐全(区别在于速度、电压、电路、输入输出电平等,具体的需要参考对应的元件手册)。
不同厂家datasheet对于针脚的描述也有差别,但功能上来说同一类型封装都是pin to pin的:
595芯片最大的一个特点就是可以级联,最少只需要占用控制器三个IO口就可以控制很多片595。只要电路设计合理级联个上百片不成问题。(想象一下如果用来驱动继电器,级联100片595,每片可以驱动8个,总共可以驱动800个,所占用的只是控制器三个IO口)
595根据具体的型号不同,输出口可能拥有几毫安到几十毫安的电流,可以直接驱动很多小电流器件,最常见的用来驱动数码管、LED、光耦等。
控制稍大功率负载,这里为什么写~500mA呢?因为有一种神奇的器件叫做达林顿管阵列,常见的都是芯片类型,在这里推荐使用型号为ULN2803
的芯片,该芯片有八路,和同样八路的595刚好匹配,另外该芯片可以用来驱动最大需求50V 500mA的负载。595+2803
的组合可用场景已经比较多了,最常见的被用于驱动继电器。
控制大功率负载纯用芯片方案就不太合适了,一般会用到继电器或是接触器,常见的比如用595+光耦+2803+继电器
,用光耦做隔离,要求更高的场合还会用小继电器来驱动大继电器。
595级联总线长了就容易出现电路上信号完整性方面的问题,最常见的就是就是因为信号反射引起过冲振铃等,从而导致逻辑上一次电平变化过程中595捕获到多次、或者电压过冲过高引起595工作异常,具体表现出的现象是595输出错乱抖动等。
下两图是使用ESP32驱动(输出电流最大20mA),串联了16片595,总线长度为1米下,捕捉的SHCP / SRCLK 总线末端的波形,信号频率分别为250KHz和50KHz:
可以看到我的控制源输出的高电平是3.3V的,但过冲电压已经超过了5V,还可以看到非常明显的振铃。两图对比还能发现降低信号频率几乎无法改善该现象。
信号反射问题主要是因为线路中阻抗不匹配,可以通过端接电阻来抑制,电路接法很多,常见的比如 串联电阻
、并联电阻
、戴维宁接法
等等。
下面是在控制器输出端(源端)串联了电阻后,同样串联了16片595,总线长度为1米下,捕捉的SHCP / SRCLK 总线末端的波形:
上图串联100欧姆
上图串联200欧姆
上图串联300欧姆
上面的三个图中可以看到串联电阻后信号特性有了改善,串入200欧姆时波形已经很不错了,加大到300欧姆时波形的上升下降特性已经不那么好了。
实际电路设计中需要根据各自的需求来确定端接方式和元件取值,不能一概而论。
系统上电时因为电平的不确定有可能导致595不正常的输出(常常表现为595输出口全部输出高电平),这时候需要根据电路和设计需求进行处理,一般来说主要处理595的OE在上电时为高电平就行,可以参考下面链接:
http://www.dumenmen.com/thread-660-1-1.html
如果控制器的输出能力不够或是总线过长时可能会出现驱动能力不足的现象,这时候可以在总线中分段加入74系列244等缓冲器/驱动器来提高驱动能力。
595的驱动程序比较简单,功能框图、控制逻辑、时序图如下:
我一般用下来只用到 SHCP
、 STCP
、 DS
、OE
四个控制线 ,下面是控制程序示意(程序基于Arduino):
//下面代码保存为chip_595_driver.h
#ifndef chip_595_driver_h
#define chip_595_driver_h
#include
class Chip595Driver
{
public:
Chip595Driver(uint8_t STCP, uint8_t SHCP, uint8_t DS, uint8_t OE = 255, uint8_t holdrate = 1);
~Chip595Driver(void);
void enable(void);
void disable(void);
void write(uint8_t *data, int length);
private:
uint8_t _pinSTCP;
uint8_t _pinSHCP;
uint8_t _pinDS;
uint8_t _pinOE;
uint8_t _holdrate;
void hold(uint8_t times);
};
#endif //chip_595_driver_h
//下面代码保存为chip_595_driver.cpp
#include "chip_595_driver.h"
Chip595Driver::Chip595Driver(uint8_t STCP, uint8_t SHCP, uint8_t DS, uint8_t OE, uint8_t holdrate)
: _pinSTCP(STCP),
_pinSHCP(SHCP),
_pinDS(DS),
_pinOE(OE),
_holdrate(holdrate)
{
if (_pinOE != 255)
{
pinMode(_pinOE, OUTPUT);
digitalWrite(_pinOE, HIGH);
hold(2);
}
pinMode(_pinSTCP, OUTPUT);
digitalWrite(_pinSTCP, LOW);
hold(1);
pinMode(_pinSHCP, OUTPUT);
digitalWrite(_pinSHCP, LOW);
hold(1);
pinMode(_pinDS, OUTPUT);
digitalWrite(_pinDS, LOW);
hold(1);
}
Chip595Driver::~Chip595Driver(void)
{
}
void Chip595Driver::enable(void)
{
if (_pinOE != 255)
{
digitalWrite(_pinOE, LOW);
hold(2);
}
}
void Chip595Driver::disable(void)
{
if (_pinOE != 255)
{
digitalWrite(_pinOE, HIGH);
hold(2);
}
}
void Chip595Driver::write(uint8_t *data, int length)
{
for (int i = 0; i < length; i++)
{
uint8_t tmp = *(data + i);
for (uint8_t j = 0; j < 8; j++) //写入1byte数据
{
if (tmp & 0x80) //按位从MSB开始写入1bit数据
{
digitalWrite(_pinDS, HIGH);
}
else
{
digitalWrite(_pinDS, LOW);
}
tmp <<= 1;
digitalWrite(_pinSHCP, LOW);
hold(1);
digitalWrite(_pinSHCP, HIGH); //上升沿
hold(1);
}
}
digitalWrite(_pinSTCP, LOW);
hold(1);
digitalWrite(_pinSTCP, HIGH); //上升沿
hold(1);
}
void Chip595Driver::hold(uint8_t times)
{
delayMicroseconds(times * _holdrate);
}
上面的程序使用在Arduino core for ESP32上测试用来驱动74HCT595D时可以使用,其他Arduino平台需要根据电路特性调整延时时间,非Arduino平台可以自行调整代码使用。可以使用下面代码进行测试:
//下面代码使用Arduino core for ESP32以ESP32模块作为控制器进行测试
#include "chip_595_driver.h"
Chip595Driver chip595bus(2, 4, 16, 15); //声明595总线对象
#define CHIP_595_SUM 4 //总线中串联的595芯片数量
uint8_t chip595DataBuf[CHIP_595_SUM] = {0}; //595每个输出口用1bit表示
//每片595有8个输出口
//即每片595状态用1byte表示
void setup()
{
/* 595总线最好在系统上电时优先初始化 */
chip595bus.write(chip595DataBuf, CHIP_595_SUM); //总线上输出数据,并将数据595存储寄存器
//如果595输出使能的话将会更新595输出状态
//传输过程中优先传输总线末端的值
//即chip595DataBuf[0]代表离控制器最远的595
//当前chip595DataBuf均为0,该操作相当于清零595存储寄存器
chip595bus.enable(); //使能595输出,当前情况下所有595都将输出低电平
chip595DataBuf[0] = 0x01;
chip595bus.write(chip595DataBuf, CHIP_595_SUM); //该次输出后离控制器最远的595的Q0将输出高电平
chip595DataBuf[0] = 0x04;
chip595bus.write(chip595DataBuf, CHIP_595_SUM); //该次输出后离控制器最远的595的Q2将输出高电平、Q0将输出低电平
chip595DataBuf[CHIP_595_SUM-1] = 0xFF;
chip595bus.write(chip595DataBuf, CHIP_595_SUM); //该次输出后离控制器最近的595的Q0~Q7将输出高电平
//离控制器最远的595的Q2输出高电平保持不变
}
void loop()
{
}
对于74系列595芯片的使用来说主要就是上面一些内容了,如果有更多可以说明的内容后面再补充。