09.01.2010

Удвоение пинов Arduino (2)

Часть 2. Размножение входов или shiftIn

(окончание, начало см. Часть 1. Размножение выходов или shiftOut)

Представим себе обратную ситуацию – есть много входов (например, от датчиков контактного типа или, попросту говоря - кнопок), которые надо считывать. И если универсальные пины Arduino легким движением руки при помощи оператора pinMode() превращаются в брюки во входы или выходы, то время вспомнить, что за такую универсальность приходится расплачиваться довольно сложной схемой пина, в которой, на самом-то деле, входная и выходная части отделены друг от друга.

Для рассмотренной в предыдущей части микросхемы 74HC595 существует "брат-близнец" 74HC597 - Serial-out Shift register w/Latches, который работает с точностью до наоборот: по команде "защелкивает" данные на входе, которые затем можно считать последовательно:



Работать с такой микросхемой надо аналогичным образом, учитывая назначение выводов:

  • D0-D7 - параллельные входы;
  • Ds - последовательный вход от предыдущего чипа (для каскадирования);
  • Q - последовательный выход;
  • MR - инверсный сигнал сброса (активен в LOW);
  • SHCP - синхронизация регистра
  • STCP - синхронизация защелки
  • PL - инверсный сигнал записи с параллельных входов (активен в LOW).
Схема:

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

Кстати, защелка по входу в нашем применении не нужна, можно использовать чип и без оной. Рассмотрим CD4021B, восьмибитный регистр с последовательным или параллельным входом и последовательным выходом:



PI-# - в данном случае расшифровывается как Parallel In, всего восемь входов.

CLOCK - вход тактовых импульсов, которые должен генерировать микроконтроллер, при каждом происходит сдвиг на один разряд.

PARALLEL/SERIAL CONTROL - официально управляет режимом по входу - единица соответствует параллельному входу, ноль - последовательному. На практике получается чуть интереснее: пока режим параллельный - по каждому фронту CLOCK происходит обновление состояния всех восьми бит регистра. Если мы хотим "зафиксировать" состояние всех восьми выводов в определенный момент времени, надо дать один CLOCK в параллельном режиме, а потом перейти в последовательный - и прочесть 8 бит - при этом счетчик заполнится тем, что поступит со входа SERIAL IN, а Arduino к этому моменту получит все 8 бит информации, записанной в режиме параллельного ввода. Поэтому, иногда этот вход на схемах маркируют как latch - по смыслу того, что он делает.

Q6, Q7, Q8 - это выходы соответствующих разрядов последовательного счетчика, для использования всех восьми разрядов надо считывать данные с Q8.

Вообще, серия 4000В - не очень скоростная (соответствует нашей 1561), максимальное быстродействие 90 нс. Учтите, что мы-то ее будем использовать не на предельном питании +15 В, так что готовьтесь к 160...320 нс, тактировать её можно с частотой 3..6 МГц. Немного, но для считывания, например, механических контактных групп - вполне подойдет.

Схема будет выглядеть очень похожим образом:



Номинал резисторов на этой схеме - 10К, смысл академический - нельзя "бросать" вход КМОП-схемы в плавающем состоянии, на нем должен быть устойчивый потенциал.

К сожалению, синтаксис Wiring не может нас порадовать отдельным оператором shiftIn, но при необходимости ее можно написать самостоятельно. Вот пример кода, считывающего значения кнопок с предыдущей схемы:

#define DATA  9
#define LATCH 8
#define CLOCK 7

#define CLKUS 1 

byte dataInput; 

void setup() {
  pinMode(DATA,INPUT);
  pinMode(LATCH,OUTPUT);
  pinMode(CLOCK,OUTPUT);
  
  Serial.begin(9600);
}

void loop() {
  
  dataInput = 0;
  digitalWrite(LATCH,HIGH); // Parallel mode

  for (int i=7; i>=0; i--) {
   digitalWrite(CLOCK,HIGH); 
   delayMicroseconds(CLKUS);
   dataInput |= (digitalRead(DATA) ? (1<<i) : 0);
   digitalWrite(CLOCK,LOW);    
   delayMicroseconds(CLKUS);
   if (i==7) digitalWrite(LATCH,LOW); // Serial Mode
  }
  
  Serial.println(dataInput, BIN);
  delay(1000);
}

