И один в поле воин…


Маленькие изыскания в вопросе использования одного пина Arduino для индикации. Причем я решил ограничиться вопросом подключения ТОЛЬКО светодиодов. Варианты с 1-wire или RS протоколами, а так же использование пина для ввода данных в данном опусе рассматриваться не будут

Человеческий мозг устроен таким образом, что самые интересные и невероятные решения проблемы рождаются в экстремальных ситуациях. Экстрима нам конечно же не нужно, мы просто смоделируем такую ситуацию. Представьте себе, что при проектировании некого устройства, у вас остался свободным только один пин ардуинки. Как же его по максимуму задействовать?. Первое, что приходит в голову — подключить светодиод. Не спорю, даже один светодиод весьма много чего может сказать: горит, не горит, определенным образом подает вспышки и т.п. Вот пример. Все это имеет право на жизнь, но два светодиода — все же гораздо нагляднее.
Ага, два светодиода и всего один пин. Ну посмотрим….

Для удобства отладки и тестирования, я на скорую руку набросал отладочный cкетч и собрал простейшую схему на макетке с одной кнопкой. При нажатии на кнопку соответствующий выход меняет свое состояние. (Позже в перечень режимов добавился и Hi-Z). Чтобы понимать «что сейчас»- при режиме Hi-Z дополнительно светится встроенный светодиод на 13 пине.

//**************************************************
// Отладочный скетч опуса "И один в поле воин..."
// для robocraft.ru, кибервесна-2016
// при нажатии кнопки последовательно происходит переключение
// пина между след. состояниями: Hi-Z, HIGH, LOW
//**************************************************
#define ledPin 3
#define BUTTON_PIN 5
void setup()
{
  pinMode(13, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);      // устанавливаем пин как вход
}

enum mode{p_HIGH, p_LOW, p_HZ, END}; // именнованные режимы
byte currentMode = p_HZ;
//==== Основной цикл ====
void loop()
{

  if (digitalRead(BUTTON_PIN))
  {
    currentMode++;
    if (currentMode == END)
      {
        currentMode =0;
      }
  }
  // end if
  if (currentMode == p_HZ)
    {
      digitalWrite(13, HIGH);
    }
  else
  {
     digitalWrite(13, LOW);
  }

 switch (currentMode)
 {
   case p_HIGH:
     pin_High();
   break;

   case p_LOW:
     pin_Low();
   break;

   case p_HZ:
     pin_Hz();
   break;
 }// end switch

 delay(200);


} //end main loop

//===============
void pin_High()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
}
//===============
void pin_Low()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
}
//===============
void pin_Hz()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  pinMode(ledPin, INPUT);
}

Напомню, что пин может находится в состоянии LOW или HIGH. Попросту говоря, если в пин записан «0», то пин Arduino выступает в роли земли, а если «1» — в роли источника питания (правда весьма слабенького, с допустимым током до 40 мА). Записывая в пин то или иное состояние, мы реально сможем включать нужный нам светодиод. А что же со вторым светодиодом? Как его подключить? Первое, что пришло на ум — первый светодиод подключаем непосредственно к пину, а дальше использовать транзистор в качестве инвертора и второй светодиод подключить к инвертированному выходу.

Давайте и попробуем этот вариант.

Как это работает?
Когда в пине выставлен «0» транзистор T1 закрыт и ток течет по цепочке [+Vсс -> R3 -> Led2] и, соответственно, светится только красный светодиод. При записи в пин «1» — загорается зеленый светодиод, открывается транзистор и ток начинает течь по линии [+Vсс -> R3 -> T1(кэ)]. Налицо крупный недостаток такого варианта: при подаче уровня HIGH, кроме горящего нужного нам светодиода, транзистор T1 ТОЖЕ будет находиться в открытом состоянии и через него будет НАПРАСНО течь ток, как через байпас.

Но, несмотря на распространенность n-p-n транзисторов, свет клином на них не сошелся. Может не совсем это верно, но я почему-то запомнил такую фразу:»n-p-n транзистор нужен, чтобы тянуть к земле, а p-n-p — к питанию«. Автора этой фразы уже не установить, но я достаточно часто ею пользуюсь. А давайте попробуем вариант с p-n-p транзистором.

