Пользователь
0,0
рейтинг
13 апреля 2014 в 04:19

Уменьшаем количество проводов в Arduino — I2C LCD экран и RTC часы по двум проводам из песочницы

Совсем недавно познакомился с Ардуино, поэтому многим мои примеры покажутся простыми.

Однако таким же новичкам, как я, данная информация будет полезна и сэкономит массу времени.

I2C — стандарт общения устройств по 2м проводам, при этом количество устройств, которое висит параллельно на этих проводах может быть очень большим. У каждого устройства есть свой адрес, по которому происходит обращение к устройству. Адресацию можно менять, если на устройстве есть перемычки, которыми можно установить дополнительное смещение относительно базового адреса, жестко прописанного в устройстве.

Это в двух словах.

Начал я все с того, что купил 16х2 символьный LCD дисплей на Ибее. Подключив стандартно — понял что это не то, что нужно. Масса проводов — занимает кучу ножек, хаос и беспорядок.

image

Погуглил, понял что есть дисплеи с интерфейсами, упрощающие подключение. погуглил еще, нашел переходник на I2C для моего LCD. Месяц ожидания, ура-ура, подключил.

image

Выглядит гораздо интереснее!



Проблема возникла на этапе поиска рабочих библиотек и примеров. Как оказалось потом — в основной наиболее известной библиотеке bitbucket.org/fmalpartida/new-liquidcrystal/downloads — примеры — не рабочие! Это заняло некоторое время и силы=)

Конкретно вот тут лежат рабочие примеры и ссылка на библиотеку. arduino-info.wikispaces.com/LCD-Blue-I2C
Там же можно найти описания различных версий LCD-I2C переходников, которые можно купить.

Я покупал вот такой www.ebay.com/itm/310565362720 собран он на чипе PCF8574

Данная версия библиотеки требует установку параметров дисплея в следующем виде

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address


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

Правильная строка, с адресом и нужными пинами — зависит от конкретного дисплея. Новичку понять суть и забить необходимые данные — сложно!
Все что выше — рабочий вариант. Он, наверняка, даже лучше той библиотеки, которую я сейчас использую. Но он показался мне чересчур избыточным и громоздким.

Вторая библиотека заработала сразу. arduino-info.wikispaces.com/file/detail/LiquidCrystal_I2C1602V1.zip/341635514
К сожалению, не помню откуда взял демо для данной библиотеки, но в ней используется упрощенная настройка LCD, что мне было и нужно.

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display


В данной строке назначается адрес устройства и определяется тип дисплея, 16 символов в 2 строки.

Первая и вторая библиотеки между собой не совместимы! . Примеры для одной библиотеки не работают с другой. Нижеследующий код будет относиться к последней указанной библиотеке для работы с I2C LCD.

После подключения экрана, захотелось что-то на него выводить. Просто цифры не интересно. Пусть будут часы=)) Наверное, все проходят этот путь, включая меня. Софтовые часы Ардуины вполне себе позволяют показывать время, но проблема в сбросе данных при отключении. А так как кнопок у меня нет, устанавливать часы приходится через кабель по COM порту через консоль. Вообщем, надоело мне это очень быстро.

Часы реального времени RTC1307 — наверное самая распространенная микросхема для часов. Плюсов много — отдельная микросхема, независима от основного питания Ардуино при наличии батарейки, не зависима от основной программы — время считает точно! Преимущество часов на базе 1307 — I2С. При этом никаких дополнительных выходов задействовать не нужно — управляется все по тем же 2м проводам как и LCD.

Библиотека для работы с часами по I2c заработала сразу, примеры рабочие. github.com/adafruit/RTClib

Программа простая, все прозрачно. Цифры создаются функциями, в которых отдельными блоками выводится цифра. Блоки по 5x8 точек задаются в пользовательских символах, максимально их может быть 8. Пример взят с форума arduino.cc.

Отрисовка происходит в отдельной функции, цифры выводятся по одной. Бегающие крестики — прихоть автора, т.е. моя)) Смещение цифр контролируется при вызове функции отрисовки.



Код для Ардуины
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib

#include <Wire.h>
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