В основном теле loop() раз в секунду происходит считывание всех кнопок путем кратковременной подачи высокого уровня на пин LATCH. Дальше в цикле формируется восемь перепадов CLOCK и данные запишутся в соответствующие разряды переменной dataInput, после чего будут в двоичном формате выведены в последовательный порт (наблюдайте результаты в мониторе последовательного порта). Задержки умышленно даны с запасом, если есть желание - попробуйте их уменьшить до предела по документации.

Прием с каскадированием для повышения разрядности:



Скетч при этом почти не изменится:

#define DATA  9
#define LATCH 8
#define CLOCK 7

#define CLKUS 1 

word dataInput; 

void setup() {
  pinMode(DATA,INPUT);
  pinMode(LATCH,OUTPUT);
  pinMode(CLOCK,OUTPUT);
  
  Serial.begin(9600);
}

void loop() {
  
  dataInput = 0;
  digitalWrite(LATCH,HIGH); // Parallel mode

  for (int i=15; i>=0; i--) {
   digitalWrite(CLOCK,HIGH); 
   delayMicroseconds(CLKUS);
   dataInput |= (digitalRead(DATA) ? (1<<i) : 0);
   digitalWrite(CLOCK,LOW);    
   delayMicroseconds(CLKUS);
   if (i==15) digitalWrite(LATCH,LOW); // Serial Mode
  }
  
  Serial.println(dataInput, BIN);
  delay(1000);
}


Разрядность переменной, в которую читается состояние увеличилась в два раза - с 8 до 16 бит, чтение выполняется за 16 тактов. В остальном - тоже самое.

Часто бывает так, что импортный чип достать сложнее, зато наш (советский или белорусский) валяется без дела, да так и просится в схему. Мне в свое время сильно помог К561ИР9 (функциональный аналог CD4035B):



RESET - сброс всех разрядов в ноль в произвольный момент времени.

PI1..PI4 - параллельные входы, как в CD4021.

Q1..Q4 - это параллельные выходы, как в 74HC595 (см. часть 1)

J/K - это последовательный вход. Замечу, что он выполнен в виде J/K триггера, и если J и К соединить, то получится D-триггер, как в CD4021.

P/S - управление режимом ввода, как в CD4021. По логической единице будут записаны данные с параллельных входов, по нулю - с последовательного входа J/K.

T/C - прямой/дополнение (true/complement). Микросхема универсальна до такой степени, что умеет менять знак у выводимого кода (т.е. выдавать не само число, а дополнение к нему). Нам это навряд ли понадобится, поэтому притянем этот вход к Vcc - это вечный true.

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



Можно использовать скетчи от предыдущих примеров практически без изменений:
  • в режиме ввода считывать данные последовательным кодом с digital9
  • в режиме вывода подавать последовательный код через digital10.
В последнем случае, увы, комбинация на выходах Q1..Q3 будут сдвигаться. А если бы не этот недостаток, то при наличии защелки на выходе получился бы идеальный вариант - универсальная схема, которая поможет расширить и входы, и выходы!

Время взвешивать за и против.

Плюсы Arduino / Seeeduino Mega: компактность, гибкость, быстродействие, попутно получаем 128К  памяти программ.

Плюсы логических микросхем: цена решения, на надо платить за лишнее/неиспользуемое, нагрузочная способность линии, доступность.

Казалось бы, на этом можно ставить точку, но... спустя какое-то время появилась и третья статья про увеличение числа пинов Arduino.

(в тексте использованы материалы статьи Parallel to Serial Shifting-In with a CD4021BE).