Как это работает?
Когда в пине выставлен «0» транзистор открыт и ток течет по цепочке [+Vсс -> Т1(эк) -> R3 -> Led2] и светится только красный светодиод. При записи в пин «1» — ток течет из пина Ардуино через резистор R1 и загорается зеленый светодиод led1, транзистор T1 закрыт. Вот, уже ГОРАЗДО лучше…

Однако, есть более простой вариант: два последовательно включенных светодиода с токоограничивающими резисторами. Кстати, такое решение я впервые увидел в подборке типовых схем подключения — ABC ArduinoBasicConnection

Как это работает?
Тут все просто. При «1» на пине, ток будет течь по цепочке [pin -> led2 -> R2]. В случае «0» на пине — ток пойдет по цепи [+Vcc -> R1 -> led1 -> pin]. Что же, вполне неплохо и работоспособно.

Все вышеперечисленные варианты схем имели один очень важный недостаток. У нас получалось, что либо один светодиод горит, либо другой. Выключить оба светодиода (в описанных выше схемах) не представляется возможным. Или нет? Может как нибудь можно? Да-да-да…напрягаем извилины. Пин может находиться еще и в состоянии Hi-Z pinMode(pin, INPUT). С точки зрения схемотехники я рассматриваю это состояние как будто этой части с Ардуино вообще в схеме и нет. Проверяем на последней схеме. Блин! А при таком состоянии пина (INPUT) у нас оба светодиода тускло подсвечиваются, значит по всей нашей цепочке течет, пусть и незначительный, но ток, достаточный для свечения светодиодов. Не есть хорошо 🙁
А давайте попробуем немного изменить последнюю схему, добавив парочку диодов. Ну и что мы имеем в таком варианте?

Как это работает?
Чтобы диод начал открываться, необходима разность потенциалов между анодом и катодом порядка 0.5 вольт минимум. Для светодиода эта величина составляет >1.5 вольта (в зависимости от типа и цвета светодиода). При напряжении питания 5 вольт, мы получаем 0.5В х 4+1.5В х 2 = 5 вольт. Следовательно, все диоды находятся в закрытом состоянии. При выставлении на пине «1» ток будет течь по цепи D3 -> D4 -> R2 -> LED2. Круто! Теперь есть возможность включать (правда, по отдельности) каждый светодиод и гасить оба.

А давайте задействуем этот режим по полной. Ведь можно к одному пину подключить 3 светодиода и управлять включением каждого из них. Забегая вперед, скажу, что к сожалению в таком варианте выключить все светодиоды ТЕПЕРЬ ТОЧНО не получится. Думаю, что в ряде случаев, этим недостатком вполне можно пренебречь. По сути, эта схема — это чуть расширенная версия предыдущей. Добавлен узел, который будет активен когда pin Ардуино будет находится в состоянии Hi-Z.

Как это работает?
пин = HIGH. Т1 — закрыт, Т2 — открыт. Ток протекает по цепи [Pin -> D3 -> D4 -> R5 -> Led3]
пин = LOW. Т1 — открыт, Т2 — закрыт. Ток протекает по цепи [+Vcc -> D1 -> D2 -> R4 -> Led2]
пин = INPUT. Диоды D1, D2, D3, D4 — закрыты и незначительный микроток не достаточен для зажигания Led2 и Led3. Т1 и Т2 — открыты. Ток протекает по цепи [+Vcc -> T1(эк) -> R1 -> Led1 -> T2(кэ)]

Кстати, последний вариант подключения 3-х светодиодов мне ОЧЕНЬ понравился и я произвел для него замеры напряжений на разных участках цепи.

Небольшое видео работы такого варианта схемы.

