Сдвиговый регистр. Регистры сдвига 8 битный сдвиговый регистр

Сдвиговый регистр - это набор последовательно соединённых триггеров (обычно их 8 штук). В отличии от стандартных регистров, сдвиговые поддерживают функцию сдвига вправо и влево. (т. е. переписывание данных с каждого предыдущего триггера на следующий по счёту).

Функционал и назначение у сдвиговых регистров довольно велик. Сегодня мы познакомим одного из них с Arduino (Отличный способ множить выходы у Arduino: занимаем 3, получаем 8).

Наверное самая популярная микросхема, представляющая собой такой регистр - это 74HC595.

Работает на интерфейсе SPI: ноги DS, ST_CP, SH_CP - это шины управления. Соответственно: шина данных(MOSI), защёлка(SS) и тактовая линия(SCK). Подключаем на любые 3 контакта Arduino (библиотека SPI в коде не будет задействована). У меня это 12, 10, 13 выходы Arduino (стандарт).

Ноги Q0, Q1, ..., Q7 - это выходы регистра (разряды). Для того, чтобы следить за состоянием каждого из них, повесим на каждый вывод по светодиоду (с последовательно соединённым резистором. Номинал от 150 до 330 Ом)

VCC и GND - это питание. Подключаем к +5v и GND.

