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

Подключаем кучу устройств к Arduino по 5 проводам

Стандартная Arduino/CraftDuino имеет 20 цифровых пинов (6 из них — ещё и аналоговые входы), что бывает недостаточно для решения некоторых задач: тот же LCD-дисплей требует минимум 6 пинов. Для подключения нескольких устройств, не требующих двунаправленной передачи данных, вполне подойдут сдвиговые регистры.

В предыдущих статьях про вывод и ввод через сдвиговые регистры мы уже рассмотрели самое простое их применение, а именно управление светодиодами путём посылки байта и считывание состояний кнопок. Для того, чтобы заставить работать тот же LCD-дисплей через регистр, придётся применить парочку архитектурных программерских трюков. При наличии некоторого опыта в C++ это не покажется сложным (:

Начнём с того, что авторы библиотек Arduino старались сильно не заморачиваться насчёт расширяемости функционала, и не получится просто сказать объекту LCD «подключись через сдвиговый регистр». Так что придётся немножко поработать руками и мозгами за них: доработать библиотеки напильником. Заодно упростим себе работу со SPI. Ну что ж, приступим.

С первого взгляда не очень-то понятно, а как, чёрт возьми, заставить работать тот же LCD через сдвиговый регистр? Что записывать в регистр, какие его выводы должны менять состояние и как? Очевидно, что для начала нужно как-то предоставить возможность управлять отдельными выводами регистра так же, как и обычными пинами Arduino.

Вспомним, как подключается LCD, а затем заглянем в исходный код библиотеки LiquidCrystal и увидим, что там чуть ли не в каждой функции дёргаются пины, к которым подключен LCD, вызовами функции digitalWrite(), а режимы этих пинов устанавливаются через pinMode(). Логично будет написать такие же функции и для сдвиговых регистров.

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

В целях унификации определим интерфейс «контроллера линии»:

class LineDriver
{
public:
  virtual void lineConfig(uint8_t pin, uint8_t mode) = 0;
  virtual void lineWrite(uint8_t pin, uint8_t value) = 0;
  virtual uint8_t lineRead(uint8_t pin) = 0;
};

Этот интерфейс содержит объявления функций lineConfig(), lineWrite() и lineRead() — это аналоги pinMode(), digitalWrite() и digitalRead(). Всю работу с нужными пинами библиотека LCD должна будет делать через интерфейс LineDriver. Для начала создадим драйвер по умолчанию, который будет просто вызывать стандартные функции:

class DefaultLineDriver: public LineDriver
{
public:
  virtual void lineConfig(uint8_t pin, uint8_t mode)
  {
    pinMode(pin, mode);
  }

  virtual void lineWrite(uint8_t pin, uint8_t value)
  {
    digitalWrite(pin, value);
  }

  virtual uint8_t lineRead(uint8_t pin)
  {
    return digitalRead(pin);
  }

  static DefaultLineDriver* getInstance()
  {
    return &g_instance;
  }

private:
  static DefaultLineDriver g_instance; // один глобальный экземпляр драйвера
};

Теперь добавим в класс LiquidCrystal указатель на экземпляр LineDriver и изменим конструкторы класса и функцию init() так, чтобы они принимали соответствующий аргумент:

// LiquidCrystalExt.h
class LiquidCrystal: public Print
{
…
public:
  LiquidCrystal(uint8_t rs, uint8_t enable,
    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
    LineDriver *line_driver = 0, uint8_t backlight = 0xFF);
…
protected:
  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
    LineDriver *line_driver = 0, uint8_t backlight = 0xFF);
…
  LineDriver *_pins;
…
};

// LiquidCrystalExt.cpp
LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  LineDriver *line_driver, uint8_t backlight)
{
  init(1, rs, 0xFF, enable, d0, d1, d2, d3, 0, 0, 0, 0, backlight, line_driver);
}

void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
  uint8_t backlight, LineDriver *line_driver)
{
  _pins = line_driver;

  if (!_pins)
    _pins = DefaultLineDriver::getInstance();
…
}

По умолчанию аргумент line_driver будет равным нулю и его можно не указывать при объявлении объекта LCD-дисплея — тогда будет использоваться драйвер по умолчанию (DefaultLineDriver) и дисплей будет работать, как обычно, через пины Arduino.

Теперь добавим поддержку интерфейса LineDriver в класс для работы с SPI:

class SPI_Bus: public LineDriver
{
public:
  …
  virtual void lineConfig(uint8_t pin, uint8_t mode)
  {
    /* Оставляем метод пустым, т.к. возможность управления состоянием выводов
     * зависит от конкретного устройства.
     */
  }