А теперь ВООБЩЕ трэш! А восемь светодиодов можем? Можем, да хоть 16… правда необходимо задействовать микросхему 74HC164 (или несколько, если хотим каскадировать включение). Этот вариант схемы подсмотрен у небезызвестного DiHalt-а. Итак, эта микросхема — ни что иное, как сдвиговый регистр. Но, в отличии от горячо мною любимой микросхемы 74HC595, у 74HC164 нету “защелки”. Т.е. данные СРАЗУ попадают на выходы. Это и хорошо и плохо… Хорошо: не нужен дополнительный сигнал на “защелку”; плохо: все продвижения данных будут видны красивыми “переливами”.

Линию RESET мы использовать не будем, а сразу заблокируем ее подтянув к питанию. Два входа данных (A, B) чаще всего объединяют, и тактовым входом (Clock, он же CLK) обеспечивают продвижение битиков по регистру. Ага, все же нужно два пина как минимум: 1-для выставления данных, 2-для тактирования. И тут на помощь к нам приходит “Интегрирующая RC-цепочка”

Выходным напряжением в интегрирующей цепи является напряжение на конденсаторе. Естественно, если конденсатор разряжен, оно равно нулю. При подаче импульса напряжения на вход цепи, конденсатор начнет накапливать заряд, и накопление будет происходить по экспоненциальному закону, соответственно и напряжение на нем будет нарастать по экспоненте от нуля до своего максимального значения. Более научную версию с формулами можно прочитать тут. Резистор ограничивает ток заряда конденсатора, поэтому чем больше его сопротивление, тем больше время заряда конденсатора. Также и для конденсатора — чем больше емкость, тем большее время он заряжается.

Если мы пустим через такую RC-цепочку длинный импульс, то конденсатор успеет как полностью зарядиться, так и полностью разрядиться. Если подадим короткий импульс, по времени намного меньше чем постоянная времени t, то напряжение на конденсаторе изменится совершенно незначительно. Я бы здесь провел аналогию с автомобилем: если при движении по дороге резко нажать на педаль газа и отпустить, то стрелка тахометра просто “взлетит”, а вот стрелка спидометра лишь немного изменить свое положение.

Как это работает?
Выставляем на пин нужный нам битик и ждем пока значение на выходе RC-цепочки не «устаканится» до нужного значения и делаем весьма короткий тактовый “дрыг”. Выставленный бит попадает на выход регистра. И так 8 раз 🙂 Понятное дело, что тактовый импульс снимается до RC цепи, а данные — после.
Схема такая:

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

К сожалению, у меня не оказалось одинарного 7-ми сегментного индикатора, зато “до фига” сдвоенных. Я оставил место под перемычку, запаяв которую, можно сделать вывод одинаковой информации сразу на две половинки индикатора.

Итого: три провода (питание, земля и управляющий) 🙂 Ниже код, который демонстрирует банальный отсчет от 0 до 9

//задержка нужная для заряда-разряда конденсатора (подобрана экспериментально)
#define led_delay 10
//Пин подключения платы индикации
#define outPin 8
// таблица кодов для индикации 7-сегментника
//                                0   1    2    3    4    5   6    7    8   9
const byte PROGMEM Encode7seg[]={252,192, 181, 213, 201, 93, 125, 196, 253,221};

void setup()
{
  pinMode(outPin,OUTPUT);
}


//демонстрашка простого отсчета от 0 до 9
void loop()
{
  for (byte z=0;z<10;z++)
  {
    byte num_out=pgm_read_byte(&Encode7seg[z]);

    out7seg(num_out);
    delay(500);
  }

}// end main loop

//=====================
// void output 7segment
void out7seg(byte out_byte)
{
  for (byte i=0;i<8;i++)
  {
    digitalWrite(outPin,((out_byte>>i) & 0x01)); //выделяем нужный бит и выставляем на линию
    delay(led_delay);//ждем, пока RC цепочка устаканится в нужном значении
    digitalWrite(outPin,LOW);//делаем быстрый синхроимпульс
    digitalWrite(outPin,HIGH);
  }//end for
}

А ниже можно посмотреть результат работы

Ну вот, пожалуй, и все. Естественно, далеко не все варианты применения одного пина мне удалось рассмотреть в этой статье. Оставим небольшой задел на будущее…

P.S. Буду очень-очень рад, если кому-то этот материал будет полезен 🙂


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

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
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение