23 ноября 2018

Опыт использования ЖК-дисплеев на основе продукции МЭЛТ

Программирование микроконтроллеровСхемотехникаЭлектроника для начинающих
Эта статья посвящена увлекательному приключенческому квесту, который мне пришлось пройти в процессе создания обновленного внешнего датчика для метеостанции, описанной вот в этой статье полтора года назад. По опыту эксплуатации предыдущей версии очень хотелось создать датчик с контрольным дисплеем, чтобы можно было без проблем периодически проверять (и поверять) наиболее капризный компонент станции — датчик скорости ветра. Приключения начались тогда, когда я стал подбирать для этой цели дисплей и по ряду причин, о которых далее, остановился на продукции родного МЭЛТ. Но прежде чем я перейду к описанию приемов нетрадиционного секса способов справиться с выбранными мной произведениями этой фирмы, стоит кратко остановиться на главной причине всей этой грандиозной модернизации, которую я затеял.

В комментариях к той статье мне справедливо указали по поводу конструкции датчиков, что ось подобного устройства должна иметь твердое острие и опираться на столь же твердое основание (вспомним наручные часы «на стольких-то камнях»). Я это, конечно, знал, но тогда не смог придумать способ обеспечить легкую ось с острием достаточной твердости, поэтому, наоборот, пошел по пути минимизации трения, погрузив латунное (для флюгера) или дюралевое (для датчика скорости) острие в мягкий фторопласт (см. чертежик в указанной статье). В полном понимании, что это решение временное и недолговечное и в недалеком времени придется придумывать что-то поосновательней.

Результат прошедших двух сезонов эксплуатации показал тем не менее, что такое решение вполне годится для флюгера, который, конечно, пропилил латунной осью фторопластовое основание до металла, но ему это ничуть не повредило — минимальное трение там и не требуется, даже отчасти наоборот. Хуже было с датчиком скорости, в котором пропилился не только фторопласт в основании, но и само острие из мягкого дюраля стерлось миллиметра на два по длине. В результате прежде всего недопустимо возрос порог трогания и датчик пришлось модернизировать. Модернизации подвергся и сам анемометр, так как лазерный компакт-диск, на основе которого он был выполнен, от солнца расслоился и приобрел неопрятный внешний вид (надо же, я и не знал ранее, что компакт-диски состоят из двух слоев).

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

Об измерительной схеме датчика


В связи со снижением порога трогания встал вопрос о значительном времени, которое занимает измерение малых частот, поступающих с датчика скорости (подробности см. в этой публикации о способах измерения малых частот). Чтобы не ограничивать порог трогания искусственно, в данном случае следовало бы измерять частоты, начиная от 1-2 герц: учитывая, что в датчике 16 отверстий по окружности (см. фото датчика в первоначальной статье), это соответствует примерно одному обороту за 8-16 секунд, что заведомо ниже любого порога трогания. То есть таймаут на ожидание прихода следующего импульса частоты должен быть не менее 1 с (см. указанную статью о способах измерения), что обессмысливает историю с энергосбережением: чтобы получить приемлемое время обновления и вместе с тем успевать осреднять данные во избежание дребезга показаний на дисплее, мы вынуждены будить контроллер каждые две секунды. И если половину из них будет занимать время ожидания импульсов, то никакого энергосбережения не получится — учитывая еще, что все это время работает излучающий светодиод датчика, потребляя около 20 мА.

Некоторые подробности в скобках
Замечу в скобках, что в связи с этой проблемой я сразу вспомнил измеритель течений, который был сконструирован в нашем ОКБ в начале восьмидесятых, еще до появления всяческих контроллеров. В нем было реализовано истинное векторное осреднение: а именно запись всех показаний тактировалась самим сигналом с вертушки датчика скорости — примерный аналог побудки по внешнему прерыванию. Иными словами, если течения никакого нет, то никакой записи и не производилось, и схема ничего не потребляла — работали только часы реального времени. Порог трогания той вертушки, выполненной в виде импеллера с нулевой плавучестью, составлял, правду сказать, 2-3 см/сек, а направление измеритель указывал, поворачиваясь всем корпусом. Дык то в воде, которая в 700 раз плотнее воздуха! За время осреднения, которое там составляло часы, такая вертушка хоть разок, да провернется, потому пустых измерений там почти не было. А для метеостанции, как уже говорилось, математически корректный метод осреднения не подходит, так как она и при отсутствии ветра должна что-то реальное показывать. Следовательно, тут мы не можем обойтись без искусственно ограниченного таймаута на ожидание импульсов с датчика.

Можно нагородить сложное управление пробуждением контроллера от двух источников: нормально от внешнего прерывания с датчика скорости (то есть во время ожидания импульсов с датчика контроллер также уходит в режим энергосбережения), а при отсутствии ветра — принудительное от Watchdog. Это имело бы смысл только при изменении принципа съема показаний датчика скорости с оптического на менее энергозатратные схемы (которые еще надо поискать — датчик Холла, например, потребляет 5-10 мА, что непринципиально меньше оптической конструкции). Но все упростилось из-за того, что датчик у меня теперь питается от солнечной батареи, что позволило просто отказаться от режима энергобережения.

Для тактирования съема показаний я не стал возиться с таймерами или отсчитывать ардуиновские millis(), а просто поставил примитивный внешний генератор частоты с периодом около 1,5 секунд на таймере 555:


Как мы помним, в схеме датчика используется «голый» контроллер Atmega328 в DIP-корпусе, запрограммированный через Uno и установленный на панельку, собственно Arduino используется только для макетирования. Выход генератора был заведен на вывод прерывания INT0, вывод 4 микросхемы (вывод D2 платы Uno). Прерывание по положительному перепаду (RISING) устанавливает некий флаг, по которому в основном цикле и происходит снятие очередных показаний. Частота с датчика при этом измеряется также методом прерывания (выход датчика заведен на вход прерывания INT1, вывод 4 (D3), см. последний метод в той самой статье), потому максимальное общее время ожидания вдвое превышает период измеряемой частоты. При таймауте в 1 сек, таким образом, минимальная измеряемая частота равна 2 Гц (один оборот анемометра за 8 сек). В каждом четвертом цикле происходит осреднение и готовые данные посылаются в основной модуль, то есть обновление показаний происходит примерно раз в 6 секунд.

Всю эту историю с обновленным датчиком придется калибровать и периодически проверять, не возросло ли трение, сравнивания показания с ручным анемометром. Потому очень неудобно, когда показания отображаются в одном месте (в доме), а внешний датчик установлен совсем в другом — на садовой беседке. Встал вопрос об установке контрольного ЖК-дисплея в корпус датчика. Так как красот тут не требуется, то информационные требования к нему минимальны: однострочного 10-символьного дисплея вполне достаточно. Зато геометрические требования довольно жесткие: дисплей должен умещаться в существующий корпус по ширине, которая составляет 70 мм. Надо сказать, что в силу моей органической нелюбви к ЖК-дисплеям (тусклые, малоконтрастные, с мелкими буковками, отвратительного качества подсветкой, и к тому же много потребляют, о чем далее), я почти не в курсе имеющегося в рознице ассортимента. И потому сразу выяснилось, что нужных мне дисплеев надо еще очень сильно поискать в продаже: стандартный ЖК-дисплей 16х2, кои в магазинах доминируют, имеет плату длиной 80 мм и в мой датчик не умещаются, а все остальные типы еще больших габаритов, причем независимо от фирмы. В природе, конечно, существуют и разновидности меньших размеров, но то в природе, а не в отечественной рознице.

Решение: о, МЭЛТ!


