12 мая 2016
Кравченко Виктор

Arduino: Сдвиговый регистр 74НС595 или размножаем/экономим выходы платы

Цифровые устройства Arduino Arduino Lang
01

Попробую очень коротко и наглядно показать что же делает этот замечательный сдвиговый регистр и почему он является одним из самых распространенных радиоэлектронных компонентов в наборе каждого уважающего себя электронщика.

02
Проверено — автор рекомендует:
http://ali.pub/d3fq8 Сдвиговые регистры 74HC595N на Aliexpress
Как покупать на Aliexpress со скидкой от 5,5%

Видео-инструкция о покупке со скидками на Aliexpress
03

Итак, 74HC595 — восьмиразрядный (это означает, что он имеет 8 управляемых выходов) с последовательным вводом, последовательным или параллельным выводом информации, с триггером-защелкой.

Даташит на сдвиговый регистр 74HC595 — 74hc595_74hct595_nxp.pdf (330 KB)
04

При помощи 3 проводов, подключенных к плате Arduino можно получить $8 \times n$ выходов, где $n$ — количество используемых сдвиговых регистров. Выглядит он вот так:

05
Vcc Питание
Q0...Q7 Параллельные выходы
DS или SER (Data Serial) Вход для последовательных данных
OE Вход для переключения состояния выходов из высокоомного в рабочее (активация при получении LOW)
ST_CP или RCK (STorage register Clock input, storage — хранилище) Синронизация («защелкивание») выходов
SH_CP или SCK (SHift register Clock input, shift — сдвиг) Вход для тактовых импульсов
MR Сброс значений регистра (активация при получении LOW)
Q7S Выход для последовательного соединения регистров
GND Земля
06

Чтобы было понятно как это работает, покажем анимированную схему:

07
На данной схеме расположение пинов сдвигового регистра отличается от реального расположения и сгрупировано подобным образом исключительно для удобства восприятия.
08 На заметку:
Поскольку параметром управления сигналом в функцию shiftOut() подается набор битов — байт, то имеет смысл ознакомиться со статьей Arduino - битовая арифметика или операции над битами.
09

Теперь, покажем на практике, как можно использовать сдвиговый регистр. Начнем, конечно же, с управления небольшим количеством светодиодов — 1 регистр, 8 светодиодов:

10
11

Скетч:

12 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); digitalWrite(latchPin, LOW); // устанавливаем синхронизацию "защелки" на LOW shiftOut(dataPin, clockPin, LSBFIRST, B01001100); // передаем последовательно на dataPin digitalWrite(latchPin, HIGH); //"защелкиваем" регистр, тем самым устанавливая значения на выходах } void loop() { }
13 На заметку:
Стоит обратить внимание на отсутствие циклично исполняемого кода в процедуре loop() — регистр сохраняет заданное состояние при сохранении питания.
14 На заметку:
В функции shuftOut() в качестве третьего аргумента передается параметр (bitOrder — порядок битов), который определяет в какой последовательности подаваемые биты будут интерпретироваться регистром — в прямом или обратном.
  • LSBFIRST (Least Significant Bit First) — означает, что вывод в регистр начнется с последнего бита. Например, при передаче байта 00010111 на выходах регистра окажутся значения (с 1 по 8 пины) — 00010111.
  • MSBFIRST (Most Significant Bit First) — означает, что вывод в регистр начнется с первого бита. При передаче байта 00010111 на выходах регистра окажутся значения в обратном порядке (с 1 по 8 пины) — 11101000.
15

Как видно из примера, у платы Arduino задействовано всего 3 выхода.

16

Сдвиговый регистр и управление «бегающим» огоньком.

Как вы наверное догадались и вышенаписанного, можно очень просто задать алгоритм бегущего огонька. Загрузим следующий скетч в предыдущий пример:

17 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 byte path[4]= { B10000001, B01000010, B00100100, B00011000 }; void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); } void loop() { for (int i=0; i<4; i++) { digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, LSBFIRST, path[i]); digitalWrite(latchPin, HIGH); delay(50); } for (int i=0; i<4; i++) { digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, LSBFIRST, path[3-i]); digitalWrite(latchPin, HIGH); delay(50); } }
18

В скетче, в переменной path задается карта по которой зажигаются светодиоды, но есть ещё один удобный способ — способ формирования байта при помощи функции bitWrite():