byte LT[8] = 
{


  B00111,
  B01111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte UB[8] =
{
  B11111,
  B11111,
  B11111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};
byte RT[8] =
{

  B11100,
  B11110,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte LL[8] =
{


  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B01111,
  B00111
};
byte LB[8] =
{
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111,
  B11111,
  B11111
};
byte LR[8] =
{


  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11110,
  B11100
};
byte MB[8] =
{
  B11111,
  B11111,
  B11111,
  B00000,
  B00000,
  B00000,
  B11111,
  B11111
};
byte block[8] =
{
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
// loop counter
int count = 0;



void setup () {
  Serial.begin(57600);
  Wire.begin();
  rtc.begin();
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  lcd.home();

  lcd.createChar(0,LT);
  lcd.createChar(1,UB);
  lcd.createChar(2,RT);
  lcd.createChar(3,LL);
  lcd.createChar(4,LB);
  lcd.createChar(5,LR);
  lcd.createChar(6,MB);
  lcd.createChar(7,block);

  // sets the LCD's rows and colums:
  lcd.clear();  



  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(__DATE__, __TIME__));
  }
}



void custom0(int x)
{ // uses segments to build the number 0

  lcd.setCursor(x,0); // set cursor to column 0, line 0 (first row)
  lcd.write(0);  // call each segment to create
  lcd.write(1);  // top half of the number
  lcd.write(2);
  lcd.setCursor(x, 1); // set cursor to colum 0, line 1 (second row)
  lcd.write(3);  // call each segment to create
  lcd.write(4);  // bottom half of the number
  lcd.write(5);
}

void custom1(int x)
{
  lcd.setCursor(x,0);
  lcd.write(1);
  lcd.write(2);
  lcd.print(" ");
  lcd.setCursor(x,1);
  lcd.write(4);
  lcd.write(7);
  lcd.write(4);
}

void custom2(int x)
{
  lcd.setCursor(x,0);
  lcd.write(6);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(4);
}

void custom3(int x)
{
  lcd.setCursor(x,0);
  lcd.write(6);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(4);
  lcd.write(4);
  lcd.write(5); 
}

void custom4(int x)
{
  lcd.setCursor(x,0);
  lcd.write(3);
  lcd.write(4);
  lcd.write(7);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);
}

void custom5(int x)
{
  lcd.setCursor(x,0);
  lcd.write(3);
  lcd.write(6);
  lcd.write(6);
  lcd.setCursor(x, 1);
  lcd.write(4);
  lcd.write(4);
  lcd.write(5);
}

void custom6(int x)
{
  lcd.setCursor(x,0);
  lcd.write(0);
  lcd.write(6);
  lcd.write(6);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(5);
}

void custom7(int x)
{
  lcd.setCursor(x,0);
  lcd.write(1);
  lcd.write(1);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);
}

void custom8(int x)
{
  lcd.setCursor(x,0);
  lcd.write(0);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(5);
}

void custom9(int x)
{
  lcd.setCursor(x,0);
  lcd.write(0);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);

}

//void clearnumber(int x)
//{ // clears the area the custom number is displayed in 
// lcd.setCursor(x,0);
// lcd.print("   ");
// lcd.setCursor(x,1); 
// lcd.print("   ");
//}




void loop () {

  digitalClockDisplay();  
  delay(1000);

}


void digitalClockDisplay(){
  // digital clock display of the time
  DateTime now = rtc.now();

  printDigits(now.hour()/10,0); 
  printDigits(now.hour()%10,4); 

  printDigits(now.minute()/10,9);
  printDigits(now.minute()%10,13);

  // lcd.setCursor(7, 1);
  //  lcd.print(now.second()/10);
  //   lcd.print(now.second()%10);


  if (now.second()%10%2==0){
    lcd.setCursor(7, 0);
    lcd.print("+ ");
    lcd.setCursor(7, 1);
    lcd.print(" +");
    lcd.setCursor(3, 1);
    lcd.print("+");
    lcd.setCursor(12, 0);
    lcd.print("+");
    lcd.setCursor(3, 0);
    lcd.print(" ");
    lcd.setCursor(12, 1);
    lcd.print(" ");
  }
  else
  {
    lcd.setCursor(7, 0);
    lcd.print(" +");
    lcd.setCursor(7, 1);
    lcd.print("+ ");
    lcd.setCursor(3, 0);
    lcd.print("+");
    lcd.setCursor(12, 1);
    lcd.print("+");
    lcd.setCursor(3, 1);
    lcd.print(" ");
    lcd.setCursor(12, 0);
    lcd.print(" ");
  }
  //нарисовали двоеточие

}