В конце концов я обнаружил сразу два МЭЛТ’а, которые чудесно соответствуют моей задаче. Первый из них — однострочный 10-символьный MT-10S1 с контроллером, который, как уверяют производители, «аналогичен HD44780 фирмы HITACHI и KS0066 фирмы SAMSUNG». У него достаточно крупные знаки: более 8 мм в высоту, что вообще-то характерно для китайских дисплеев куда больших размеров. Ширина платы 66 мм, габариты выступающего экрана (внешние) — 62х19,5. Потребление меня в данном случае волнует не очень (ибо внешний датчик питается от солнечной батареи заведомо большей мощности, чем необходимо), но по привычке взглянув на строку в даташите, я выяснил, что оно тоже меньше обычного — 0,7 мА (все обычные ЖК-дисплеи на аналогах HD44780 потребляют от 1,2 мА и выше). До кучи есть еще подсветка, как обычно у всех таких типов — довольно убогая и при этом потребляющая массу энергии.



Второй дисплей MT-10T7 еще восхитительнее: ровно в тех же габаритах уместилось 10 семисегментных цифр высотой аж 13 мм. Некоторые подозрения вызвал нестандартный, и, судя по всему, самопальный интерфейс (для которого в даташите даже приведен пример программирования в словесном псевдокоде). Дисплей не содержит настоящего контроллера: там набор статических триггеров-защелок, управляющихся комбинационной логикой. Но благодаря такой простоте вся эта конструкция потребляет всего 30 мкА, то есть она реально годится для приборов, которые круглосуточно работают на батарейном питании (потребление 1,4 мА у обычных дисплеев и даже 0,7 мА у МТ-10S1 сильно превышают допустимые для такого применения величины — посчитайте сами, сколько проработает такой дисплей, даже без учета остальных узлов прибора, например, от батареек ААА емкостью порядка 1500 мА-ч).



Короче, дайте два!

MT-10T7


Попытка самостоятельно воспроизвести алгоритм для MT-10T7, описанный в даташите (и на Arduino, и на чистом ассемблере), к успеху не привела. Что было сделано не так, я разбираться не стал, потому что наткнулся на вот эту публикацию, где автор (eshkinkot) привел весьма грамотно написанный и обстоятельно оформленный пример обращения с MT-10T7, после чего все сразу заработало. Если кому интересно, то вот тут лежит модифицированный пример eshkinkot’а, дополненный всеми допустимыми на семигментных индикаторах осмысленными символами, в том числе буквами, не совпадающими с цифрами:






На этих картинках контраст экрана немного подрихтован установкой делителя по выводу Vo — 18 кОм (к питанию): 10 кОм (к земле), хотя и без него контраст «по умолчанию» вполне приемлемый.

Я затем дописал к указанному примеру функцию, которая воспроизводит на дисплее произвольное число в пределах трех-четырех десятичных разрядов — положительное или отрицательное, целое или с плавающей точкой, то есть общее число знаков может достигать пяти: «-12,7», например. Так как точка в семисегментном коде не занимает отдельного знакоместа, то максимальное число выводимых разрядов равно 4. Входными параметрами для этой функции служат: а) массив (char buf[5]), содержащий ACSII-представление числа, б) реальное количество символов в нем (ii от 1 до 5) и в) позиция (pos от 0 до 9), куда помещать первый (слева) знак числа (используемые по ходу дела функции и обозначения см. в означенной публикации eshkinkot’а или в примере по ссылке):

Код функции
void writeASCIIdig_serial(char buf[5], byte ii, byte pos)
//отображает на дисплее число из буфера
{
   boolean dot; //признак присутствия точки в массиве
   //выравнивание числа вправо, начиная от pos:
    pos=pos+(4-ii);
    //если есть точка, то на символ меньше: 
    for (byte i=0; i <= ii; i++) if (buf[i]=='.')  pos++;
    //выводим поразрядно:
    for (byte i=0; i <= ii; i++){ 
    //если след. символ точка, то выводим с точкой:
    if (buf[i+1]=='.') dot=true; else dot=false; 
    switch (buf[i]) { //decoder ASCII -> 7-сегментный код
    case '0':
      writeSymbol(pos, DIGIT_ZERO, dot);
      break;
    case '1':
      writeSymbol(pos, DIGIT_ONE, dot);
      break;
    case '2':
      writeSymbol(pos, DIGIT_TWO, dot);
      break;
    case '3':
      writeSymbol(pos, DIGIT_THREE, dot);
      break;
    case '4':
      writeSymbol(pos, DIGIT_FOUR, dot);
      break;
    case '5':
      writeSymbol(pos, DIGIT_FIVE, dot);
      break;
    case '6':
      writeSymbol(pos, DIGIT_SIX, dot);
      break;
    case '7':
      writeSymbol(pos, DIGIT_SEVEN, dot);
      break;
    case '8':
      writeSymbol(pos, DIGIT_EIGHT, dot);
      break;
    case '9':
      writeSymbol(pos, DIGIT_NINE, dot);
      break;
    case '-':
      writeSymbol(pos, SYMBOL_MINUS, dot);
      break;
    } //end decoder
    if (buf[i]!='.') pos++; //если не точка, то +1 позиция
  }//end for i
}


