Папа, а что такое многозадачность Windows? - Подожди, сейчас дискету отформатирую - тогда покажу. Посмотрим как у нас с этим обстоят дела в ESP32 решив какую-нибудь практическую задачу. Информации в Интернет на эту тему более чем достаточно, копипастить её всю у меня нет ни какого желания.
Схема:
С компьютера через USB-UART на скорости 5'000'000 бод отправляется массив размером 76800 байт. Данные этого массива позволяют закрашивать дисплей последовательно в красный, зеленый, синий цвета.
Код для Arduino:
Функция Serial2.readBytes(disIMG, 76800); ожидает массив с компьютера размером 76800 байт через интерфейс UART2 и сохраняет его в оперативной памяти SRAM - время выполнения 198ms
Функция myGLCD.pushImage(0, 0, 320, 240, disIMG); отправляет массив данных из оперативной памяти SRAM на экран через интерфейс SPI - время выполнения 38ms
Суммарное время выполнение этих двух операторов составляет 236ms и вроде бы обновлять картинку чаще чем один раз в 236ms нет ни какой возможности, но наша еспешка работает под управлением FreeRTOS — многозадачной операционной системой реального времени.
А еще у нас есть целое ядро с индексом 0, которое в данном проекте пинает балду. В среде Arduino IDE, при разработке IoT проектов, оно используется для сетевых коммуникаций (Wi-Fi, Bluetooth), при подключении соответствующих библиотек. Библиотеки для работы с коммуникациями используют это ядро втихаря, стараясь не привлекать внимание ардуинщиков и поэтому мы можем продолжать уверенно мигать светодиодом используя различный "овнокод" - на нашем подключении например к вафле это ни как не отразится.
У нас же сетевых подключений в рамках этой задачи нет и не предвидится, поэтому на "овнокодим" и на свободном ядре микроконтроллера.
Очевидно, что SPI у нас работает быстрее чем UART и зачем нам тогда ждать отправки картинки на дисплей? Будем делать все параллельно - массив мы вычитываем быстрее, чем туда пишем. Алгоритм:
- 1-е ядро занимается приемом данных и пишет их в массив в SRAM;
- 0-е ядро читает данные из того-же массива в SRAM и шлет их в дисплей;
Поскольку, картинку на дисплей через SPI мы выводим быстрее, чем успеваем принимать её через интерфейс UART - мы её успеем вычитать раньше, чем её перезапишет новая порция данных (0-е ядро еще и чаю попить успеет). Общую скорость работы программы мы сможем сократить до самого узкого места - времени приема данных по UART т.е. с 236ms => 198ms.
Пишем:
Тут ничего интересного, создаем массив для картинки дисплея и объект для работы с дисплеем, открываем два UART порта (один для отладки, второй для передачи данных)
А вот тут уже поинтереснее. В 20-й строке мы принимаем данные, а вот отправку картинки запускаем на 0-м ядре и соответственно сразу переходим к ожиданию новой порции данных. Вызываем: Task1code - выглядит как обычная функция C++, за исключением расширения vTaskDelete(). После его вызова FreeRTOS узнает, что задача завершена и ее не следует переносить. Не забудьте вызвать эту функцию, иначе сторожевой таймер перезапустит ESP32.
Отправляем с компьютера 76800 байт и видим, что наша программа стала работать на 38ms быстрее.
Теперь мы ограничены только скоростью UART.
Код для Windows написан на языке С# - отправляем каждые 200ms
Все работает, цвета не сыплются и ничего не виснет.
Что и где можно почитать по теме в Интернет:
Понятно, что FreeRTOS тема очень не простая, и то что у меня запустилась такая достаточно тяжелая задача (да еще и на библиотечных функциях) - это скорее исключение чем правило. Фишка в том, что ядра то два, а периферия то одна. Представьте, чтобы было если попытаться подергать 2 ядрами одновременно один и тот-же SPI например, ну и повезло что библиотеки не используют какие-либо общие прерывания.
Впрочем тут у меня изначально было предположение - раз работа ведется с разными интерфейсами - проблем возникнуть не должно и можно обойтись без семафоров, мьютексов и прочих прелестей цивилизации, без которых серьезное (а главное надежное) приложение написать скорее всего не выйдет.
Оглавление канала ТУТ
Всем удачи!