  virtual void lineWrite(uint8_t line_num, uint8_t value)
  {
    /* Каждому байту соответствует 8 "линий данных" (виртуальных пинов).
     * Проверяем, не превышает ли номер линии максимальный.
     */
    if (line_num < m_bandwidth * 8)
    {
      /* Рассчитываем номер байта в буфере и номер бита в байте */
      const uint8_t byte_index = line_num / 8,
                    bit_index = line_num % 8;

      /* Сначала стираем нужный бит в буфере */
      m_buffer[byte_index] &= ~(1 << bit_index);
      /* Потом устанавливаем нужное значение бита ("уровень на линии") */
      m_buffer[byte_index] |= (value == LOW ? 0 : 1) << bit_index);

      /* Шлём изменённое содержимое буфера. Если работаем со сдвиговым
       * регистром, то нужная ножка регистра поменяет свой уровень на
       * тот, который задан в аргументе value.
       */
      communicate(&SPI_Bus::operationSendBuffer);
    }
  }

  virtual uint8_t lineRead(uint8_t line_num)
  {
    /* Если такой линии нет, возвращаем "низкий уровень на линии" */
    if (line_num >= m_bandwidth * 8)
      return LOW;

    /* Линия есть, считываем состояние линий в буфер */
    communicate(&SPI_Bus::operationReceiveEntireBuffer);

    const uint8_t byte_index = line_num / 8,
                  bit_index = line_num % 8;

    /* Читаем нужный бит из буфера и возвращаем соответствующий ему
     * уровень на линии.
     */
    return (m_buffer[byte_index] >> bit_index) & 1 ? HIGH : LOW;
  }
  …
};
Как можно заметить из приведённого кода, в классе SPI_Bus данные буферизуются: если из регистра ничего не считывать, а только управлять отдельными его выводами через функционал LineDriver, то регистр будет поддерживать уровни напряжений на выводах до тех пор, пока вы не установите новые. Это как раз то, что нужно для управления устройствами — менять состояния отдельных выводов, не трогая при этом другие.

Всё, теперь можно работать со сдвиговыми регистрами, как с наборами пинов:

/* Допустим, у нас есть один сдвиговый регистр с 8 выходами,
 * защёлка которого (SS) подключена к 10му пину Arduino.
 */
SPI_Bus shift_register(_8bit, 10);
/* Аналог digitalWrite(3), только вместо 3-го пина Arduino
 * мы управляем 3-м выходом сдвигового регистра.
 */
shift_register.lineWrite(3, LOW);

Так как сделано это через реализацию интерфейса LineDriver, мы можем передавать указатель на объект сдвигового регистра в конструктор объекта LCD:

SPI_Bus shift_register(_8bit, 10);

/* А LCD-дисплей подключен к сдвиговому регистру:
 * RS ⇨ выход QA (0)
 * E ⇨ QB (1)
 * DB4-DB7 ⇨ QC-QF (2-5)
 */
LiquidCrystal lcd(0, 1, 2, 3, 4, 5, &shift_register);

Теперь при работе с LCD будут меняться уровни не на пинах Arduino, а на выходах сдвигового регистра. Одна строка кода для подключения регистра, и один дополнительный аргумент для объекта LiquidCrystal — и можно управлять дисплеем по 3м проводам SPI. При этом ещё два выхода регистра остались свободными, и мы можем их использовать, как обычные пины.

Но мы не ограничимся одним лишь LCD — есть же ещё сервоприводы. Я проделал аналогичные манипуляции с библиотекой Servo, и теперь точно так же можно управлять сервоприводами через сдвиговый регистр:

SPI_Bus shift_register(_8bit, 10);
Servo servo(&shift_register);

/* А дальше всё делается, как обычно */
void setup()
{
  servo.attach(6); // вывод QG регистра
}

А что насчёт ввода? Ранее я писал о чтении состояний кнопок через сдвиговый регистр. Теперь, с библиотекой SPI_Bus, это можно сделать так:

SPI_Bus shift_register(_8bit, 10);

void setup()
{
  /* Говорим регистру, чтобы дёргал защёлку до чтения по SPI */
  shift_register.setSelectionPolicy(SPI_Bus::SELECT_BEFORE);
}

void loop()
{
  uint8_t states = shift_register.read8bits(); // считываем состояния
}

А теперь пора использовать наши сдвиговые регистры на полную катушку — подключим к Arduino сразу несколько устройств всего по 5 проводам: LCD-дисплей, 4 сервопривода, RGB-светодиод, несколько кнопок и dip-переключатель на 3 позиции.

Схема подключения:


Вся логика запитана от +5 В Craftduino, а сервы — напрямую от +5 В блока питания. Сама Craftduino запитана от +12 В провода БП. Тут нужно быть поосторожнее с нагрузкой: если она будет слишком велика, то стабилизатор на Craftduino будет перегреваться из-за слишком высокого входного напряжения. В этой схеме подавать +12 В вполне допустимо, т.к. компоненты, запитанные от крафтины, не особо жадные.

Общий вид:


Здесь я сделал пару хитростей для уменьшения количества проводов. Во-первых, сделал breakout-платы для 74HC595 (в корпусах SSOP), которые легко соединяются каскадно, используя общие линии SPI и питание:




Во-вторых, сделал для сигнальных линий сервоприводов 4-жильный шлейф:


