Продолжаю рассказ! После относительно успешных экспериментов с использованием Arduino для управления ЦАП, я решил немного расширить функционал и наглядность. Так сказать, расширить эстетическую часть проекта.
Ещё в процессе работы над предыдущими версиями меня напрягала малая «ёмкость» дисплея «1602». Конечно можно заказать «2004», но он размером-то отличается, а функционалом нет: как был символьный, так и остался.
Но ведь прогресс не стоит на месте, поэтому появилась идея попробовать что-то новое.
И я занялся поиском.
Содержание статьи / Table Of Contents
А сейчас? Открыл интернет-магазин. И завис на несколько часов. Во-первых почти все есть. Во-вторых из того, что есть, надо выбрать то, что надо. А надо то самую малость - дисплей с возможностью работы с Arduino.
И размером, чтоб не как монитор компа, а для установки в мой форм-фактор корпуса.
И чтоб работал по SPI или I2C — у нас свободных ног мало, а городить динамическую индикацию мы еще не умеем, а учиться не будем, потому как не надо оно нам в нашем проекте.
Из возможных кандидатов были выбраны два варианта.
Цветной дисплей 1,8" TFT LCD разрешением 128*160 точек на базе контроллера ST7735 и и цветной дисплей 2,4" TFT LCD с разрешением 240*320 точек на контроллере ILI9341.
Оба имеют интерфейс SPI и полный набор библиотек под Arduino.
OLED я не рассматривал в принципе — там ценовой порог невменяем, а то что доступно, как правило не цветное.
При первом включении оказалось, что 1,8 экран хоть и хорош, но маловат размером.
Дело в том, что вывод на дисплей через систему команд Arduino сильно ограничен возможностями самого контроллера. Это нам не i7 или топовый AMD.
И даже не последний ARM. И тактовая частота не та, и объем памяти невелик. Поэтому что зашито в библиотеки, с тем мы и работаем.
Хотя зашито там, в принципе, довольно много для такой железки. Например, можно рисовать геометрические фигуры, задавать им цвет, размер, заливку определенным цветом. Можно писать текст.
Но есть и ограничения. Невозможно применить свой шрифт и главное изменить его размер под свои нужды. Имеем размер шрифта 1-2-3, без промежуточных значений. На фото выше — шрифт размера 1. Самый маленький.
Поэтому оставим маленький дисплей для игр на досуге и обратимся к его большему собрату.
Можно, например, сделать фоторамку:
И вывести любимую картинку в разрешении 320*240 пикселей.
Но мы же не фоторамки делаем. Нам дисплей нужен для других задач.
А здесь размер экрана побольше.
Уже можно размещать меню, и вообще развернуться пошире.
Не обращайте внимания на экран слева — это просто очередная демонстрация возможностей.
Я попутно назаказывал кучу разных датчиков и сделал метеостанцию для дома. В примерах для этого экрана есть более впечатляющие эффекты. Ссылку на набор библиотек от Adafruit см. внизу статьи. Оттуда же берем набор команд и легко меняем их под себя.
Фотограф из меня такой-же как программист — в реальности оно красивее смотрится.
Ну и думал я над тем, какой контроллер применить. В предыдущих статьях я рассказывал, что выдергивал прошитый контроллер из отладочной платы и создавал для него отдельное окружение на новой плате. Здесь я решил пойти немного другим путем. В линейке Arduino есть готовые встраиваемые решения. Одно из них — Arduino Nano. На борту та же Мега328, но в другом корпусе, а также программатор, стабилизаторы питания 3,3В и 5В, кварц и все необходимое для автономной работы.
Выглядит это так:
И снизу:
Размером все это с широкий сокет. Снизу обычная зажигалка для оценки размера. Жаль, что раньше я их не увидел. Ну ладно, теперь надо придумать, куда это все привязать. К какому ЦАП.
Мой взгляд упал на этот ЦАП. Он так и напрашивался под новый проект.
Во-первых, он уже работал с АК4399. Во-вторых, он имел в своем комплекте почти полный набор деталей. Дело за малым — разобрать его и немного пересобрать.
Что и было сделано:
Фактически, я использовал наработки предыдущего проекта и у меня уже была прошивка под АК4399. Теперь будем пилить ее под TFT.
И я начал ваять. Попутно, проезжая мимо меня с официальным визитом заглянул на огонек мой коллега-программист, и предложил мне сделать электронную регулировку громкости и помочь в написании логики под нее. У него как раз была лишняя PGA2311.
Я не стал отказываться. Регулировать громкость можно и средствами АК4399, там есть аттенюатор, но с точки зрения аудиофилов это не правильно.
Хотя в бытовой аппаратуре подобное решение сплошь и рядом.
Но мне кажется, что правильнее будет PGA2311 прикрутить как регулятор громкости «ушного» усилителя, а выход оставить без изменений.
Работать оно начало сразу. И прошивка и переключение режимов.
На ходу появилась идея задействовать все возможности АК4399, а также разместить индикацию режимов работы, которую я раньше выводил на светодиоды — на дисплей.
Посчитав ноги Меги и набор требуемых для вывода режимов — призадумался. Немного не хватает. Надо подумать, от чего отказаться.
И главное, как все это оформить графически.
Например, если у человека нет слуха, то говорят: «Медведь на ухо наступил».
А мне кто на что наступил, если я не могу придумать дизайн графического интерфейса?
Решил, что это будет в виде блок-схемы, а на ней и рядом — в текстовом виде вся индикация.
В процессе программирования выяснилось, что в отличие от символьного дисплея, где один символ переписывает другой, в TFT невозможно символ или знак просто переписать — он пишет его просто поверх старого. Поэтому нужно или закрасить предыдущий символ в цвет фона, или написать его цветом фона. Что несколько усложняет задачу.
Блок управления планировался примерно таким.
По обоим сторонам индикатора разместить кнопки выбора режимов.
Слева — селектор входов, справа - регулировка громкости и режим работы ЦФ.
Снизу — линейка уровня громкости.
Набросав все это, и с помощью коллеги-программиста дописав обработку регулировки громкости, я понёс его с работы домой на выходные — послушать и покрутить режимы.
Испытания прошли успешно, но в процессе я поймал себя на мысли, что для переключения входов я пытаюсь непроизвольно нажимать одну и ту же кнопку выбора, вместо отдельной для нужного входа.
Раз оно само напрашивается, я решил отказаться от трех кнопок селектора входов и заменить ее одной, с выбором по кругу, как я это делал в предыдущей конструкции с энкодером.
В результате получилось вот так.
И освободились ноги под индикацию еще чего нибудь.
Теперь можно отрисовать результирующую схему и сделать нормальную плату.
Пришлось применить две штуки 74АС245 как конвертеры уровня, так как на одной не хватило каналов.
И «размазать» равномерно сигналы по ним. Но получилось довольно компактно.
Кнопки регулировки громкости выведены ближе к разъему наушников на отдельную плату.
Вот так это выглядит для РСМ режима:
А так для DSD:
Теперь, чтоб оно не болталось по столу — прикручу все блоки к остову старого корпуса.
Там пришлось спилить лицевую и заднюю панель, так как они будут изготовлены заново.
Ну а пока он лежит на столе и тихонько поет, я расскажу о нюансах и тонкостях, которые меня подстерегали в процессе.
Рассказывать всю логику работы прошивки я не буду — она почти не изменилась, а вот работа с дисплеем и прочее думаю будет интересна.
Я постарался откомментировать сам скетч, но некоторые моменты поясню отдельно.
Итак:
Для работы с дисплеем я использовал библиотеку от Adafruit. В начале описываем сам дисплей, а также можно описать все цвета, что он поддерживает.
Есть еще опция tft.color565 (R,G,B), где в качестве составляющих цвета можно руками указать уровень каждого от 0 до 255, но это на любителя. Все это вытаскивается из файла примеров, что идет в комплекте с бибилиотеками.
Мне хватило стандартных.
Дальше начинается самое интересное, так сказать — креативная часть.
После включения, дисплей залит белым полем.
Его надо залить черным — это будет наш фон.
Теперь изображение на экране нужно повернуть.
tft.setRotation(); имеет 4 значения от 0 до 3. Это поворот с кратностью 90 градусов.
Удобно.
Нет необходимости вертеть плату под экран.
У меня поворот на 90 градусов.
Теперь можно нарисовать геометрическую фигуру.
А можно, нарисовав ее, сразу залить нужным цветом.
Есть команды для рисования, есть команды для заливки.
Например: tft.fillTriangle(x1, y1, x2, y2, x3, y3, COLOR);
Интуитивно понятно, что x и y — это координаты вершин треугольника от верхнего левого угла дисплея. COLOR- цвет заливки.
Если надо просто нарисовать, но не заливать, то используем tft.drawTriangle(x1, y1, x2, y2, x3, y3, COLOR);
Для окружности чуть попроще - tft.drawCircle(x, y, r, COLOR); или tft.fillCircle(x, y, r, COLOR);
Здесь x, y координаты центра, r — радиус. Естественно все в пикселях, а не в миллиметрах.
Ну а теперь по аналогии возьмем прямоугольник.
tft.drawRect(x, y, dx, dy, COLOR); или tft.fillRect(x, y, dx, dy, COLOR);
"Что за фигня?" - скажете вы, - "У прямоугольника четыре координаты!"
Но не забывайте — это программирование, тут все немного не так. Здесь dx и dy — это сколько отступить по x и y до диагонального угла.
То есть с трапецией мы пролетели — ее одной строкой не нарисовать!
Зато с линией все тупо — tft.drawLine(x1 , y1, x2, y2, COLOR); - рули от начала линии в конец как угодно.
С командой tft.drawRoundRect(x, y, dx, dy, r, COLOR); и tft.fillRoundRect(x, y, dx, dy, r, COLOR); тоже не сложно — тут r — радиус закругления углов.
В принципе все это есть в примерах к библиотеке, и легко можно подпилить все это под себя.
С размером текста тоже интуитивно понятно — ставим куда надо курсор, ставим размер шрифта, его цвет и что надо написать.
Только вот загвоздка в том, что если надо будет написать что-то другое в эти же координаты, то надо стереть предыдущую надпись, иначе они наслоятся друг на друга.
Например, чтобы заменить надпись I2S на COAXIAL, надо сначала написать I2S цветом фона или плюхнуть на ее место черный квадрат. Квадрат проще — там одна строка, а с записью чуть побольше. Но например если это цифровое значение, то лучше квадрат не юзать — будет излишнее мерцание, что не есть гуд.
И да, по-русски он не разговаривает, по крайней мере я не смог его заставить. Поэтому все пишем латинскими буквами.
Ну, думаю, по экрану более-менее понятно, теперь более детально остановлюсь ещё на парочке моментов.
Логика работы программы не сильно изменилась, но меня интересовало управление PGA2311 и запись значений в EEPROM.
Мой коллега-программист уже пробовал и то и другое, а также наступил на ряд граблей в обоих случаях.
Грабля номер один. Согласно даташиту на PGA2311 регулировка усиления ее происходит посылкой на нее двоичного кода, который задается формулой:
For N = 1 to 255:
Gain (dB) = 31.5 − [0.5 w (255 − N)]
При N = 0 наступает полный MUTE.
Грабли заключаются в том, что при значениях от 0 до примерно 185 идет работа с резистивной матрицей, аналогичной обычному потенциометру на входе, а после — усиление входного сигнала путем изменения сопротивления цепью ОС встроенного ОУ. Так как микросхема питается от +/-5В по аналоговой части, и при входном сигнале больше 1 В она начинает уходить в ограничение.
Получается дикий перегруз и отключение.
Варианта два — ограничить входное напряжение до минимально возможного значения, либо ограничить диапазон резулировки до 185.
Мы пошли вторым путем:
Читаем состояние кнопок Vol+ или Vol-.
Прибавляем или отнимаем значение volumeValue.
Почему 43? А потому что вместе с увеличением громкости у нас отрисовывается линейка в нижней части экрана. А там как раз 43 деления. Ну так получилось.
Эти два блока программы хоть и находятся в разных местах кода, но связаны между собой значением volumeValue.
От его увеличения или уменьшения зависит то, сколько зеленых прямоугольников в линейке будут включены или выключены.
Я бы так написать не смог — это высший пилотаж... А для чего формула то? Формула тоже плод фантазии коллеги-программиста по моему техзаданию.
Как я уже говорил выше, для регулировки громкости мы используем диапазон цифровых значений от 0 до 185. Но, шагов регулирования у нас всего 43 (по числу сегментов) и к тому же на участке от 0 до 32 или больше, все равно ничего не слышно. Получается часть шкалы просто не работает, а все регулировки производятся возле максимального значения.
Что естественно не удобно. Поэтому шкала начинается не от 0 а от значения offset. Которое можно менять и подставлять свое значение в формулу.
Ещё одна сложная для меня задача состояла в том, чтоб запомнить значения парамеров громкости, включенного входа или парамера ЦФ. А при включении питания эти параметры восстановить. Сложность была в том, чтоб запись происходила только тогда, когда любое из значений поменяется, и при этом не сразу, а по истечении определенного времени — например 3-5 сек. Сдвинул громкость, если всё устраивает, то через 5 сек значение запишется в память. Сменил вход, он через 5 сек его запомнил. Выключил питание, на следующий день включил — у меня всё как вчера.
Сам процесс записи в EEPROM хорошо описан в документации, и его использование не сложно. Как выяснилось. Но таймер... Я сидел над ним весь день. Искал похожие примеры в сети. Пробовал разные варианты.
Да, я знал, что в Arduino есть функция millis(). Примерно знал как она работает. Но как ее применить — у меня не хватало мозгу.
Всё оказалось просто. Так просто, что увидев написанный на моих глазах за 30 (!) секунд код, я даже выругаться не смог — слов не нашлось подходящих.
Как известно из документации, значение millis() меняется по нарастающей. Это время в миллисекундах от момента включения контроллера. Оно никак не привязано к дате, времени суток или года. Просто время работы.
Когда это время достигнет значения 4294967296 мсек, оно обнуляется в 0. Это примерно 49 дней и 17 часов. Да не суть, это нам и не надо, просто факт такой научный.
Так вот, по факту отпускания кнопки, мы приравниваем переменной timeout текущее значение millis(). Нам не важно сколько там. А потом просто вычисляем разность между текущим millis() и timeout.
Как только разность стала больше 5000 мс (5 сек), запускаем процедуру записи в EEPROM.
И обнуляем timeout. Вот так.
Примерно три строки кода!
Часто писать в EEPROM не желательно, все же гарантированных 100000 циклов иногда может и не хватить. Будем аккуратны.
Хитрые команды cli(); и sei(); - это грабля номер два. Если в процессе работы программы используются прерывания (да и на всякий случай если их нет), то на время записи в EEPROM необходимо их запретить, а потом разрешить. В противном случае, если в момент записи будет вызвано прерывание, то происходит циклический процесс записи в EEPROM с перегревом всего контроллера. Закончить этот процесс можно только отключив питание. Я не проверял, но товарищ чуть не сжег Мегу.
Запись — EEPROM.write (a, value);
а — адрес ячейки, value — значение, которое надо записать.
Флажок EEP нужен для того, чтоб после записи на 1 сек зажечь маленькую надпись «write» в левом верхнем углу дисплея, показывая, что все записано.
Чтение из EEPROM происходит при загрузке программы — по команде EEPROM.read(a);
Где a — адрес ячейки памяти.
Все остальные моменты откомментированы в самой программе — их здесь описывать сильно долго. Но думаю что их не сильно сложно понять.
Дальше нужно пихать всё в корпус. Тут проще. Изготовить нужно только лицевую и заднюю панель, да заказать боковины.
Все остальные части остались от старого аппарата. Предварительно собранный девайс вызвал уныние — как то бедненько на панели. Раньше хоть светодиоды горели и что-то показывали.
А тут все на дисплее и панель пустая… Зато получившийся функционал с лихвой перекрывает все изъяны дизайна.
Но привыкаешь быстро, и уже старые аппараты, сделанные мной в «аналоговом» исполнении уже начинают казаться убогими и корявыми.
А в составе рабочего комплекта все смотрится как-то веселее.
И когда немного понимаешь, как это работает, то можешь уже немного больше, чем раньше.
Например модернизировать любимый усилитель. Выкинуть регулятор громкости и заменить его на PGA2320 (у чипа питание ±15В), да и защиты уже могут быть цифровыми.
Но чего я не могу изменить у продуктов Atmel, а точнее компактной и паябельной Atmega328— это быстродействие. Некоторые задачи реально требуют других ресурсов для выполнения задач в реальном времени. Например измерение частоты семплов без использования прескалеров для РСМ и DSD. Или отлов постоянки на входе усилителя (а тут как правило Atmega328P немного проигрывает аналоговой защите). Да и отрисовка некоторых моментов на дисплее оставляет желать лучшего.
Поэтому можно попробовать пощупать встраиваемое устройство такого плана:
И что из этого у меня получилось — я расскажу в следующей серии.
А пока всё.
↑ Файлы
Рабочий код с комментариями, а также разводка использованных плат тут:🎁tft.7z 206.31 Kb ⇣ 153
Заливайте, переписывайте под себя — оно работает.
Даташит на дисплей:
🎁ili9341.pdf 3.37 Mb ⇣ 102
↑ Ссылки
Библиотека Adafruit_ILI9341 на ГитхабеС уважением, Алексей.
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.