Старт ARM. RTOS часть 4-ая. Семафоры.

Всем привет, продолжаем дальше на практике изучать FreeRTOS на базе STM32F429i – DISCO. Сегодня на повестки дня семафоры.
Семафор — объект, ограничивающий количество потоков, которые могут войти в заданный участок кода. Определение введено Эдсгером Дейкстрой. Семафоры используются при передаче данных через разделяемую память.
Семафор — это объект, с которым можно выполнить три операции.

init(n):
счётчик := n
enter():
ждать пока счётчик станет больше 0; после этого уменьшить счётчик на единицу.
leave():
увеличить счётчик на единицу.

Предположим, что есть такой участок кода:

semaphore.init(5);
// .....
// .....
void DoSomething()
{
    semaphore.enter();
    // .......
    semaphore.leave();
}

Тогда не более пяти потоков могут одновременно выполнять функцию DoSomething().
В более сложных семафорах может использоваться очередь; при этом потоки, ожидающие освобождения семафора, будут проходить через семафор именно в том порядке, в котором они вызывали enter().
Применение семафоров
Вот некоторые из проблем, которые могут решать семафоры.
запрет одновременного выполнения заданных участков кода;
поочерёдный доступ к критическому ресурсу (важному ресурсу, для которого невозможен (или нежелателен) одновременный доступ).
Следующий пример показывает, как наладить поочерёдный доступ к консоли.

semaphore.init(1);
 // Поток 1:
semaphore.enter();
cout < < "Состояние массива: ";
for (int i=0; i<n; i++)
    cout << a[i] << ' ';
cout << '\n';
semaphore.leave();
 // Поток 2:
semaphore.enter();
cout << "Нажато Esc.\n";
semaphore.leave();

Этот код поможет предотвратить появление вывода наподобие

Состояние массива: 1 2 3 Нажато Esc.
4 5 6

Проблемы семафоров
Во-первых, можно написать программу с «утечкой семафора», вызвав enter() и забыв вызвать leave(). Реже встречаются ошибки, когда дважды вызывается leave().
Во-вторых, семафоры чреваты взаимной блокировкой потоков. В частности, опасен такой код:

// Поток 1:
semaphore1.enter();
semaphore2.enter();
// ...
semaphore2.leave();
semaphore1.leave();
 // Поток 2:
semaphore2.enter();
semaphore1.enter();
// ...
semaphore1.leave();
semaphore2.leave();

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

#include <assert.h>
#define configASSERT(x) assert(x)
#define LONG_TIME 0xffff
xSemaphoreHandle xBinarySamaphore;

extern void interrupt_button(void){
	static portBASE_TYPE xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;	
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_SET);	
	xSemaphoreGiveFromISR(xBinarySamaphore, &xHigherPriorityTaskWoken);
	if( xHigherPriorityTaskWoken == pdTRUE )
  {
			portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
	}
}

void SystemClock_Config(void);
static void StartThread(void const * argument);
static void MX_GPIO_Init(void);

typedef struct TaskParam_t{
	signed long int period;
} Task;
Task p1;

void vTask1(void *pvParameters){	
	Task *task_period;
	task_period = (Task *)pvParameters;
		for(;;){
			HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_RESET);
			xSemaphoreTake( xBinarySamaphore, LONG_TIME );
			vTaskDelay(task_period->period);
	}	
	vTaskDelete(NULL);
}

int main(void)
{
	p1.period = 100;
	vSemaphoreCreateBinary(xBinarySamaphore);
	HAL_Init();
  SystemClock_Config();
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  MX_GPIO_Init();
	xTaskCreate(vTask1, (signed char *) "LED_GREEN", configMINIMAL_STACK_SIZE + 256, (void *) &p1, 1, NULL);
	vTaskStartScheduler();
	for(;;);
} 

С семафорами на сегодня все! В следующий раз разберем пример для мьютекса. 🙂
Проект freeRTOS_semaphore.7z