Просто взял 4 жилы от старого шлейфа для привода гибких дисков (флопарей), припаял к ним 4 пина гребёнки PLS и залепил всё это поликапролактоном (ПКЛ) — последний для этих целей рулит не по-детски (:

Ну, и в-третьих, припаял к LCD-дисплею легендарный транзистор КТ315 с килоомным резистором на базе — для управления подсветкой, подстроечный резистор на 10 кОм — для настройки контрастности, а также 9-жильный шлейф (4 линии данных DB7-DB4, Enable, RS, подсветка, +5V, GND):




Можете рассмотреть некоторые части системы отдельно. Вот сдвиговые регистры 74HC595, к которым подключены LCD, сервы и RGB-светодиод:


Регистр 74HC165 c подключенными кнопками и dip-переключателем:


Заметьте, кнопки и dip-переключатель подключены к «земле» резисторами на 1 кОм — я это сделал лишь потому, что провода ещё больше загромоздили бы плату. Так как к питанию они подключены через резисторы на 10 кОм, получается, что входы 74HC165 подключены к резистивным делителям напряжения с коэффициентом 10:1, но так как при нажатии кнопки делитель выдаёт напряжение около 0.5 В, это не вызывает никаких проблем — такое напряжение является логическим нулём, так как меньше необходимого порога.

Можете убедиться, к Craftduino идёт всего 5 сигнальных проводов:


Ну и повторюсь — питается это всё от обычного компьютерного блока питания — оттуда берётся +12 В на вход Craftduino, +5 В для питания серв и «земля»:


Суть эксперимента такова: пусть при щёлкании dip-переключателем включается/выключается подсветка LCD и загораются/гаснут два значка на нём, при нажатии и удержании кнопок вращаются сервоприводы, и параллельно ещё меняются цвета RGB-светодиода. Для этого примера потребуются библиотеки LineDriver, SPI_Bus, ServoExt и LiquidCrystalExt, ссылки на которые приведены в конце статьи.

/* Включаем все необходимые библиотеки */
#include <LineDriver.h>
#include <SPI.h>
#include <SPI_Bus.h>
#include <ServoExt.h>
#include <LiquidCrystalExt.h>

enum
{
  SERVOS_AMOUNT = 4, // количество серв
  SERVOS_FIRST_PIN = 9, // вывод сдвигового регистра для первой сервы
  SERVO_ROTATE_STEP = 2, // насколько поворачивать серву, когда нажата кнопка

  SYMBOL_HEIGHT = 8, // высота символа LCD
  SYM_DANGER = 0, // код символа "череп и кости"
  SYM_LIGHTNING = 1, // код символа "молния"

  RED_LINE = 2, // номер вывода регистра для красного канала светодиода
  GREEN_LINE = 0, // и зелёного
  BLUE_LINE = 1, // и синего

  SWITCH1 = 1 << 4, // бит первого dip-переключателя в байте состояний входов
  SWITCH2 = 1 << 5, // второго
  SWITCH3 = 1 << 6, // и третьего

  FIRST_BUTTON = 1 << 0, // бит первой кнопки управления сервами
};

SPI_Bus shreg_in(_8bit, 9); // сдвиговый регистр параллельной загрузки 74HC165
SPI_Bus shreg(_16bit, 10); // каскад из двух сдвиговых регистров 74HC595
/* LCD подсоендинён к 74HC595, в качестве последнего аргумента указан вывод
 * для управления подсветкой.
 */
LiquidCrystal lcd(5, 6, 7, 13, 14, 15, &shreg, 4);

/* Сервы, подсоёдинённые к каскаду из 74HC595 */
Servo servos[SERVOS_AMOUNT] = { &shreg, &shreg, &shreg, &shreg };

/* Направления вращения серв. Про это дело производители договорится
 * не сподобились, так что сервы SG-90 и Robbe 10g при одинаковом коде
 * вращаются в разные стороны, и два последних направления пришлось
 * инвертировать для гармонии.
 */
int servo_directions[SERVOS_AMOUNT] = { 1, 1, -1, -1 };

/* Символ "череп и кости" */
uint8_t danger[SYMBOL_HEIGHT] =
{
  B01110,
  B10101,
  B11011,
  B01110,
  B00000,
  B10001,
  B01110,
  B10001,
};

/* Символ "молния" */
uint8_t lightning[SYMBOL_HEIGHT] =
{
  B00010,
  B00100,
  B01000,
  B11111,
  B00010,
  B00100,
  B01000,
  B00000,
};


void setup()
{
  /* Защёлкой 74HC165 нужно дёргать перед считыванием состояний входов */
  shreg_in.setSelectionPolicy(SPI_Bus::SELECT_BEFORE);

  /* Поворачиваем все сервы в среднее положение */
  for (int i = 0; i < SERVOS_AMOUNT; ++i)
  {
    servos[i].attach(SERVOS_FIRST_PIN + i);
    servos[i].write(90);
  }

  /* Сервы управляются  в фоновом режиме прерываниями таймеров через те же
  * сдвиговые регистры, что и экран, так что на время работы с экраном
  * прерывания лучше запретить во избежание хаоса на шине SPI и порчи
  * содержимого внутреннего буфера в shreg.
  * Буфер там нужен для запоминания последнего состояния выходов, если что.
  */
  noInterrupts();
  lcd.createChar(SYM_DANGER, danger);
  lcd.createChar(SYM_LIGHTNING, lightning);
  lcd.begin(8, 2);
  lcd.print("Yo dawg!");
  interrupts();
}


/* Эта функция показывает символ symbol в столбце pos второй строки LCD,
* если enabled == true, иначе в эту позицию выводится пробел.
*/
void indicate(char symbol, uint8_t pos, bool enabled)
{
  lcd.setCursor(pos, 1);
  lcd.write(enabled ? symbol : ' ');
}


void loop()
{
  static unsigned long last_micros = 0;
  static uint8_t last_inputs = 0;

  /* Проверяем состояния входов регистра 74HC165 каждые 10 мс */
  if (micros() - last_micros >= 10000)
  {
   last_micros = micros();

   noInterrupts();
   /* Читаем состояния входов */
   const uint8_t inputs = shreg_in.read8bits();
   /* Первый dip-переключатель занимается подсветкой. Включение/выключение
    * подсветки - быстрая операция, можно и 100 раз в секунду выполнять.
    */
   lcd.backlight((inputs & SWITCH1) != 0);
   interrupts();

   /* А вот сам LCD-дисплей имеет очень низкую скорость обновления, и мы
    * не сможем обновлять его 100 раз в секунду, так что будем это делать
    * только когда состояния соответствующих переключателей изменились.
    */
   if (inputs != last_inputs)
   {
     noInterrupts();
     indicate(SYM_DANGER, 3, (inputs & SWITCH2) != 0);
     indicate(SYM_LIGHTNING, 5, (inputs & SWITCH3) != 0);
     interrupts();

     last_inputs = inputs;
   }

   /* Теперь порулим сервами */
   for (int i = 0; i < SERVOS_AMOUNT; ++i)
   {
     /* Соответствующая серве кнопка нажата? */
     if ((inputs & (FIRST_BUTTON << i)) != 0)
     {
       /* Рассчитываем следующее положение сервы */
       int new_angle = servos[i].read() + servo_directions[i] * SERVO_ROTATE_STEP;

       /* Если положение выходит за допустимые пределы, меняем
        * направление вращения и корректируем положение.
        */
       if (new_angle < 0 || new_angle > 180)
       {
         new_angle = constrain(new_angle, 0, 180);
         servo_directions[i] *= -1;
       }

       /* Обновляем позицию сервы */
       servos[i].write(new_angle);
     }
   }

   /* В довесок будем менять цвет светодиода 2 раза в секунду */
   static uint8_t ticks = 0, led_color = 0;

   if (ticks >= 50)
   {
     noInterrupts();
     /* Загоняем на линии R, G, B первый, второй и третий биты
      * переменной led_color.
      */
     shreg.lineWrite(RED_LINE, led_color & (1 << 0));
     shreg.lineWrite(GREEN_LINE, led_color & (1 << 1));
     shreg.lineWrite(BLUE_LINE, led_color & (1 << 2));
     interrupts();
   
     ticks = 0;
     ++led_color; // меняем цвет
   }

   ++ticks;
  }
}

А теперь видео-демка:


Этот исходник в виде файла вы можете взять тут. Библиотеки лежат в репозитории RoboCraft на GitHub, можно скачать архивы самых свежих версий: LineDriver, SPI_Bus, ServoExt и LiquidCrystalExt.
Есть принципиальная схема в формате программы DipTrace.
Рисунки печатной платы — верх и низ — имеют разрешение 600 DPI (точек на дюйм), распечататься они должны в размере около одного квадратного дюйма. Так же есть проект платы в формате DipTrace. Резисторы на плате должны быть номиналом около 100 кОм, но у меня были только 4.7 кОм — их и пришлось поставить. На работу платы не влияет (:
Фотки из статьи доступны в альбоме на Яндекс.Фотках.

Использованное железо:
  • Три макетки Breadbord
  • Craftduino
  • LCD-дисплей на базе контроллера HD44780, 8x2 символов
  • Сервоприводы SG-90 (2 шт.) и Robbe 10g (2 шт.)
  • RGB-светодиод
  • Сдвиговые регистры 74HC595 и 74HC165. У меня 74HC595 в корпусах SSOP, посаженные на платы, но ничто не мешает вам использовать вариант в DIP-корпусе — просто придётся соединять всё это кучей проводов
  • 4 кнопки и один dip-переключатель
  • Резисторы 10 кОм, 1.5 кОм, 1 кОм и 500 Ом, один подстроечный резистор на 10 кОм (можно поставить и здоровенный потенциометр такого же номинала)
  • Транзистор КТ315 (подойдёт любой другой)

Disclaimer
Будьте аккуратны с подключением питания и ничего не напутайте — блок питания запросто может выдать ток в несколько ампер, и если эти амперы достанутся Arduino, а не сервам, то Arduino от этого жужжать и вращаться не станет. Если у вас после прочтения этой статьи сгорит дом, собака и подшивка Playboy — я не виноват (:
  • +5
  • 9 июня 2011, 23:58
  • burjui

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

RSS свернуть / развернуть
+
0
мега-супер!
на видео заметил, что верхняя серва иногда немного дёргается :)
avatar

noonv

  • 10 июня 2011, 07:15
+
0
нижняя тоже, возможно автор не в тот момент кнопку отпускал.
Спасибо за интересную статью! я уж думал мегу доставать
avatar

Alek

  • 10 июня 2011, 09:01
+
+1
Тут и дребезг контактов, и неучтённые задержки от SPI в библиотеке ServoExt. Последнюю я в ближайшее время постараюсь допилить — там вообще адовый говнокод (камень в сторону разработчиков библиотеки).
avatar

burjui

  • 10 июня 2011, 11:32
+
+2
Интерсная статья, вот только arduino содержит 20 цифровых пинов. ДВАДЦАТЬ.
Юзание аппаратного инф. обмена — зачот.
Однако минусов такого использования два — никаких тебе внешних прерываний и быстрое снижение скорости работы периферии.

Дисклеймер порадовал :) Хоть к 100 Амперному источнику исправную ардуинку подруби — будет нормально работать и кушать свои честные 50мА.
avatar

Radiolok

  • 10 июня 2011, 09:39
+
+1
Ну да, тут особо не поразгоняешься, когда аппаратно только SPI.transfer() может работать, а вся остальная работа делается библиотекой SPI_Bus. Хотя для конкретных задач можно написать нечто более ограниченное в возможностях, но менее тормозное. Кстати, о скоростях: у gcc с оптимизацией кода для AVR явные проблемы robocraft.ru/forum/viewtopic.php?f=27&t=26.
Что до прерываний, то они есть в более продвинутых девайсах, как-то I2C-расширителях выводов. О них я в своё время тоже напишу. Но сдвиговые регистры банально дешевле в разы, к тому же.
А дисклеймер скорее про нечаянное подключение питания с БП на линии логики. Такое иногда случается, но понимаешь это обычно по вспышке и характерному запаху.
avatar

burjui

  • 10 июня 2011, 11:44
+
0
не-не-не. Никаких расширителей при прерываниях! Я имею в виду конкретно прерывания МК -INT и PCINT. (ну и входной захват по таймеру и аналоговый компаратор до кучи) Без них о RTOS например и речи быть не может. Когда какой-то обслуживающий контроллер типа как описано — последовательный опрос/обновление применимы. но когда речь идет о следящей системе, без прерываний никуда.
А скорость работы вполне себе высокая — из SPI в МК при определенной сноровке можно и Мбиты выжимать. Речь идет о том, что девайсов много и в конечном итоге обновление может стать очень медленным.
avatar

Radiolok

  • 10 июня 2011, 13:06
+
0
Дык я о том, что I2C-расширители имеют выход INT, который можно подрубить к МК — тогда можно будет не заниматься постоянным опросом в цикле. Кстати, скорости I2C при этом (400 кГц) вполне хватит.
avatar

burjui

  • 10 июня 2011, 13:11
+
0
Мде, мегу купить проще. Хоть 1280 за 30$ сразу чем брать за 25$ UNO, а потом выдумывать, кодить и прочее — скупой платит дважды народная мудрость…
avatar

nimbus2k

  • 6 июля 2011, 13:52
+
0
Смотря, какие цели и финансы. Если уж на то пошло, дешевле взять китайский клон Arduino Nano за ещё меньшие деньги и пару грошовых сдвиговых регистров. Тем более, эта статья может служить трамплином в тему расширения выводов голого МК с очень малым количеством выводов — ATtiny13A, например, или какого-нибудь PIC. Мир не сошёлся клином на одном Arduino (:
К тому же, я писал этот код скорее для демонстрации одного из методов программирования, который позволяет абстрагироваться от аппаратной части и повысить поддерживаемость и расширяемость кода. На этих принципах строятся такие сложные системы, как ядро Linux — его модель драйверов многоуровневая, и позволяет писать кроссплатформенный код: к примеру, драйвер какой-нибудь USB веб-камеры или DVD-ROM в большинстве случаев будет работать одинаково успешно и на x86, и на ARM без переписывания кода.
avatar

burjui

  • 6 июля 2011, 15:58
+
0
А теперь посчитайте то время которое потратили на код программы?! Мне в 10раз быстрее заработать 30у.е. на ардуино мегу 1280 чем потратить неделю-две на код + его отладку еще месяц для оттачивания. В итоге выигрыш нулевой. Этот метод лишь тем кто УЖЕ имеет что-то и ему не хватает портов. А тем кто планирует проект где будет море всего действительно проще сразу купить мегу. Ведь всё потом упрется в обьём ОЗУ и ФЛЕША в мелких МК
avatar

nimbus2k

  • 6 июля 2011, 16:17
+
0
да и в принципе никто не мешает к готовой ардуине сделать «распределитель» расширитель, ну как угодно назовите — добавить просто одну еще атмегу в самом простом обвязе(кварц да 2 кондера)(я брал атмега8а дип + кварц ровно 2 бакса на Украине) и на ней делать 2ю программу которая будет строго взаимодействовать с первой ардуиной общение через сериал порты, тут и распределенность и цена смешная)
Теперь по поводу цен, нано будет всеравно не меньше 20у.е., 2 — 595 будут еще 1 доллар +нужен текстолит, нужно рисовать плату, травить, сверлить еще 1-2вечера нав всё, итого выйдет 22 и выше у.е. А мега1280 с 54портами всего 30 долларов, и где смысл? Так еще бонус больше прерываний, больше озу, флеша, количество АЦП больше в 2раза
avatar

nimbus2k

  • 6 июля 2011, 16:27
+
0
Вы невнимательно прочитали мой комментарий. Я говорил о любом китайском клоне, который будет стоить дешевле 20$ — это раз. Сдвиговые регистры доступны в DIP-корпусах (в т.ч. прямо здесь в магазине), что удобно для макетирования — это два.
Ну и использовать вторую мегу со своей прошивкой для расширения выводов — это пальба термоядерной боеголовкой по кишлаку, чтобы уничтожить одного террориста.
Не будьте столь категоричны, а то по-вашему выходит, что всегда нужно просто покупать железо пожирнее, и это будет лучшее решение всех проблем.
avatar

burjui

  • 6 июля 2011, 16:37
+
0
Кричать-то зачем? Код я написал один раз, а использовать его могут хоть 100500 человек по 10 раз. К чему спорить, если вы сами сказали, что кому-то это может пригодиться? Вам не пригодилось — ну и ладно, забудьте, что я писал эту статью (:
avatar

burjui

  • 6 июля 2011, 16:27
+
0
Кричать не буду, а покритиковать немного захотелось. Сам столкнулся с такой ситуацией, когда тупо не хватает портов. Тоже изучал расширители портов. А потом просто купил мегу и радости нет предела. Сейчас ваяю большой модуль где мозгом будет мега2560. Из постоянного подключения DS1307, ULN2803, DS18B20, матрица 32х16 двухцветная,SD карта шилд, кнопки и ИК приёмник. Из того что будет к ней подключаться как модули в случе гулянок -MSGEQ7, L293DNE (2моторчика с зеркалами для лазерного спирографа), и общение с RGB светильником на 27вт светодиодов работающих на атмеге8й(ардуино). И всё пойдет, нет даже полетит))) и портов хватит. Еще в планах сделать на 2х сервах поворотный механизм для светильника в двух осях. Из микросхем и оборудования всё есть, сейчас дорисовываю платки, код на 30%
avatar

