Быстрая разработка для микроконтроллеров в Simulink на примере полифункционального зарядника

efe18cf68eac3c7b69a350b94768b0f3.png

Полифункциональный зарядник — хорошая платформа чтобы показать преимущество гибридной графической нотации перед текстовой нотацией С/С++ . Для этого используется MATLAB Simulink под Windows. Метод разработки напоминает SIL (software-in-the-loop), но модель выполняется на ПК в реальном времени и при взаимодействии с реальным железом.

SIL — это когда пишется не сразу конечный софт, а модель софта. Модель является такой же программой, но выполняется на компьютере. Это типа эскиза. Основные алгоритмы в модели реализованы, но сопутствующие вещи отсутствуют. Например данные накапливаются, и реально их бы на SD карту писали, а в модели мы их на график выводим. Или в дивайсе есть специальные способы взаимодействия с пользователем вроде кнопочек и переключателей, а в модели все делаем мышкой. Нам там еще светодиодами информационными моргать, но в модели это опускаем.

Модели в стиле SIL на вход подают разные тестовые сценарии и смотрят чтобы все было как задумано на выходе. Потом из SIL модели в MATLAB-е можно сгенерировать программные модули на языке C/C++ для компиляции и последующей загрузки в микроконтроллер. При этом можно быть уверенным, что в микроконтроллере алгоритм будет работать точно также как и на ПК.

Но в MATLAB можно пойти дальше. Можно SIL модель скомпилировать и запустить на ПК в среде Windows в режиме жёсткого реального времени с длительностью такта, скажем, в 5…10 мс.

С таким тактом модель способна без потерь принимать и отдавать данные от внешнего устройства по протоколу TCP. Что из этого следует?

Следует то что модель можно как угодно модифицировать и рефакторить и мгновенно запускать в паре с устройством пропуская фазу компиляции и загрузки бинарника. В нашем случае непосредственно измерения токов и напряжений, управление ключами и ЦАП производятся платой зарядника, а алгоритм зарядника выполняется на ПК общаясь с платой физически через USB интерфейс. Поверх USB работает TCP протокол. Такое решение позволяет всем важным массивам данных оставаться и накапливаться на ПК. Там их можно анализировать и визуализировать как угодно, прогонять повторно и отлаживать цифровую фильтрацию, детекцию и прочее. Компьютер намного удобнее в этом плане чем плата с микроконтроллером.

Это удобно, но не это главный повод радоваться ускорению разработки.

Модель мы создаём в графической нотации MATLAB Simulink. Я называю ее гибридной потому что в ней по прежнему можно широко использовать текстовые программные инструкции. Это придаёт разработке ту лёгкость когда незнание не становиться препятствием. Если не знаете как нарисовать, то тогда просто напишите это. Если лень писать, то можно просто создавать таблицы истинности.

Много сказано о преимуществах и недостатках графических нотаций. Они широко распространены в языках SQL запросов, в представлениях бизнес процессов, в языках спецификаций типа SDL. Они все отличаются друг от друга как небо и земля. Сильнее чем Python от assembler-а. Так и нотация Simulink представляет собой нечто особенное и не сравнимое с какими-нибудь простыми концепциями типа автоматного программирования.
Это я бы назвал набором удобных графических представлений действий и состояний с сопутствующими библиотеками, средствами редактирования, рефакторинга, отладки, верификации, кодогенерации и т.д.

Сразу оговорюсь, что весь софт с нуля для микроконтроллера в среде MATLAB Simulink я не создаю. На плате полифункционального зарядника уже присутствует программная платформа с операционной системой реального времени, файловой системой, сетевым протоколом и прочей функциональностью. Весь этот слой программного обеспечения MATLAB заменить пока не может. Хотя есть множество примеров проектов в MATLAB под голые платы, но они очень функционально ограничены.

Рекламировать MATLAB Simulink нет смысла, скажу только что в нем привлекает лично меня.

Самое важное — это огромный набор функциональных блоков, в частности цифровых фильтров на любой вкус. Именно с использования фильтров началось моё увлечение матлабом. Очень удобная визуализация любых сигналов и состояний переменных.

