- Что такое семафор?
- Как использовать семафор в FreeRTOS?
- Объяснение кода семафора
- Принципиальная электрическая схема
- Что такое мьютекс?
- Как использовать Mutex в FreeRTOS?
- Пояснение к коду мьютекса
В предыдущих руководствах мы рассмотрели основы FreeRTOS с Arduino и объект ядра Queue в FreeRTOS Arduino. Теперь, в этом третьем руководстве по FreeRTOS, мы узнаем больше о FreeRTOS и его передовых API, которые помогут вам глубже понять платформу многозадачности.
Семафор и мьютекс (взаимное исключение) - это объекты ядра, которые используются для синхронизации, управления ресурсами и защиты ресурсов от повреждения. В первой половине этого урока мы увидим идею семафора, как и где его использовать. Во второй половине мы продолжим работу с Mutex.
Что такое семафор?
В предыдущих руководствах мы обсуждали приоритеты задач, а также узнали, что задача с более высоким приоритетом вытесняет задачу с более низким приоритетом, поэтому при выполнении задачи с высоким приоритетом может существовать вероятность того, что повреждение данных может произойти в задаче с более низким приоритетом, потому что еще не выполняется, и данные поступают к этой задаче непрерывно от датчика, что приводит к потере данных и сбоям во всем приложении.
Итак, есть необходимость защитить ресурсы от потери данных, и здесь важную роль играет семафор.
Семафор - это механизм сигнализации, в котором задача в состоянии ожидания передается другой задачей для выполнения. Другими словами, когда задача 1 завершила свою работу, она покажет флаг или увеличит флаг на 1, а затем этот флаг будет получен другой задачей (задача 2), показывая, что теперь она может выполнять свою работу. Когда task2 завершит свою работу, флаг будет уменьшен на 1.
Итак, по сути, это механизм «дать» и «взять», а семафор - это целочисленная переменная, которая используется для синхронизации доступа к ресурсам.
Типы семафоров в FreeRTOS:
Семафор бывает двух типов.
- Двоичный семафор
- Подсчет семафоров
1. Двоичный семафор: он имеет два целых значения 0 и 1. Он чем-то похож на Queue длины 1. Например, у нас есть две задачи: task1 и task2. Task1 отправляет данные в task2, поэтому task2 постоянно проверяет элемент очереди, если есть 1, затем он может читать данные, иначе он должен подождать, пока он не станет 1. После приема данных задача2 уменьшает очередь и делает ее 0 Это означает, что задача1 снова может отправить данные в task2.
Из приведенного выше примера можно сказать, что двоичный семафор используется для синхронизации между задачами или между задачами и прерыванием.
2. Семафор подсчета: он имеет значения больше 0 и может рассматриваться как очередь длиной больше 1. Этот семафор используется для подсчета событий. В этом сценарии использования обработчик событий будет «отдавать» семафор каждый раз, когда происходит событие (увеличивая значение счетчика семафоров), а задача обработчика «принимает» семафор каждый раз, когда обрабатывает событие (уменьшая значение счетчика семафоров).
Следовательно, значение счетчика представляет собой разницу между количеством произошедших событий и количеством обработанных.
Теперь давайте посмотрим, как использовать Semaphore в нашем коде FreeRTOS.
Как использовать семафор в FreeRTOS?
FreeRTOS поддерживает различные API-интерфейсы для создания семафора, взятия семафора и передачи семафора.
Теперь для одного и того же объекта ядра может быть два типа API. Если нам нужно передать семафор из ISR, то обычный API семафоров использовать нельзя. Вы должны использовать API, защищенные от прерывания.
В этом руководстве мы будем использовать двоичный семафор, потому что его легко понять и реализовать. Поскольку здесь используется функция прерывания, вам необходимо использовать API, защищенные прерыванием, в функции ISR. Когда мы говорим о синхронизации задачи с прерыванием, это означает перевод задачи в состояние выполнения сразу после ISR.
Создание семафора:
Чтобы использовать любой объект ядра, мы должны сначала его создать. Для создания двоичного семафора используйте vSemaphoreCreateBinary ().
Этот API не принимает никаких параметров и возвращает переменную типа SemaphoreHandle_t. Для хранения семафора создается глобальная переменная sema_v.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Предоставление семафора:
Для задания семафора существует две версии: одна для прерывания, а другая - для обычной задачи.
- xSemaphoreGive (): этот API принимает только один аргумент, который является именем переменной семафора, например sema_v, как указано выше, при создании семафора. Его можно вызвать из любой обычной задачи, которую вы хотите синхронизировать.
- xSemaphoreGiveFromISR (): это версия API xSemaphoreGive () с защитой от прерываний. Когда нам нужно синхронизировать ISR и обычную задачу, тогда следует использовать xSemaphoreGiveFromISR () из функции ISR.
Принимая семафор:
Чтобы взять семафор, используйте функцию API xSemaphoreTake (). Этот API принимает два параметра.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: имя семафора, который будет взят в нашем случае sema_v.
xTicksToWait: это максимальное количество времени, в течение которого задача будет ждать в заблокированном состоянии, пока семафор станет доступным. В нашем проекте мы установим для xTicksToWait значение portMAX_DELAY, чтобы задача task_1 продолжала бесконечно ждать в заблокированном состоянии, пока не станет доступен sema_v.
Теперь давайте воспользуемся этими API и напишем код для выполнения некоторых задач.
Здесь сопрягаются одна кнопка и два светодиода. Кнопка будет действовать как кнопка прерывания, которая подключена к контакту 2 Arduino Uno. При нажатии этой кнопки будет сгенерировано прерывание, и светодиод, подключенный к контакту 8, будет включен, а при повторном нажатии он погаснет.
Итак, при нажатии кнопки xSemaphoreGiveFromISR () будет вызываться из функции ISR, а функция xSemaphoreTake () будет вызываться из функции TaskLED.
Чтобы система выглядела многозадачной, подключите другие светодиоды к контакту 7, который будет постоянно мигать.
Объяснение кода семафора
Начнем писать код для, открыв IDE Arduino
1. Сначала включите заголовочный файл Arduino_FreeRTOS.h . Теперь, если какой-либо объект ядра используется как семафор очереди, для него также должен быть включен заголовочный файл.
#include #include
2. Объявите переменную типа SemaphoreHandle_t для хранения значений семафора.
SemaphoreHandle_t interruptSemaphore;
3. В void setup () создайте две задачи (TaskLED и TaskBlink) с помощью API xTaskCreate (), а затем создайте семафор с помощью xSemaphoreCreateBinary (). Создайте задачу с равными приоритетами, а затем попробуйте поиграть с этим числом. Кроме того, настройте контакт 2 как вход, включите внутренний подтягивающий резистор и подключите контакт прерывания. Наконец, запустите планировщик, как показано ниже.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, «Светодиод», 128, NULL, 0, NULL); xTaskCreate (TaskBlink, «LedBlink», 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); если (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Теперь реализуйте функцию ISR. Создайте функцию и назовите ее так же, как второй аргумент функции attachInterrupt () . Чтобы прерывание работало должным образом, вам необходимо устранить проблему дребезга кнопки, используя функцию миллис или микрон и отрегулировав время дребезга. Из этой функции вызовите функцию interruptHandler (), как показано ниже.
long debouncing_time = 150; летучие беззнаковые длинные last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = микросхемы (); } }
В функции interruptHandler () вызовите API xSemaphoreGiveFromISR () .
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Эта функция предоставит TaskLed семафор для включения светодиода.
5. Создайте TaskLed функции и внутри в то время цикла, вызовите xSemaphoreTake () API и проверить, если семафор успешно принят или нет. Если он равен pdPASS (т.е. 1), переключите светодиод, как показано ниже.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, ВЫХОД); while (1) { если (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Также создайте функцию для мигания другого светодиода, подключенного к контакту 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, ВЫХОД); в то время как (1) { digitalWrite (7, HIGH); vTaskDelay (200 / портTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / портTICK_PERIOD_MS); } }
7. Функция пустого цикла останется пустой. Не забывай об этом.
void loop () {}
Вот и все, полный код можно найти в конце этого руководства. Теперь загрузите этот код и подключите светодиоды и кнопку к Arduino UNO в соответствии со схемой.
Принципиальная электрическая схема
После загрузки кода вы увидите, что светодиодный индикатор мигает через 200 мс, а при нажатии кнопки сразу же загорится второй светодиод, как показано на видео, приведенном в конце.
Таким образом, семафоры можно использовать в FreeRTOS с Arduino, где необходимо передавать данные от одной задачи к другой без каких-либо потерь.
Теперь давайте посмотрим, что такое Mutex и как его использовать FreeRTOS.
Что такое мьютекс?
Как объяснено выше, семафор - это механизм сигнализации, аналогично Mutex - это механизм блокировки, в отличие от семафора, который имеет отдельные функции для увеличения и уменьшения, но в Mutex функция принимает и отдает сама по себе. Это метод предотвращения порчи общих ресурсов.
Чтобы защитить общий ресурс, ему назначается токен-карта (мьютекс). Тот, у кого есть эта карта, может получить доступ к другому ресурсу. Остальным следует дождаться возврата карты. Таким образом, только один ресурс может получить доступ к задаче, а другие ждут своего шанса.
Давайте разберемся мьютекс в FreeRTOS с помощью примера.
Здесь у нас есть три задачи: одна для печати данных на ЖК-дисплее, вторая для отправки данных LDR на ЖК-дисплей и последняя задача для отправки данных о температуре на ЖК-дисплей. Итак, здесь две задачи используют один и тот же ресурс, например ЖК-дисплей. Если задача LDR и задача температуры отправляют данные одновременно, то одно из данных может быть повреждено или потеряно.
Итак, чтобы защитить данные от потери, нам нужно заблокировать ресурс LCD для задачи 1, пока она не завершит задачу отображения. Затем задача ЖК-дисплея разблокируется, и задача 2 сможет выполнять свою работу.
Вы можете наблюдать за работой Mutex и семафоров на диаграмме ниже.
Как использовать Mutex в FreeRTOS?
Мьютексы также используются так же, как семафоры. Сначала создайте его, а затем отдайте и возьмите, используя соответствующие API.
Создание мьютекса:
Чтобы создать мьютекс , используйте API xSemaphoreCreateMutex () . Судя по названию, Mutex - это тип двоичного семафора. Они используются в разных контекстах и для разных целей. Двоичный семафор предназначен для синхронизации задач, а Mutex используется для защиты общего ресурса.
Этот API не принимает никаких аргументов и возвращает переменную типа SemaphoreHandle_t . Если мьютекс не может быть создан, xSemaphoreCreateMutex () вернет NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Взятие мьютекса:
Когда задача хочет получить доступ к ресурсу, она использует Mutex с помощью API xSemaphoreTake () . Это то же самое, что и двоичный семафор. Он также принимает два параметра.
xSemaphore: имя мьютекса, который будет использоваться в нашем случае mutex_v .
xTicksToWait: это максимальное время, в течение которого задача будет ждать в заблокированном состоянии, пока мьютекс станет доступным. В нашем проекте мы установим для xTicksToWait значение portMAX_DELAY, чтобы задача task_1 находилась в заблокированном состоянии бесконечно, пока не станет доступен mutex_v .
Предоставление мьютекса:
После доступа к общему ресурсу задача должна вернуть мьютекс, чтобы другие задачи могли получить к нему доступ. API xSemaphoreGive () используется для возврата Mutex .
Функция xSemaphoreGive () принимает только один аргумент, который является мьютексом, который должен быть указан в нашем случае mutex_v.
Используя вышеуказанные API, давайте реализуем Mutex в коде FreeRTOS с помощью Arduino IDE.
Пояснение к коду мьютекса
Здесь целью этой части является использование последовательного монитора в качестве общего ресурса и двух разных задач для доступа к последовательному монитору для печати какого-либо сообщения.
1. Заголовочные файлы останутся такими же, как семафор.
#include #include
2. Объявите переменную типа SemaphoreHandle_t для хранения значений Mutex.
SemaphoreHandle_t mutex_v;
3. В void setup () инициализируйте последовательный монитор со скоростью 9600 бод и создайте две задачи (Task1 и Task2) с помощью API xTaskCreate () . Затем создайте мьютекс с помощью xSemaphoreCreateMutex (). Создайте задачу с равными приоритетами, а затем попробуйте поиграть с этим числом.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println («Мьютекс не может быть создан»); } xTaskCreate (Task1, «Task 1», 128, NULL, 1, NULL); xTaskCreate (Task2, «Задача 2», 128, NULL, 1, NULL); }
4. Теперь сделайте задачи для Task1 и Task2. В то время как петля функции задачи, перед печатью сообщения на последовательном мониторе мы должны принять мьютекс с помощью xSemaphoreTake () затем выводят сообщение, а затем вернуть мьютекс с помощью xSemaphoreGive (). Тогда дайте немного отсрочки.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println («Привет от Task1»); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Аналогичным образом реализуем функцию Task2 с задержкой 500 мс.
5. Пустой цикл () останется пустым.
Теперь загрузите этот код в Arduino UNO и откройте монитор последовательного порта.
Вы увидите, что сообщения печатаются из task1 и task2.
Чтобы проверить работу Mutex, просто прокомментируйте xSemaphoreGive (mutex_v); от любой задачи. Вы можете видеть, что программа зависает на последнем сообщении печати .
Вот как можно реализовать семафор и мьютекс в FreeRTOS с Arduino. Для получения дополнительной информации о Semaphore и Mutex вы можете посетить официальную документацию FreeRTOS.
Полные коды и видео для Semaphore и Mutes приведены ниже.