Управление HD44780 по линии питания

Автор оригинала: Vinod.S
  • Перевод
  • Tutorial


Вообще-то, способы уменьшения количества проводов там и так предусмотрены. Вот, например, четырёхбитный режим. Всё равно много? Есть дисплейные модули с I2C. Но нет предела совершенству, и если у вас найдётся лишнее Arduino (рано или поздно появляющееся у многих), дисплей можно отнести от источника данных на некоторое расстояние и подключить двухпроводным кабелем.

Один из способов передачи питания и данных по одной и той же паре проводов состоит в следующем. Сигнал с данными модулируют высокой частотой и объединяют, а на противоположном конце линии разделяют для подачи в демодулятор при помощи дросселей и конденсаторов. Но при питании постоянным током можно воспользоваться более простым способом, похожим на применяемый в домофонных ключах. Модулятор и демодулятор в этом случае не требуются, на передающей стороне достаточно ключа, коммутирующего цепь питания:



Транзисторы — AO3400A и AO3401A. Второй из них выдерживает 5 А (по другим данным — 4), а здесь коммутировать ему приходится всего 200 мА. Поток последовательных данных на ключ может быть подан с какого-либо микроконтроллера, но поскольку конструкция экспериментальная, автор воспользовался ПК, на котором запущен Python-скрипт, и конвертером USB-UART.

Прерыватель питания у автора получился неказистым, но очень крепким:



На противоположном конце линии пульсирующее напряжение подано на однополупериодный выпрямитель с фильтром (хотя выпрямление в данном случае не требуется, просто нужно, чтобы напряжение до диода оставалось пульсирующим, а после — нет), от которого питаются Arduino и дисплейный модуль. Arduino преобразует последовательные данные в параллельные, необходимые модулю. Внимание, у модулей на КБ1013ВГ6 цоколёвка бывает нестандартной. Пульсирующее напряжение до диода поступает на один из цифровых входов Arduino.



Так это выглядит в реале:



Прошивка максимально упрощена, она самостоятельно не вырабатывает даже команды инициализации контроллера дисплея (и все остальные команды), поэтому подавать их должна передающая сторона. Но при этом она распознаёт простейшие однобайтные команды, адресованные непосредственно ей. Здесь х — младший полубайт, команда — старший полубайт:

0xA[x] — подать полубайт x на выводы 4 — 7 модуля
0xB[x] — подать младший бит полубайта x на вход RS модуля
0xC[x] — подать младший бит полубайта x на вход EN модуля
0xD[x] — выставить яркость подсветки (регулируется ШИМом) пропорционально полубайту x

Всё необходимое ПО находится здесь. Чтобы показать, как всё работает, автор составил такой сценарий:



И запустил:

Поддержать автора
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

