01 |
Попробую очень коротко и наглядно показать что же делает этот замечательный сдвиговый регистр и почему он является одним из самых распространенных радиоэлектронных компонентов в наборе каждого уважающего себя электронщика. |
|
02 |
|
Проверено — автор рекомендует: http://ali.pub/d3fq8 Сдвиговые регистры 74HC595N на AliexpressВидео-инструкция о покупке со скидками на Aliexpress |
03 |
Итак, 74HC595 — восьмиразрядный (это означает, что он имеет 8 управляемых выходов) с последовательным вводом, последовательным или параллельным выводом информации, с триггером-защелкой. |
Даташит на сдвиговый регистр 74HC595 — 74hc595_74hct595_nxp.pdf (330 KB)
|
04 |
При помощи 3 проводов, подключенных к плате Arduino можно получить $8 \times n$ выходов, где $n$ — количество используемых сдвиговых регистров. Выглядит он вот так: |
|
05 |
|
|
06 |
Чтобы было понятно как это работает, покажем анимированную схему: |
|
07 |
|
На данной схеме расположение пинов сдвигового регистра отличается от реального расположения и сгрупировано подобным образом исключительно для удобства восприятия.
|
08 | На заметку: |
Поскольку параметром управления сигналом в функцию shiftOut() подается набор битов — байт, то имеет смысл ознакомиться со статьей Arduino - битовая арифметика или операции над битами.
|
|
09 |
Теперь, покажем на практике, как можно использовать сдвиговый регистр. Начнем, конечно же, с управления небольшим количеством светодиодов — 1 регистр, 8 светодиодов: |
|
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 — порядок битов), который определяет в какой последовательности подаваемые биты будут интерпретироваться регистром — в прямом или обратном.
|
|
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, «защелка») подключаются параллельно и управляются синхронно: |
|
23 |
Как это выглядит в реальной ситуации? Рассмотрим последовательное включение 2 сдвиговых регистров: добавим ещё один регистр на схему — он будет управлять дополнительными 8 светодиодами: |
|
24 |
К пину 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: |
|
29 |
Сдвиговый регистр 74HC595 и ШИМ (широтно-импульсная модуляция) У тех, кто соберется использовать сдвиговый регистр 74HC595 в своих проектах рано или поздно может возникнуть вопрос, а как же использовать ШИМ, ведь мы же часто управляем при помощи регистра светодиодами, а выходы регистра могут иметь только 3 состояния — логический ноль LOW, логическая единица HIGH и высокоимпедансное состояние (пин не имеет физического контакта с электрической цепью). И действительно ШИМ сдвиговым регистром не поддерживается. Но есть одна небольшая хитрость — мы можем использовать выход регистра OE (Output Enable input) — он отвечает за переключение из высокомного состояния в ноль. Выход OE — можно назвать логическим нолем для всех выходов. Таким образом, если мы подключим этот пин к ШИМ-выходу Arduino, то сможем таким образом смещать логический ноль, тем самым имитировать ШИМ на светодиодах. |
|
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 |
Похожие запросы:
|
|