• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

LCD (HD44780) по трём проводам

Woody_MC

New member
добрый день.

приобрел себе игрушку NodeMCU v1.0 и нигде не нашел нормального решения подключения знакосинтезирующих ЖК экранов.

использовать 6 линий считаю жирным решением, а с i2c расширителем PCF8574T не очень нравится.
есть куча копеечных 74HC595 и решение на нем получается более простое, как в аппаратном, так и в программном плане.

все существующие решения на 74HC595+HD44780 используют линию E на LCD на выводе сдвигового регистра, что заставляет отправлять 3 (можно попробовать сократить до 2) загрузки данных: 1. выставление шины данных. 2. выставление линии E + данные 3. сброс линии E.

я же попробовал объединить линию E (hd44780) с линией защелки LE (74hc595). решение кривое, но работает! есть идея сделать задержку путем использования 2х линий CLK + LE через элемент И (можно на транзисторе), чтобы выдерживать задержку между выставлением шины данных и линии E для корректной работы HD44780.

кто-то делал уже подобное или никто не экономит ресурсы и использует графические экраны по i2c?
 

pvvx

Активный участник сообщества
Обычно используется таракан типа PIC12 или немного больше, он дополнительно опрашивает и клавиатуру или энкодер с кнопкой, да сбрасывает зависший ESP8266.
 

Woody_MC

New member
ну опрос кнопок и энкодера можно без внешнего МК сделать, я противник подобных "костылей".
зависание ESP это то, с чем стоит смириться, т.е. не использовать эту игрушку для серьезных проектов, ИМХО.

вот нашёл в сети свой вариант подключения, только я еще завел подсветку на регистр через ключ. а посему, для выключения надо перегружать регистр, а при защелке будет взводиться сигнал Е на HD44780, что не есть хорошо, посему хочу управлять сигналом Е независимо от LE на 74hc595.


цель - сделать метеостанцию. есть немного (около 60 шт) TM161FD индикаторов, хочется их применить, а то лежат мёртвым грузом.
 

Woody_MC

New member
вроде выше написал что дисплеи есть, зачем мне новые?:confused:
про PCF так же упомянул - не хочу я по i2c (ну вот не нравится мне это решение по ряду причин, вдаваться в подробности не хочется).

повторюсь - индикаторы (проверял несколько разных типов) работают, пришлось немного поковыряться с инициализацией HD44780 кристалла.
осталось переделать линию E, дабы рулить подсветкой независимо от загрузки дисплея (подсветка висит на одном из выходов 74hc595).

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

сейчас пишу библиотеку с функциями из LiquidCrystal, может кому-то пригодится в будущем. как я понял здесь многие ищут уже готовое :)
 

Сергей_Ф

Moderator
Команда форума
а "может кто-то уже делал, дабы не изобретать велосипед",
ну если поиск по 74HC595 +esp8266 тут и на гитхабе ничего не дал, то Вы, наверное, пионер в этом деле. Так что творите и выкладывайте, кому-нибудь пригодится.
Хотя, лично я согласен с @pvvx, меня i2c устраивает для этих целей.
 

pvvx