Модуль MT-10T7 для контрольного вывода числовых значений удобнее, чем обычные строчно-матричные дисплеи: у него цифры большого размера, а десятичная точка не занимает отдельного знакоместа и, следовательно, в тех же позициях можно уместить на один знак больше. Но для моих целей удобнее, если есть возможность вывода букв (иначе направление придется выводить в компасных градусах, что несколько непривычно). Потому я для данного случая обратил свой взор на идентичный по размерам однострочный матричный MT-10S1, который, несмотря на ряд недостатков, и перекочевал в законченную конструкцию. Заодно у него уже есть подсветка, которой лишен MT-10T7 (для этого нужно было сразу покупать MT-10T8), и я решил, что в данном случае ее наличие не помешает.

MT-10S1


У дисплея MT-10S1 буковки-цифирки в полтора раза поменьше, но тоже вполне достойного размера. Кроме того у него экран экономно упакован в общие габариты: 10-значных импортных аналогов нет, но в винстаровском WH1601L (где символы даже чуть меньше по высоте) от общей длины платы и экрана на один знак приходится на целый миллиметр больше. Ну и уменьшенное почти вдвое потребление контроллера (в сравнении с тем же WH1601L). Собственно, на этом преимущества заканчиваются, дальше начинаются «особенности».

Модуль хвастается тем, что, как уже говорилось, имеет контроллер, совместимый с HD44780 фирмы HITACHI. То есть он без лишних напрягов должен работать с любимой Liquid Crystal. Мало того, «умолчательная» страница кодировки совпадает с англо-кириллической страницей HD44780 и его многочисленных аналогов, то есть MT-10S1 без проблем должен работать с Liquid Crystal Rus, никаких кодовых страниц для этого переключать не требуется. И он действительно все это делает, но с нюансами.

Первый нюанс — в однострочном варианте разработчики, видимо, экономят на регистрах, и в памяти на один регистр приходится только 8 символов строки (адреса 00h – 07h), а оставшиеся два символа относятся уже к другому регистру (40h-41h). То есть дисплей де-факто двухстрочный, просто обе строки физически расположены в одну линию. При ближайшем рассмотрении оказалось, что то же самое характерно и для WH1601 (только там второй регистр занимает полные восемь разрядов). Почему так неудобно сделано — совершено неясно, в обычных дисплеях 16х2 регистры шестнадцатибитные, и едва ли такое усечение удешевляет продукт, скорее наоборот из-за необходимости производить разные версии контроллера (если они разные, в чем я вовсе не уверен). Я подумал было, что с этим связано меньшее, чем обычно, потребление MT-10S1, но такой же WH1601 потребляет 1,2-1,4 мА, то есть ничем не отличается от своих продвинутых собратьев.

Ну, казалось бы, и ладно — в Liquid Crystal Rus обнаружилась функция setDRAMModel() и соответствующая константа LCD_DRAM_WH1601. Для такого режима в библиотеке прописано очевидное преобразование адресов:

if (ac>7 && ac<0x14) command(LCD_SETDDRAMADDR | (0x40+ac-8));

Но не знаю, как это функционирует на других однострочных дисплеях, а MT-10S1 в таком режиме работать напрочь отказывается — экран остается просто пустым. Так как речь идет об адресации, то простой самописной функцией поверх библиотеки это не исправишь, а ковыряться в библиотеке и выяснять, в чем дело, я не стал — у меня и без того подправленных вариантов Liquid Crystal уже больше полудюжины, не хочется их плодить и далее.