Комментарии 47

    +3

    Полезная статья. Несколько раз мне пригодился бы такой подход.
    Автор конечно мастер скриншотов :)

      +4
      В чём польза, в экономии одного провода?
        +4
        В том, что его можно сдать в металлоприёмку.
          +1
          Если два провода уже проложены, а третий проложить возможности нет.
          +1
          Потрясающе. Автор изобрёл велосипед 1-wire.
          +2
          Милый Kunststück.
          Может — даже полезный, если рассматривать с т.з. того, что не надо засирать радиодиапазон для такого низкоскоростного устройства.
            +3
            вместо ардуины можно было 595 сдвиговым регистром и парой RC цепочек обойтись.
              +7
              А куда тогда девать ардуину? Она должна быть… везде, даже в генераторе меандра простейшем (555 юзают только деды и не продвинутые).
                +1
                555 юзают только деды

                Не, деды делают на рассыпухе…
                  +1
                  На самом деле — китайцам положена какая-нибудь премия вроде той что дали Курниковой за «вклад в развитие высокоскоростного интернета». «Левые» тиражи любого заказанного устройства привели к замене всего чего можно микропроцессорами…
                    +1

                    На лампах

                    +2
                    Вот вы смеётесь с ардуины, а у меня акумы для шуруповерта с Bluetooth…
                  +2
                  Это интересное решение. Даже придраться особо не к чему. Взял на заметку и повторю.
                  Что изменю у себя:
                  1. Инициализацию дисплея полностью перенесу на видео-мк.
                  2. Вместо ардуино, поставлю 20-ти ногую stm32f042/f030
                  2. В схеме, вместо n-mosfet AO3400A, поставлю биполярник BCR512
                    +1
                    Если результат будет, я даже готов купить с десяток таких корвертеров.
                      +1
                      Без подсветки 1602 потребляет около 3 ма. Лет 20 назад питал по аналогии с 1-wire. На выходе мастера был только резистор подтяжки на 470 ом, без всего этого огорода. Притом что к мастеру планировалось подключить 1602 в кол-ве 16 штук.
                      +12
                      Схема — трэш и угар. Почему все ардуинщики не могут нарисовать схему, чтобы было понятно нормальным людям?
                        0
                        А что не так со схемой??? Ну кроме того что там на входе нет токоограничивающего резистора и резистора подтяжки (которые, кстати говоря, при определенных условиях, не обязательны)???
                        И нормальные люди — это кто?? Те кому надо все разжевать и в рот положить или те кто все же способен самостоятельно добыть недостающие знания или синтезировать их самостоятельно???
                          +11
                          Нормальная схема? Серьезно?image
                            +2
                            Аааааа. Дак вы об этом убожестве… Тут с вами не поспоришь. Но все же и тут при желании можно разобраться.
                            +1
                            А статью зачем читать?
                            самостоятельно добыть недостающие знания или синтезировать их самостоятельно
                            я и так могу. Зачем мне бесполезная картинка?
                          +1
                          Идея очень интересная в плане практического применения.

                          коммутировать ему приходится всего 200 мА.
                          Ардуино потребляет 17 ма и подсветка 40 мА (максимальный ток выхода). Как получилось 200 мА?

                          Хотел бы обратить внимание что примененный модуль ЖКИ у Вас новый, с яркими диодами подсветки. Это видно по выступающему справа от экрана блестящему кожуху. На обычных (старых) модулях ток подсветки примерно 160 мА (от 5В). При их использовании выход желательно бы усилить транзистором.
                            +2
                            У меня попадались 1602 с алишки, которые жрали до 1А легко с подсветкой на полную мощность.
                              +1
                              1А 5В это 5 ватт мощности на светодиоде. Это же прожектор-фонарик!.. Может, китайцы не поставили токоограничивающий резистор?
                            +4
                            Вот жесть то, еще адруинку запихать… Если уже так хочется передать данные по двум проводам хоть на километр, то меняйте полярность, модулируя переменку хоть фазой, хоть частотой.
                              +3
                              По буду занудой… На километре эти 5В упадут настолько, что работать не будет. 5В и 1А на километр — это надо ВВГ 2.5 мм2 или что-то на уровне, а не китайские говно-провода с сечением 0.1 мм2 :))

                              P.S. просто случай из жизни, одни ардуинщики знакомые пытались запитать цепочку устройств от 12В. Первые 20 устройств работали, а потом что-то переставали. Суммарное потребление было 12В и пара ампер, через километр было уже 5...6В, провод какой-то китайский с алишки около 0.5 мм2.
                                +2
                                Помню, я лично пытался питать роутер от его родного БП (12В переменки, в роутере — выпрямитель и линейный стабилизатор) по неиспользуемым парам UTP. Метров 70 что-ли… Не получилось…
                              +2
                              У меня возникает только один вопрос — зачем там процессор?
                              Прекрасно это все реализуется одной примитивной микрухой 74hc595. более того я так делал и оно работает!

                              схема проверенная в железе.


                              image

                                +2
                                Не показывает картинку, проверьте пожалуйста.
                                  +1
                                  Вот обновленная картинка
                                  image

                                  Именно так данные и латч — через КС цепочки + диоды для возможности положительные полуволны пролетать 1 тактом. в общем случае на атмеге 8 мегагерц получается 4 таких экрана перерисовывать 10 раз в секунду без визуальных глюков. кондеры в релизе одинаковые — отличаются в 10 раз резисторы. Нога порта атмеги легко вытягивает питание и передачу данных и даже подсветку трем экранам. без подсветки максимум что пробовал — 10 экранов — работает стабильно.
                                    +1
                                    Нога порта атмеги легко вытягивает питание и передачу данных и даже подсветку трем экранам.
                                    На все экраны выводится одинаковая информация?
                                      +1
                                      Сдвиговые регистры можно включить цепочкой друг за другом, тогда будет разная.
                                      +1
                                      Можно пример кода для полного понимания процесса?
                                      Вообще давно интересуюсь однопроводными протоколами. Найти простое и надёжное решение в интернете не очень легко. Но вот человек предложил,- и сразу оказывается, что это все давно и так знали. Где они были, когда я искал? Я, кстати, пришёл к такому же варианту, что и в переведённой статье. Для него достаточно просто контроллера без обвязки, и места на плате меньше, чем любой другой вариант. Но неинтересно, так как слишком просто.
                                        +1
                                        Код быдлячий — из первого проекта ProofOfConcept и потом был переделан на прерывания но не в этом суть. принцип работы остался тем же

                                        код
                                        void LcdWireOut(unsigned char a)
                                        {
                                            unsigned char b=0;
                                            //unsigned char mask = 1;
                                            
                                            for(b=0;b<8;b++)
                                            {
                                                if(a&(0b00000001<<b))
                                                {
                                                    OwDisp=1;
                                                    delay_us(1);
                                                    #asm("cli");
                                                        OwDisp=0;
                                                        //delay_us(1);
                                                        #asm("nop");
                                                        #asm("nop");
                                                        #asm("nop");
                                                        #asm("nop");
                                                        OwDisp=1;
                                                    #asm("sei");                        
                                                }            
                                                else
                                                {
                                                    OwDisp=1;   
                                                    OwDisp=0;
                                                    delay_us(20);
                                                    OwDisp=1;   
                                                }
                                                //mask=mask<<1;
                                            }
                                             
                                            delay_us(200);
                                            OwDisp=0;
                                        }
                                        
                                        void UpdLcdWire()
                                        {
                                            unsigned char a=0;
                                            if(LCD_B0)
                                                a |= 1 << 7;
                                            
                                            if(LCD_BKL)
                                                a |= LCD_BKL << 6; 
                                                
                                            if(LCD_DB7)
                                                a |= LCD_DB7 << 5;
                                                
                                            if(LCD_DB6)
                                                a |= LCD_DB6 << 4;
                                                
                                            if(LCD_DB5)
                                                a |= LCD_DB5 << 3;
                                                
                                            if(LCD_DB4)
                                                a |= LCD_DB4 << 2;
                                                
                                            if(LCD_DE)
                                                a |= LCD_DE  << 1;
                                                
                                            if(LCD_RS)
                                                a |= LCD_RS;          
                                                
                                            LcdWireOut(a);    
                                        }
                                        
                                        void lcd_init(void)
                                        {
                                            LCD_B0=0;
                                            LCD_BKL = 1;
                                            
                                            LCD_DE=0;
                                            LCD_RS=0;
                                        
                                            LCD_DB4=0;
                                            LCD_DB5=0;
                                            LCD_DB6=0;
                                            LCD_DB7=0;
                                        
                                            UpdLcdWire();
                                        
                                            delay_ms(50);//wait for LCD internal initialization
                                        
                                            lcd_init_write(0x30);
                                            lcd_init_write(0x30);
                                            lcd_init_write(0x30);
                                            lcd_init_write(0x20);//set 4-bit mode
                                            lcd_write_command(0x28);//4 bit mode, 2 strings
                                            lcd_write_command(0x04);//AC=decrement, S=no shift screen
                                            lcd_write_command(0x85);
                                        
                                            lcd_clear();
                                        }
                                        
                                        void lcd_init_write(char cmd)
                                        {
                                            LCD_DE=0;
                                            LCD_RS=0;//command mode
                                            
                                            LCD_DB4=(cmd&16)>>4;
                                            LCD_DB5=(cmd&32)>>5;
                                            LCD_DB6=(cmd&64)>>6;
                                            LCD_DB7=(cmd&128)>>7;
                                            UpdLcdWire();
                                        
                                            LCD_DE=1;
                                            UpdLcdWire();
                                        
                                            LCD_DE=0;
                                            UpdLcdWire();
                                        }
                                        
                                        void lcd_write_command(char cmd)
                                        {
                                            LCD_RS=0;//command mode
                                            lcd_send_data(cmd);
                                        }
                                        
                                        /// Send to data register ///
                                        void lcd_write_data(char data)
                                        {
                                            LCD_RS=1;//data mode
                                            lcd_send_data(data);
                                        }
                                        
                                        /// Send prepared data ///
                                        void lcd_send_data(unsigned char dta)
                                        {
                                            LCD_DE=0;
                                            
                                            LCD_DB4=(dta&16)>>4;//out higher nibble
                                            LCD_DB5=(dta&32)>>5;
                                            LCD_DB6=(dta&64)>>6;
                                            LCD_DB7=(dta&128)>>7;
                                            
                                            LCD_DE=1;//latch-up
                                            UpdLcdWire();
                                        
                                            LCD_DE=0;
                                            UpdLcdWire();
                                        
                                            LCD_DB4=(dta&1);//out lower nibble
                                            LCD_DB5=(dta&2)>>1;
                                            LCD_DB6=(dta&4)>>2;
                                            LCD_DB7=(dta&8)>>3;
                                            
                                            LCD_DE=1;
                                            UpdLcdWire();
                                        
                                            LCD_DE=0;
                                            UpdLcdWire();
                                        }
                                        
                                        void lcd_putchar(char chr)
                                        {
                                            #ifdef WithoutXYControl
                                            if(chr<31)
                                                return;
                                            #else
                                            if((chr<31)||(lcd_x_cnt>=20))
                                                return;
                                            #endif     
                                                
                                            lcd_write_data(chr);
                                        
                                            #ifndef WithoutXYControl
                                            lcd_x_cnt+=1;
                                            #endif
                                        
                                        }
                                        
                                        void lcd_puts(char *str)
                                        {
                                            char k;
                                            while (k=*str++) lcd_putchar(k);
                                        }
                                        
                                        

                                          +1
                                          Спасибо. Прояснилось не сильно, правда. Циклограммы бы глянуть, но это я уже много хочу.
                                            +2
                                            все же просто!
                                            функция UpdLcdWire собирает байт для отправки в линию — тут пример только на один сдвиговик/экран.
                                            дальше функция LcdWireOut передает побитово 9 бит в шину.
                                            изначально она выдает в линию 1-0-1 без задержек если в текущем передаваемом бите единица — так кондер на линии данных не успеет просесть до нуля и защелка захватит единицу на входе.
                                            Если же бит был равен нулю то 1-0 делается почти без задержки а вот второй этам 0-1 делается с задержкой — тогда кондер на линии данных успеет разрядиться до порога ниже 2-х вольт и перепад 0-1 на тактовом входе — засосет в защелку нолик. и так далее 8 раз. линия вконце бита остается в единице так как перепад 1-0 на такте не создает процесса сдвига.
                                            В то же время на второй защелке сдвиговыйРегистр-выход все время бОльшим конденсатором удерживается всегда единица так как за 20 микросекунд он не успеет сколь-либо значимо разрядиться а диод шоттки каждый раз при единице на выходе будет резко дозаряжать его до полного бака. и только в конце после всех 8-ми бит мы надолго — 200 миллисекунд опускаем линию в ноль тем самым разрядив до нуля кондер на LE и данные при поднятии 0-1 — захлопнутся со сдвиговой цепочки на выход.

                                            ключевые моменты таковы:
                                            1) задержка 0-1 при передаче нуля должна быть больше 15 микросек но не дольше 100 — для этого глушим прерывания на это время.
                                            2) передача единицы — должна проходить с задержкой не более 5 микросек. в идеале без задержки но 4 nop туда вставлены так как шоттка не успевает зарядить кондер на линии входных данных ну и сопротивление выхода и так далее.
                                            3) вконце нужно надолго погасить линию в ноль но вопервых паразитрное питание = кондер разрядится так что будет мигать подсветка, а во вторых кроме как экран перерисовывать у проца есть и другие дела потому 200 мсек просто как оптимальный минимум. Важно чтоб микруха на входе ST-CP почуяла переход 0-1.

                                            Диоды нужны чтобы небыло проблем с длительной передачей одних нулей или единиц. без них после каждой передачи 4-х нулей пришлось бы на 100 микросекунд задержаться в единице чтоб восполнить потерю напруги на конденсаторе выходной защелки.

                                            Как это все выглядит на осцилограмме?
                                            А вот так
                                            image

                                            image

                                            Собственно на осцилограмме видно 20 микросек 8 микросек и некоторая неоднородность т.к. иногда в процесс передачи вмешивается прерывание но оно не рушит передачу данных т.к. критически важные места защищены. впоследствии код переведен на прерывания по таймеру и двойную буферизацию и стало возможно подготавливать следующий снимок состояния ног пока собственно текущее состояние ещё передается + переписался драйвер управления экранами — вместо управления каждым экраном по очереди я тупо храню всё содержимое каждого из экранов в оперативке и когда надо экраны перерисовать — просто впаралель всем экранам передаю одну команду — курсор в начало первой строки — и выпаливаю подряд 1-2-3-4-5-6-7-8 символы каждому экрану впаралель — так сильно ускоряется массовое обновление но ценой оперативки на экранный буфер и скоростью изменения одной буквы неа одном экране — перерисовывается всеравно весь массив — впрочем это не недостаток — так вероятность остановки одного из экранов с ошибкой в передаче данных практически сводится к нулю или к малозаметрому проявлению на 50 мсек этой самой ошибки — следующее обновление всеравно перерисует все экраны.
                                              +1
                                              Спасибо за исчерпывающий ответ! Вы не хотели бы оформить это в публикацию? Информация очень нужная, и найти её как комментарий нереально, а было бы это статьёй на Хабре… Даже в нынешнем виде информации больше, чем в статье, под которой коммент расположен.
                                              По коду мне не всё понятно, у меня в Си мало опыта. Но сам принцип ясен, не сильно отличается от Управление семисегментными индикаторами по одному проводу или Управление по одному проводу.
                                                +2
                                                ну в первом примере просто сдвиговик — во время сдвига оно глюки на экран показывает хотя если быстро и нечасто то не заметиш а второй случай — ну там доп триггер применен — тоже не очень… всяко два резика два кондера и один сдвоенный диод попроще будут и на выход только то что нужно выдают.
                                    +1
                                    Прекрасно это все реализуется одной примитивной микрухой 74hc595. более того я так делал и оно работает!
                                    точно два провода, вместе с питанием?
                                      +1
                                      Да, питание от данных точно так же отделяется диодом с большой ёмкостью.
                                      Клоки, данные и чипселект для защелкивания данных — разделяются двумя RC цепочками. Как на схеме из комментария выше (скорее всего), которую не видно.
                                        +1
                                        не видно, оттуда и вопрос
                                      +2
                                      Микруху эту еще заказать надо, а ардуина у многих валяется в ящике стола. Кроме того, на ардуину можно навесить дополнительных функций, например инициализацию, скроллинг символов.
                                        0
                                        к чему мелочиться, можно передавать закодированную информацию и ардуиной раскодировать на дисплей. можно еще много чего придумать чтоб оправдать оверкилл, но промышленное решение будет самое дешевое и надежное, с 595 микросхемой :)
                                          +1
                                          Всё равно будет какой-то свой протокол поверх этого однопроводного интерфейса, что ничем особо не отличается от какого-нибудь кривого (посылать в УАРТ байт для передачи одного бита) управления сдвиговым регистром по одному проводу импульсами разной длительности.
                                          А «разгружать» основной процессор поставив дополнительную ардуину в качестве «видеокарты» для 16х2 — так себе идея.
                                          Только если надо здесь и сейчас что-то соорудить из того что есть под рукой, приходилось как-то заменять пару микросхем медленной, обычной 74 логики микроконтроллером, в качестве временного патча.
                                          +2
                                          Вот обновленная картинка
                                          image

                                          Именно так данные и латч — через КС цепочки + диоды для возможности положительные полуволны пролетать 1 тактом. в общем случае на атмеге 8 мегагерц получается 4 таких экрана перерисовывать 10 раз в секунду без визуальных глюков. кондеры в релизе одинаковые — отличаются в 10 раз резисторы. Нога порта атмеги легко вытягивает питание и передачу данных и даже подсветку трем экранам. без подсветки максимум что пробовал — 10 экранов — работает стабильно.
                                            +1
                                            Но тут все же три провода.
                                            P.S. в закладки внес, пригодится.
                                              +1
                                              два… три то если надо много то перемычка разрывается и питание дается отдельно. или когда подсветке надо больше газу :) изначально два
                                          +1
                                          С ардуинкой? Надпись «Data overkill» смотрелась бы забавнее и правдивее.

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

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