Активный участник сообщества
вроде выше написал что дисплеи есть, зачем мне новые?:confused:
А зачем старые?
про PCF так же упомянул - не хочу я по i2c (ну вот не нравится мне это решение по ряду причин, вдаваться в подробности не хочется).
Не могли бы уточнить - чем не нравиться. Не подумайте, что придираюсь - просто интересно, да и вписанные варианты даны для альтернативы другим.
Стандарт интерфейса этих индикаторов ещё до 1990-х. Хорошо ещё, что через 20 лет сделали 3-х вольтовые и избавили от отрицательного источника на регулятор контраста... 3-х вольтовые схемы не работают со старыми 5-ти вольтовыми индикаторами. Там уровни CMOS - вынь и положь полные 5В. У вас в схеме для этого не хватает микросхемы с выходами с "открытым коллектором" и подтяжками на 5В. 74HC595N - это вроде CMOS level миркуха. Она может не правильно работать от ESP8266, если её питание от 5В как у индикатора - т.е. возможны глюки или придется делать сигналы управления очень длинными, чтобы уровень переключения достигал соответствующего "единицы".
Поищите старые индикаторы для своего проекта. Приделаете ещё схему с отрицательным смещением на регулировку контраста и не забудьте - светодиодная подсветка у старых индикаторов жрет за 100 мА, что больше чем потребляет ESP8266 (у новых хватает и 20 мА для нормальной подсветки). На контраст можно использовать отрицательный выход у микросхемы выходного интерфейса RS-232, благо этот вывод потребляет мало. Ну или емкостной-коммутаторный преобразователь в отрицательное напряжение. Там надо то всего до -5В на все старые типы.
В проекте удобнее, чтобы регулировка контраста была в меню. Прилепите выход ШИМ на него, а то его часто надо крутить на старых индикаторах - зависит от температуры и общего питания индикатора. У них очень плохой угол обзора и это тоже требует дополнительной тонкой подстройки "контраста". Посмотрите, правильную ли версию (там буквы разные) индикатора вы имеете для его угла обзора, что придется учесть при его расположении в приборе и может ли он отображать русские буквы...
Управление данными индикаторами должно быть сделано по длительности сигналов согласно даташиту на HD44780. Лучше брать задержки побольше - будет работать на всем диапазоне положительных температур (от +0С до +40С). В отрицательном диапазоне старые индикаторы не работали, а те, которые работали типа до -5С - стоили дорого.
Ну и так, как интерфейс с данными индикаторами очень тормозной, то процессор у вас будет заниматься исключительно передачей данных на индикатор. По прерываниям управлять сигналами для этого индикатора ESP8266 почти не вписывается. Но наверное можно вписать, чтобы не занимать всё время CPU на передачу данных к вашему индикатору. В данном случае наверно лучше взять RTOS версию SDK и в одном из процессов сделать драйвер для этого индикатора. Иначе когда будут работать WiFi и другие задачи?
По этим мелким причинам, такие индикаторы никто уже не использует в последнее десятилетие. Или берут современную реаркорнацию их в тот-же размер, для замещения старых, но с другим интерфейсом. Для новых устройств проще заказать индикатор со своим управлением и матрицей, или использовать что посовременнее и дешевле.

PS: Может вывод сделать на осциллографическую вакуумную трубку? Она лучше смотрится - лучше углы обзора и температурный диапазон. "Изобретать велосипед" там не надо - есть море схем, как подключить: Яндекс.Картинки "А то лежат мертвым грузом".
Гораздо лучше смотрится что-то типа 1.8 "Цветной 128x160 SPI TFT Модуль ЖК Дисплей OLED Для Arduino Uno R3 C51 STM32 купить на AliExpress и стоит меньше чем паять 74HC595N.
Но актуальнее что-то типа этого AUO начинает поставки квадратных дисплеев AMOLED размером 1,3 дюйма по диагонали со встроенным сенсорным экраном
 
Последнее редактирование:

Woody_MC

New member
спасибо за развернутый ответ.
i2c хотел использовать для опроса датчиков по прерыванию на таймере, а работу тормозного индикатора повесить в основном цикле именно из-за его тормознутости, посему не хочу на него же вешать индикатор.
кстати, а WiFi соединение разве обрабатывается в основном цикле?
управление контрастом ЖК через ШИМ делал и как это реализовать тоже знаю. отрицательное питание так же брал от драйвера max232 через делитель или вешал осциллятор на ногу МК и формировал отрицательное напряжение.
74hc595 прекрасно работает ногодрыгом (на максимальной скорости ESP) с уровнями 3.3В, питаясь от 5В (цифры из даташита:
74HC595 Vcc=4,5V Vih=3.15-2.4V Vil=2.1-1.35V
74HCT595 Vcc=4,5-5.5V Vih=2.0-1.6V Vil=1.2-0.8V)

сделать хочу (для себя, не для продажи) некоторое количество метеостанций. так же хочу попробовать на oled дисплее 0.96"/1.3".
занимаюсь этим как хобби, в свое время (12-14 назад) работал с МК atmel, сейчас работаю с более высокоуровневыми железками, на которых делают "умный дом" состоятельные люди.

