Вторник, 26.11.2024
Мой сайт
Меню сайта
Статистика

Онлайн всего: 32
Гостей: 32
Пользователей: 0
Главная » 2022 » Январь » 29 » Короткая библиотека для LCD дисплея
08:43
Короткая библиотека для LCD дисплея

Короткая библиотека для LCD дисплея

Есть у меня LCD дисплей с 16-ю ножками PIN-ами, про который было тут:
https://akostina76.ucoz.ru/blog/2022-01-25-7444
Я, вообще говоря, знать не хочу про то, как он там внутри работает. Я хочу подключить его проводами и потом просто писать какой-то функцией на нём какие-то буквы. Точнее двумя функциями. Я знаю размеры дисплея (4 строки, 20 колонок), потому вначале нужно устанавливать курсор в нужную строку и колонку (первой функцией) и потом писать букву (второй функцией).
Но дисплей - электронное устройство. Оно подключено 8 (данные)+2 (счётчик, сброс) проводами к контроллеру. Даже эту информацию надо где-то хранить или передавать при каждом обращении к дисплею. Но это хоть понятная информация. Я знаю, что я к нему и куда подключала. Но ещё у дисплея хитрое внутреннее устройство, набор конкретных внутренних команд, например. Этого я не вижу и видеть не хочу. Все это должно быть какой-то внутренней информацией, которая хранится и как-то сама внутри используется. Требуется хранилище для этой информации.
Дальше - больше. Надо полагать, кроме одной команды иногда надо отправлять их последовательности.  И мало ли какую внутреннюю работу там надо делать, потому во внутреннем хранилище неплохо бы хранить целые программные тексты.
Проблемы информационного описания сложных объектов и устройств возникли давно. Результатом стало объектно-ориентированное программирование как стандарт программирования. Первый программный переход - переход от обычного Си к Си++.
Библиотека Arduino это описание объекта на языке Си (точнее Си++). Чтобы хотя бы чуть-чуть сократить программный текст, я взяла стандартную библиотеку и убрала из неё некоторые возможности. В библиотеке есть тексты примеров и сам программный текст. Обычно он состоит из двух файлов. Описание – объекта - LCD_short.h (расширение *.h) и программные тексты LCD_short.cpp (расширение *.cpp).
Полный текст LCD_short.h:
#ifndef LCD_short_h
#define LCD_short_h

#include <inttypes.h>
#include "Print.h"

// commands
#define LCD_CLEARDISPLAY 0x01                    // 0000 0001           void LCD_short::clear()
#define LCD_RETURNHOME 0x02                      // 0000 0010           void LCD_short::home()

#define LCD_ENTRYMODESET 0x04       // 0000 0100 Задание направления прокрутки
#define LCD_DISPLAYCONTROL 0x08     // 0000 1000 Задание состояния дисплея (включен, курсор...)

#define LCD_CURSORSHIFT 0x10        // 0001 0000
#define LCD_FUNCTIONSET 0x20        // 0010 0000 
Задание установок дисплея
#define LCD_
GRAMADDR 0x40       // 0100 0000
#define LCD_SETDDRAMADDR 0x80       // 1000 0000
Положение курсора

// Флаги направления прокрутки, все меньше LCD_ENTRYMODESET=0000 0100
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00


// Флаги состояния дисплея, все меньше LCD_DISPLAYCONTROL=0000 1000:
#define LCD_DISPLAYON 0x04     // 0000 0100
#define LCD_DISPLAYOFF 0x00    // 0000 0000 0
#define LCD_CURSORON 0x02      // 0000 0010
#define LCD_CURSOROFF 0x00     // 0000 0000 0
#define LCD_BLINKON 0x01       // 0000 0001
#define LCD_BLINKOFF 0x00      // 0000 0000   0

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08   // 0000 1000
#define LCD_CURSORMOVE 0x00    // 0000 0000              0
#define LCD_MOVERIGHT 0x04     // 0000 0100
#define LCD_MOVELEFT 0x00      // 0000 0000 0

//
Флаги установок диспля, все меньше LCD_FUNCTIONSET=0010 0000
#define LCD_8BITMODE 0x10      // 0001 0000
#define LCD_4BITMODE 0x00      // 0000 0000  0
#define LCD_2LINE 0x08                     // 0000 1000
#define LCD_1LINE 0x00                     // 0000 0000          0


class LCD_short : public Print {
public:
  //
Функции, которые можно использовать:
  LCD_short(uint8_t rs, 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); //
Создание объекта, передача номеров PIN-ов

   
  void begin(uint8_t cols, uint8_t rows); //
Размеры экрана

  void clear();                          // command(LCD_CLEARDISPLAY);
  void home();                         // command(LCD_RETURNHOME)

  void noDisplay(); // displaycontrol &= ~LCD_DISPLAYON;   command(LCD_DISPLAYCONTROL | _displaycontrol);
  void display();  // _displaycontrol |= LCD_DISPLAYON;   command(LCD_DISPLAYCONTROL | _displaycontrol);
  void noBlink(); // displaycontrol &= ~LCD_BLINKON;   command(LCD_DISPLAYCONTROL | _displaycontrol);
  void blink(); // displaycontrol |= LCD_BLINKON;   command(LCD_DISPLAYCONTROL | _displaycontrol);
  void noCursor(); //   _displaycontrol &= ~LCD_CURSORON;   command(LCD_DISPLAYCONTROL | _displaycontrol);
  void cursor(); // _displaycontrol |= LCD_CURSORON;   command(LCD_DISPLAYCONTROL | _displaycontrol);

  void setCursor(uint8_t, uint8_t);
  virtual size_t write(uint8_t);   // send(value, HIGH);   _rs_pin = HIGH
   
  using Print::write;
private:
  //
Внутренние функции объекта:
  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);
  void command(uint8_t);                       // send(value, LOW);   _rs_pin = LOW
  void send(uint8_t, uint8_t);  // digitalWrite(_rs_pin, mode=HIGH
или LOW); write8bits(value);
  void write8bits(uint8_t); // for { digitalWrite(_data_pins[i], (value >> i) & 0x01); } ; pulseEnable();
  void pulseEnable(); //   digitalWrite(_enable_pin, HIGH);  delayMicroeconds(1); digitalWrite(_enable_pin, LOW);

  //
Внутренние переменные объекта:
  uint8_t _rs_pin; // LOW:
команда.  HIGH: символ .
  uint8_t _enable_pin; //
счетчик - часы.
  uint8_t _data_pins[8]; //
массив PIN-ов данных

  uint8_t _displayfunction; // Настройки дисплея (способ передачи, количнство строк и т.д
  uint8_t _displaycontrol; // Дисплей включен/выключен, с курсором/безкурсора, с мигающем символом/без него
  uint8_t _displaymode;  // Направление прокрутки

  uint8_t _numlines; // Количество строк дисплея
  uint8_t _row_offsets[4]; // Адреса начала строк дисплея
};

#endif


Хорош он тем, что с одной стороны настоящий, а с другой достаточно короткий. В строках define – внутренние команды дисплея. Это - двоичная информаций, которую дисплей обрабатывает. Дальше идёт название объекта, работающего с дисплеем –  «class LCD_short». После слова public – список функций, которые я буду использовать. Я всех этих длинных текстов обычно и не увижу, потому что я сразу возьму из примера четыре, которые мне нужны:
LCD_short lcd(12, 11, 5, 4, 3, 2,6,7,8,9);
void setup() {
 
lcd.begin(20, 4);
 
lcd.setCursor(0, 1);
 
lcd.write("A");
}

