В общем, лекарство от обозначенной в предыдущей заметке болезни выглядит так (см. рис. 1).
В разрыв SPI-цепей CS и MISO впаивается вот такая «пилюля» (асинхронный буфер с 3-мя состояниями выхода). И переводит линию MISO в Z-состояние когда CS = 1.
Я тут потихоньку перевожу прошивку своего баяна на FreeRTOS. Начинал с самого простого и очевидного и продвигался по мере роста сложности задачи.
Вчера оформил в виде отдельной задачи проигрывание MIDI-файлов автоаккомпанемента. Понятно, что в результате эта проигрывалка оказалась написана не так, как если бы её с нуля писать под FreeRTOS — в этом случае наверняка что-то было бы по-другому. Но я решил революций не устраивать, и вместо полного переписывания сделал адаптацию.
В соответствующем классе, в отличие от всех остальных, которые уже были переведены на FreeRTOS, хранится довольно много данных (всякие настройки, стейт-машины, всякие вспомогательные данные и т.п.). Наружу из класса торчат публичные функции-члены, с помощью которых внешняя задача управляет процессом (запускает, останавливает, меняет т емп, меняет мелодию и т.п.). С точки зрения класса этого проигрывателя, управляющие воздействия — это изменение внутренних данных. А есть ещё сама задача проигрывания MIDI-файла. Она эти самые внутренние данные «использует». Раньше, когда весь код баяна выполнятся в контексте одной задачи (главный цикл loop()), управляющие воздействия не конфликтовали с проигрыванием, т.к. выполнялись последовательно — т.е. сначала, при необходимости, что-то менялось во внутренних данных (например менялся темп воспроизведения), а затем проходила очередная итерация воспроизведения, которая использовала уже измененные данные.
А тут управляющие воздействия будут проводиться в контексте другой задачи, т.е. может оказаться так, что внутренние потроха могут измениться в момент их использования функцией проигрывания. Т.е. появляется новый ресурс — «потроха MIDI-проигрывателя», доступ к которому в каждый момент времени должна иметь только одна задача. Т.е. потроха проигрывателя нужно просто защитить мьютексом. Так оно и было сделано.
И правая половина работает, и левая (справа и слева на рис. 1 соответственно).
Нюанс заключается в том, что каждая половина может принимать и отправлять сообщения только тогда, когда от центрального модуля отключена другая. Не живут они обе на шине SPI одновременно.
Похоже, что чуда не произошло, и известная проблема с SPI-slave устройставми на RP2040 проявилась в полный рост. (RP2040, работая как SPI-slave, не переводит линию MISO в Z-состояние, когда SPI интерфейс неактивен.) Ничего не поделаешь, надо с этой проблемой бороться.
Придётся делать дополнительные мини-платы «SPI адаптеров» для правой и левой плат. И лепить на них асинхронные буферы с тремя состояниями на выходе. Знать бы про это заранее — я бы на самих платах такие буферы предусмотрел. Там дел-то на 3 копейки, и подключение элементарное.
Ничего не поделаешь, таковы издержки разработки, когда проблема может проявиться на поздней стадии, когда платы уже готовы.
Находясь под впечатлением от написания прошивки для платы активной клавиатуры под FreeRTOS, я решил, что и прошивку центральной платы тоже надо переводить на FreeRTOS, чтобы всё было «по красоте».
И хороший момент заключается в том, что переход можно делать постепенно, модуль за модулем. Потому что FreeRTOS-ное хозяйство отлично уживается с тем, что написано в традиционном «ардуинном» фреймворке.
Вот сегодня, например, я перевел на FreeRTOS-ные рельсы модуль драйвера SPI клавиатуры (т.е. клавиатуры, которая умеет присылать нажатия с измеренной velocity). А основная логика, включая автоаккомпанемент, продолжает нормально работать по-старому.
Так что баянчик будет переезжать на новую платформу в комфортных условиях.
По результатам запайки этой версии баяна я, пожалуй, поменяю своё отношение к SMD компонентам. Пожалуй, с проводными детальками больше геморроя при пайке. SMD, пожалуй, проще паять. Правда резюки и кондеры я беру большие: 1206. Мельче — категорически отказать.
Рис. 1
P.S. Заодно, кстати, была доведена до условно-готового состояния прошивка клавиатур. Функционал реализован полностью, осталась мелкая подстройка всякого (при необходимости, по ходу дела).
Полный цикл опроса всех датчиков одной клавиатуры занимает 100 мкс. С учётом того, что при самом быстром нажатии на клавишу соответствующий рычаг пролетает из одного крайнего положения в другое примерно за 2 мс, получается, что за это время система успеет опросить состояние соответствующего датчика примерно 20 раз. Этого вполне достаточно для надёжной и точной регистрации времени нажатия. Собственно, эти тайминги практически не отличаются от тех. что были получены в прототипе, где я отрабатывал способ измерения времени.
Руками, конечно трудно точно и быстро магнит подносить, но тут уж как получилось. Главное тут слышно: звук меняется при изменении скорости поднесения магнита к датчику.
В реальной жизни магнит будет двигаться в несколько раз быстрее, чем можно поднести рукой. Так что результат тестирования — строго положительный.
Сегодня я добился нормального обмена данными между платой клавиатуры и центральным модулем. Всё работает на реальных платах, не на макетке.
Класс драйвера SPI-клавиатуры я писал «вслепую», т.е. без поэтапной отладки на реальном железе. Поэтому неудивительно, что взлетело не сразу, т.к. были ошибки в коде (забыл проинициализовать соответствующие пины CS, а также в одном месте неправильно определял размер сообщения).
Но сейчас — работает. Как Мастер может по своей инициативе отправить команду в модуль клавиатуры, так и клавиатура может запросить у Мастера SPI-обмен и отправить мастеру своё сообщение.
Рис. 1
Но вся эта красота по неизвестной мне (пока) причине не даёт инициализироваться SD карте (на которой записаны MIDI-файлы для автоаккомпанемента).
Буду выяснять, что там не так с точки зрения SD-карты.
P.S. Заменил SD-карту на другую — оказалось что всё работает. Видимо та, первая — при смерти; неустойчиво работает (в нескольких запусках при одинаковых условиях — разный результат).
Светодиоды показывают ошибку «от клавиатуры пришло какое-то неопозненное сообщение». Ну, то есть с висящего в воздухе входа SPI прочитался мусор. Это нормально.
Но поскольку на этапе сборки/настройки будет удобно включать схему без одной или без двух клавиатур, то, пожалуй, мусор на входе придётся игнорировать, как будто вообще ничего не приходило.
Сам драйвер-то уже умеет определять, что клавиатура не подключена, и отправлять соответствующее информационное сообщение в системную очередь.
Ну, надо приступать к пайке плат правой клавиатуры.
На голом C++, без FreeRTOS оно заработало ещё в конце декабря. То был, как говорится, proof of concept. А 6-го января заработал SPI-slave, написанный под FreeRTOS. Пришлось, правда, слегка подпилить ардуинную библиотеку SPISlave, чтобы оно нормально работало в FreeRTOS. И мою доработку даже включили в т.н. «ядро» rp2040.
И с тех пор я продвинулся с позиции «нет ничего, кроме заготовки отправляльщика-принимальщика сообщений по SPI» до состояния «имею практически полнофункциональную прошивку которая умеет работать в штатном режиме, а также имеет поддержку режима настройки». (Да, активную клаву надо будет настраивать для того чтобы выставить как можно бОльшее расстояние между положениями, которые прошивка принимает за «полностью отпущено» и «до упора нажато».)
А на стороне центрального модуля я отрезал поддержку клавиатуры построенной на регистрах сдвига, и добавил связь с модулем клавиатуры.
Теперь надо запаять плату центрального модуля и проверять на реальном железе работу обновленного центрального модуля, а затем и всего остального.
В общем, выходные прошли не зря. Я продвинулся очень неплохо.