Попросили тут помощи по теме, решил оформить постом.
Итак, я как-то писал о входных сдвиговых регистрах серии 74HC165 (далее — просто «регистры»), но как-то не учёл сложности включения их каскадом («гирляндой», daisy chain). Объясню, в чём суть. Допустим нам нужно обработать 24 кнопки, а у регистра всего 8 входов. Первое решение, которое приходит в голову — обработать данные с 3х регистров независимо. Но это решение плохо тем, что на каждый регистр нужно выделять свою линию SS для работы по SPI — по одной линии на каждый вывод SH/~LD, а использовать одну общую не получится, так как по шине SPI полезут биты сразу с трёх регистров, и программа прочитает мусор.
Для решения этой проблемы придумали каскадное подключение. Идея такова: раз при каждом дёргании CLK биты, захваченные со входов регистра, проталкиваются к выходу (QH и ~QH), то почему бы не заставить регистр проталкивать после своих битов ещё и биты, захваченные с внешнего источника — ещё одного сдвигового регистра:
При этом используется всего одна линия SS на все регистры, благодаря чему подключение к Arduino требует всего 3 провода — SS, SCK и MISO.
Проиллюстрирую такое подключение на примере 3х регистров (таких же, как и в предыдущей статье по ним). Смотрим на расположение выводов:
Соединяем регистры, как в предыдущей статье, добавив для каскада следущее:
- Вывод SER третьего регистра соединяем с GND, чтобы с SER читались нули, а вывод QH подключем к SER второго.
- Вывод QH второго регистра соединяем с SER первого.
- Вывод QH первого регистра соединяем с MISO на Arduino (пин 12).
SCK (пин 13) и SS (пин 10) — общие для всех регистров. Ну и для проверки на вшивость соединим через резисторы 10 кОм (у меня под рукой были только 510 Ом, если что) выводы 1C, 2B и 3A с землёй, а выводы 1E, 2F и 3G — с питанием (+5 В) и напишем скетч, в котором будем проверять состояние только этих выводов, ибо с остальных будет читаться мусор — они же не подключены. Для проверки на изменение состояний входов можно потыкать их проводком, соединённым с землёй или питанием через резистор.
Как это выглядит у меня:
Теперь модифицируем чуток скетч, считывающий состояние входов регистра, чтобы он работал через библиотеку SPI_Bus (проще кодить) и опрашивал только указанные выше входы:
#include <LineDriver.h>
#include <SPI.h>
#include <SPI_Bus.h>
SPI_Bus reg(_24bit, 10, MSBFIRST);
void setup()
{
Serial.begin(9600);
reg.setSelectionPolicy(SPI_Bus::SELECT_BEFORE);
}
void loop()
{
static uint32_t last_input_states = 0;
/* Читаем наши 24 бита разом. В C++ нет типов данных для 24-битных чисел,
* поэтому используем read32bits(), который считает столько бит,
* сколько мы указали при создании объекта reg (24 бита), но вернёт их
* одним 32-битным значением.
*/
uint32_t states = reg.read32bits();
if (states != last_input_states)
{
uint32_t changed = states ^ last_input_states;
last_input_states = states;
for (int i = 0; i < reg.bandwidth() * 8; ++i)
{
/* А вот тут проверяем только нужные нам входы */
if ((i == 0 || // 3A
i == 6 || // 3G
i == 9 || // 2B
i == 13 || // 2F
i == 18 || // 1C
i == 20) && // 1E
(changed & 1))
{
Serial.print("#");
Serial.print(i);
Serial.print(" -> ");
Serial.println(states & 1);
}
changed >>= 1;
states >>= 1;
}
}
}
That’s all, folks! (:
Исходнички:
- Библиотека SPI_Bus и необходимая ей LineDriver. Также, их можно клонировать из репозиториев на GitHub.
- Скетч: архив, репозиторий.




15 комментариев на «“Каскад входных сдвиговых регистров”»
выходы регистра не прыгают при включении ардуины?
в моём случае это было очень критично и пришлось отказаться от такого каскада…
Не понял смысла вопроса. Может, вы про 74HC595, которые на выход работают?
А можно приложить схему подключения? Спасибо.
Увы, я схему не делал в своё время, а сейчас уже не нарисую в силу криворукости и отсутствия в моём распоряжении трёх таких регистров.
Функция SPI_Bus::read16bits() из библиотеки SPI_Bus, скачанной по ссыслке, всегда дает 0
Попробуйте из зепозитория
Добрый день. Для чайников же пишите, так? Епт, куда и в каком виде положить SPI_Bus и LineDriver?
На одном регистре все завел без проблем. Считать же больше восьми бит при каскадном подключении регистров не могу из-за того, что новые библиотеки не видятся, соответственно не воспринимается reg.setSelectionPolicy(SPI_Bus::SELECT_BEFORE);.
Доброго времени суток. Короче, ёпт, библиотеки кидай в папку libraries, где у тебя проекты Arduino лежат. Ну, или там, где Arduino IDE установлена. .
Спасибо за скорый ответ, та и за полезный пост вообще.
Проблема в следующем: обычно, програмил в с++ и если надо было докинуть новую библиотеку (это файл с *.h), то его просто добавлял в лайбрариез и компилятор начинал её видеть и ей пользоваться. Заглянул в лайбрариез у ардуино. Там куча папок с названиями и уже внутри папок среди прочего может лежать та или иная библиотека. Ну, думаю, кину SPI_Bus.h и LineDriver.h в корень лайбрариез. Не помогло. Компилятор ругается. Кинул эти библиотеки вместе с папками, так как там всё в папках — не помогло. Вот за мысль подкинуть библиотечку к самому скетчу спасибо. Скорее всего так и надо было сразу сделать. Давно ничего не писал вот и туплю.
Собственно, после первых эмоций и неудачливых попыток с библиотеками, просто отказался от spi вообще и тупо программно сам начал параллельно считывать данные, стробировать (сдвигать побитово на выход регистра) и последовательно считывать данные ардуиной в булиновский массив нужного размера. Оказалось программно покороче и экономнее в плане использования памяти. Вопрос: такой алгоритм на много медленнее приведенного вами? Почему?
подскажите пожалуйста, скачал SPI_Bus и LineDriver в списке библиотекв Arduino IDE они есть но при компиляции выдает следующие:
C:\Users\User\Documents\Arduino\libraries\LineDriver\LineDriver.cpp:22:22: fatal error: WProgram.h: No such file or directory
#include «WProgram.h»
^
compilation terminated.
Ошибка компиляции.
Приветствую! Ознакомился с вашей работой «Каскад входных сдвиговых регистров», спасибо за сей труд! Если же требуется обработать гораздо большее количество входов, около 300, и такое же количество выходов, получится ли это реализовать на каскаде сдвиговых регистров? Придется организовать считывание в массив? И вывод данных из массива в каскад регистров?
С уважением, Александр.
Вы правильно всё поняли. Но у каскадов сдвиговых регистров есть один недостаток: задержка считывания прямо пропорциональна длине каскада. На каждый считываемый бит нужно 1 раз «дёрнуть» CLK, а для 300 входов — 300 раз, соответственно. Частота «дёргания» ограничена быстродействием МК, и при большом количестве входов задержка может оказаться неприемлемой — возможно, придётся отказаться от функций Arduino и работать с периферией МК напрямую, что немного сложнее (но эффективнее).
Впрочем, если вы не планируете как-либо обрабатывать считываемые данные, и просто нужно, скажем, на каждую кнопку зажигать светодиод, то вы можете просто соединить выход последнего входного регистра в цепочке со входом первого выходного, и тогда можно обойтись без массива, а задача МК сведётся к дёрганию ножками:
// запомнили входы LATCH(IN) -> 0 LATCH(IN) -> 1 // начинаем читать входы в выходы LATCH(OUT) -> 1 // сдвигаем биты 300 раз { CLOCK(IN) -> 1 CLOCK(IN) -> 0 CLOCK(OUT) -> 1 CLOCK(OUT) -> 0 } // выставляем прочитанные входы на выход LATCH(OUT) -> 0Быстродействие наверное теоретически ограничено частотой процессора, ведь еще есть минимальная длительность сигналов CLK и сам бит информации, верно?
Задача следующая: Есть порядка 300 кнопок, необходимо в случае нажатия какой либо из них отправить в СОМ порт номер кнопки и зажечь светодиод в этой кнопке, для надежности наверное лучше еще принимать ответ от компьютера- подтверждение правильного приема: в виде того же номера и только после этого включать светодиод. Нажатия будут не чаще чем раз в секунду. Использовать планируется nano 16 МГц.
Еще как вариант есть идея разделить количество кнопок между несколькими платами, скажем по 100 на плату, а 4 платой принимать данные с 3-х и отправлять уже в СОМ. Но это в случает слишком большой задержки при использовании 1 платы.
Верно, но максимальная частота работы использованных в статье регистров — 50 МГц, так что вы ограничены только частотой МК.
Ну да, нужен массив. Вы пульт охраны делаете? 🙂 Если да, то проверять ответ обязательно, как и сообщать о его отсутствии.
Тогда вообще можно не париться.
Если ещё актуально, подскажите а как в serial выслать лишь нажатую кнопку, а не 8бит?