После слова private: - внутренняя (скрытая) информация объекта. Это внутренние функции и переменные. Например, номера PIN-ов я передаю один раз при создании объекта. В тот момент они (для последующего использования) записываются во внутренние переменные _rs_pin; _enable_pin; _data_pins[8];.
Это происходит при создании объекта:
// Начальные настройки объекта (вызов из конструктора)
void LCD_short::init(uint8_t rs, 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)
{
  // Присвоить внутренним переменным объекта переданные значения номеров PIN-ов:
  _
rs_pin = rs;
  _
enable_pin = enable;
  _
data_pins[0] = d0;
  _
data_pins[1] = d1;
  _
data_pins[2] = d2;
  _
data_pins[3] = d3;
  _
data_pins[4] = d4;
  _
data_pins[5] = d5;
  _
data_pins[6] = d6;
  _
data_pins[7] = d7;
  // Все PIN-ы предназначены для записи данных:
 
pinMode(_rs_pin, OUTPUT);
 
pinMode(_enable_pin, OUTPUT);
 
for (int i = 0; i < 8; i++) {
   
pinMode(_data_pins[i], OUTPUT);
  }

Наличие сигнала на PIN-е сброса тут определяет, что передаётся. Нет сигнала – команда, есть сигнал – буква (которую надо нарисовать).
И то и другое (код команды и код символа) засовывается чрез PIN-ы данных внутренней функцией send:
inline void LCD_short::command(uint8_t value) {
 
send(value, LOW);
}

inline size_t LCD_short::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}

Сама функция Send:
void LCD_short::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);
  write8bits(value);

}
.. т.е вначале она устанавливает нужное значение того самого сброса, а потом вызывает функцию записи данных в шину данных.
А эта функция, в свою очередь:
void LCD_short::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    // Вытаскивание отдельных бит переданного байта:
   
digitalWrite(_data_pins[i], (value >> i) & 0x01);
    // Пусть
value = 1000 1000
    // Сдвиг ан 4 вправо (
i=4 => (value >> 4) даёт 0001 0001
    // Обнуление всех биткроме последнего командой И с единицей (& 0x01)): 0001 0001 И 0000 0001 =0000 0001 
  }
  pulseEnable();
}

… вначале рассовывает биты байта по проводам (PIN-ам) а потом инициирует тик часов (pulseEnable) чтобы информация записалась:
void LCD_short::pulseEnable(void) {
 
digitalWrite(_enable_pin, LOW);
 
delayMicroseconds(1);   
 
digitalWrite(_enable_pin, HIGH);
 
delayMicroseconds(1);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
 
delayMicroseconds(100);   // commands need > 37us to settle
}
… длиной в одну микросекунду.
Командой идёт установка курсора в нужную позицию:
void LCD_short::Cursor(uint8_t col, uint8_t row)
{
  // Если передан ошибочно большой номер строки - запись на последнюю:
 
if ( row >= _numlines ) {
    
row = _numlines - 1;    // we count rows starting w/0
  }
  // LCD_SETDDRAMADDR = 0x80= 1000 0000. К нему добавляется сдвиг на сдвиг строки и колонку.
  // Добавляемое меньше 0x80, потому можно использоваь команду ИЛИ
 
command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
.. а вывод символа в том месте – функция write(символ).
Исходный и укороченный вариант лежат тут:
https://disk.yandex.ru/d/LOT780WrYkg2QQ

Работоспособность укороченного я не проверяла, хотя старалась удалять другие варианты использования аккуратно.  Смысл варианта – демонстрация того как это всё вообще работает.
Это я про то, что «страшная» проблема перевешивания дисплея на другие PIN-ы:
https://akostina76.ucoz.ru/blog/2022-01-25-7445

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

Просмотров: 104 | Добавил: akostina76 | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Форма входа
Поиск
Календарь
«  Январь 2022  »
ПнВтСрЧтПтСбВс
     12
3456789
10111213141516
17181920212223
24252627282930
31
Архив записей
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Copyright MyCorp © 2024
    Бесплатный конструктор сайтов - uCoz