В этом посте я хочу поделиться с вами программой написанной мной для разработки ПО под микроконтроллеры по стандарту близкому к AUTOSAR.
В 2018 году меня позвали работать в EPAM Беларусь на аутсорсный проект по разработке ПО для рулевых реек. Я с большим удовольствием согласился и уехал жить и трудиться в Беларусь.
На этом проекте я познакомился со стандартом разработки ПО в автомобильной промышленности - AUTOSAR.
Пару слов об AUTOSAR
Согласно стандарту, архитектура приложения разделяется на 3 уровня:
Application;
Run Time Environment (RTE);
Basic software.
Главная идея стандарта в том, чтобы разделить систему на компоненты, а также определить уровни ответственности каждого из уровней. При этом application в основном стараются делать независимым от железа, чтобы можно было безболезненно переносить ПО с одного МК на другой. Система разделяется на независимые компоненты, и они взаимодействуют друг с другом с помощью RTE. Это позволяет иметь возможность протестировать каждый компонент в отдельности и добиться практически 100% покрытия кода тестами.
Подробнее о стандарте можно почитать здесь.
Согласно стандарту, компоненты могут иметь следующие составляющие:
Порты для взаимодействия друг с другом;
Функции, вызываемые по событиям из RTE (runnables);
Калибровочные параметры – Calibration Data (CData);
Память принадлежащая отдельному экземпляру компонента – Per Instance Memory (PIM);
Переменные взаимодействия между runnables одного компонента – Inter Runnable Variable (IRV).
Взаимодействие runnables компонента с его портами CData, PIM, IRV происходит с помощью функций RTE.
Взаимодействие компонентов проекта между собой происходит с помощью портов. Существует несколько видов портов, и они образуют между собой пары, которые могут быть соединены друг с другом. Выделим главные из них: sender-receiver, client-server.
Sender порт – порт для записи в порт;
Receiver порт – порт для чтения из порта;
Server порт – предоставляет клиентам определенную функциональность;
Client порт – позволяет вызывать функционал другого компонента.
На картинке ниже представлен пример взаимодействия компонентов SWC1 и SWC2, а также внутренняя структура SWC1.
Картинка взята отсюда.
AUTOSAR GUI Editor
Меня очень вдохновила концепция разбития системы на отдельные независимые компоненты, и я решил написать свой собственный небольшой автозар редактор, который бы удовлетворял нужды моего домашнего проекта. С этим редактором я и хочу с вами поделиться. Конечно, весь функционал стандарта в редакторе мне не удалось реализовать, также пришлось отойти от сохранения файлов проектов в формате arxml. На данный момент реализованы следующие возможности:
Создание компонентов и композиций;
Создание client-server и sender-receiver интерфейсов;
Создание простых типов данных с указанием разрешенного диапазона. Этот диапазон может применяться на этапе тестирования компонента;
Создание комплексных типов данных(struct), массивов и перечислений;
Создание портов в компонентах и композициях и задание им их интерфейсов;
Соединение портов, имеющих одинаковый интерфейс, при этом один write порт может быть подключен к нескольким read портам, также как и server порт к нескольким клиентским портам;
Возможно размножение компонентов одного типа (Multiple instantiation);
Возможно создание PIM и CData для компонентов, при этом для каждого образца компонента можно задать своё индивидуальное начальное значение для PIM и CData;
Поддерживается создание периодически запускаемых runnable компонента, каждый runnable может иметь свою частоту вызова;
Создание задач планировщика с приоритетами и их собственными периодами и распределение runnables компонентов по этим задачам;
Автоматическая генерация RTE файлов, которые объединяют компоненты друг с другом согласно проекту;
Автоматическая генерация шаблонов компонентов для первоначального использования этих файлов в проекте;
Генерация test environment для отдельно взятого определения компонента;
Генерация планировщика вызова runnables;
Проверка проекта на ошибки.
Есть и определенные ограничения на данный момент:
Во вложенных композиции не может быть вложено других композиций, все композиции находятся в Main композиции.
Отсутствуют асинхронные клиент-серверные операции;
Первоначально я сделал генерацию тасок ОС как тасок FreeRTOS, но ввиду того, что у меня периодически отказывала одна из тасок и не хотелось отлавливать баг в программе, я решил переписал планировщик таким образом, чтобы ОС таски вызывались одна за другой в соответствии с их приоритетом и отойти от использования FreeRTOS в моём домашнем проекте. Генерация фриртосных тасок осталось в анналах git истории редактора.
Сам исходный код редактора я выложил на гитхаб в открытый доступ.
По умолчанию, при открытии программы создается пустая композиция Main. Далее можно начать создавать структуру проекта, компоненты, интерфейсы.
Создание элементов проекта
Создание компонентов, композиций, интерфейсов и типов данных делается с помощью выбора необходимого элемента в одном из пунктов меню insert.
Далее, как только необходимый элемент был создан, откроется окно для его настроек.
Чтобы поместить компонент в композицию, необходимо создать его definition, затем выбрать в дереве объектов композицию, в которую необходимо добавить образец компонента, и после перетащить definition в композицию(drag and drop). Если всё выполнено верно, то в композиции появится новый компонент. Для того, чтобы сделать возможность создавать несколько образцов компонента, необходимо поставить галочку Multiple instantiation. В меню компонента также можно задать ему runnables, порты, PIM и CData.
Для того, чтобы создать порт для компонента, необходимо создать сперва интерфейс для данного типа порта, создать порт в defenition компонента, а уже потом выбрать интерфейс порта.
Для того чтобы задать init values для PIM и CData необходимо выбрать компонент в композиции и во вкладке Component properties вписать нужные данные в поля.
Чтобы создавать соединения между портами компонентов, необходимо чтобы порты соединяемых компонентов имели один и тот же интерфейс. Нажав Insert->Connection зажимаем ЛКМ над одним портом компонента, а затем отжимаем её над другим. Если всё хорошо, то появится прямая линия, соединяющая порты компонентов. Можно добавить несколько точек, в соединительную линию, чтобы она обходила элементы.
Перемещение по композиции
Зажав колесико, можно перемещать композицию
Покрутив колесико - изменять масштаб
Двойной клик по колесику - растягивает текущую композицию в отображаемое окно
Перемещение компонентов по композиции осуществляется путем зажатия ЛКМ на компоненте, перемещения, а затем отпускания ЛКМ.
Размеры компонентов можно менять с помощью якорей по углам.
Планировщик
Настройка планировщика осуществляется с помощью окна Runnables management, которое может быть запущено с помощью System-> Runnables order. Для того, чтобы планировщик был корректно создан, необходимо сперва создать таски ОС и затем распределить runnables компонентов по этим таскам.
На данный момент поле Stack size in bytes не используется. Но при необходимости, можно создать(откопать в git истории) генератор планировщика, который создает таски RTOS с указанным значением размера стэка.
Генерация RTE
Перед генерацией RTE очень благоприятно убедиться в отсутствии ошибок в проекте, например не указанных интерфейсах в портах или пропущенных типов данных в структурах. Делается это с помощью команды System->Check errors.
Далее необходимо указать место, куда будут сохраняться сгенерированные RTE файлы и шаблоны компонентов в окне Project Settings. (Project->Settings…). Здесь же необходимо будет указать частоту системного таймера, на котором будет основываться расчет вызовов задач планировщика.
Если были указаны относительные пути, как на картинке выше, то папки с файлами будут сгенерированы относительно места расположения проекта.
После того, как все ошибки устранены и указаны пути сохранения, жмем Project->Generate RTE и получаем папку RTE и папки с шаблонами компонентов. Важно знать, что файлы в папках RTE и RteSkeleton всегда будут перезаписываться при перегенерировании RTE.
Настройка проекта в STM32CubeIDE
Пример выполнен на STM32CubeIDE, чтобы показать, как можно использовать порты, runnables, таски и СData с помощью моего редактора.
Рассказывать как сделать проект в STM32CubeIDE я не буду, т.к. по этому поводу уже написано множество статей. Скажу лишь основные действия, которые необходимо выполнить, чтобы проект собирался и запускался:
Необходимо создать папку Components в корне проекта и скопировать в неё сгенерированные шаблоны компонентов
Написать код для компонентов или просто оставить как есть, чтобы убедиться для начала, что проект собирается.
Добавить в Includes проекта каждую include папку компонента, а также папку RTE.
4. Добавить в Source Location папки Components и RTE
5. В main.c добавить:
#include "Rte_Task_Scheduler.h"
а также вызывать работу планировщика в бесконечном цикле.
6. В stm32f4xx_it.c между /* USER CODE BEGIN Includes */ и /* USER CODE END Includes */ добавить:
#include "Rte_Task_Scheduler.h"
и также обновить обработчик прерывания SysTick_Handler.
7. После этого настройка проекта в кубе завершена и можно собирать проект и запускать его на железке.
Пример также выложил на github.
Конечно, AGE может содержать ошибки и ему есть ещё куда расти, но своей основной задачей – генерацией RTE, он справляется на ура.
Если программа кого-нибудь заинтересует, в следующей статье расскажу, как делать тесты на компоненты.
Заключение
Данный редактор позволил мне сделать архитектуру моего приложения под STM32 более наглядной и гибкой. Если необходимо написать какой-либо математически нагруженный компонент я его могу теперь безболезненно протестировать в Eclipse и уже потом легко внедрить в проект в STM32. Также стал наглядно виден поток данных от одних компонентов к другим, что позволяет легко и быстро находить источники проблем в проекте, если они возникают.