В очередной раз, при обдумывании самоделки на Atmega, встал вопрос проектирования соединений. В моем случае 12 внешних коннекторов и 21 связь. Расположение, соединение, пересечения, программирование, ошибки, ошибки, ошибки.
При кажущейся простоте задачи поломать мозг придется день, а то и два. Без опыта — месяц.
И… лень взяла свое.
Как это делается
Обычный алгоритм в таком случае:
используем макетную плату (самодельную или Arduino) не меняя или добавляя разъемы и разводку
подгоняем под макетку внешние коннекторы, чтобы не было пересечений
пишем программный код для настройки контроллера и макросы-переменные.
А что если последовательность пинов в коннекторах уже жестко заданы?
Как например у драйвера L298N. Пересечения проводов или дорожек сильно усложнят проектирование, сборку и эксплуатацию.
Попробуем решить задачу с помощью генетического алгоритма. И для начала определимся с моделью.
Список разъемов
Шина i2c для компаса I2C(SDA, SCL)
UART для связи с внешним миром UART(RXD, TXD)
Ультразвуковой сенсор SONAR1(Echo, Trig)
Управление маршевыми двигателями DRIVE(ENA, IN1, IN2, IN3, IN4, ENB) с помощью L298N — длинный коннектор как раз для шлейфа
Напряжение батареи и потребляемый ток PWR(LVL, CUR).
Модель
Получается ген длиной 21 хромосом, которые отображают пины микроконтроллера, а точнее их соединения с коннекторами.
VCC и GND пины коннекторов игнорируем, так как шины мы можем вынести за коннекторы.
Каждая ячейка может иметь значение от 1 до 32 (количество лап у микросхемы). Значения в гене не могут повторяться.
Не допускается пересечений проводников (соединения делаются последовательно, если следующий пин занят — проскакиваем далее)
Количество вариантов соединений:
40 564 819 207 303 340 847 894 502 572 032.
Правда это количество рассчитано для сочетаний с повторениями, но такой способ позволяет быстро оценить сложность.
Ускоряемся
Для уменьшения пространства поиска используем функции пина коннектора (ADC, INT, PWM, PCINT). Например, если пин может быть только ADC, то вести к нему линию PWM или дискретного входа бессмысленно.
Данный фильтр уменьшает количество вариантов до 8 748 869 014 201 881 088. Разница ощутима. Но миллиарды миллиардов вариантов это тоже много.
Так же ранее использовались «ручные» эмпирические правила:
начинаем процесс соединения с уникальных пинов (SDA, SCL, RXD, TXD)
после соединяем разъемы с большим количеством пинов
последними соединяем пины с более общим спектром функций.
Но волшебный Collections.shuffle задающий последовательность коннекторов для обработки, решает эту задачу успешно на автомате.
Решение
Запускаем алгоритм и получаем решение для Atmega328p TQFP32. У меня на бюджетном ноутбуке находит менее чем за минуту.
Алгоритм находит решение за пару тысяч эпох. Иногда не находит и за миллион. В таком случаем просто надо перезапустить программу, потому что инициализация происходит случайно.
Без фильтра по функциям пинов алгоритм так же находит решение. Правда за 74 минуты и 13 перезапусков алгоритма по миллиону эпох на каждый. Перед каждым запуском делаем shuffle последовательности соединений.
Остается только соединить проводами макетку или нарисовать дорожки на плате с микроконтроллером.
Детали
Чтобы описать всё, надо будет написать не одну статью. Я постарался комментировать непонятные и самые интересные моменты в java-коде.
Желающие углубиться в тему могут заглянуть в git-проекта.