Второе чем привлекает графическая нотация Simulink — это минимум имён. Т.е. буквально прямо в Simulink можно ничего не именовать. В его подсистеме Stateflow именовать обязательно надо только состояния. Именованные переменные и функции тоже понадобятся рано или поздно, но их будет минимум. По моему мнению все языки с текстовыми нотациями ведут одну общую войну за передел, структуризацию и минимизацию пространств имен и одновременно это их бич, поскольку ресурс имён ограничен у человека. Имена придумывать, запоминать, отличать, вспоминать — сложно. Графическая нотация резко снижает затраты на содержание пространства имён.

Третья привлекательная черта графической нотации — быстрота рефакторинга. Все заранее предусмотреть невозможно. Программные модули пишутся поначалу с большими пробелами в функциональности, потом наступает черед уточнений, выполняемых с помощью рефакторинга. Например, в текстовой нотации чтобы в конструкции switch внести новое состояние приходится обычно выполнять редактирование в нескольких местах и в нескольких файлах. Кроме того, что это несколько утомительно, это еще и источник ошибок. В Simulink это делается в одном месте на диаграмме и вставкой всего одного блока. Буквально одно движенье мышью. С рефакторингом целиком функций в текстовых нотациях еще хуже. Если нужно разделить функцию на несколько, то это выливается в целый анализ по поводу эффективных способов передачи аргументов и возврата результатов, сепарацию и перенос локальных переменных, создание структур или даже классов и проч. В Simulink это делается простым copy-paste нужного фрагмента с одного уровня на другой или в блок подсистемы. Т.е. экономия времени в этом плане в графической нотации неоспоримая.

Четвёртое преимущество заключается в обозримости. Если в текстовой нотации открыв файл и увидев функцию мы не сможем понять где она вызывается не предприняв целый ряд поисков, то в окне Simulink мы всегда видим весь путь от корня модели до подсистемы на которую смотрим. Это избавляет от ментальных усилий по запоминанию структуры алгоритма.

Разработка программы тестера аккумуляторов

Назначение программы заключается в зарядке аккумулятора и последующей разрядке в течении нескольких циклов. По результатам работы программы выясняется реальная ёмкость аккумулятора, время необходимое для полного заряда, оптимальные режимы зарядки. Особенностью алгоритма является его сравнительно невысокое быстродействие. Достаточно выполнять алгоритм с тактом в 10 мс чтобы эффективно управлять зарядником.
Второй задачей алгоритма было найти приемлемые эвристики для процесса заряда без использования информации о температуре аккумулятора. В частности поэтому нигде в модели не учитывается температура аккумулятора.

Разработка модели в Simulink

Если разработка предусматривает перенос алгоритма на микроконтроллер, то в первую очередь надо озаботиться легко портируемым интерфейсом входных и выходных данных алгоритма.

Верхний уровень моделиВерхний уровень модели

Поскольку переносить на целевой микроконтроллер будем только ядро, то его надо изолировать от всех вспомогательных подсистем необходимых для работы технологии SIL.
К этим вспомогательным подсистемам относится передатчики и приёмники данных, блоки с настраиваемыми параметрами и всяческие отладочные визуальные компоненты. В интерфейсах применяются как одиночные сигналы так и шины объединяющие несколько сигналов. Выбор того или иного способа определяется только лишь удобством восприятия при разработке модели. Важные сигналы делаются одиночными. Однотипные объединяются в шины.

Ниже изображено содержимое блока устанавливаемых параметров. Чтобы не загромождать верхний уровень модели все параметры объединены в шину. Во время преобразования MATLAB-ом модели в исходники на языке C шины преобразуются в структуры, так что применение шин и удобно и полезно.

Параметры аккумулятора и процесса зарядаПараметры аккумулятора и процесса заряда

Структуры передатчика и приёмника выполнены способом аналогичным описанному в этой статье

Структура приёмника данныхСтруктура приёмника данныхСтруктура передатчика данныхСтруктура передатчика данных

