А объединяет их моё безудержное желание идти своим путем там, куда нормальный разработчик и под дулом пистолета не сунется – это если кратко. А тем, кого данная аннотация не испугала, готов представить под катом развернутое описание того, как был проведен уникальный генетический эксперимент по скрещиванию перечисленных в заголовке сущностей, и что из этого вышло.
Я – самоучка-многостаночник, т.е. и электронщик, и программист, и швец, и жнец (с дудой не вышло – слухом обделен). И, естественно, в моём анамнезе есть радиокружок и бегущие огни… Но к сути: автомат световых эффектов (бегущие огни) – это нечто, доводящее до истерики своим однообразием. Даже китайские гирлянды с десятком эффектов надоедают почти сразу. И, казалось бы, в то время, как космические автомобили бороздят просторы галактики, не сделать автомат световых эффектов, который был бы не так надоедлив – что может быть проще?! А вот попробуйте найти готовые решения – сумеете? Поскольку (см. первую строку статьи) изобретение велосипедов практически мой конёк, я даже искать не стал, а сразу приступил к.
Итак, для гибкости применим микроконтроллер (кто б сомневался). Для красочности – «умные» светодиоды WS2812B (или подобные). Но что делать с неповторимостью эффектов? И тут все просто – надо сделать не жестко запрограммированные эффекты, а генерируемые динамически по определенным алгоритмам с элементами случайности.
Вы подумали, что сейчас пойдет речь про микропитон (пардон, майкропайтон) и аппаратную Nucleo или другую ардуиноподобную штуку? Это не наш путь! Свой собственный интерпретируемый скриптовый язык – как вам?! А чтобы совсем было не скучно, то добавить поддержку microSD в качестве носителя этих самых скриптов. И микроконтроллер для пущего веселья взять «дохленький» (на «нормальном» и дурак сумеет). Только AVR, только хардкор!
Итак, основа – atmega328P, схема, практически, из этого контроллера и состоит:

