Попросили тут помощи по теме, решил оформить постом.
Итак, я как-то писал о входных сдвиговых регистрах серии 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 и работать с периферией МК напрямую, что немного сложнее (но эффективнее).
Впрочем, если вы не планируете как-либо обрабатывать считываемые данные, и просто нужно, скажем, на каждую кнопку зажигать светодиод, то вы можете просто соединить выход последнего входного регистра в цепочке со входом первого выходного, и тогда можно обойтись без массива, а задача МК сведётся к дёрганию ножками:
Быстродействие наверное теоретически ограничено частотой процессора, ведь еще есть минимальная длительность сигналов CLK и сам бит информации, верно?
Задача следующая: Есть порядка 300 кнопок, необходимо в случае нажатия какой либо из них отправить в СОМ порт номер кнопки и зажечь светодиод в этой кнопке, для надежности наверное лучше еще принимать ответ от компьютера- подтверждение правильного приема: в виде того же номера и только после этого включать светодиод. Нажатия будут не чаще чем раз в секунду. Использовать планируется nano 16 МГц.
Еще как вариант есть идея разделить количество кнопок между несколькими платами, скажем по 100 на плату, а 4 платой принимать данные с 3-х и отправлять уже в СОМ. Но это в случает слишком большой задержки при использовании 1 платы.
Верно, но максимальная частота работы использованных в статье регистров — 50 МГц, так что вы ограничены только частотой МК.
Ну да, нужен массив. Вы пульт охраны делаете? 🙂 Если да, то проверять ответ обязательно, как и сообщать о его отсутствии.
Тогда вообще можно не париться.
Если ещё актуально, подскажите а как в serial выслать лишь нажатую кнопку, а не 8бит?