Дисплей MT-10S1 нужно объявлять, как двухстрочный: lcd.begin(16, 2); (вместо 16 можно подставить 10 или 12, ничего не изменится, так как реальное число символов в одной строке все равно 8). Попытка инициализировать его, как однострочный (цифирка 1 во второй позиции) приведет к сбою — фон окрасится темным. А выводить многоразрядные числа можно только в пределах 8 знаков, у более длинных строк крайние символы сверх 8 просто пропадут. Поэтому 9 и 10 знаки либо фактически годятся только для вывода вспомогательных величин (единиц измерения, например), либо нужно разбивать строку-число на отдельные разряды, и при переходе за 8-й символ менять позицию курсора на первый символ второй строки.

Для страждущих вот здесь можно скачать пробный скетч для этого дисплея (подключение выводов — в тексте скетча или на схеме ниже). Кстати, контраст (о котором в заводском даташите ни слова, и вывод Vo обозначен, как NC) здесь регулируется обычным образом, но этого действительно делать не надо: в отсутствие подсветки фон кажется несколько темноватым, но при попытке его осветлить подключением делителя к выводу Vo заметно теряется контраст при включении подсветки.

Интерфейс с контроллером


После проверки того, что все работает, как надо, встал в полный рост вопрос о том, как все это стыковать с контроллером датчика. Чтобы обеспечить управление от него непосредственно, свободных выводов у контроллера датчика, конечно, не хватало, а городить городеж с контроллерами больших размеров совсем не хотелось — удобнее, когда построение системы модульное, и вывод на дисплей не мешает работе основного алгоритма, уже отлаженного ранее. Оставалось задействовать какой-либо из последовательных интерфейсов.

Здесь напрашивается I2C-решение на основе PCF8574 (или ее многочисленных аналогов), тем более, что сама по себе эта микросхема — просто навороченный сдвиговый регистр, и потому потребляет несколько десятков мкА при работе и менее 10 мкА в покое. В совокупности с MT-10T7 они образуют отличную пару для создания малопотребляющих устройств с индикацией, и у МЭЛТ даже запасен готовый вариант на такой случай: MT-10T11 с общим потреблением в 30 мкА.

А вот для MT-10S1 такого удобного решения нет — почему-то дополнением в виде аналога PCF8574 среди строчных дисплеев МЭЛТ снабжаются только версии с конфигурацией 20х4 (UPD: в комментах подсказали, что есть еще МТ-16S2H конфигурации 16х2 с таким же интерфейсом, правда, у него размеры выходят за нужные мне габариты). Готовый модуль типа, описанного в этой статье, в данном случае применять неудобно, так как вторая неприятная особенность дисплея MT-10S1 — нестандартная разводка выводов. Выводы все те же самые (HD44780 все-таки, точнее его отечественный аналог КБ1013ВГ6), но расположены совершенно в нестандартном порядке. Проверил ради интереса и импортные 16х1, и МЭЛТ’овские двухстрочные/четырехстрочные — у всех у них стандартный порядок выводов, а MT-10S1 на этом фоне почему-то выделяется. Так что придется городить самопальное решение.

В результате я банально приставил к дисплею все тот же контроллер ATmega328, программируемый по той же схеме — через UNO, а затем вставляемый в панельку на отдельной плате, снабженной только необходимыми для запуска аксессуарами: кварцем с кондерами, емкостью по питанию и RC-цепочкой по выводу Reset (см. схему датчика в первоначальной статье, где контроллер подключен по аналогичной схеме).

