23.05.2010

Однострочный LCD в двухстрочном режиме

С помощью библиотеки LiquidCrystal, входящей в стандартный комплект ArduinoIDE 0018, можно выводить алфавитно-цифровую информацию на LCD-дисплеи, совместимые с контроллером HD44780U. В Россию, например, импортируется большое количество дисплеев WinStar - они доступны по цене и неприхотливы в использовании.

Однако, существует пара досадных проблем, среди которых отсутствие поддержки русских букв (вроде как решена) и плохая работа с однострочными дисплеями - именно про нее пойдет повествование ;)

Дело в том, что у HD44780U максимальный размер алфавитно-цифрового дисплея составляет 80 знакомест. И хотя производители изготавливают дисплеи на любой вкус (2 строки по 20 символов, 1 строка по 16, 2 по 8 и т.д.), контроллер всегда оперирует памятью DDRAM размером в 80 байт. А вот по поводу  того, сколько на самом деле физический дисплей имеет строк и колонок, должен заботиться программист: например, записывать символы только в те ячейки памяти, за которыми закреплены физические знакоместа в ЖКИ.

Всё это было мне невдомек, пока однажды я не познакомился с дисплеем WH1601A-NGG-CT, 16 знакомест в один ряд:



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



Таким образом, интуитивно правильно написанный скетч (вариация на тему примера "HelloWorld" к библиотеке LiquidCrystal):

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 1);
  // Print a message to the LCD.
  lcd.print("hello, world!");
}

void loop() {
}


Дает вот такой огорчающий результат:



Как я уже упоминал, дисплей логически работает в двухстрочном режиме, поэтому для отображения второй половины надо обязательно вызывать конструктор как lcd.begin(16, 2), в противном случае мы вообще никогда ничего во второй половине не увидим. Зато можно разбивать все строки попалам и перед отображением второй половины ставить lcd.setCursor(0, 1), хотя, на мой взгляд - это насилие над личностью программиста. Про что-то подобное возмущался пользователь abend в форуме arduino, его направили читать документацию - и, видимо, он до сих пор ее читает.

Так как же быть? Если бы это был логически однострочный дисплей, то вывод в ячейки памяти DDRAM с адресами 0x0 - 0x0f отобразился бы на экране. У нас же диапазон прерывается - сначала от 0x0 до 0x7, а далее - от 0x40 до 0x47. Всё, что потребуется - "подменить" обращения к адресам 0x08-0x0f на 0x40 - 0x47, но так, чтобы основная программа ни о чем не догадалась. Неприятный момент заключается в том, что адресный регистр работает в режиме автоинкремента и придется перед каждым выводом в память проверять, не попали ли мы в запрещенный диапазон 0x08-0x0f - иначе говоря, читать содержимое адресного регистра.

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

Библиотечное API расширено функцией setDRAMModel(uint8_t), которой надо передать в качестве аргумента константу режима трансляции - в данном случае - LCD_DRAM_WH1601, после чего результат команды print будет уже гораздо более ожидаемым:

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 10, 11, 5, 4, 3, 2);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // set translation mode
  lcd.setDRAMModel(LCD_DRAM_WH1601);
  // Print a message to the LCD.
  lcd.print("hello, world! :)");
}

void loop() {
}




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

Файл-архив LiquidCrystal-WH1601-0018.zip надо распаковать в arduino-0018/libraries/, желательно предварительно сохранив старый каталог LiquidCrystal - на всякий случай.

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

  1. Добрый день. Спасибо за статью. Но у меня не получилось запустить дисплей. ArduinoIDE выдаёт ошибки с Вашей библиотекой.
    In file included from HelloWorldWH1601.pde:39:
    C:\Program Files (x86)\Arduino\libraries\LiquidCrystal/LiquidCrystal.h:87: error: conflicting return type specified for 'virtual void LiquidCrystal::write(uint8_t)'
    C:\Program Files (x86)\Arduino\hardware\arduino\cores\arduino/Print.h:48: error: overriding 'virtual size_t Print::write(uint8_t)'

    ОтветитьУдалить
    Ответы
    1. Видимо, версия Вашей ArduinoIDE отличается от той, про которую статья. Это ничего, бывает - прошло около пяти лет. Здесь проект на Github, скорее всего именно он Вам и нужен.

      Удалить
    2. Спасибо за столь скорый ответ. И спасибо за Ваши библиотеки. Да, этот проект я уже нашел. И действительно он помог. Неподвижную надпись на русском он вывел без проблем. Теперь вот пытаюсь сделать движущуюся надпись. Но пока не очень выходит.

      Удалить
  2. Подключил такой дисплей через платку i2c отображает только первый символ в любых примерах. Случайно нет примера использования такого дисплея с i2c шиной?

    ОтветитьУдалить
    Ответы
    1. Такой дисплей по i2c я не подключал. А дисплей без этой платки работает правильно? Используется библиотека LiquidCrystalRus или какая-то другая?

      Удалить
    2. здравствуйте . у меня высвечивается только ")"

      Удалить
    3. У нас на форуме даже ветка про дисплеи есть: http://mk90.org/forum/viewtopic.php?f=9&t=38

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

      Удалить
    4. У меня wh1601a-ygh-ctk . Подключил как wh1602 . На дисплее посередине такой знак )

      #include

      // initialize the library with the numbers of the interface pins
      LiquidCrystalRus lcd(12, 10, 11, 5, 4, 3, 2);

      void setup() {
      // set up the LCD's number of rows and columns:
      lcd.begin(16, 2);
      // set translation mode
      lcd.setDRAMModel(LCD_DRAM_WH1601);
      // Print a message to the LCD.
      lcd.print("hello, world! :)");
      }

      void loop() {
      }

      И как включить подсветку?

      В предедущем сообшении была ошибка потому удалил ,

      Удалить
    5. А если оригинальную библиотеку из ArduinoIDE подключить и "Hello World" напечатать - начало будет видно?

      Подсветка включается согласно документации - надо напряжение подать на светодиод подсветки.

      Удалить
    6. Получилось подключить с вашей библиотекой. Даже на русском. Подскажите, как вашу библиотеку переделать под i2c?

      Удалить
  3. Почти понял . Вопрос куда соеденить пин RW """ С точки зрения схемы, потребуется подключить пин RW к какому-то из свободных цифровых выходов Arduino, потому что именно он управляет направлением передачи данных по шине. Если раньше можно было спокойно вешать его на GND (всегда вывод в дисплей), теперь про такую экономию придется забыть.""" Можно электрическую схему если нетрудно

    ОтветитьУдалить
    Ответы
    1. А что собираетесь читать из дисплея, если не секрет?

      Удалить
    2. hello, world! :) . Я только пробую . """Как я уже упоминал, дисплей логически работает в двухстрочном режиме, поэтому для отображения второй половины надо обязательно вызывать конструктор как lcd.begin(16, 2), в противном случае мы вообще никогда ничего во второй половине не увидим. Зато можно разбивать все строки попалам и перед отображением второй половины ставить lcd.setCursor(0, 1), хотя, на мой взгляд - это насилие над личностью программиста. Про что-то подобное возмущался пользователь abend в форуме arduino, его направили читать документацию - и, видимо, он до сих пор ее читает."""""""""""" таким способом всё работает хорошо

      Удалить