Теперь взглянем внутрь ядра модели

Верхний уровень ядра моделиВерхний уровень ядра модели

Ядро модели является смесью подсистем Simulink и диаграмм Stateflow. В подсистеме Simulink (диаграмма Stateflow тоже подсистема, но дальше в тексте она будет исключаться из понятия подсистема) удобно выполнять поточную цифровую обработку сигналов. Там не важны состояния процесса, важнее непрерывные потоки данных и операции над ними. А в Steflow уже внимание концентрируется на стадиях процесса в виде состояний и признаках перехода от одной стадии к другой. Нельзя сказать что в обычных подсистемах Simulink нельзя организовать состояния и стадии, но они там не будут иметь ясно обозначенных графических элементов. Словом Simulink и Stateflow это как две разные графические нотации в рамках одной среды.

О разработке детектора окончания заряда будет сказано ниже, а сейчас ещё более углубимся в диаграмму Stateflow

Верхний уровень диаграммы StateflowВерхний уровень диаграммы Stateflow

Здесь сразу можно заметить иерархическую организацию диаграммы. Каждый прямоугольник со скруглёнными углами есть состояние или диаграмма содержащая состояния. Если в текстовой нотации мы создаём новые функции или файлы чтобы разбить текст на более мелкие и читаемые фрагменты, то здесь мы переносим часть диаграмм внутрь других диаграмм. Но можем и проще, просто охватить прямоугольником диаграммы уже существующее скопление состояний или диаграмм и они становятся частью новой диаграммы более высокого уровня. Т.е. создавать иерархии в Stateflow очень просто, не в пример текстовым нотациям.

Если заглянем ещё глубже, то увидим основной алгоритм зарядника

Диаграмма основного алгоритма зарядникаДиаграмма основного алгоритма зарядника

В жёлтых прямоугольниках комментарии. Особенностью графической нотации является присущая ей уникальность дизайнерского исполнения. Это не отступы обсуждать в текстовой нотации, здесь поле для фантазии и холиваров гораздо шире.
Но тем не менее я считаю, что комментарии нужны всегда, даже если тут кристально понятная диаграмма. Другая слабость автора — ортогональные линии связей. Из-за этого можно увидеть пустые сегменты без условий и операторов в цепочках связей. Это делается просто из эстетических соображений. Лишнего кода они не добавят. Это как операторы goto, но поскольку тема молодая, публичной порицания за такое я пока не ожидаю.

На диаграмме изображён очень простой алгоритм быстрой зарядки константным током. Но добавлены ещё стадии релаксации после отключения зарядки и поддержки плавающего заряда некоторое время до того как подключится нагрузка на аккумулятор для его разряда. Это сделано для того чтобы не упустить какие либо странности в поведении аккумулятора и с отладочными целями. Кроме того введено множество проверок на аварийные или нештатные ситуации. Зарядка отключается если какое либо напряжение опускается ниже нормы, если перегревается DCDC преобразователь, если ток длительное время остаётся выше определенного уровня, если возникает перегрузка по току, если возникают реверсные токи во время зарядки, если переданный заряд превышает ёмкость аккумулятора.

Некоторые защиты выполнены не в модели, а в фирмваре платы зарядника, поскольку они должны сработать максимально быстро. В частности защита по току реагирует в течении 50 мкс. Такую скорость модель с тактом в 10 мс обеспечить не может.

А вот такой симпатичный график можно наблюдать в течении работы модели в Simulink

f8b1fb238425a9a2729879369829132d.png

Для наблюдения доступны все переменные и состояния диаграмм. При желании все можно сохранить и многократно воспроизводить для последующего анализа.

Пример разработки детектора окончания зарядки

Окончание зарядки можно определить по снижению тока зарядки до определенной величины или спустя некоторое заведомо большое время. Первый вариант плох тем что величина остаточного тока не специфицируется достаточно точно для аккумуляторов и может меняться с течением жизни аккумулятора и сильно зависит от температуры. Второй вариант плох своей неэкономичностью.

