В этом репозитории содержится программный код библиотеки для отправки данных различного типа в рабочее пространства Simulink для реализации исследований, процессов калибровки и настройки, отладки, а также реализации интерфейса Hardware-in-the-Loop. На данный момент реализация включает отправку, прием находится в процессе тестирования и будет добавлен в следующем обновлении
Актуальность проекта: апрель 2024 года Над репозиторием работал: Программист РЦР ДГТУ Назаров Александр (@casonka)
В основе проекта лежит следующая структура :
1224l-matlab-serial-stm32:
|
|
|--inc:
| |--matlab_serial.h
|
|--src:
|--matlab_serial.c
matlab_serial.h - заголовочный файл, содержащий шаблоны функций библиотеки
matlab_serial.c - файл с реализацией функций библиотеки
В основе программного кода реализован набор служебных макросов, улучшающих читаемость исходного кода, несколько реализаций перегрузки дженерик макросами, а также функциям для передачи данных, имеющих типы:
- uint8_t
- int8_t
- uint16_t
- int16_t
- uint32_t
- int32_t
- float
Методы библиотеки содержат необходимый минимум защиты от аргументов, ссылающихся на NULL, несоответствия вызываемому типу данных с инициализированным и ошибки динамического выделения памяти.
Благодаря такой реализации удалось получить функции с упрощенным подобием динамической типизации и уменьшить количество требуемых аргументов функции. Подробная информация и примеры реализации библиотеки смотри в следующем разделе.
Для минимальной работы драйвера связи с пространством Simulink необходимо выполнение как минимум двух функций:
matlab_serial_init(obj, interface, pointer, start_symbol, end_symbol);
Функция инициализации сериализатора, содержит следующие аргументы:
- obj - объект типа matlab_serial_t, необходимый для организации обмена;
- interface - объект типа UART_HandleTypeDef необходимый для привязки к объекту сериализации;
- pointer - переменная, которую необходимо передать в рабочее пространство;
- start_symbol - значение стартового байта пакета, передаваемого в Matlab;
- end_symbol - значение двухбайтового конца пакета, передаваемого в Matlab;
Благодаря выполнению _Generic аннотации в теле макроса происходит перегрузка и выбор нужной функции в зависимости от типа передаваемого параметра pointer. В теле макроса автоматически используется ссылка на указанный аргумент, если это потребуется, поэтому пользователю нет необходимости дополнительно их указывать.
Для инициализации передачи массивов реализована следующая функция инициализации:
matlab_serial_init_array(obj, interface, pointer, start_symbol, end_symbol);
Список аргументов повторяется с первым вариантом функции и кроме другого названия из параметра pointer
используется ссылка не на параметр, а на нулевой элемент массива _Generic((&pointer[0]),\
поэтому при инициализации убедитесь, что в аргумент передается одномерный массив.
Для передачи отслеживаемых переменных используют функцию передачи:
matlab_serial_send(matlab_serial_t *object,uint32_t timeout);
Помимо передачи объекта, вторым аргументом указывается таймаут передачи информационных пакетов.
Результат выполнения функций можно отслеживать при помощи переменной типа status_t
, ниже приведен список результатов выполнения, по которым можно определить ключевые состояния работы функций библиотеки:
typedef enum {
STATUS_OK = 0x0, // Успешное выполнение функции
OBJECT_NULL = 0x1, // Передан аргумент с ссылкой на NULL
INVALID_DATA_TYPE = 0x2, // Несоответствие передаваемого типа с инициализованным
UNDEFINED_DATA_TYPE = 0x3, // Тип данных не был инициализирован
SENDING_ERROR = 0x4, // Ошибка отправки данных
RECEIVING_ERROR = 0x5, // Ошибка приема данных
ALLOCATE_ERROR = 0x6, // Ошибка при выделении динамической памяти
END_FUNCTION_WITH_ERROR = 0xF, // Функция завершена с ошибкой (не используется)
}status_t;
После завершения работы с сериализатором необходимо освободить занимаемую память, для этого необходимо явно указать функцию с передачей ссылки на объект:
matlab_serial_finish(matlab_serial_t *object);
Суммируя вышесказанное, полная последовательность работы выглядит согласно ниже указанному листингу:
// создание объекта матлаб сериализатора
matlab_serial_t object;
// создание отслеживаемой переменной
float value = 0.4;
/*! инициализация сериализатора для отслеживания float переменной
* huart1 может быть заменен на любой другой интерфейс UART, сгенерированный
* в вашем Cube MX или CubeIDE проекте
*/
matlab_serial_init(object, huart1, value, 0x3A, 0x0D0A);
// loop
// объявление передачи данных с таймаутом 100 миллисекунд
matlab_serial_send(&object, 100);
// При необходимости завершаем работу с объектом, освобождая занимаемую память
matlab_serial_finish(&object);
Для получения дополнительной информации рекомендуется обратиться к заголовочному файлу matlab_serial.h
.
Поскольку в основе функций инициализации лежит реализация динамического выделения памяти, это делает невозможным полноценно использовать функции в локальном пространстве, например в многократно выполняемой функции. Рассмотрим типовой случай приводящий к утечке памяти в ходе пользования библиотекой:
float value = 0.0;
void foo(void)
{
// ОШИБКА приводящая к утечке памяти
matlab_serial_t object;
matlab_serial_init(object, huart1, value, 0x3A, 0x0D0A);
matlab_serial_send(object, 100);
}
int main(void)
{
// Выполнения инициализации, настройка регистров микроконтроллера
while(1)
{
foo();
}
}
В приведенном фрагмента matlab_serial_t
переменная инициализируется в качестве локальной переменной, объект инициализируется и сразу же отправляет данные, в процессе завершения выполнения функция очищает локальную переменную, однако выделяемая в таком случае динамическая память объекта остается и занимает место, поэтому многократное выполнение функции влечет за собой утечку памяти и скорейший переход в HardFault.
В качестве возможных вариантов исправление ошибки будет указывание объекта глобальной переменной, а функцию инициализации проделать однократно:
float value = 0.0;
matlab_serial_t object;
void foo(void)
{
matlab_serial_send(object, 100);
}
int main(void)
{
// Выполнения инициализации, настройка регистров микроконтроллера
matlab_serial_init(object, huart1, value, 0x3A, 0x0D0A);
while(1)
{
foo();
}
}
В таком случае запись считается корректной и может быть использована разработчиком как шаблон. Допускается запись инициализации в локальном пространстве в следующем виде:
float value = 0.0;
matlab_serial_t object;
void foo(void)
{
matlab_serial_init(object, huart1, value, 0x3A, 0x0D0A);
matlab_serial_send(object, 100);
matlab_serial_finish(&object);
}
int main(void)
{
// Выполнения инициализации, настройка регистров микроконтроллера
while(1)
{
foo();
}
}
Таким образом память освобождается и объект может быть инициализирован снова.
Настройка Simulink программы является одним из основных шагов для успешного обмена с микроконтроллером. Инструкция по её настройке была написана на основе этого репозитория. Переходите по ссылке для получения дополнительной информации по настройке Simulink.
Для организации передачи используются блоки расширения Embedded Coder. Расположение блоков может быть реализовано, как на приведенном ниже рисунке.
Данный шаблон можно использовать как пример для реализации своих Simulink проектов. Для соединения используется блок Serial Configuration с указанием COM порта и настроенной скорости в бодах. Для примера в приведенных ниже рисунках демонстрируется настройка для приема одного байта (рисунок слева) и массива трех байт (рисунок справа) информации.
![]() |
![]() |
---|
Блок Cast
необходим для преобразования значения к необходимому типу и необходим в том случае, если вы пытаетесь передать float
переменную. В таком случае параметр принимаемой переменной в блоке Serial Receive необходимо изменить на single
, а блоке Cast
указать double
, этого будет достаточно для успешного приема float
переменной. Настройка преобразования приведена в рисунке, приведенном ниже.
- Добавить функции для приема данных с Matlab Simulink на микроконтроллер;
- Добавить реализацию инициализации режима Hardware-in-the-Loop;
- Добавить дополнительные функции для повышения вариативности выполнения функций отправки (по таймеру, внешнему событию);
- Расширить транспорт, добавив реализацию обмена при помощи DMA;
Спасибо за внимание! Если репозиторий помог тебе в разработке и исследовании, мне было бы приятно получить от тебя star, это мотивирует меня продолжать наполнение библиотеки.