Кстати, о цепочке по Reset
Кстати, о цепочке по Reset: у меня там на резистор в несколько кОм стоит конденсатор аж 1 мкФ, то есть время задержки при включении питания составляет несколько миллисекунд. Не много ли? Пособие нас учит, что, как и для всего семейства Mega, внешняя цепочка здесь вообще не требуется, корректный запуск по идее осуществляет внутренняя схема, причем гораздо быстрее. Но привычка ставить внешнюю RC-цепочку по выводу 1 контроллера для задержки запуска при включении осталась у меня со времен забытого уже семейства AVR Classic, где при недостаточно быстром нарастании напряжения питания контроллер мог запускаться некорректно. Да и в семействе Mega Brown-out Detector может работать не очень как следует. В критичных случаях все равно стоит ставить внешний монитор питания, ну, а здесь RC-цепочка ничему не помешает, зато может помочь в случаях с плохими источниками питания. Разработчики Arduino-плат это, кстати, прекрасно знают, потому на плате Uno, например, стоит такая же цепочка 10 кОм/100 нФ.

А два одинаковых AVR-контроллера сам бог велел стыковать по обычному Serial-интерфейсу, который все равно, кроме процесса программирования, нигде больше в этом проекте не применяется, и для использования которого уже все есть под рукой. Такое решение, кстати, по цене компонентов не отличается от решения на основе PCF8574 и вполне может конкурировать с ним по части энергосбережения в варианте с MT-10T7 — на тот случай, если под рукой не окажется упомянутого выше MT-10T11.

Итого схема модуля MT-10S1 с контроллером выглядит следующим образом (на схеме обозначение выводов ATmega328 дано в скобках после выводов платы Arduino):



В контроллере я применил режим энергосбережения (ну да, оно здесь не очень требуется, но зачем держать микросхему включенной всю дорогу без нужды?). Причем пробуждение происходит по сигналу с того же генератора меандра на микросхеме 555, что и тактирование основного контроллера, только на этот раз по падающему фронту (FALLING), чтобы немного разделить функции измерения и пересылки данных.

Загадка природы
С этим связана одна загадка природы, разрешить которую я так и не смог. Известно, что Mega вывести из состояния глубокого сна можно только внешним асинхронным прерыванием, так как тактовый генератор при этом выключен и синхронное прерывание просто не может произойти. А все семейство 28-выводных AVR-контроллеров, ведущее свою родословную от ATmega8 (48/88/168/328) имеет в качестве такового только прерывания INT0 и INT1 по низкому уровню (и еще прерывание PCINT, но в Arduino оно не задействовано). С этим связаны все официальные рекомендации и в материалах Atmel и на сайтах Arduino. В примере на сайте arduino.cc прямо написано: «In all but the IDLE sleep modes only LOW can be used». И это как бы не подвергается сомнению, например, то же самое более развернуто повторяет Монк в своей книжке: «Обратите внимание на то, что выбран тип прерывания LOW. Это единственный тип прерывания, который можно использовать в данном примере. Типы RISING, FALLING и CHANGE не будут работать».

Прерывание по низкому уровню очень неудобное в применении, так как один раз произойдя, при наличии этого самого низкого уровня на выводе оно будет происходить снова и снова, и надо принимать специальные меры, чтобы избавляться от лишних срабатываний. Так вот, ковыряясь на форумах в поисках различных решений этой задачи, я вдруг пару раз наткнулся на примеры кода, в которых для выхода из сна явным образом используется INT0 типа RISING или FALLING. Разумеется, я отнес это на счет безграмотности авторов. Но когда вот здесь споткнулся о фразу: «Хотя можно использовать и любой другой тип прерываний (RISING, FALLING, CHANGE) — все они выведут процессор из состояния сна», то решил, назло врагам, провести живой эксперимент — благо для этого все было под рукой.

И, к моему изумлению, все отлично заработало. Режим энергосбережения — SLEEP_MODE_PWR_DOWN; в силу ненадобности здесь я не принимал меры для дополнительного снижения потребления с отключением всяких других функций, но все равно тактовый генератор заведомо отключен. И тем не менее контроллер исправно просыпается по falling edge, запрашивает данные, отображает их на дисплее и засыпает снова. Для чистоты эксперимента я извлек МК из платы UNO и вставил в свою панельку с подключенным кварцем, и все равно всё продолжало работать. Это видно по потреблению: почти 17 мА в обычном режиме и 0,9-1 мА при включенном энергосбережении (из них 0,7 мА следует отнести на счет дисплея).