nimbus2k

  • 6 июля 2011, 16:40
+
0
Статья классная, Мне очень помогла в освоении домашней автоматики. Уважаемый, Burjui, есть ли у вас есть время, можете допилить LiquidCrystalRus под SPI. К сожалению, мои знания в программинге слабоваты.
avatar

shsmith

  • 4 сентября 2011, 17:31
+
0
Давно уж через SPI работает, просто свежие версии я выкладываю в репозиторий на GitHub. Последнюю версию всегда можете качнуть целиком, одним архивом.
avatar

burjui

  • 4 сентября 2011, 22:06
+
0
Огромное спасибо! Ждем новых статей. Особенно про расширение выходов ШИМ. :)
avatar

shsmith

  • 5 сентября 2011, 09:13
+
0
Скачал последнюю версию LiquidCrystalRus и обнаружи, что она не работает с дисплеем 20х4.
Видно это из того, что 3ю строку начинает писать с 17 символа 1й строки. Код рабочий, работал с LiquidCrystalExt. Поправьте, если есть возможность.
Спасибо!
avatar

shsmith

  • 3 октября 2011, 13:40
+
0
Не подскажите, что за программу использовали для создания столь красочной схемы?
avatar

Hibissscus

  • 24 сентября 2011, 00:10
+
0
Schematic, часть пакета DipTrace.
avatar