вывод на ЭЛТ не интересен, а вот ГРИ - можно :cool:

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

pvvx

Активный участник сообщества
кстати, а WiFi соединение разве обрабатывается в основном цикле?
В том-то и дело, что нет на Arduino в ESP8266. Об нем надо заботиться самому - отдавать время на его работу, завершая loop() (или делая специальные вставки).
Есть RTOS SDK - там само...
А с текстовыми ЖКИ намаялся вдоволь - самое плохое это их тормоза по интерфейсу. По этому проще вынести на другой простой MCU, а управлять типа по 1-wire с UART. На это не требуется время у основного MCU. Катит и любая синхронная шина типа SPI - 3 провода или UART - 2 провода, т.к. обычно туда данные для индикатора, а обратно с кнопок - рычагов управления... I2C для ESP8266 - не есть решение, т.к. это ногодрыг, занимающий время у CPU на ерунду. Для RTL871x - можно и I2C - там оно аппратное и даже с DMA. :)
Метеостанция - это во первых надежность не ESP8266, во вторых надо опрашивать датчики и часто автономное питание, т.е. работа WiFi очень редка, а ESP8266 жрет.
 
Последнее редактирование:

Woody_MC

New member
В том-то и дело, что нет на Arduino в ESP8266. Об нем надо заботиться самому - отдавать время на его работу, завершая loop() (или делая специальные вставки).
если не затруднит, пните в нужном направлении (где почитать можно с примерами), а то боюсь что столкнусь с данной проблемой а первопричину самостоятельно буду долго искать.:confused:
 

pvvx

Активный участник сообщества
Общая идея такая. Есть функция loop_wrapper в core_esp8266_main.cpp, весь пользовательский код (т.е. setup и loop) запускается оттуда. Функция loop_task (в том же файле) — это обработчик таска, которому назначен приоритет 0. Функция loop_wrapper, а вместе с ней и пользовательский код, запускаются из этого обработчика таска. При запуске функции loop_wrapper мы смотрем, есть ли у нас сохраненный стек (т.е. было ли прервано выполнение функции loop_wrapper на предыдущем вызове таска). Если нет — сохраняем регистры, указатель на верхушку стека (SP) и указатель на инструкцию (PC) — это даст нам возможность "выпрыгнуть" из функции loop_wrapper, когда потребуется, после этого начинаем выполнять loop_wrapper. Если сохраненное состояние есть — восстанавливаем записанные SP, PC, и другие регистры, и продолжаем выполнение loop_wrapper из того места, где оно было приостановлено. Для переключения контекста есть две функции: esp_yield и esp_schedule. Первая сохраняет PC, SP, и другие регистры и "выпрыгивает" из loop_wrapper, тем самым возвращая управление системе, вторая — просто вызывает system_os_post для запуска таска при следующей возможности.
Ограничение такого подхода — можно сделать только один дополнительный контекст.
Такой вот странный метод в Arduino для ESP8266, когда есть ROM функция ets_run() с обработкой внешней функции по указателю... Зачем иметь два стека? Чтобы меньше осталось :) Стек у ESP8266 на всё один и до 6 килобайт. Всё в ESP NON_OS работает из ROM функции ets_run() - таски и программные таймеры... Если ей не отдавать управление - система умрет.
 
Последнее редактирование:

Woody_MC

New member
правильно я понимаю, что это организация параллельного потока (программное прерывание) для выполнения обязательных емких процедур в теле программы без блокировки аппаратных прерываний?
если это так, то надо еще изучить что и когда надо обрабатывать для стабильной работы. буду это иметь ввиду, когда перейду от уровня "led blinking" да "hello world!" на что-то посерьезнее :D
 

pvvx