Не выходя из изумленного состояния, я перечитал даташиты от Atmel, заглянул в книжку Евстифеева (с их переводом), даже просмотрел старинное Atmel’овское пособие по семейству Classic, потом затратил полдня на поиски хоть какого-то объяснения происходящего (и по-русски и по-английски) в двух известных всем поисковиках, но нигде не нашел даже намека. Разве что в Atmel’овские Application Notes не полез, потому что сомнительно, чтобы там публиковалось что-то противоречащее даташитам.Буду счастлив, если кто-нибудь знающий объяснит мне, чего я здесь недопонимаю.
UPD: живая проверка на ассемблере (ATmega8) показала полное соответствие даташитам, то есть работает только прерывание по уровню. Единственное объяснение, которое приходит на ум — в Arduino каким-то образом подключили к обычному прерыванию еще и прерывание PCINT. Попытка прояснить ситуацию изучением текста системных библиотек Arduino ничего не дала — там черт ногу сломит.

Пересылка данных из контроллера датчика в контроллер дисплея через UART организована в форме диалога. Просыпаясь, каждое 4-е прерывание контроллер дисплея запрашивает данные по очереди:

. . . . .
	if (flag==1) { //флаг устанавливается каждое 4-е прерывание ~6 с
  Serial.print('T'); //посылаем запрос данных
  while(!Serial.available()); //ждем градусов T
  iit = Serial.readBytes(buft,5); // считать 5 байт максимум,
   // в ii реально прочитаное количество
  Serial.print('H'); //посылаем запрос данных
  while(!Serial.available()); //ждем влажности
  iihh=Serial.readBytes(bufhh,5); // считать 5 байт максимум,
   // в ii реально прочитаное количество
  Serial.print('S'); //посылаем запрос данных
  while(!Serial.available()); //ждем скорости
  iiss=Serial.readBytes(bufss,5); // считать 5 байт максимум,
   // в ii реально прочитаное количество
  Serial.print('D'); //посылаем запрос данных
  while(!Serial.available()); //ждем направления
  iid=Serial.readBytes(bufd,5); // считать 5 байт максимум,
   // в ii реально прочитаное количество
  flag=0; //сброс флага запроса
}
. . . . .

Здесь buft, bufhh, bufss и bufd — массивы (не строки!) из пяти байт, которые содержат данные о температуре, влажности, скорости и направлении в виде ASCII-разложения соответствующих чисел. Для того, чтобы не принять лишнего, в setup'e указан сокращенный таймаут на прием:

. . . . .
Serial.begin(9600);
Serial.setTimeout(10); // лимит времени 10 миллисекунд
. . . . .

Так удобнее выводить на дисплей: во-первых, вы сразу имеете и длину принятого числа, во-вторых функция Serial.print() со стороны контроллера датчика все равно посылает ASCII-строку, с установленными паузами как раз в те самые 10 мс между посылками:

. . . . .
  //посылка данных на дисплей по запросу:
    if (Serial.available()>0) //ждем запроса
    { char ch=Serial.read();
    if (ch=='T') {
      Serial.print(temperature,1);
      delay(10);}
    if (ch=='H') {
      Serial.print(humidity,0);
      delay(10);}
    if (ch=='S') {
      float wFrq=(3+0.8*f)/10; //из герц в м/с 
      if (wFrq>0.3) Serial.print(wFrq,1);
      else Serial.print(0.0,1); 
      delay(10);}
    if (ch=='D') {
//      Serial.println(wind_G);
      Serial.println(wind_Avr);
      delay(10);
      }//end ch
      }//end serial
. . . . .

Расчет скорости в м/с здесь идентичен тому, который производится в основном модуле станции (порог трогания установлен наугад равным 0,3 м/с) и его придется также менять по результатам калибровки.

Если попытаться принять данные обычным Serial.read(), а затем результат приема вывести на дисплей функцией вроде lcd.print(t,1), где t — температура в градусах, равная, например, 12.7, то MT-10S1 в ответ на такую команду выведет «49.5». Догадались, или подсказать? Это первые три знака в последовательности «49 50 46 55», то есть в ASCII-разложении числа «12.7». Потому проще сразу принять массив символов и непосредственно вывести на дисплей столько знаков, сколько было послано (count — счетчик, который увеличивается на единицу каждое прерывание):

