Каскад входных сдвиговых регистров


Попросили тут помощи по теме, решил оформить постом.
Итак, я как-то писал о входных сдвиговых регистрах серии 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! (:

Исходнички:


15 комментариев на «“Каскад входных сдвиговых регистров”»

  1. выходы регистра не прыгают при включении ардуины?

    в моём случае это было очень критично и пришлось отказаться от такого каскада…

    • Не понял смысла вопроса. Может, вы про 74HC595, которые на выход работают?

    • Увы, я схему не делал в своё время, а сейчас уже не нарисую в силу криворукости и отсутствия в моём распоряжении трёх таких регистров.

  2. Добрый день. Для чайников же пишите, так? Епт, куда и в каком виде положить 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.
      Ошибка компиляции.

  3. Приветствую! Ознакомился с вашей работой «Каскад входных сдвиговых регистров», спасибо за сей труд! Если же требуется обработать гораздо большее количество входов, около 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 платы.

    • Быстродействие наверное теоретически ограничено частотой процессора, ведь еще есть минимальная длительность сигналов CLK и сам бит информации, верно?

      Верно, но максимальная частота работы использованных в статье регистров — 50 МГц, так что вы ограничены только частотой МК.

      необходимо в случае нажатия какой либо из них отправить в СОМ порт номер кнопки и зажечь светодиод в этой кнопке, для надежности наверное лучше еще принимать ответ от компьютера

      Ну да, нужен массив. Вы пульт охраны делаете? 🙂 Если да, то проверять ответ обязательно, как и сообщать о его отсутствии.

      Нажатия будут не чаще чем раз в секунду.

      Тогда вообще можно не париться.

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение