В данном уроке мы рассмотрим работу светофоров. В светофор представляет собой некий механизм, который позволяет выполнения некой задачи только при наличии разрешения на выполнения данной задачи. Другими словами задача ждет пока будет зеленый цвет на выполнение, если красный — задача не выполняется.
Существует два вида семафоров:
Бинарные семафоры – могут принимать значения 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); }
Рассмотрим некоторые функции для работы с светофором:
Для того, чтобы принять (считать) семафор, необходимо использовать API функцию xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait ), где:
xSemaphore: имя семафора.
xTicksToWait: максимальное время, которое задача будет ждать в состоянии блокировки (Blocked state) до тех пор, пока семафор не станет доступным. При установке параметра xTicksToWait в значение portMAX_DELAY задача будет бесконечно долго ждать разрешения выхода с блокировки.