void printDigits(int digits, int x){
  // utility function for digital clock display: prints preceding colon and leading 0

  switch (digits) {
  case 0:  
    custom0(x);
    break;
  case 1:  
    custom1(x);
    break;
  case 2:  
    custom2(x);
    break;
  case 3:  
    custom3(x);
    break;
  case 4:  
    custom4(x);
    break;
  case 5:  
    custom5(x);
    break;
  case 6:  
    custom6(x);
    break;
  case 7:  
    custom7(x);
    break;
  case 8:  
    custom8(x);
    break;
  case 9:  
    custom9(x);
    break;

  }



}









Буду признателен опытным программистам, если подскажут как сделать код компактнее. Кажется, есть масса возможностей для оптимизации однотипных данных, но не знаю как это реализовать. Видел примеры на том же форуме arduino.cc — но не понял сути происходящего — код слабо закомментирован и я не понимаю откуда ноги растут. Что-то там было связано с памятью контроллера LCD/ Буду признателен за комментирование кода.

UPD: пока заметка лежала в песочнице, сделал еще несколько вариантов часов. Кому-то это интересно? Сделал псевдо 3d шрифт из 3х2 символов и добавил бипер, чтобы тикал каждую секунду

image

Сделал тонкий шрифт из 2х2 символов для вывода на одном 1602 экране часов, минут, секунд + отображение дня недели и даты. Тонкий шрифт — нарисовал сам по какой-то случайной картинке — в виде кода не нашел. Еще добавил бипер + RGB светодиод, который меняет оттенок каждую секунду. Вообщем, все что было — все прикрутил))

image
Роман @romanvl
карма
25,2
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

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

  • +3
    Спрячьте, пожалуйста, код под спойлер.
  • +3
    Тему Вы затронули полезную, это точно.

    Когда я в конторе разработкой электроники занимался, паралельные интерфейсы ЖК-дисплеев ужасно бесили при дизайне и разводке плат.

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

    Поэтому чаще ограничивались паралельным интерфейсом с 4 разрядами вместо 8 (почти все ЖК-дисплеи это умеют) — в сумме вместе с управляющими получалось штук 8-9 проводников вместо 12-13.

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

    И по поводу использования библиотек — я бы осмелился порекомендовать на данном этапе пробовать без них. Сколько помню у данного чипа достаточно простой и внятный к I2C был подход, так что методически было бы весьма полезно читать мануал по нему нежели искать по форумам нужную библиотечку :)

    Впрочем повторюсь, сама тема актуальная весьма, фотографии достаточно неплохо получились — так что пост вызывает одобрительные чувства!
    • +1
      Ну я только начал, и использую конструкции, которые понимаю. Это мне позволяет как-то двигаться дальше=))

      По библиотекам — согласен — получается то одно не работает, то другое. Я пока буду осваивать — как костыли их оставлю. Потом буду прописывать все в коде функциями.

      Спс. Буду массивы пробовать внедрять.
      • +2
        Так вы попробуйте помимо Arduino ещё и язык Си поизучать… Нарочно! И вообще программирование — задачки небольшие порешайте и т.п. — чтобы это из головы в пальцы перетекло и не мешало вам заниматься серьёзными вещами ;-)

        Это страшно поможет!

        Многие разработчики firmware которых я знал как раз испытывали сложности в разработке из-за того что уровень скиллов программистских у них был… Ну как у ленивого студента — им трудно писать код такой чтоб его потом было легко поддерживать, трудно искать ошибки в этом коде… А как результат устройства работают то криво, то ненадёжно — причём с электроникой особенно стрёмно когда не знаешь, то ли баг в программе, то ли где-то контакт пропадает (или появляется) когда не нужно :D
    • 0
      К сожалению дисплеев работающих по I2C вообще очень мало, и не всегда удавалось подыскать а тем более заказать подходящий.

      На ебее есть универсальные дисплеи работающие в том числе через uart. т.е. всё что нужно — VCC, GND, RX. Соответсвенно, элементарно подключается к чему угодно, например к openwrt-роутеру.
      роутер с дисплеем
  • +1
    Переходник I2C для LCD хорош для прототипа устройства, а при сборке часов на основе м/к atmega он не имеет смысла, если хватает выводов микроконтроллера…

    На счет оптимизации кода — прочитайте про PROGMEM…
    • 0
      Я даже куски кода выдел, сложновато прям сразу вникнуть. Но, думаю, все впереди.
  • +1
    Вы не затронули вопрос с pullup-резисторами для i2c, они нужны при подключении многих устройств.

    Важно при начале сборки любой схемы с i2c проверить каждый по отдельности этим — playground.arduino.cc/Main/I2cScanner — конфликты адресов сложно вылавливать.

    И последнее — видимо, вы тестировали это только на Uno. Дальше вам точно захочется сделать из прототипа компактную схему и вы полезете на Ebay покупать крохотную Ардуино Pro Micro, основанную на железе от Ардуино Leonardo. И тут с большой вероятностью вас ждёт облом, так как 9 из 10 библиотек для i2c устройств несовместимы с Leonardo или ведут себя непредсказуемо.

    (написано по результатам танцев с бубном вокруг Leonardo, часов на 1302, барометра 185, пары LCD-дисплеев и одного i2c level shifter в течение недели).
    • 0
      Я не понимаю смысла покупать микро, у меня лежит пара про мини по 100 рублей (которая без usb ), я сегодня проверю на ней — отпишусь. По железу там один к одному. Про микро не ипользовал, может какая специфика.
      • +1
        Мой спич был про то, что на каждой Leonardo и Pro Micro должна быть надпись «не глотать» и «не давать маленьким детям» «не надеяться, что пример i2c кода из интернета сразу заработает или вообще заработает».

        ProMini можете не испытывать — там всё идентично Uno.
        • 0
          Понятно. Ну если есть старое железо, то предполагается уже есть и опыт по настройке этого железа под современные требования)) Я не рассматриваю другие МК кроме меги328 и меги8. Первой хватает для новичка за глаза, вторая под мелкие проекты.
          • 0
            А почему бы сразу не попробовать под атмегу писать? Ну или аттини например.
            • 0
              Всмысле писать в AVR stuidio?

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

              Чем прошить код в мк? Ранее я уже повторял схемы и у меня есть com программатор (Громова, не помню) для меги и тиньки, но, чтобы прошить — нужно выставлять фьюзы во внешей программе. Минимальное понимание есть, но это далеко не так удобно как с ардуиной. Я куплю нормальный USB программатор-отладчик, но не скоро, ибо не горит. Есть более важные вещи, осциллограф например.

              Готовые библиотеки ардуины позволяют сразу получить результат. А мне это важнее, чем месяц ковыряния и непонятная перспектива, что будет дальше.

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

    • 0
      Cпасибо за ссылку на сканер I2C адреса устройства. Я в этом слабо пока разобрался.
  • 0
    О. Интересная тема, спасибо. Я ещё жду посылку с моим стартовым комплектом, но эти вещи меня уже тревожат. Концепция сдвига регистров и каскадных можем меня не порадовала, показавшись сложной. Хотя возможно это из-за отсутствия опыта.
    • +1
      Я вот тоже сидел ждал пока придут железки. Есть тот же Протеус, которому можно скормить компиленый код из Ардуиновой IDE и посмотреть как работает схемка без железа, но меня не впечатлило.
      • 0
        Не, это скучно. Хочется демонически хохотать при вспышках молнии, чтобы потом выбегать в дымящемся халате с криками «HE IS ALIVE!»
        • 0
          Это уже роботы… а роботы это дорого, долго, но занимательно. Даже фенольного робота чтобы собрать — нужно умудриться найти игрушки подходящей комплектности. Сегодня ездил искал фенольные танки — и не нашел! Похоже они канули в лету как и 2007 год… roboforum.ru/forum88/topic2214.html
          • 0
            Фенольные роботы и фенольные танки?
            Это что-то из военной области? =)
  • 0
    То есть фактически это расширитель портов?
    • 0
      Ну да, плата собрана на PCF8574
  • 0
    Вот делал часики на ардуине,
    можно пригодиться:

    andyplekhanov.narod.ru/hard/arduino/arduino.htm
    • 0
      Классно)) Однако, если под рукой нет экрана от 6610, то дорого выходит его покупать. Я себе заказал TFT от китайфона с тачем за 200 рублей… Как предет буду мучать. Оказался самый оптимальный вариант.
  • 0
    Существуют ли реле, подключающиеся через I2C? Для управления электромоторами например
    • 0
      Это микросхема MCP23017 + любое реле шилд или микросхема-ключ. Вот только управлять сервами так не получится…
      • +1
        Ещё подойдет этот же переходник i2c для LCD экрана, в котором PCF8574 — аналог MCP23017, но на 8 выводов, а не на 16 как MCP23017
        • 0
          Спасибо за информацию. Хоть и не я вопрос задавал.
          • +1
            Если интересна тема расширителей портов, то тут есть ещё варианты с примерами для Arduino:
            playground.arduino.cc/Code/I2CPortExpander8574

            MCP23017 это как MCP23016, но у второй выходы с открытым коллектором.
            • 0
              Спс! Сколько в плейграунде и форуме копаюсь, всегда что-то новое нахожу)
  • +1
    Добрый день!
    Чисто теоретический вопрос: А вместо i2c можно ли для LCD использовать сдвиговый регистр? Нам же вроде для LCD не нужно никакие данные читать, а только писать и для этого вполне подойдёт сдвиговый регистр для выхода? Я так понимаю, что в этом случае нужно будет писать какую то свою библиотеку для работы с LCD через сдвиговый регистр.
    • +1
      Можно, все уже придумано
      cjparish.blogspot.ru/2010/01/controlling-lcd-display-with-shift.html
    • +1
      Вот еще статейка:
      robocraft.ru/blog/arduino/541.html
      • +1
        Спасибо за ссылку на мою давнишнюю статью — как раз в тему всяких I2C-переходников. Людям советую не заморачиваться с переходниками, если более-менее развиты программерские скиллы: в статье как раз расписано, как заставить работать LCD и сервомашинки через сдвиговые регистры, которые легко заменить на I2C-микросхемы, дописав интерфейс к ножкам микросхемы. Дело в том, что микросхемы в DIP-корпусах стоят копейки и изучаются за 1 день, после чего можно делать переходники с чего угодно на SPI или I2C. А кто и в с производством плат ЛУТом знаком, тот сможет сэкономить место, заказав микросхемы в мелких корпусах и приделав плату с ними прямо на заднюю стенку LCD или другого устройства.
  • +1
    -> нашел переходник на I2C для моего LCD
    А можно ссылочку на этот переходничок?
    • 0
      Автор выше писал: habrahabr.ru/post/219137/#comment_7491639 Хотя такую информацию неплохо было бы добавит в статью, а то сразу вопрос возникает: Что за переходник? :-)
    • 0
      дело в том что их три вида, и в них есть мелочи, которые отличаются, в том числе адресация, чипы, на которых они собраны…

      Конкретно я брал тут www.ebay.com/itm/310565362720
      Кстате, это очень хороший продавец, рекомендую. У него взял уже массу всяких шабашек и микрух.

      Дополнил текст
      • 0
        Спаривал именно этот переходник с таким шилдом того же продавца: www.ebay.com/itm/170928504704
        На нём ряд контактов экрана напрямую не доступен для макетирования, а те пины, что выведены сзади, не в том порядке, что на переходнике. Но соединить можно, конечно же. Работает нормально. Ну, а блок кнопок подключается как обычно: Vcc, Gnd, A0.

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