Активный участник сообщества
правильно я понимаю, что это организация параллельного потока (программное прерывание) для выполнения обязательных емких процедур в теле программы без блокировки аппаратных прерываний?
если это так, то надо еще изучить что и когда надо обрабатывать для стабильной работы. буду это иметь ввиду, когда перейду от уровня "led blinking" да "hello world!" на что-то посерьезнее :D
Нет никакого параллельного потока в Arduino ESP. Тем более Arduino не предназначена для многозадачных решений.
Написали что-то типа while(1) и всё - система умерла. Через полторы секунды вылезет WDT перезагрузка...
Пока крутиться ваш while(1) или другой код - таймеры тоже не работают.
Если хотите ногодрыг с настроенными интервалами - фигу - система тоже запрещает когда ей угодно и аппаратные прерывания, а часть функций WiFi тоже использует аппаратные прерывания, передавая события таскам, которые запускаются только из ets_run() :)
В итоге вся система работает по событиям, распределение которых находиться в ets_run().
В Arduino встроен костыль - delay() он назначает программный таймер и отдает управление старому процессу системы через стек (зачем так - решение писателя Дурины). Т.е. вы должны длинные циклы вашей программы разбавить десятками delay() с неопределенным временем исполнения. Число в delay(x) - условно, т.к. когда завершиться обработка других задач, CPU попадет в est_run() и там уже, после любого прерывания вызовет процедуру кал-бака таймера, на который автор Дурины повесил переключение контекста.
В ESP8266 вы не имеете возможности использовать и NMI на процедуру исполняемую более 1 us. Если прервет WiFi обработку на более 1 us - упадет система. :)
Вся система в Arduino для ESP только для "hello world" и мигания одним светодиодом. :)
 
Последнее редактирование:

Woody_MC

New member
pvvx,
Нет никакого параллельного потока в Arduino ESP. Тем более Arduino не предназначена для многозадачных решений.
я понимаю что параллельно там ничего не работает, я имел ввиду 2 параллельно работающих основных цикла, один из которого запускается и завершается событием, приостанавливая другой.
Вся система в Arduino для ESP только "hello world" и мигания одним светодиодом. :)
так я и пытаюсь заменить надпись hello world на часы, температуру, влажность и давление. может еще min|max значения писать в лог с штампом даты и времени и все это добро собирать через TCP/UDP дырку. думаю это то реально сделать :cool:
 

pvvx

Активный участник сообщества
я понимаю что параллельно там ничего не работает, я имел ввиду 2 параллельно работающих основных цикла, один из которого запускается и завершается событием, приостанавливая другой.
Примерно так.
Если например SPIFFS затормозил, т.к. у него скопилась дефрагментация и он пытается открыть файл (обычно это более 1 сек в таких случаях), то неизвестно - упадет или нет система по WDT :) Кроме простого таймерного WDT в системе работает второй счетчик, на таймере WiFi части, и когда от него сработает прерывание, то оно проверит, был ли исполнен список некоторых системных процедур (их флаги) и перезагрузит систему, если нет. По этому детские вставки ClearWDT не прокатят :)
В общем удачи в освоении глюкодрома от Espressif и писателей Arduino. Может вас это не коснется, если задача очень простая и не требовательная.
 
Последнее редактирование:

Woody_MC

New member
В общем удачи в освоении глюкодрома от Espressif и писателей Arduino. Может вас это не коснется, если задача очень простая и не требовательная.
а какие есть еще альтернативы? с RTL понятно. а среда разработки? немного знаком с IAR, но там тоже не все однозначно. для серьезных проектов я самоделками не пользуюсь, как написал выше - это хобби ради, а не зарабатывания денег для.
 

Jury_78

New member
Наверно для метеостанции wifi не так уж и нужен, достаточно радиоканала, например на 433МГц или что то на NRF24L01.
 

pvvx

Активный участник сообщества
а какие есть еще альтернативы? с RTL понятно. а среда разработки? немного знаком с IAR, но там тоже не все однозначно. для серьезных проектов я самоделками не пользуюсь, как написал выше - это хобби ради, а не зарабатывания денег для.
Делают же что-то ESP8266, и говорят что "работает". Задача у вас простая - переслать пару байт - справитесь...
 

Woody_MC

New member
набросал библиотеку но отладить стабильность инициализации не удалось :(
Код:
#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
  #include "WProgram.h"
#endif

#include  "SpiLCD.h"
//#include <stdio.h>
//#include <inttypes.h>

//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡ BIT OPERATIONS ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
#define BOOL(q)       (!(!(q)))
#define BIT_SET(a,b)  ((a)|=(1<<(b)))
#define BIT_CLR(a,b)  ((a)&=~(1<<(b)))
#define BIT_FLP(a,b)  ((a)^=(1<<(b)))
#define BIT_CHK(a,b)  BOOL((a)&(1<<(b)))
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡

//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡ LCD OPERATIONS ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
#define lcd_RS_Set()      BIT_CLR(spi_data,spi_rs_bit)
#define lcd_RS_Clr()      BIT_SET(spi_data,spi_rs_bit)
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡


SpiLCD::SpiLCD(uint8_t sdo, uint8_t clk, uint8_t le){    //lcd backlight bit is 0 lcd rs bit is 2
    _clk = clk;
    _sdo = sdo;
    _le = le;
    pinMode(_sdo,  OUTPUT);
    pinMode(_clk,  OUTPUT);
    pinMode(_le,   OUTPUT);
    init(0, 2);
}
SpiLCD::SpiLCD(uint8_t sdo, uint8_t clk, uint8_t le, uint8_t bl, uint8_t rs){
    _clk = clk;
    _sdo = sdo;
    _le = le;
    pinMode(_sdo,  OUTPUT);
    pinMode(_clk,  OUTPUT);
    pinMode(_le,   OUTPUT);
    init(bl, rs);
}

void SpiLCD::setFunction(bool dl, bool n, bool font){
    lcd_function = (1<<5)|(dl<<4)|(n<<3)|(font<<2);
}
void SpiLCD::setMode(bool id, bool shift){
    lcd_mode = (1<<2)|(id<<1)|(shift<<0);
}
void SpiLCD::setDisplay(bool display, bool cursor, bool blinking){
    lcd_display = (1<<3)|(display<<2)|(cursor<<1)|(blinking<<0);
}

void SpiLCD::init(uint8_t bit_backlight, uint8_t bit_rs){
    spi_rs_bit = bit_rs;
    spi_bl_bit = bit_backlight;
}
void SpiLCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize){
  if (lines > 1) {
    BIT_SET(lcd_function,FNC_LINE);
  }
  numlines = lines;

  setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != 0) && (lines == 1)) {
    BIT_SET(lcd_function,FNC_FONTSIZE);
  }
  
  // Power on LCD Delay > 45ms
  delay(100);
  //***Reset Sequence***
  lcd_RS_Clr();
  spiSend(RESET_DISPLAY << 4);
  pulseEnable();
  delayMicroseconds(4500);
  spiSend(RESET_DISPLAY << 4);
  pulseEnable();
  delayMicroseconds(160);
  spiSend(RESET_DISPLAY << 4);
  pulseEnable();
  delayMicroseconds(80);
  spiSend(RETURN_HOME << 4);
  pulseEnable();
  delayMicroseconds(80);
  //***End Reset Sequence***
  command(lcd_function);
  command(lcd_mode);
  command(lcd_display);
  clear();
  home();
}

void SpiLCD::backlight(){
    setBacklight();
    spiSend(0x00);
}
void SpiLCD::noBacklight(){
    resetBacklight();
    spiSend(0x00);
}
void SpiLCD::setBacklight(){
    BIT_SET(spi_data,spi_bl_bit);
}
void SpiLCD::resetBacklight(){
  BIT_CLR(spi_data,spi_bl_bit);
}
void SpiLCD::toggleBacklight(){
  BIT_FLP(spi_data,spi_bl_bit) ;
}
bool SpiLCD::getBacklight(){
  return BIT_CHK(spi_data,spi_bl_bit);
}

void SpiLCD::clear(){
    command(CLEAR_DISPLAY);
    delayMicroseconds(1640);  // this command takes a long time!
}
void SpiLCD::home(){
    command(RETURN_HOME);
    delayMicroseconds(1640);  // this command takes a long time!
}