На практике неплохо показывает себя алгоритм определения окончания заряда по скорости падения зарядного тока. Он мало чувствителен к температуре. Используется эффект стабилизации тока на одном уровне при полном заряде аккумуляторе.

Для отработки алгоритма воспользуемся ранее записанным графиком зарядного тока полученным во время работы нашей модели. И сделаем такую тестовую модель для отладки

Тестовая модель для отладки работы детектора окончания зарядкиТестовая модель для отладки работы детектора окончания зарядки

Для возможности отладки реакции детектора на нештатные ситуации добавлен генератор произвольных сигналов. Детектор помимо сигнала окончания зарядки Result имеет выход сигнала тревоги Alarm означающей аномальное поведение тока.

Модель детектора выглядит вот так:

Модель детектора окончания зарядкиМодель детектора окончания зарядки

Важной частью модели является экспоненциальный фильтр

Модель экспоненциального фильтраМодель экспоненциального фильтра

В палитре Simulink можно найти готовый экспоненциальный фильтр. Это будет компонент Moving Average из DSP System Toolbox. Там экспоненциальный фильтр получают выбором метода Exponential Weighting. Экспоненциальный фильтр интересен тем, что как и бегущее среднее он не даёт осциллирующих выбросов на фронтах сигналов. Но экспоненциальный фильтр при этом быстрее реагирует на изменения сигнала чем бегущее среднее при том же качестве сглаживания и не требует глубокого буфера для накопления данных (о расходе памяти нужно думать и здесь, поскольку модель будет впоследствии скомпилирована в микроконтроллер).

В нашем случае была сделана своя реализация фильтра, поскольку требовался сигнал сброса фильтра. Глобальный сброс есть у всей модели и он нужен чтобы перезапускать детектор при смене режимов работы зарядника. Наша модель также даёт задавать коэффициент фильтра через внешний вывод.

Другой важный элемент модели — детектор размаха сигнала. Ток может снизится на каком-то интервале времени, но при этом сильно колебаться. Это не будет признаком окончания заряда. Поэтому реализован детектор отсутствия колебаний превышающих заданную величину на интервале времени:

Модель детектора размаха сигналаМодель детектора размаха сигнала

Детектор размаха выполнен на вычислителях бегущего максимума и минимума. В конце интервала измерения берётся разница между максимумом и минимумом и сравнивается с пороговым значением. Затем следует сброс и новые вычисления в течении следующего интервала.

После некоторого числа итераций и рефакторинга модели были получены приемлемые результаты

Результаты тестирования моделиРезультаты тестирования модели

Здесь преимущества MATLAB выразились в быстром тюнинге коэффициента фильтра и быстрой симуляции различных искажений тока подтвердивших устойчивость детектора к непредсказуемым ситуациям.

Нужно ли оптимизировать модели?

Если модель предназначена для выполнения на микроконтроллере, то оптимизировать очень желательно. Особое внимание надо уделять типам данных. Микроконтроллеры операции с плавающей точкой делают значительно медленней чем целочисленные. Особенно затратны операции деления с плавающей точкой. А в нашем экспоненциальном фильтре как раз есть две такие операции. Чтобы избежать деления фильтр можно переделать на целочисленную арифметику с использование сдвигов вместо деления. Тогда демонстрационный фильтр будет выглядеть так:

3e54cd191a467d70e90f8d7946fd2bd7.png

Делаем испытательную модель для сравнения работы фильтра с плавающей точкой и фильтра целочисленного с применением сдвигов:

43db759f73cb90b123990304229fed1d.png

Получаем результат в виде графика:

1af8899786a020303483fa1dd1d96f6f.png

Целочисленный фильтр слегка грубее фильтра с плавающей точкой, но вполне приемлем. Правда переход на целочисленную арифметику потребует модификации остальных блоков модели работающих со значением тока. Поэтому к такой модификации следует прибегать когда действительно необходимо, например когда микроконтроллер не содержит сопроцессор операций с плавающей точкой, как ARM Cortex-M0 или ARM Cortex-M33 или когда подобных фильтров в модели очень много. В нашем случае оставлена модель с плавающей точкой, поскольку фильтр всего один и одна лишняя микросекунда беспокойств не причиняет.