burjui

  • 24 сентября 2011, 17:21
+
0
А нельзя ли схемку перезагрузить в PNG?
А то хотел распечатать, чтобы перед глазами была, но jpg все циферки съедает.
avatar

GraninDm

  • 3 ноября 2011, 11:08
+
0
А зачем на пинах G, SCLR, SERR, CLK_INH использовали 100к подтяжку? По идее они то не нужны. Или, все же, есть причина?
avatar

Singrana

  • 17 февраля 2012, 13:17
+
0
Да щас «не нужны»! (:
SCLR вообще, когда в LOW, очищает содержимое сдвигового регистра, CLK_INH в LOW запрещает тактирование, а G в HIGH вообще запрещает вывод на ноги регистра. Если их оставить висящими в воздухе, регистр будет выдавать на ноги всякую хрень, ибо будет ловить наводки от сети 220 В.
avatar

burjui

  • 17 февраля 2012, 13:33
+
0
Не adminЯ не про кинуть в воздух, а на питание и землю без резаков. Собственно, как и видел ранее. Просто тут впервые увидел про резаки, вот и интересен их смысл
avatar

Singrana

  • 17 февраля 2012, 13:35
+
0
А, это. Ну да, если входы высокоимпендансные, то можно и без резюков. Но я их таки влепил — для пущей надёжности.
avatar

burjui

  • 17 февраля 2012, 13:37
+
0
Ну, тогда понятно :)
avatar