Собственно, главное в моём велосипеде – это скриптовый алгоритмический язык. Скудные ресурсы выбранного контроллера не позволяют выполнять интерпретацию серьёзных языков типа того же микропитона, поэтому я придумал специализированный ассемблер. Собственно, от ассемблера у меня только трех- и двухбуквенные мнемоники команд и наличие в командах не более двух операндов, но функционально каждая мнемоника соответствует достаточно сложной функции, например, CLR очищает все внутренние переменные, а PNT реализует обновление до 254 светодиодов в гирлянде.
Концептуально мой ассемблер – 8-битный, т.е. все переменные занимают физически 1 байт, отсюда ограничение на число светодиодов в гирлянде – не более 254 (один пал жертвой необходимости контролировать переполнение вычислений). Это достаточно много, если идет речь о гирлянде на ёлку, хотя, конечно, по сравнению с пиксельными дисплеями почти ничто…
В распоряжении того, кто вздумает создать скрипт светового эффекта на моём ассемблере, есть 36 однобайтных переменных, любое количество которых может быть «назначено» массивом с доступом по индексу. Массив значений RGB-цвета каждого светодиода в гирлянде так же доступен, причем в особо экзотических случаях это пространство так же можно использовать для хранения промежуточных результатов вычислений (хотя это я погорячился – вряд ли такой самурай найдется среди живущих).
Как я уже говорил, синтаксис для простоты интерпретации, очень ограниченный: все переменные и функции именуются двумя символами – ни больше, ни меньше, — это сделано для совместимости с записью чисел только в шестнадцатеричном формате. Т.е. интерпретатор, взяв два символа, не совпадающие ни с одной из мнемоник команд, может быть точно уверен, что это будет число (результат функции или значение переменной тоже число). В итоге любая переменная именуется V*, где вместо звездочки – одна из цифр или букв английского алфавита, а, например, функция генерации псевдослучайного числа – RD.
Данный подход позволил, например, при описании RGB-цвета использовать такие варианты:
-
FF003E— прямо заданный RGB цвет;
-
RD0000— красный компонент случайный, а остальные обнулены;
-
V1V2V3— RGB‑составляющие находятся, соответственно, в переменных V1, V2 и V3.
Для косвенного обращения к массивам введено обозначение X* (звездочка имеет тот же смысл), что означает «взять значение из V* и использовать его в качестве номера ячейки в массиве для извлечения значения». Например, массив состоит из V0, V1 и V2, а переменная VJ используется в качестве индексной, тогда запись единицы в третий элемент массива будет реализован такими командами: VJ=02 XJ=01 (нумерация с нуля, поэтому для обращения к третьему элементу используем число 2).
Математические действия поддерживаются в объеме «школьной арифметики» — сложение-вычитание, умножение-деление и взятие остатка от деления – вот и весь ассортимент, да и то с «нюансиком»: только вычитание может привести к переполнению результата, т.е. 0-1=255, в то время как все прочие арифметические действия никогда не переполняются (250+250=255). И формат мнемоники немного экзотический, например, эквивалент традиционной для Си записи V1 += V2 в моём языке выглядит проще: V1+V2, т.е. первый операнд всегда приемник результата.
Так же есть ряд встроенных функций: генерация псевдослучайного числа, задержка, ввод аналогового сигнала и еще несколько других.
Есть возможность организовать циклы с глубиной вложенности до 5 уровней, а также условное выполнение команд с той же глубиной вложенности.
Команды могут записываться как по одной в строке, так и по несколько (ограничение на длину строки тоже 255 символов), между командами может быть любое количество символов-разделителей (пробелы, табуляции), но в тело самих команд не может содержать разделителей, т.е. запись V1=01 корректна, а V1 = 01 – недопустима.
Вот чего нет, так это подпрограмм…
Для обновления гирлянды светодиодов имеется две команды: PNT (обновляет светодиоды и тут же возвращает управление) и WT=SS (обновляет светодиоды и затем ожидает SS «тиков» по 10 мс, тем самым задавая темп эффекта). Во время ожидания каждые 10 мс происходит автоматическое изменение яркости светодиодов, для чего предусмотрен особый параметр «затухание».
Предусмотрены и комментарии – любые символы, не распознанные, как корректная мнемоника языка, трактуются комментарием, т.е. игнорируются. Однако, следует признать, что комментарии, равно как и «любое количество разделителей», приводит к увеличению времени интерпретации команд, что для достаточно маломощного микроконтроллера может быть критично, т.е. все эти излишества не приветствуются.
А теперь несколько примеров (с комментариями).
Одинокий бегущий огонек красного цвета по всей длине гирлянды:
CLR инициализация всех параметров PC=FF0000 цвет «рисования» красный GB=FF глобальная (т.е. сразу всех светодиодов) яркость максимальная PM=00 заливка первого светодиода цветом рисования RPT начало главного цикла RLC сдвиг цвета светодиодов по всей гирлянде влево WT=10 обновление гирлянды и задержка в 160 мс INF конец главного цикла – команда бесконечного повторения
В чем же фишка подобного описания эффекта? Да в гибкости! Если гирлянда состоит из 10 светодиодов, или из 50, или из 150 – скрипт будет один и тот же, и в каждом случае, добежав до конца гирлянды, красный огонек начнет сначала свой бег. Чисто аппаратными средствами такой эффект реализовать, по-моему, в принципе нереально.
А теперь разнообразим эффект непредсказуемостью цвета. В сущности, вся модификация скрипта сведется к первой его строке: PC=RDRDRD, и все! Однако, случайно генерируемые таким способом цвета чаще всего будут достаточно бледными. Чтобы повысить сочность, будем формировать цвет огонька по правилу: RGB-составляющие могут принимать случайным образом значение только FF или 00, при том, что в цвете хотя бы одна составляющая должна быть равна FF. Сделать это можно таким циклом:
RPT повторять VR=00 принимаем красный равным 0 IRD>80 если случайное число больше 128 VR=FF красный на полную EI конец «если» VG=00 принимаем зеленый равным 0 IRD>80 если случайное число больше 128 VG=FF зеленый на полную EI конец «если» VB=00 принимаем синий равным 0 IRD>80 если случайное число больше 128 VB=FF синий на полную EI конец «если» VC=VR VC+VG VC+VB вычисляем сумму составляющих цвета LVC=00 повторяем цикл, если сумма нулевая PC=VRVGVB теперь цвет рисования будет ярким и случайным (из 6 вариантов).
Далее команды скрипта те же, что и ранее.
Конечно, алгоритм получения случайного сочного цвета так себе, можно и получше придумать, но в формате этой статьи подобные экзерсисы неуместны.
Упомянутая ранее возможность автоматически менять яркость светодиодов во время «ожидания» позволяет буквально парой команд реализовать весьма занятные эффекты, например:
RPT GI=RD задать глобально цвет по случайному индексу GB=00 яркость на минимум GF=FE управление «затуханием» WT=FF задержка на 2,5 секунды INF
Сможете угадать, что делает вышеприведенный скрипт? Он реализует такой эффект: вся гирлянда плавно разгорается до максимума и затем так же плавно гаснет, причем каждый раз новым случайным цветом. Как видите, для плавного изменения яркости от минимума до максимума и наоборот, потребовалось всего одна команда WT (с нюансом – ранее был задан параметр «затухания»). Признаюсь, яркость меняется нелинейно…
В общем, я надеюсь, идея языка и его применимости понятна. Есть в нем и несколько занятных фишек, например, предусмотрено 2 аналоговых входа, сигналы на которых можно использовать, как переменные в скриптах, а так же «матричный режим» для создания несложных 2D-эффектов (если гирлянду развесить определенным способом)… Но об этом, если кому интересно, лучше прочесть в хелпе к IDE, без которой создавать скрипты на этом языке оказалось затруднительно (хотя изначально задумка была – обойтись блокнотом Windows), ведь ни одна «нормальная» IDE, ни один отладчик не поддерживает это чудо… Вот и пришлось в придачу сделать собственную IDE, в которой можно и скрипт написать, и отладить его в «почти реальном времени» без реальной светодиодной гирлянды. Ссылка на IDE также в конце статьи.