18 комментариев:

  1. Большое спасибо за статьи!
    Хотелос-бы еще услышать статью о расширении аналоговых входов, когда надо поставить не много кнопочек, а например много резисторов :)
    И очень полезно, было-бы если вы выкладывали схемы из статей, в формате проэктов "Протеуса".
    Не всегда хватает умения собрать обсуждаемую схему не только в железе но и в эмуляторе. А для вас я думаю это труда не составит.
    Спасибо.

    ОтветитьУдалить
  2. О, спасибо за отзыв! Просто даже силы появляются писать, когда знаешь, что кому-то это интересно ;)

    Схемы я рисую в Eagle, про Proteus много слышал - типа, что это такая сублимация для тех, кто ленится даже собрать на макетке ;)

    ОтветитьУдалить
  3. Использовал PCF8574 для расширения выходов в свое время (LCD приделывал). На сколько знаю, данная микросхема I/O, т.е можно сразу и писать и читать (причем, согласно спецификации, даже в комбинации, т.е. часть портов читаем, а в часть пишем). Хотя, я еще не пробовал читать через нее.

    ОтветитьУдалить
  4. Я пробовал читать/писать с PCF8574, проблем нет. Только про работу в комбинации первый раз слышу...

    ОтветитьУдалить
  5. >> Например, от датчиков контактного типа или, попросту говоря - кнопок.

    Не совсем верно с кнопками сравнивать - кнопки аккордами редко когда надо читать. А без аккордов можно и без дополнительных микросхем увеличить разрядность.

    ОтветитьУдалить
  6. Я ничего не сравнивал ;) Я привел пример ;)

    ОтветитьУдалить
  7. А как можно прицепить CD4021 или 74hc597(а лучше и то и то) напрямую к COM порту(или не напрямую, без контроллера)

    ОтветитьУдалить
  8. Рискну предположить, что через согласующую схему. Например, через MAX232CPE.

    ОтветитьУдалить
  9. Допустим! Тгда вопрос как? Может есть где горовые рисунки...или нарисуте мне кто-нибудь пожалуйста..

    ОтветитьУдалить
  10. Почитайте даташит, там вполне доступно описано! В этом чипе два четыре конвертера - два перехода с COM на TTL и два - обратно. Подключите так, как требует Ваша задача. В качестве примера рекомендую изучить схему Freeduino MaxSerial.

    ОтветитьУдалить
  11. Блин, у меня не получиться... я в схемах дундук! Могу только спаять.
    И наверно я очень нагло буду выглядеть... а как управлять этими регистраммичерез vb 6.0

    ОтветитьУдалить
  12. хмм ... жуть какая

    нет ну можно конечно из VB через MSComm управлять CLOCK/LATCH модемными линиями DTR/RTS , а по линии DSR читать данные с регистра, но мягко говоря это как-то не выглядит разумным решением.

    А сделать "честный" UART на регистре сдвига без микронтроллера затруднительно.

    ОтветитьУдалить
  13. http://svetelektro.com/Pictures/PC_prislusenstvo/4021/obr1.png вот готовый вариант подцепления к com-порту.
    Кто с исходниками на vb 6.0 может помоч?
    Или есть еще регистр интересный 74hc597, суть впринцыпе аналогичная. И есть брат близнец, работает наоборот, вот у меня есть схема и исходники управления.. Может кто по аналогии на 74hc595 сможет перестроить?

    ОтветитьУдалить
  14. У CD4021 разве не 8 - GND, 16 - VDD?

    ОтветитьУдалить
  15. Здравствуйте Илья,

    спасибо за статью. У вас отсутствует код для записи в Q* К561ИР9 в явном виде. Вот он:
    void put595(byte output) {
    for (int i = 0; i < OUT_PINS; ++i) {
    digitalWrite(OUTDATA, output & 1);
    digitalWrite(CLOCK, HIGH);
    delay(CLKUS);
    digitalWrite(CLOCK, LOW);
    delay(CLKUS);
    output >>= 1;
    }
    }

    ОтветитьУдалить
  16. Не могу сообразить какую роль в строке
    dataInput |= (digitalRead(DATA) ? (1<<i) : 0);
    играют символы "?" и ":"
    Объясните пожалуйста начинающему

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

      Удалить