Singrana

  • 17 февраля 2012, 13:38
+
0
Адаптировать бы библиотеки под arduino 1.0
avatar

Sicness

  • 18 февраля 2012, 17:34
+
0
Сделано. Обновил ссылки на архивы в посте, чтобы указывали на архивы свежих версий на GitHub.
avatar

burjui

  • 20 февраля 2012, 16:04
+
0
А графический дисплей 128х64 можно подключить через сдвиговые регистры?
avatar

Aquarius

  • 27 февраля 2012, 15:00
+
0
Если найдёте подходящую библиотеку или сами модифицируете существующую. Я с графическими не работал.
avatar

burjui

  • 27 февраля 2012, 15:23
+
0
А аналоговые выходы можно похожим образом расширить?
avatar

Aquarius

  • 27 февраля 2012, 16:08
+
0
Для этого есть специальные микросхемы — мультиплексоры. Но я в этом не разбираюсь, спросите лучше Zoltberg'а.
avatar

burjui

  • 27 февраля 2012, 16:29
+
0
Подскажите пожалуйста какой лучше выбрать для данной статьи и в чем между ними разница:
M74HC595B1
SN74HC595N
По datasheet вроде идентичны.
avatar

xneo

  • 2 апреля 2012, 13:07
+
0
Так и есть — можно брать любую. Просто это базовая стандартная логика, и её выпускают все, кому не лень, хотя характеристики и идентичны.
avatar