Какие свойства модели надо назначить чтобы она выполнялась в реальном времени

Для запуска работы модели в реальном времени используется инструмент Simulink Desktop Real-Time, а именно режим Connected IO mode.

Для этого нужно просто в настройках модели выбрать соответствующий System target file:

Настройка решателя у нас вот такая:

eed07a92a2994d30497d77bad9ec5f39.png

Время окончания работы определяется в самой модели, шаг семплирования задаётся в файле рабочего пространства, который будет загружен вместе с моделью. Он в данном случае 0,01 сек.

И практически это всё что нужно настраивать. Далее жмём Run и смотрим результат.

Как измеряются характеристики реального времени во время выполнения модели

Очень важный вопрос в нашем методе выполнения программы состоит в том насколько точно выдерживаются интервалы времени считывания и записи сигналов в плату зарядника.
Как уже было сказано, для алгоритма выбран интервал времени в 10 мс. Это вполне адекватное время для задачи управления зарядом. Аккумуляторы медленно заряжаются и медленно реагируют.

Ниже дан скриншот диаграммы в субблоке процесса зарядки. В этом окне можно визуально наблюдать как чередуются состояния и ветви исполнения. Все активные состояния и ветви подсвечиваются синим. Но средств точного измерения времени приходы и отправки пакетов во внешние устройства из диаграмм MATLAB нет. Измерением в нашем случае занимается сам дивайс. Поскольку только в нем можно точно зафиксировать когда получены данные и когда отправлены.

Вид диаграммы состояния процесса заряда аккумулятора во время выполнения. Синим цветом обозначены активные состоянияВид диаграммы состояния процесса заряда аккумулятора во время выполнения. Синим цветом обозначены активные состояния

Чтобы не усложнять протокол обмена с MATLAB (а он состоит из посылки всего одной строки) наблюдение за характеристиками реального времени проводятся в программе FreeMaster. Как было замечено раньше зарядник оснащён RTOS и может одновременно поддерживать несколько каналов связи. Один из каналов обслуживает протокол FreeMaster по TCP поверх USB. Интересно то, что работа всех каналов связи нагружает процессор в среднем не более чем на 10%.

Окно программы FreeMaster с данными в реальном времени о нагрузке процессора и интервалах времени обмена данными с зарядникомОкно программы FreeMaster с данными в реальном времени о нагрузке процессора и интервалах времени обмена данными с зарядником

Как видно из графика в окне FreeMaster в процессе работы алгоритма в среде MATLAB на ПК интервалы времени соблюдаются достаточно точно. В данном случае разброс не превышает 60 мкс. В худшем случае, когда на ПК одновременно запускались другие программы такие как: браузер, MS Office, антивирусы, разные CAD-ы, компиляция проектов в IDE с использованием всех ядер процессора разброс мог достигать 2 мс. Это все ещё очень хороший показатель жёсткости соблюдения реального времени.

Безопасность зарядника при сбоях работы ПК обеспечивается тем, что зарядка отключается как только теряется USB и TCP соединение. Также отключается нагрузка от аккумулятора если он разряжался в этот момент.

Заключение

Технология SIL с прямым управлением зарядником из среды MATLAB оказалась очень удобным инструментов в разработке алгоритмов. С её помощью удаётся в течении нескольких дней получить исчерпывающую информацию о поведении аккумуляторов из разных состояний и в разных режимах использования. В результате на выходе имеем надёжный алгоритм зарядки на языке C для целевой платы практически не нуждающийся в интеграционном тестировании.

Вид дисплея полифункционального зарядника в процессе разряда аккумулятора под управлением MATLAB SimulinkВид дисплея полифункционального зарядника в процессе разряда аккумулятора под управлением MATLAB Simulink

Файлы модели для изучения находятся здесь.

© Habrahabr.ru