Я рассматриваю проекты для минималистской игровой системы на основе PIC18F85J5. Частью моего дизайна является то, что игры можно загружать с SD-карты без перепрограммирования чипа или перепрограммирования памяти программы. Я выбрал этот чип, потому что он имеет интерфейс внешней памяти, который позволит мне запускать код из внешней SRAM.
Основная идея заключается в том, что внутренняя память программы будет содержать интерфейс для просмотра SD-карты, и, как только пользователь выберет программу, он скопирует шестнадцатеричный файл с SD-карты на внешний ОЗУ, а затем выполнит переход во внешнее ОЗУ. ,
Внутренняя программная память также будет иметь различные библиотеки для графики, ввода контроллера и других различных утилит.
Я вполне уверен, что знаю, как сделать так, чтобы внутренние части прошивки работали нормально. Проблема заключается в создании программ для запуска из внешней оперативной памяти. Это не то же самое, что нацеливание на обычную картинку, и ему нужно знать о функциях библиотеки, которые доступны во внутренней памяти, но не перекомпилировать их, а только ссылаться на них. Также необходимо начать использовать адреса сразу после 32 Кб внутренней флешки, а не с нуля. Есть ли хороший способ для компиляции программы с использованием этих типов ограничений?
Я использую MPLab IDE, но я не очень знаком с ним или с тем, как выполнить такую настройку.
Ответы:
У вас есть две отдельные проблемы:
Это на самом деле очень просто. Все, что вам нужно сделать, это создать файл компоновщика, который содержит только диапазоны адресов, которые вы хотите, чтобы код занимал. Обратите внимание, что вам нужно не только зарезервировать определенный диапазон адресов памяти программ для этих внешних приложений, но также и некоторое пространство ОЗУ. Это пространство ОЗУ, как и адреса памяти программ, должно быть фиксированным и известным. Просто сделайте только те фиксированные и известные диапазоны адресов доступными для использования в файле компоновщика. Не забудьте сделать их НЕ доступными в файле компоновщика базового кода.
После того, как базовый код загрузит новое приложение во внешнюю память, оно должно знать, как его выполнить. Самое простое, вероятно, - запустить выполнение с первого места во внешней памяти. Это означает, что вашему коду потребуется один раздел CODE по этому абсолютному начальному адресу. Это содержит GOTO к правой метке в остальной части кода, который будет перемещаться.
Не существует простого способа сделать это с помощью существующих инструментов Microchip, но это не так уж и плохо.
Гораздо большая проблема заключается в том, как вы хотите справляться с изменениями базового кода. Упрощенная стратегия состоит в том, чтобы создать базовый код, запустить программу над полученным файлом карты для сбора глобальных адресов, а затем написать файл импорта с инструкциями EQU для всех глобально определенных символов. Этот файл импорта будет включен во весь код приложения. Связывать здесь нечего, поскольку исходный код приложения по существу содержит фиксированные адресные ссылки на точки входа базового кода.
Это легко сделать и будет работать, но подумайте, что произойдет, когда вы измените базовый код. Даже незначительное исправление может привести к перемещению всех адресов, и тогда весь существующий код приложения окажется бесполезным и его придется перестраивать. Если вы никогда не планируете предоставлять обновления базового кода без обновления всех приложений, то, возможно, вам это сойдет с рук, но я думаю, что это плохая идея.
Лучший способ - иметь определенную область интерфейса по выбранному фиксированному известному адресу в базовом коде. Для каждой подпрограммы, которую может вызвать код приложения, будет один GOTO. Эти GOTO будут размещаться по фиксированным известным адресам, а внешние приложения будут вызывать только те местоположения, которые затем будут переходить туда, где подпрограмма фактически окажется в этой сборке базового кода. Это стоит 2 слова памяти программы на экспортируемую подпрограмму и два дополнительных цикла во время выполнения, но я думаю, что оно того стоит.
Чтобы сделать это правильно, вам нужно автоматизировать процесс генерации GOTO и результирующего файла экспорта, который будут импортированы внешними приложениями для получения адресов подпрограммы (фактически перенаправителя GOTO). Возможно, вам удастся сделать что-то разумное при использовании макросов MPASM, но если бы я делал это, я бы определенно использовал свой препроцессор, поскольку он может записывать во внешний файл во время предварительной обработки. Вы можете написать макрос препроцессора, чтобы каждый редиректор мог быть определен одной строкой исходного кода. Макрос делает все неприятные вещи под капотом, который генерирует GOTO, внешнюю ссылку на фактическую целевую подпрограмму, и добавляет соответствующую строку в файл экспорта с известным постоянным адресом этой подпрограммы, и все с соответствующими именами. Возможно, макрос просто создает кучу переменных препроцессора с обычными именами (вроде расширяемого массива во время выполнения), а затем файл экспорта записывается один раз после всех вызовов макроса. Одна из многих вещей, которые мой препроцессор может сделать, чего не могут макросы MPASM, - это манипулирование строками для создания новых имен символов из других имен.
Мой препроцессор и куча других связанных вещей доступны бесплатно по адресу www.embedinc.com/pic/dload.htm .
источник
Вариант 1: устный перевод
Это не дает прямого ответа на вопрос (это отличный вопрос, кстати, и я надеюсь извлечь урок из ответа, который непосредственно к нему относится), но это очень часто встречается при выполнении проектов, которые могут загружать внешние программы для записи внешних программ в интерпретируемый язык. Если ресурсы ограничены (какими они будут на этом процессоре, вы думали об использовании PIC32 или небольшого процессора ARM для этого?), Обычно ограничивают язык подмножеством полной спецификации. Еще дальше в цепочку входят предметно-ориентированные языки, которые делают только несколько вещей.
Например, проект elua является примером интерпретируемого языка с низким ресурсом (64 КБ ОЗУ). Вы можете сжать это до 32 КБ ОЗУ, если удалите некоторые функции (Примечание: он не будет работать на вашем текущем процессоре, который является 8-битной архитектурой. Использование внешней ОЗУ, вероятно, будет слишком медленным для графики). Он обеспечивает быстрый и гибкий язык, на котором новые пользователи могут легко программировать игры, если вы предоставите минимальный API. Для этого языка доступно множество документов. Существуют и другие языки (например, Forth и Basic), которые вы можете использовать аналогичным образом, но я думаю, что Lua - лучший вариант на данный момент.
Аналогичным образом вы можете создать свой собственный предметно-ориентированный язык. Вам нужно будет предоставить более полнофункциональный API и внешнюю документацию, но если бы все игры были похожи, то это было бы не слишком сложно.
В любом случае, PIC18, вероятно, не тот процессор, который я бы использовал для чего-то, что включает в себя пользовательское программирование / скриптинг и графику. Возможно, вы знакомы с этим классом процессоров, но я бы предположил, что сейчас самое время использовать что-нибудь с драйвером дисплея и большим объемом памяти.
Вариант 2: просто перепрограммируйте все
Однако, если вы уже планируете программировать все игры самостоятельно на C, не стоит загружать только игровую логику с SD-карты. Для перепрограммирования у вас есть всего 32 КБ флэш-памяти, и вы можете легко получить карту памяти microSD емкостью 4 ГБ. (Примечание: карты большего размера часто являются SDHC, с которыми сложнее взаимодействовать). Предполагая, что вы используете каждый последний байт из 32 КБ, это оставляет место на SD-карте для 131 072 копий вашей прошивки с любой игровой логикой, которая вам нужна.
Существует множество приложений для написания загрузчиков для PIC, таких как AN851 . Вам необходимо спроектировать загрузчик так, чтобы он занимал определенную область памяти (вероятно, верхнюю часть области памяти, вы должны указать это в компоновщике), и указать, что полные проекты встроенного ПО не достигают этой области. Приложение разъясняет это более подробно. Просто замените «Загрузочный раздел PIC18F452» на «Загрузочный раздел, который я указываю в компоновщике», и все это будет иметь смысл.
Затем ваш загрузчик просто должен позволить пользователю выбрать программу для запуска с SD-карты и полностью скопировать ее. Пользовательский интерфейс может состоять в том, что пользователь должен удерживать нажатой кнопку, чтобы войти в режим выбора. Обычно загрузчик просто проверяет состояние этой кнопки при перезагрузке и, если она не удерживается, загружается в игру. Если он удерживается, ему нужно будет разрешить пользователю выбрать файл на SD-карте, скопировать программу и продолжить загрузку [новой] игры.
Это моя текущая рекомендация.
Вариант 3: Глубокая магия, включающая хранение только части шестнадцатеричного файла
Проблема с вашим предполагаемым механизмом состоит в том, что процессор не имеет дело с API-интерфейсами и вызовами функций, он имеет дело с числами - адресами, на которые может перейти указатель инструкции, и ожидать наличия кода, который выполняет вызов функции в соответствии со спецификацией API. Если вы попытаетесь скомпилировать только часть программы, компоновщик не будет знать, что делать, когда вы звоните
check_button_status()
илиtoggle_led()
. Возможно, вы знаете, что эти функции существуют в шестнадцатеричном файле на процессоре, но ему нужно точно знать, по какому адресу они находятся.Компоновщик уже разбивает ваш код на несколько разделов; Вы можете теоретически разбить это на дополнительные разделы с некоторыми
-section
и#pragma
заклинаниями. Я никогда не делал этого и не знаю как. До тех пор, пока вышеупомянутые два метода не подведут меня (или кто-то не отправит здесь удивительный ответ), я, вероятно, не изучу этот механизм, и поэтому я не могу научить его вам.источник