burjui

  • 2 апреля 2012, 13:12
+
0
Спасибо, Артем!
avatar

xneo

  • 3 апреля 2012, 04:11
+
0
Добрый день. Подскажите, пожалуйста, почему нельзя использовать общую линию SS при подключении 74*595 и 74*165 регистров? Или все-таки можно?

Спасибо.
avatar

brujodentista

  • 14 мая 2012, 10:58
+
+1
Продублирую ответ из лички, дабы остальные тоже могли просветиться:

Потому что 74HC165 всю малину портит тем, что использует SS не совсем по-SPIшному. В SPI принято выбирать устройство прижатием соответствующей ему линии SS к земле на время работы с ним, тогда как для 74HC165 нужно сначала дёрнуть SS вниз-вверх, а потом уже считывать данные.

(в общем-то, учитывая исключительную простоту устройства шины SPI, можно сказать, что 74HC165 не поддерживает SPI, а просто имеет общие с ней принципы работы)
avatar

burjui

  • 14 мая 2012, 14:18
+
+1
Спасибо!
avatar

brujodentista

  • 14 мая 2012, 15:21
+
+1
burjui Громадное спасибо! Ваша статья очень многому научила!
avatar

TRAHOMOTO

  • 22 августа 2012, 12:35
+
0
Здравствуйте. Подскажите пожалуйста, почему у меня все работает не так как надо)
Я подключил 2 микросхемы 74hc595, как у вас в статье и цепляю к ним LCD вот так:
SPI_Bus shreg(_16bit,10);

LiquidCrystal lcd(7, 6, 5, 4, 3, 2, &shreg, 1);

Далее идет мой большой код) И в одном месте, я еще мигаю светодиодом, вот так:
shreg.lineWrite(9, j);
  j=!j;

И у меня вместе со светодиодом мигает подсветка экрана… Пробовал цеплять его на 10 ножку сдвигового региста, тогда кракозябры появляются на экране. Что может быть не так?
avatar

tolyadzyuba

  • 5 сентября 2012, 09:20
+
0
Убрал все 100КОм резисторы, и заработало нормально.
avatar

tolyadzyuba

  • 5 сентября 2012, 09:42
+
0
Добрый день!

Только начинаю осваивать arduino, но уже столкнулся с необходимостью применения недорогого решения данной задачи.
Сейчас проект собран на Меге, но она очень большая. По размеру подходит Nano, но в ней очень мало выводов.

Правильно ли я понял, что для увеличения только цифровых входов и выходов нужно подключить лишь следующие библиотеки:
#include <LineDriver.h>
#include <SPI.h>
#include <SPI_Bus.h>


Сейчас стоит задача увеличить количество digital I/O c 14ти до 37ми.
25 из которых будут выходы и 12 входы.

avatar

z00lu

  • 12 мая 2013, 09:58
+
0
Как надо изменить код обработки LOW на шести каналах при подключении одного сдвигового регистра и использовании библиотек из этой статьи?
Как выглядит код инициации выводов регистра, чтобы с ними можно было работать как с обычными цифровыми выходами?

Сейчас void loop выглядит следующим образом:

 // обработка первого канала
 if(digitalRead(Can1) == LOW){digitalWrite(out1, HIGH);}
 else{digitalWrite(out1, LOW);}

 
 // обработка второго канала
 if(digitalRead(Can2) == LOW){digitalWrite(out2, HIGH);}
 else{digitalWrite(out2, LOW);}

 //обработка третьего канала
 if(digitalRead(Can3) == LOW){digitalWrite(out3, HIGH);}
 else{digitalWrite(out3, LOW);}
 
 //обработка четвертого канала
 if(digitalRead(Can4) == LOW){digitalWrite(out4, HIGH);}
 else{digitalWrite(out4, LOW);}
 
 //обработка пятого канала
 if(digitalRead(Can5) == LOW){digitalWrite(out5, HIGH);}
 else{digitalWrite(out5, LOW);}
 
 //обработка шестого канала
 if(digitalRead(Can6) == LOW){digitalWrite(out6, HIGH);}
 else{digitalWrite(out6, LOW);}

avatar

z00lu

  • 12 мая 2013, 12:39
+
0
Понял в чем была ошибка, у Меги вывод ss на 53 контакте по умолчанию

Курим дальше…
С выводами на один регистр понятно
1. подключаем библиотеки
#include <LineDriver.h>
#include <SPI.h>
#include <SPI_Bus.h>

2. Инициализируем регистр
SPI_Bus shift_register(_8bit, <b>номер_порта_SS_по_даташиту_платы</b>);

3. Переписываем все digitalWrite(переменая_цифрового_выхода_на_дуине, HIGH_или_LOW) следующим образом:
shift_register.lineWrite(номер_выхода_регистра, HIGH_или_LOW);


Подскажите что надо прописать при подключении трех регистров каскадом, как указать в скетче выходы второго и третьего регистра?
avatar

z00lu

  • 12 мая 2013, 18:02
+
0
SPI_Bus shreg(_16bit, 10); // каскад из двух сдвиговых регистров 74HC595
/* LCD подсоендинён к 74HC595, в качестве последнего аргумента указан вывод
* для управления подсветкой.
*/
LiquidCrystal lcd(5, 6, 7, 13, 14, 15, &shreg, 4);


