CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

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

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

Исходнички:
  • +1
  • 22 октября 2011, 14:36
  • burjui

Комментарии (14)

RSS свернуть / развернуть
+
0
выходы регистра не прыгают при включении ардуины?

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

lehha

  • 23 октября 2011, 10:30
+
+1
Не понял смысла вопроса. Может, вы про 74HC595, которые на выход работают?
avatar

burjui

  • 23 октября 2011, 14:14
+
0
А можно приложить схему подключения? Спасибо.
avatar

brujodentista

  • 14 мая 2012, 12:24
+
0
Увы, я схему не делал в своё время, а сейчас уже не нарисую в силу криворукости и отсутствия в моём распоряжении трёх таких регистров.
avatar

burjui

  • 14 мая 2012, 14:24
+
0
Функция SPI_Bus::read16bits() из библиотеки SPI_Bus, скачанной по ссыслке, всегда дает 0
avatar

Gore123

  • 29 июля 2012, 08:31
+
0
Попробуйте последнюю версию из зепозитория
avatar

burjui

  • 29 июля 2012, 22:59
+
0
Добрый день. Для чайников же пишите, так? Епт, куда и в каком виде положить SPI_Bus и LineDriver?
На одном регистре все завел без проблем. Считать же больше восьми бит при каскадном подключении регистров не могу из-за того, что новые библиотеки не видятся, соответственно не воспринимается reg.setSelectionPolicy(SPI_Bus::SELECT_BEFORE);.
avatar

zed

  • 17 июня 2014, 14:56
+
0
Доброго времени суток. Короче, ёпт, библиотеки кидай в папку libraries, где у тебя проекты Arduino лежат. Ну, или там, где Arduino IDE установлена. Вот так вот.
avatar

burjui

  • 17 июня 2014, 21:06
+
0
Спасибо за скорый ответ, та и за полезный пост вообще.
Проблема в следующем: обычно, програмил в с++ и если надо было докинуть новую библиотеку (это файл с *.h), то его просто добавлял в лайбрариез и компилятор начинал её видеть и ей пользоваться. Заглянул в лайбрариез у ардуино. Там куча папок с названиями и уже внутри папок среди прочего может лежать та или иная библиотека. Ну, думаю, кину SPI_Bus.h и LineDriver.h в корень лайбрариез. Не помогло. Компилятор ругается. Кинул эти библиотеки вместе с папками, так как там всё в папках — не помогло. Вот за мысль подкинуть библиотечку к самому скетчу спасибо. Скорее всего так и надо было сразу сделать. Давно ничего не писал вот и туплю.
Собственно, после первых эмоций и неудачливых попыток с библиотеками, просто отказался от spi вообще и тупо программно сам начал параллельно считывать данные, стробировать (сдвигать побитово на выход регистра) и последовательно считывать данные ардуиной в булиновский массив нужного размера. Оказалось программно покороче и экономнее в плане использования памяти. Вопрос: такой алгоритм на много медленнее приведенного вами? Почему?
avatar

zed

  • 17 июня 2014, 22:44
+
0
подскажите пожалуйста, скачал 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.
Ошибка компиляции.
avatar

kit4606

  • 9 марта 2016, 17:25
+
0
Приветствую! Ознакомился с вашей работой «Каскад входных сдвиговых регистров», спасибо за сей труд! Если же требуется обработать гораздо большее количество входов, около 300, и такое же количество выходов, получится ли это реализовать на каскаде сдвиговых регистров? Придется организовать считывание в массив? И вывод данных из массива в каскад регистров?
С уважением, Александр.
avatar

Sanische

  • 11 декабря 2015, 13:03
+
0
Вы правильно всё поняли. Но у каскадов сдвиговых регистров есть один недостаток: задержка считывания прямо пропорциональна длине каскада. На каждый считываемый бит нужно 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
avatar

burjui

  • 12 декабря 2015, 19:27
+
0
Быстродействие наверное теоретически ограничено частотой процессора, ведь еще есть минимальная длительность сигналов CLK и сам бит информации, верно?
Задача следующая: Есть порядка 300 кнопок, необходимо в случае нажатия какой либо из них отправить в СОМ порт номер кнопки и зажечь светодиод в этой кнопке, для надежности наверное лучше еще принимать ответ от компьютера- подтверждение правильного приема: в виде того же номера и только после этого включать светодиод. Нажатия будут не чаще чем раз в секунду. Использовать планируется nano 16 МГц.
Еще как вариант есть идея разделить количество кнопок между несколькими платами, скажем по 100 на плату, а 4 платой принимать данные с 3-х и отправлять уже в СОМ. Но это в случает слишком большой задержки при использовании 1 платы.
avatar

Sanische

  • 13 декабря 2015, 11:29
+
0
Быстродействие наверное теоретически ограничено частотой процессора, ведь еще есть минимальная длительность сигналов CLK и сам бит информации, верно?
Верно, но максимальная частота работы использованных в статье регистров — 50 МГц, так что вы ограничены только частотой МК.

необходимо в случае нажатия какой либо из них отправить в СОМ порт номер кнопки и зажечь светодиод в этой кнопке, для надежности наверное лучше еще принимать ответ от компьютера
Ну да, нужен массив. Вы пульт охраны делаете? :) Если да, то проверять ответ обязательно, как и сообщать о его отсутствии.

Нажатия будут не чаще чем раз в секунду.
Тогда вообще можно не париться.
avatar

burjui

  • 13 декабря 2015, 13:18

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.