Используем светофоры в системе FreeRTOS на базе платы Arduino Nano в среде программирования Arduino IDE.

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

Существует два вида семафоров:

  1. Бинарные семафоры.
  2. Счетные семафоры.

Бинарные семафоры – могут принимать значения 0 и 1. Пока одна задача выполняется, она выдает значение очереди 0 (красный цвет), как только она выполнится, она дает номер очереди 1 (зеленый цвет) тем самым разрешает работу второй задачи.  После выполнения второй задачи, мы выставляем опять  0 очередь, и переходим в режим ожидания когда снова появится разрешение.

Данный подход хорошо подходит для синхронизации между задачами или для синхронизации между задачей и прерыванием.

Счетный семафор – может принимать значение больше 1. данный семафор используется для счета событий.  То есть обработчик события будет каждый раз уменьшать значение семафора при появлении данного события, а обработчик будет увеличивать значение светофора при каждом выполнении.

Данный подход подходит, когда нам надо знать разницу между числом событий которые случились и числом событий которые были выполнены (обработаны).

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

Программный код:

#include <Arduino_FreeRTOS.h>
#include <semphr.h>

long d_time = 150;
volatile unsigned long last_micros;

SemaphoreHandle_t interruptSemaphore;

void setup() {
pinMode(2, INPUT_PULLUP);
xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL );
xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL );
interruptSemaphore = xSemaphoreCreateBinary();
if (interruptSemaphore != NULL) {
attachInterrupt(digitalPinToInterrupt(2), Interrupt, LOW);
}
}

void loop() {}

void interruptHandler() {
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}

void TaskLed(void *pvParameters)
{
(void) pvParameters;
pinMode(8, OUTPUT);
for (;;) {
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) {
digitalWrite(8, !digitalRead(8));
}
}
}
void TaskBlink(void *pvParameters)
{
(void) pvParameters;
pinMode(9, OUTPUT);
for (;;) {
digitalWrite(9, HIGH);
vTaskDelay(500 / portTICK_PERIOD_MS);
digitalWrite(9, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void Interrupt() {
if((long)(micros() - last_micros) >= d_time * 1000) {
interruptHandler();
last_micros = micros();
}
}

Разберем наш код:

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

SemaphoreHandle_t interruptSemaphore;
interruptSemaphore = xSemaphoreCreateBinary();

Если семафор удачно создался, мы настраиваем прерывание по входу:

attachInterrupt(digitalPinToInterrupt(2), Interrupt, LOW);

digitalPinToInterrupt(2) — указываем, что прерывание по 2 ножке.

Interrupt — функция которая будет запускаться при срабатывании прерывания.

LOW — прерывание произойдет при переходе с высокого уровня в низкое.

В функции interruptHandler() мы будем вызывать API функцию xSemaphoreGiveFromISR().

void interruptHandler() {
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}

 

Рассмотрим некоторые функции для работы с светофором:

  1. xSemaphoreGive() — эта API функция использует один аргумент, —  имя переменной семафора. Ее можно вызвать из любой  задачи которую необходимо синхронизировать.
  2. xSemaphoreGiveFromISR() –эта функция защищенная от прерываний, аналог функции xSemaphoreGive(). Если необходимо синхронизировать функцию обработки прерывания (ISR) и обычную задачу используют функцию xSemaphoreGiveFromISR() вызывая ее из функции обработки прерывания (ISR function).

Для того, чтобы принять (считать) семафор, необходимо использовать API функцию xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait ), где:

xSemaphore: имя семафора.

xTicksToWait: максимальное время, которое задача будет ждать в состоянии блокировки (Blocked state) до тех пор, пока семафор не станет доступным. При установке  параметра xTicksToWait в значение portMAX_DELAY задача будет бесконечно долго ждать разрешения выхода с блокировки.