Строчный и рядом блочный комментарии прочитал как одно целое). Смутила фраза «в качестве последнего аргумента указан вывод для управления подсветкой» — сначала подумал, что это относится к строке кода выше, где создается SPI_Bus. Минут пять думал, где подсветка у регистра пока не дошло, что это про 4-й пин, который передали в LiquidCrystal)
avatar

RootOfLife

  • 12 сентября 2013, 10:29
+
0
Доброго времени суток!
Скачал библиотек по ссылкам, водрузи в ардуину.
сделал инклюд… три раза, LineDriver, SPI, SPI_Bus
Жму компил… получаю

In file included from LCD5110_Scrolling_Text.pde:1:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\302' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\267' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\302' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\267' in program
In file included from LCD5110_Scrolling_Text.pde:1:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:315:46: error: invalid suffix «c91d764c9a82611726495e8820e9e» on integer constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:316:62: error: invalid suffix «c91d764c9a82611726495e8820e9e» on integer constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:367:257: error: invalid suffix «x» on floating constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:401:110: error: invalid digit «9» in octal constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
In file included from C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:27,
from LCD5110_Scrolling_Text.pde:3:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\302' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\267' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\302' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:9: error: stray '\267' in program
In file included from C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:27,
from LCD5110_Scrolling_Text.pde:3:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:315:46: error: invalid suffix «c91d764c9a82611726495e8820e9e» on integer constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:316:62: error: invalid suffix «c91d764c9a82611726495e8820e9e» on integer constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:367:257: error: invalid suffix «x» on floating constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:401:110: error: invalid digit «9» in octal constant
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: stray '#' in program
In file included from LCD5110_Scrolling_Text.pde:1:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:4: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected constructor, destructor, or type conversion before '.' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:545: error: expected unqualified-id before numeric constant
In file included from c:/program files/arduino/hardware/tools/avr/lib/gcc/../../avr/include/inttypes.h:37,
from C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:25,
from LCD5110_Scrolling_Text.pde:3:
c:/program files/arduino/hardware/tools/avr/lib/gcc/../../avr/include/stdint.h:159: error: 'int8_t' does not name a type
c:/program files/arduino/hardware/tools/avr/lib/gcc/../../avr/include/stdint.h:213: error: 'int8_t' does not name a type
In file included from C:\Program Files\Arduino\hardware\arduino\cores\arduino/Arduino.h:8,
from C:\Program Files\Arduino\libraries\SPI/SPI.h:15,
from C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:26,
from LCD5110_Scrolling_Text.pde:3:
c:/program files/arduino/hardware/tools/avr/lib/gcc/../../avr/include/avr/pgmspace.h:215: error: 'int8_t' does not name a type
In file included from C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:27,
from LCD5110_Scrolling_Text.pde:3:
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:4: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected constructor, destructor, or type conversion before '.' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:501: error: expected unqualified-id before '<' token
C:\ARDUINO\libraries\LiquidCrystalRus/LineDriver.h:545: error: expected unqualified-id before numeric constant
In file included from LCD5110_Scrolling_Text.pde:3:
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:46: error: expected class-name before '{' token
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:52: error: 'LineDriver' has not been declared
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:56: error: 'LineDriver' has not been declared
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:95: error: ISO C++ forbids declaration of 'LineDriver' with no type
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:95: error: expected ';' before '*' token
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:107: error: 'LineDriver' has not been declared


как побороть?
avatar

kholonkin

  • 11 ноября 2013, 11:02
+
0
после некоторых манипуляций, остались вот эти ошибки

In file included from shreg_demo_2x.ino:3:
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:46: error: expected class-name before '{' token
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:52: error: 'LineDriver' has not been declared
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:56: error: 'LineDriver' has not been declared
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:95: error: ISO C++ forbids declaration of 'LineDriver' with no type
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:95: error: expected ';' before '*' token
C:\Program Files\Arduino\libraries\SPI_Bus/SPI_Bus.h:107: error: 'LineDriver' has not been declared


Чего делать?
avatar

kholonkin

  • 11 ноября 2013, 11:28
+
0
Помогите пожалуйста переделать библиотеку Stepper для работы шаговиками через сдвиговый регистр… Надо подключить 4 вот таких шаговика через 2 последовательных 74HC595.
У меня опыта работы с библиотеками совсем нет. Пытался сам разобраться, но не осилил.
Очень нужна помощь!
avatar

Vvasya

  • 14 апреля 2014, 16:22
+
0
В целом эта статья почти единственная на эту тему я уже несколько недель шарю по интернету и нигде нечего не могу найти
в частности меня интересует подключение дисплея 1602 через сдвиговый регистр
решил попробовать почерпнуть чтонить от сюда но проблемы начались уже с момента старта компилятора он отругал за библиотеки заявив что они не могут так называться
дальше он до шел до строчки
SPI_Bus shreg_in(_8bit, 9); // сдвиговый регистр параллельной загрузки 74HC165
и все
подскажите пожалуйста в каком направлении решать проблему
avatar

Serega82

  • 27 ноября 2015, 22:35
+
0
Кто нибудь пробовал подключать через регистр дисплей Nokia 5110 LCD?
Помогите пожалуйста. Пробовал сам переписать библиотеку по примерам, не заработало((
avatar

shelukhin89

  • 20 августа 2016, 00:26

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