// Turn the display on/off (quickly)
void SpiLCD::noDisplay() {
    BIT_CLR(lcd_display,DISPLAY);
    command(lcd_display);
}
void SpiLCD::display() {
    BIT_SET(lcd_display,DISPLAY);
    command(lcd_display);
}

// Turns the underline cursor on/off
void SpiLCD::noCursor() {
    BIT_CLR(lcd_display,CURSOR);
    command(lcd_display);
}
void SpiLCD::cursor() {
    BIT_SET(lcd_display,CURSOR);
    command(lcd_display);
}

// Turn on and off the blinking cursor
void SpiLCD::noBlink() {
    BIT_CLR(lcd_display,BLINK);
    command(lcd_display);
}
void SpiLCD::blink() {
    BIT_SET(lcd_display,BLINK);
    command(lcd_display);
}

// These commands scroll the display without changing the RAM
void SpiLCD::scrollDisplayLeft(void) {
    BIT_SET(lcd_cursor,CUR_SHIFT);
    BIT_CLR(lcd_cursor,CUR_RIGHT);
    command(lcd_cursor);
}
void SpiLCD::scrollDisplayRight(void) {
     BIT_SET(lcd_cursor,CUR_SHIFT);
    BIT_SET(lcd_cursor,CUR_RIGHT);
    command(lcd_cursor);
}

// This is for cursor follows the display shift
void SpiLCD::leftToRight(void) {
    BIT_CLR(lcd_cursor,CUR_SHIFT);
    BIT_CLR(lcd_cursor,CUR_RIGHT);
    command(lcd_cursor);
}
void SpiLCD::rightToLeft(void) {
    BIT_CLR(lcd_cursor,CUR_SHIFT);
    BIT_SET(lcd_cursor,CUR_RIGHT);
    command(lcd_cursor);
}

// This will autoscroll text from the cursor
void SpiLCD::autoscroll(void) {
    BIT_SET(lcd_mode,MODE_SHIFT);
    command(lcd_mode);
}
void SpiLCD::noAutoscroll(void) {
    BIT_CLR(lcd_mode,MODE_SHIFT);
    command(lcd_mode);
}

void SpiLCD::createChar(char location, char charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(CGRAM | (location << 3));
  for (uint8_t i=0; i<8; i++) {
    write(charmap[i]);
  }
}

void SpiLCD::setRowOffsets(uint8_t row0, uint8_t row1, uint8_t row2, uint8_t row3){
  lcd_row_offset[0] = row0;
  lcd_row_offset[1] = row1;
  lcd_row_offset[2] = row2;
  lcd_row_offset[3] = row3;
}
void SpiLCD::setCursor(uint8_t col, uint8_t row){
  const size_t max_lines = sizeof(lcd_row_offset) / sizeof(*lcd_row_offset);
  if ( row >= max_lines ) {
    row = max_lines - 1;    // we count rows starting w/0
  }
  if ( row >= numlines ) {
    row = numlines - 1;    // we count rows starting w/0
  }
  command(DDRAM | (col + lcd_row_offset[row]));
}

inline void SpiLCD::command(char value){
    lcd_RS_Set();
    dataSend(value);
}
inline size_t SpiLCD::write(char value) {
    lcd_RS_Clr();
    dataSend(value);
    return 1;
}

void SpiLCD::dataSend(char data){
    spiSend(data & 0xf0);
    pulseEnable();
    spiSend(data << 4 & 0xf0);
    pulseEnable();
}
void SpiLCD::spiSend(char data){
  data |= spi_data;
  digitalWrite(_le, LOW);
    delayMicroseconds(2);
  for(int8_t i = 7; i >= 0 ; i--){
    digitalWrite(_sdo, (data >> i) & 0x01);
    digitalWrite(_clk, HIGH);
    digitalWrite(_clk, LOW);
  }
  digitalWrite(_sdo, LOW);
  delayMicroseconds(2);
    digitalWrite(_le, HIGH);
}
void SpiLCD::pulseEnable(){
    delayMicroseconds(2);
    digitalWrite(_sdo, HIGH);
    delayMicroseconds(60);
    digitalWrite(_sdo, LOW);
}
Код:
/*
    HC44780 trouth 74HC595
    i.markin@engineer.com
*/