. . . . 
if (count%8==0){ //каждые 8 прерываний выводим 
  lcd.clear();
  if (buft[0]!='-') lcd.print("+");
  for (byte i = 0; i < iit; i++) 
  lcd.print(buft[i]); //вывели температуру
  lcd.setCursor(6, 0);
  for (byte i = 0; i < iihh; i++) 
  lcd.print(bufhh[i]); //вывели влажность
  lcd.setCursor(0, 1);
  lcd.print("%"); 
 }
 if ((count+4)%8==0){ //еще через 4 прерывания
  lcd.clear();
  lcd.setCursor(0, 0);
  for (byte i = 0; i < iiss; i++) 
  lcd.print(bufss[i]);  //вывели скорость
  lcd.setCursor(5, 0);
  dir_dd(bufd); //выводим направление
    }
. . . . .

Последняя строка нуждается в расшифровке. Дело в том, что данные о направлении посылаются в коде 0-15 (в который они все равно переводятся из кода Грея при реализации векторного осреднения). В случае семисегментного дисплея MT-10T7 они переводились в компасные градусы:

. . . . .
    dd=atoi(bufd); //преобразуем в число
    dd=dd*22.5; //пересчитываем  в градусы
    itoa(dd,bufd,10); //преобразуем обратно в строку
. . . . .

А здесь мы можем писать прямо русскими буквами, так же, как и в основном модуле метеостанции (из-за чего этот дисплей, собственно, и был выбран):

. . . . .
void dir_dd(char dd[])
{switch(atoi(dd)) {
  case 0: 
  {lcd.print("С"); break;}
  case 1: 
  {lcd.print("CСЗ"); break;}
  case 2: 
  {lcd.print("CЗ"); break;}
  case 3: 
  {lcd.print("ЗCЗ"); break;}
  case 4: 
  {lcd.print("З"); break;}
  case 5: 
  {lcd.print("ЗЮЗ"); break;}
  case 6: 
  {lcd.print("ЮЗ"); break;}
  case 7: 
  {lcd.print("ЮЮЗ"); break;}
   case 8: 
  {lcd.print("Ю"); break;}
   case 9: 
  {lcd.print("ЮЮВ"); break;}
  case 10: 
  {lcd.print("ЮВ"); break;}
  case 11: 
  {lcd.print("ВЮВ"); break;}
  case 12: 
  {lcd.print("В"); break;}
  case 13: 
  {lcd.print("ВCВ"); break;}
  case 14: 
  {lcd.print("CВ"); break;}
  case 15: 
  {lcd.print("CСВ"); break;}
  }//end switch
  }//end dir
. . . . .

Внешний вид


На фото представлен внешний вид дисплея с подключенным контроллером в рабочем состоянии:



Вот так выглядит модуль доработанного датчика в сборе:



Параметры подсветки — те, что указаны на схеме выше. Так как падение напряжения на подсветке в модулях МЭЛТ составляет 4,5 В, то при питании 12 В ток подсветки составляет 50 мА (при максимальном для данного модуля 60 мА).

Корпус максимально уплотнен, чтобы избежать попадания влажного воздуха внутрь (черное обрамление экрана дисплея — из резиновой оболочки тонкого кабеля). Белая пластина справа — ограждение датчика влажности-температуры SHT-75, вынесенного за пределы корпуса (сам датчик находится за ней). Желтый провод выше — антенна передатчика 433 МГц. Слева — разъемы, куда подключены датчики скорости и направления.

А так выглядят показания на дисплее главного модуля метеостанции (черный модуль с белой антенной справа — приемник 433 МГц):

Теги:дисплеи МЭЛТMT-10S1MT-10T7Arduinoизмерение скорости ветра
Хабы: Программирование микроконтроллеров Схемотехника Электроника для начинающих
+23
9,7k 35
Комментарии 19
Лучшие публикации за сутки