Ну и несколько деталей о внутреннем устройстве процесса интерпретации скриптов:
-
Сразу после установки SD-карты считывается конфигурационный файл, в котором указан, сколько всего светодиодов в гирлянде, как именно эта гирлянда используется (т.е. в виде полоски 1D или матрицы 2D), сколько всего скриптов на карте и ряд других параметров.
-
Затем выбирается один из имеющихся файлов для загрузки. Имя каждого файла – его номер (расширение фиксированное), т.е. 0.sc, 1.sc и т.д. Выбор очередного скрипта может быть либо случайным из имеющихся на карте, либо последовательным по их номерам.
-
Поскольку для работы с SD-картой используется SPI-интерфейс, а внутреннее устройство Petit FatFS таково, что даже для чтения одного байта считывается целый сектор, чтение файлов – очень ресурсоёмкая операция, поэтому реализовано внутреннее кэширование считываемых данных. В частности, из-за этого длинные циклы могут быть очень медленными из-за необходимости постоянно подгружать команды с карты. Это, кстати, одна из причин отсутствия подрограмм.
-
Считанные в кэш данные анализируются символ за символом, найденные команды тут же исполняются.
-
Постоянно контролируется время работы каждого скрипта и, если в конфигурации задан лимит времени работы одного скрипта, скрипт принудительно останавливается, после чего происходит выбор следующего. Если лимит времени не задан, скрипт с бесконечным циклом будет исполняться непрерывно вплоть до сброса устройства.
В общем, такой вот светодиодный велоассемблер… Я не очень надеюсь снискать за это почет и уважение, но даже если смог кого только повеселить – и то хорошо! Хотя лично меня и моих близких данное поделие радует уже третий новый год. И если вы подумали, что никаких красивых эффектов на таком примитиве сделать невозможно, я категорически не соглашусь! Вот, например, небольшая симуляция-анимация эффекта «вращающейся радуги»:
А скрипт этого эффекта вот такой:
CLR GB=FF IRD<80 REV EI VN=05 VD=E0 RPT VD+01 IVD>F0 VD=E0 EI V0=VC VP=TP RPT PI=V0 IV0<VD VB=VD VB-V0 V0=FF V0-VB V0+01 VS=01 EI IVS=00 V0-VD EI VS=00 PM=VP LVP V0=VC IV0<VD VB=VD VB-V0 V0=FF V0-VB V0+01 VS=01 EI IVS=00 V0-VD EI VS=00 VC=V0 WT=03 INF
Обещанные исходники и IDE:
ссылка на оригинал статьи https://habr.com/ru/post/715828/
Добавить комментарий