19 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); } void loop() { byte byteToSend = 0; //Создаем пустой байт B00000000 for (int bitPos = 0; bitPos < 8; bitPos++) { // В переменной хранится позиция изменяемого бита byteToSend = 0; // Обнуляем байт при каждом проходе bitWrite(byteToSend, bitPos, HIGH); // При bitPos=0 получим B00000001, при bitPos=1 - B00000010, при bitPos=2 - B00000100 и т.д. digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, byteToSend); // Инвертируем сигнал при помощи MSBFIRST, грузим с первого бита digitalWrite(latchPin, HIGH); delay(50); } }
20

Последовательное соединение сдвиговых регистров.

Параллельное соединение сдвиговых регистров рассматриваться не будет, так как оно ни чем не отличается от одиночного подключения, никогда никем не используется и нивелирует все преимущества, которые дает последовательное подключение сдвиговых регистров.

21

Для последовательного подключения большого количества сдвиговых регистров используется 9 (Q7S) выход регистра — по нему данные продавливаются по мере поступления. Выходы 11 (SH_CP, задающий тактовые импульсы) и 10 (ST_CP, «защелка») подключаются параллельно и управляются синхронно:

22
23

Как это выглядит в реальной ситуации? Рассмотрим последовательное включение 2 сдвиговых регистров: добавим ещё один регистр на схему — он будет управлять дополнительными 8 светодиодами:

24
К пину span class=monoData/span платы Arduino подключен только первый регистр, второй (как и остальные в случае подключения большего количества регистров) получают данные последовательно через 9 (span class=monoQ7S/span) выход предыдущего регистра.
К пину Data платы Arduino подключен только первый регистр, второй (как и остальные в случае подключения большего количества регистров) получают данные последовательно через 9 (Q7S) выход предыдущего регистра.
25 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); digitalWrite(latchPin, LOW); // устанавливаем синхронизацию "защелки" на LOW // начинаем "продавливать" данные по цепочке регистров, начиная с последнего shiftOut(dataPin, clockPin, LSBFIRST, B01001100); // передаем последовательно на dataPin - в первом регистре значение B01001100 shiftOut(dataPin, clockPin, LSBFIRST, B10000111); // "продавливаем" данные в следующий регистр - в первом регистре B10000111, во втором - B01001100 // и т.д. по количеству регистров digitalWrite(latchPin, HIGH); //"защелкиваем" регистр, тем самым устанавливая значения на выходах } void loop() { }
26 На заметку:
Если в сборку из нескольких сдвиговых регистров подать только один байт (одиночный вызов shiftOut()), то все последующие регистры получат те же самые данные.
27

В целом принципиальная электрическая схема по подключению неограниченного количества сдвиговых регистров выглядит следующим образом — как видите задействованных пинов платы Arduino по прежнему 3:

28
29

Сдвиговый регистр 74HC595 и ШИМ (широтно-импульсная модуляция)

У тех, кто соберется использовать сдвиговый регистр 74HC595 в своих проектах рано или поздно может возникнуть вопрос, а как же использовать ШИМ, ведь мы же часто управляем при помощи регистра светодиодами, а выходы регистра могут иметь только 3 состояния — логический ноль LOW, логическая единица HIGH и высокоимпедансное состояние (пин не имеет физического контакта с электрической цепью). И действительно ШИМ сдвиговым регистром не поддерживается. Но есть одна небольшая хитрость — мы можем использовать выход регистра OE (Output Enable input) — он отвечает за переключение из высокомного состояния в ноль. Выход OE — можно назвать логическим нолем для всех выходов. Таким образом, если мы подключим этот пин к ШИМ-выходу Arduino, то сможем таким образом смещать логический ноль, тем самым имитировать ШИМ на светодиодах.

30
31 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 int pwmPin = 6; //Пин подключен к OE входу 74HC595 для управления ШИМ void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(pwmPin, OUTPUT); digitalWrite(latchPin, LOW); // устанавливаем синхронизацию "защелки" на LOW shiftOut(dataPin, clockPin, LSBFIRST, B11110001); digitalWrite(latchPin, HIGH); //"защелкиваем" регистр, тем самым устанавливая значения на выходах } void loop() { for (int i=0; i<256; i++) { analogWrite(pwmPin,i); // Назначаем выходу ШИМ разные значения delay(2); // Делаем паузу, чтобы не мигало слишком быстро } }
32

Минус данного подхода заключается в том, что в этом случае регулируется яркость всех светодиодов, подключенных к одному сдвиговому регистру.

33

А что же делать, если нам нужно показать разную яркость светодиодов, подключенных к одному сдвиговому регистру. Здесь снова нужно будет пойти на хитрость — создать карту яркостей светодиодов, и зажигать каждую группу со своей яркостью по очереди, так быстро, чтобы создавалось ощущение постоянного свечения:

34 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int dataPin = 9; //Пин подключен к DS входу 74HC595 int latchPin = 10; //Пин подключен к ST_CP входу 74HC595 int clockPin = 11; //Пин подключен к SH_CP входу 74HC595 int pwmPin = 6; //Пин подключен к OE входу 74HC595 для управления ШИМ byte _map[2]= { B01010101, B10101010 }; void setup() { //устанавливаем режим OUTPUT pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(pwmPin, OUTPUT); digitalWrite(pwmPin, LOW); } void loop() { for (int i=0; i<=255; i++) { showLed(_map[0],i); showLed(_map[1],255-i); } for (int i=255; i>=0; i--) { showLed(_map[0],i); showLed(_map[1],255-i); } } void showLed(byte mapLed, int brightness) { digitalWrite(pwmPin, LOW); digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, mapLed); // Инвертируем сигнал при помощи MSBFIRST, грузим с первого бита digitalWrite(latchPin, HIGH); analogWrite(pwmPin, brightness); delay(5); }
35

Но если вы начнете экспериментировать с задержками и большим количеством карт яркости, то столкнетесь с очень непрятным эффектом мерцания — это связно с большим временем исполнения стандартных для языка Arduino функций-оберток типа digitalWrite, digitalRead, analogWrite, analogRead и т.д., но это тема уже совсем другой статьи...

37

Похожие запросы:

  • Arduino: плодим выходы
  • Сдвиговый регистр 74HC595 — когда не хватает ног
  • Как подключить сдвиговый регистр к Arduino
  • Увеличиваем число выводов микроконтроллера
  • 74HC595 — управление яркостью светодиода
comments powered by HyperComments