#ifndef    SPILCD_H
#define    SPILCD_H

#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
  #include "WProgram.h"
#endif

#include    <inttypes.h>
#include    <String.h>
/*
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡ LCD INITIALISATION ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
// [0][0][1][DL][N][F][*][*]
// DL=0 for 4bit data & DL=1 for 8bit data,
#define FS_DL 0
// N=0 for 1 line LCD & N=1 for 2 line LCD
#define FS_N  1
// F=0 for 5x7 dot char & F=1 for 5x10 dot char
#define FS_F  0
#define FUNCTION_SET (1<<5)|(FS_DL<<4)|(FS_N<<3)|(FS_F<<2)

// [0][0]][0][0][0][1][I/D][S]
// I/D=0 for shift right next char & I/D=1 for shift left next char
#define MS_ID 0
// S=0/1 disable/enable shift display
#define MS_S  0
#define MODE_SET (1<<2)|(MS_ID<<1)|(MS_S<<0)

// [0][0][0][0][1][D][C][B]
// D=0/1 off/on display
#define DO_D 1
// C=0/1 off/on cursor
#define DO_C 0
// B=0/1 off/on blinking cursor
#define DO_B 0
#define DISPLAY_ON_OFF (1<<3)|(DO_D<<2)|(DO_C<<1)|(DO_B<<0)
*/
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
#define CLEAR_DISPLAY    0x01
#define RETURN_HOME        0x02
#define RESET_DISPLAY    0x03
#define CGRAM            0x40
#define DDRAM            0x80

#define    DISPLAY            2
#define    CURSOR            1
#define    BLINK            0

#define    CUR_SHIFT        3
#define    CUR_RIGHT        2

#define    MODE_INC        1
#define    MODE_SHIFT        0

#define    FNC_8BIT        4
#define    FNC_LINE        3
#define    FNC_FONTSIZE    2
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡

class SpiLCD
{
  public:
    SpiLCD(uint8_t, uint8_t, uint8_t);
    SpiLCD(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
  
    void setFunction(bool, bool, bool);
    void setMode(bool, bool);
    void setDisplay(bool, bool, bool);
  
    void init(uint8_t, uint8_t);// Q4-Q7 on SHIFT REGISTER corresponds to DB4-DB7 on LCD
    void begin(uint8_t cols, uint8_t rows, uint8_t charsize);

    void backlight();
    void noBacklight();
  
    void setBacklight();
    void resetBacklight();
    void toggleBacklight();
    bool getBacklight();
  
    void clear();
    void home();

    void noDisplay();
    void display();
    void noBlink();
    void blink();
    void noCursor();
    void cursor();
    void scrollDisplayLeft();
    void scrollDisplayRight();
    void leftToRight();
    void rightToLeft();
    void autoscroll();
    void noAutoscroll();

    void createChar(char, char[]);
    void setRowOffsets(uint8_t, uint8_t, uint8_t, uint8_t);
    void setCursor(uint8_t, uint8_t);

    virtual size_t write(char);
    void command(char);
  
  private:
    void pulseEnable();
    void dataSend(char);
    void spiSend(char);
    char lcd_function = 0x24, lcd_mode = 0x04, lcd_display = 0x0C, lcd_cursor = 0x10, spi_data = 0;
    uint8_t numlines, lcd_row_offset[4], spi_rs_bit, spi_bl_bit, _clk, _sdo, _le;
};  

#endif

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

Shyster

New member
зависание ESP это то, с чем стоит смириться, т.е. не использовать эту игрушку для серьезных проектов, ИМХО.
.
Вот такое меня пугает.
У меня несколько проектов, работают больше года, зависаний не наблюдал ни на одном из них, некоторые имеет аптайм месяцы. Но они не держат постоянный коннект к WIFI, только сбрасывают данные. И один с постоянным соединением, тоже зависаний не наблюдал. Но я никогда не использую задержки, слипы, и циклы. Все делаю по счетчикам.
 
Сверху Снизу