Выход Q7` не трогаем (предназначен для последовательного соединения таких регистров)

MR - это сброс. Подключаем к +5v (сброс не активен).

Ну и OE притягиваем к земле (подключаем к контакту GND).

Получается вот, такая схема:

На BreadBoard можно разместить вот, так:

Теперь к коду:

Как говорилось ранее, библиотека SPI использоваться не будет. Есть удобная функция shiftOut() .

для начала именуем наши пины (тактовая линия - clock, данные - data, защёлка - latch):

#define clock 13 #define data 12 #define latch 10

потом в void setup() обозначаем их как выходы и сразу ставим защёлке высокий уровень, чтобы регистр не принимал сигналов:

Void setup(){ pinMode(clock, OUTPUT); pinMode(data, OUTPUT); pinMode(latch, OUTPUT); digitalWrite(latch, HIGH); }

теперь давайте попробуем что-нибудь отправить на регистр:

Для начала ставим LOW на защёлку (начинаем передачу данных. Теперь регистр принимает сигналы с Arduino).

DigitalWrite(latch, LOW);

Потом отправляем данные (т. е. отправляем байт в цифровом или двоичном виде. В двоичном проще, т. к. каждый из 8 битов отвечает за свой разряд в регистре. Проще сориентироваться глазами):

Для начала отправим байт 0b10000000; (должен будет загореться первый светодиод):

ShiftOut(data, clock, LSBFIRST,0b10000000);

И в конце выставляем HIGH на защёлку (заканчиваем передавать данные).

DigitalWrite(latch, HIGH);

В итоге весь наш код:

#define clock 13 #define data 12 #define latch 10 void setup() { pinMode(clock, OUTPUT); pinMode(data, OUTPUT); pinMode(latch, OUTPUT); digitalWrite(latch, HIGH); } void loop() { digitalWrite(latch, LOW); shiftOut(data, clock, LSBFIRST, 0b10000000); digitalWrite(latch, HIGH); }

Теперь вгружаем в ардуину. Результат должен быть таким (зажёгся первый светодиод):

(если у вас зажёгся не первый, а последний светодиод, то в функции shiftOut поменяйте LSBFIRST на MSBFIRST и всё станет на свои места).

Итак, получилось! Предлагаю создать функцию для того, чтобы каждый раз не писать эти 3 СТРОЧКИ:

Я назову её: sendbyte;

Void sendbyte(byte value){ digitalWrite(latch, LOW); shiftOut(data, clock, LSBFIRST, value); digitalWrite(latch, HIGH); }

Эта функция отправляет регистру состояние всех разрядов сразу. Это пригодится для (например). Но, чтобы использовать регистр как расширитель портов, нужно управлять каждым разрядом по-отдельности (аналогично функции digitalWrite()):

Мы можем отправлять регистру только полный байты (8 бит - 0b00000000). Если отправить не 8, а 5 бит (например: 0b00000 000) , то регистр будет ждать недостающие 3 бита. Значит, что когда мы хотим изменить состояние одного разряда регистра (включить его, или выключить) мы должны, по сути, послать ранее отправленный байт, с изменением на один бит.

(P. S.: Сейчас долгое и нудное объяснение (новичкам), кому не интересно, спуститесь чуть ниже:);

Итак, сначала создаём, так называемую (мною), базу данных, в которой будет храниться состояние каждого разряда (включен(HIGH) или выключен(LOW)). тип: boolean :

Boolean states;

Каждая переменная в данном массиве обозначает свой разряд (в нулевой (по счёту) будет храниться состояние 1 разряда, второй - 3-го, и т. д.)

Теперь напишем функцию (я назову её: sendpin). Она будет принимать 2 значения: номер разряда, и уровень, который нам надо этому разряду приписать: высокий(HIGH) или низкий(LOW).

Void sendpin(int pin, boolean state){ pin--; states=state; byte value = 0; byte add = 1; for(int i=0; i<8; i++){ if(states[i]==HIGH) value+=add; add*=2; } digitalWrite(latch, LOW); shiftOut(data, clock, LSBFIRST, value); digitalWrite(latch, HIGH); }

Из-за того, что счёт начинается с нуля, нам придётся называть первый пин нулевым. Чтобы это исправить (мы будем писать как есть(первый, значит первый), а Arduino будет сама отбавлять один), Я написал:

Затем отмечаем изменения в базе данных:

States=state;

Теперь надо сформировать из 8 битов байт и отправить его на регистр.

Для начала создаём переменные:

value - тот байт, который будем отправлять. (по умолчанию его нужно сделать нулём):

Byte value = 0;

add - это переменная, которая будет хранить в себе байт текущего разряда. для первого разряда это байт 1 (0b10000000);

Byte add = 1;

теперь нам нужно прокрутить в базе данных все 8 переменных и сформировать байт (делать это будем с помощью цикла for() :

For(int i=0; i<8; i++){ }

Итак, каждый раз мы проверяем очередной разряд в базе данных. Если он должен иметь высокий уровень, то мы прибавляем к value add и переходим на следующий разряд в цепочке (как бы сдвигаемся на разряд выше (левее). Т. е., в двоичном коде всё просто: было так: 0b01000000; сдвинули единичку влево и получилось так: 0b10000000. А вот в цифровом виде всё по-другому. Сдвиг влево аналогичен умножению на 2 (а вправо, кстати, - делению на 2)). Получается примерно так:

If(states[i]==HIGH) value+=add; add*=2;

Теперь остаётся только послать value на регистр:

DigitalWrite(latch, LOW); shiftOut(data, clock, LSBFIRST, value); digitalWrite(latch, HIGH);

В принципе, если понять, то всё очень просто.

Итак, давайте попробуем включить 2, 4, 6, и 8 разряды отдельно (4 раза напишем в цикле нашу функцию):

Sendpin(2, HIGH); sendpin(4, HIGH); sendpin(6, HIGH); sendpin(8, HIGH);

И кстати, в setup-e нужно очистить регистр (послать 0).

Можно даже такую функцию создать:

Void cleanreg(){ for(int i=0; i<8; i++) states[i]=LOW; digitalWrite(latch, LOW); shiftOut(data, clock, LSBFIRST, 0); digitalWrite(latch, HIGH); }

В общем результат таков:

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
Плата Arduino

Arduino Uno

1

1. Умножение на 2 – это сдвиг двоичного числа влево на 1 разряд.

Деление на 2 – это сдвиг числа вправо на 1 разряд.

Каждый последующий сдвиг числа – это дальнейшее умножение или деление на степень двойки.

2 Преобразование параллельного кода в последовательный и наоборот можно осуществить по схеме, приведенной на рис. 3.43.

Рис. 3.43. Схема преобразования кода параллельный\последовательный

S – data serial - последовательный вход данных

P/ S – вход управления режимом ввода (параллельный/последовательный)

Для осуществления преобразования регистр RG1 переводят в режим параллельного приема информации, а RG2 последовательного приёма, воздействуя на входы управления P/S соответствующим логическим уровнем сигнала. Параллельный код через входы D 1 - D 4 загружается в передающий регистр RG1, появляясь в параллельном виде на его выходах Q 1 – Q 4 . После этого оба регистра переводят в режим сдвига и на тактовый вход «с» подают серию из четырех импульсов. Передаваемый код вытесняется из передающего регистра в линию связи начиная со старших разрядов числа. Регистр–приёмник принимает и сдвигает каждый бит информации синхронно с передающим регистром. Таким образом, с окончанием серии из четырех тактовых импульсов, передаваемый код будет размещен в принимающем регистре и может быть прочитан из него в параллельном виде, т. к. появится на его выходах Q 1 – Q 4 . Это синхронный способ передачи кода.

При асинхронном способе синхронизирующий перепад, называемый стартовым битом, сопровождает цепочку из 5-8 бит (чаще всего байт). Это стандартный последовательный интерфейс .

Для выполнения функции как приемника так и передатчика при обмене в стандартном последовательном формате выпускаются специальные микросхемы КР580ВВ51 (ввод-вывод) или КР581ВА1.

Экономические расчеты стоимости кабеля связи и аппаратуры преобразования показывают, что при разрядности в 1-2 байта передача последовательным кодом уже при расстояниях в несколько метров становится выгоднее передачи параллельным кодом.

Сигналы сдвига подаются на RG непрерывно, а выход замкнут на вход DS. В результате однажды записанный в регистр код будет в этом кольце циркулировать. Поставив параллельно m одинаковых регистров можно записывать и считывать m-разрядные слова параллельным кодом.

Недостаток – большое время обращения

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

Например 144ИР3 имеет ёмкость 64 бит.

4. Кольцевые распределители

Распределителями называют узлы, распределяющие поток импульсов последовательно, импульс за импульсом, по нескольким выходам по определенным циклограммам.

Применяют для управления шаговыми двигателями, обслуживания матриц ПЗС и др. многоразрядных объектов (рис. 3.44).

Рис. 3.44. Кольцевой распределитель

Схема предусматривает внесение единицы через элемент ИЛИ на вход последовательного приема информации DS. Последующие импульсы сдвига с частотой следования f вх перемещают эту единицу от младших разрядов к старшим, выделяя её на выходах Q 1 – Q 4 (рис. 3.45).

Рис. 3.45 Диаграмма кольцевого распределителя

Очевидно, что частота выходных импульсов на каждом из выходов окажется в четыре раза меньше входной, а в общем виде это соотношение зависит от числа разрядов сдвигового регистра – n. f вых = f вх / n

Достоинством такого распределителя является возможность преобразования последовательности импульсов в восьмеричный (десятичный) код без применения дешифратора.

Недостаток схемы – после сбоя работоспособность можно восстановить только путем внесения новой единицы.

От этого недостатка свободна схема с самовосстановлением после сбоя (рис. 3.46).

Рис. 3.46. Кольцевой распределитель с самовосстановлением после сбоя

Очевидно, что при смещении единицы в четвертый триггер регистра будет выполняться условие: Q̅ 1 ·Q̅ 2 ·Q̅ 3 = 1. Эта единица по цепи обратной связи поступит на вход DS, после чего цикл повторится.

5. Счетчик импульсов

Кольцевой распределитель можно рассматривать как счетчик-делитель с коэффициентом счета равным числу триггеров. Соединив последовательно два регистра по четыре разряда можно построить делитель на 16 (рис. 3.47).

Рис. 3.47 Счетчик-делитель на 16

Явным недостатком такой конструкции является малая ёмкость. Действительно, затратив те же 8 триггеров, можно собрать двоичный счетчик с коэффициентом деления 2 8 = 256.

6. Кольцевой распределитель с перекрестной связью

Иногда требуется ОЧЕНЬ много выходных портов. Особенно если хотим сделать что нибудь на светодиодах. Гирлянду какую-нибудь навороченную. Что делать? Брать под это дело ATMega128 с ее полусотней выводов? Избыточно — для ламеров. Ставить i 2 с расширитель портов? Дорого. Для мажоров. Тут на помощь из вековых глубин выплывает старая добрая дискретная логика. На этот раз нас выручит грошовый сдвиговый регистр. Возьму, для примера, 74HC164 он же, для любителей совковых трешевых микросхем в неубиваемом каменном корпусе, наш КM555ИР8 .

От МК, как видно, требуется только четыре выхода. Одним (RESET) мы сбрасываем состояние регистра. Из второго (Data) побитно вылазит байтик, а тактовый CLC обеспечивает продвижение битов по регистру. Самих регистров тут три. Они сцеплены паровозом. Когда переполняется первый, то биты из него вылазят во второй, потом в третий. Итого, 24 вывода.
Катоды диодов подключены все вместе через транзистор и как только будет слово мы подаем сигнал Ready и зажигаем всю эту ботву.

Наполнять регистр просто:
1) Поднимаем и держим RESET в 1
2) Выдаем первый (старший) бит на Data .
3) Опускаем в 0 и поднимаем в 1 тактовый выход. На восходящем фронте происходит занос в регистр и сдвиг всей цепочки на один шаг.
4) Повторить со второго пункта пока все биты не выдадим.

А для сброса достаточно уронить Reset в ноль на пару микросекунд.
Все просто:)

З.Ы.
Кружок на входе регистра означает, что вход инверсный. Т.е. подал ноль — сработало
Треугольник на входе показывает по какому фронту произойдет срабатывание. Запомнить просто: _/ \_ — это, типа, импульс. А треугольник, как стрелочка, указывает на нужный фронт. ->_/ \_ передний (восходящий фронт) и _/ \_<- задний (нисходящий фронт)

по тактовому сигналу содержимое каждого предыдущего триггера переписывается в следующий по порядку в цепочке триггер . Код, хранящийся в регистре, с каждым тактом сдвигается на один разряд в сторону старших разрядов или в сторону младших разрядов, что и дало название регистрам данного типа.

В связи с названием направления сдвига в сдвиговых регистрах часто возникает путаница. Сдвиг бывает двух видов: вправо (основной режим, который есть у всех сдвиговых регистров ) и влево (этот режим есть только у некоторых, реверсивных сдвиговых регистров ). Названия эти отражают внутреннюю структуру регистров сдвига (рис. 8.14) и перезапись сигналов последовательно по цепочке триггеров. При этом триггеры, вполне естественно, нумеруются слева направо, например, от 0 до 7 (или от 1 до 8) для 8-разрядных регистров. В результате сдвиг информации регистром вправо представляет собой сдвиг в сторону разрядов, имеющих большие номера, а сдвиг информации регистром влево - это сдвиг в сторону разрядов, имеющих меньшие номера.

Однако, как известно, в любом двоичном числе слева расположены старшие разряды, а справа - младшие разряды. Поэтому сдвиг двоичного числа вправо будет сдвигом в сторону младших разрядов, а сдвиг влево - сдвигом в сторону старших разрядов. Это противоречие, не чей-то злой умысел, просто так исторически сложилось, и об этом надо помнить разработчику цифровой аппаратуры.


Рис. 8.14.

В стандартные серии цифровых микросхем входит несколько типов сдвиговых регистров , отличающихся возможными режимами работы, режимами записи, чтения и сдвига, а также типом выходных каскадов (2С или 3С). Большинство регистров сдвига имеет восемь разрядов. На рис. 8.15 представлены для примера четыре типа микросхем регистров сдвига .

Регистр ИР8 - наиболее простой из регистров сдвига . Он представляет собой 8-разрядную линию задержки, то есть имеет только один информационный вход, на который подается последовательная сдвигаемая информация (точнее, два входа, объединенных по функции 2И), и восемь параллельных выходов. Сдвиг в сторону выходов со старшими номерами осуществляется по переднему фронту тактового сигнала С. Имеется также вход сброса –R, по нулевому сигналу на котором все выходы регистра сбрасываются в нуль. Таблица истинности регистра ИР8 приведена в табл. 8.5 .


Рис. 8.15. Таблица 8.5. Таблица истинности регистра сдвига ИР8
Входы Выходы
-R C D1 D2 Q0 Q1 Q7
0 X X X 0 0 0
1 0 X X Не меняются
1 1 Х Х Не меняются
1 0 1 1 1 1 Q0 Q6
1 0 1 0 Х 0 Q0 Q6
1 0 1 Х 0 0 Q0 Q6

Регистр ИР9 выполняет функцию, обратную регистру ИР8. Если ИР8 преобразует входную последовательную информацию в выходную параллельную, то регистр ИР9 преобразует входную параллельную информацию в выходную последовательную. Однако суть сдвига не меняется, просто в ИР9 все внутренние триггеры имеют выведенные параллельные входы, и только один, последний триггер имеет выход (причем как прямой , так и инверсный). Запись входного кода в регистр производится по нулевому сигналу на входе -WR. Сдвиг осуществляется по положительному фронту на одном из двух тактовых входов С1 и С2, объединенных

Для построения регистров используются последовательноесоединение этих элементов.

Последовательный регистр (регистр сдвига или сдвиговый регистр) обычно служит для преобразования последовательного кода в параллельный и наоборот. Применение последовательного кода связано с необходимостью передачи большого количества двоичной информации по ограниченному количеству соединительных линий. При параллельной передаче разрядов требуется большое количество соединительных проводников. Если двоичные разряды последовательно бит за битом передавать по одному проводнику, то можно значительно сократить размеры соединительных линий на плате (и размеры корпусов микросхем).

Принципиальная схема последовательного (сдвигового) регистра, собранного на основе и позволяющего осуществить преобразование последовательного кода в параллельный, приведена на рисунке 1. Обратите внимание, что если для параллельных регистров подходили как триггеры работающие по потенциалу (триггеры-защелки), так и триггеры, работающие по фронту, то для реализации последовательного (сдвигового) регистра подходят только D триггеры, работающие по фронту!


Рисунок 1. Схема последовательного (сдвигового) регистра

Внутри сдвигового регистра триггеры соединены последовательно, то есть выход первого соединён с входом второго и т.д. рассмотренного последовательного регистра приведено на рисунке 2.


Рисунок 2. Условно-графическое обозначение последовательного (сдвигового) регистра

Входы синхронизации в последовательных (сдвиговых) регистрах, как и в параллельных регистрах, объединяются. Это обеспечивает одновременность смены состояния всех триггеров, входящих в состав последовательного (сдвигового) регистра.

Преобразование последовательного кода в параллельный в последовательном (сдвиговом) регистре производится следующим образом. Отдельные биты двоичной информации последовательно подаются на вход сдвигового регистра D0. Каждый бит сопровождается отдельным тактовым импульсом синхронизации, который поступает на вход синхронизации последовательного регистра C.

После поступления первого тактового импульса логический уровень, присутствующий на входе D0, запоминается в первом триггере последовательного (сдвигового) регистра и поступает на его выход, а так как он соединён с входом второго триггера, то и на его вход. Если бы последовательный (сдвиговый) регистр был собран на D триггерах, работающих по потенциалу, то этот бит тут же записался во второй D триггер! В нашем случае этого не происходит, так как к этому моменту фронт на входе синхронизации C уже закончился.

После поступления второго тактового импульса логический уровень, присутствующий на входе второго триггера последовательного (сдвигового) регистра, запоминается в нем и поступает на его выход, а так как он соединён с входом третьего триггера, то и на его вход. Одновременно следующий бит входного последовательного кода запоминается в первом триггере последовательного (сдвигового) регистра.

После поступления четвертого тактового импульса в триггерах последовательного (сдвигового) регистра будут записаны логические уровни бит, которые последовательно присутствовали на его входе D0. Теперь этими битами можно воспользоваться, например, для отображения на индикаторах.

Пусть на вход последовательного (сдвигового) регистра поступает сигнал, временная диаграмма которого изображена на рисунке 3, тогда состояние выходов этого регистра будет последовательно принимать значения, записанные в таблице 1.



Рисунок 3. Временная диаграмма работы сдвигового регистра

На рисунке 3 вместе с логическими уровнями записываются значения бит, которые передаются по соединительной линии или присутствуют на выходах сдвигового регистра.

№ такта 1 2 3 1
Q0 1 0 1 1
Q1 X 1 0 1
Q2 X X 1 0
Q3 X X X 1

В качестве примера реализации последовательного (сдвигового) регистра можно назвать отечественную микросхему 1564ИР1 или иностранную 74НС164.