Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2754
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Запуск FreeRTOS

635

23.5.2. Cемафоры

В конкурентном программировании семафор (semaphore) – это тип данных, используемый для управления доступом с помощью нескольких потоков выполнения к разделяемому ресурсу. Достаточно простая форма семафора представляет собой логическую переменную: состояние переменной используется в качестве условия для управления доступом к ресурсу. Например, если переменная равна False, то поток переводится в состояние «заблокирован», пока эта переменная снова не станет True. Говорят, что семафор был взят потоком, который его получил, то есть потоком, который первым нашел семафор, равный True. Это и в самом деле бинарный семафор (binary semaphore), поскольку он может принимать только два состояния, и во FreeRTOS он реализован как очередь всего лишь с одним элементом. Если очередь пуста, то первый поток, который пытается его получить, помещает значение «флага» в очередь и продолжает свое выполнение; другие потоки не смогут добавлять другие «флаги», пока поток, получивший семафор, не снимет с очереди свой флаг.

Более общей формой семафора является счетный семафор (counting semaphore), который позволяет нескольким потокам получать его. Так же, как бинарные семафоры, реализованные в качестве очередей, длина которых равна единице, счетный семафор может рассматриваться в качестве очередей, длина которых больше единицы. Счетный семафор обычно имеет начальное значение, которое уменьшается каждый раз, когда поток получает его. В то время как бинарные семафоры обычно используются для ограничения одновременного доступа только к одному ресурсу, счетный семафор может использоваться для:

поддержания порядка доступа к пулам разделяемых ресурсов: в этом случае значение счетчика указывает количество доступных ресурсов;

подсчета количества повторяющихся событий: в этом случае поток выполне-

ния (для простоты предположим, что это ISR) выпустит семафор (вызывая увеличение его счетчика), чтобы сообщить другому потоку, что произошло заданное событие (например, данные, поступившие от UART, готовы к обработке); этот поток может затем взять семафор и начать выполнять свои действия; если происходит другое «событие» (поступили новые данные), то ISR снова увеличит семафор, выпустив его; таким образом, поток обработки сможет снова взять семафор и выполнить свои действия.

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

FreeRTOS предоставляет два различных API-интерфейса для управления бинарными и счетными семафорами, в то время как CMSIS-RTOS устанавливает, что семафоры реализуются как счетные семафоры (оставляя мьютексам роль бинарных семафоров). Однако использование счетных семафоров увеличивает кодовую базу FreeRTOS, что может оказать существенное влияние на микроконтроллеры с небольшим объемом Flash-памяти. По этой причине FreeRTOS предоставляет их только в том случае, если макрос

configUSE_COUNTING_SEMAPHORES в файле FreeRTOSConfig.h определен и равен 1. Уровень

CMSIS-RTOS, разработанный ST, способен обнаруживать этот случай, и он использует счетные семафоры FreeRTOS, если они доступны, в противном случае используются

Запуск FreeRTOS

636

бинарные семафоры. В этом случае все параметры, относящиеся к значению счетчика семафора, не имеют смысла.

В уровне CMSIS-RTOS семафоры являются необязательными, и их необходимо включить, установив для макроса osFeature_Semaphore значение 1 в файле cmsis_os.h. В APIинтерфейсе CMSIS-RTOS семафор определяется с помощью макроса osSemaphoreDef(), который просто принимает имя семафора в качестве единственного параметра. Затем семафор эффективно создается с помощью функции

osSemaphoreId osSemaphoreCreate(const osSemaphoreDef_t *semaphore_def, int32_t count);

Как было сказано ранее, count является начальным значением семафора, который не имеет смысла, если configUSE_COUNTING_SEMAPHORES не определен или равен 0. Чтобы получить семафор, мы используем функцию

int32_t osSemaphoreWait(osSemaphoreId semaphore_id, uint32_t millisec);

которая принимает идентификатор семафора и значение тайм-аута (millisec). Если счетчик семафора больше нуля, то поток получает его (уменьшая счетчик) и может продолжить выполнение. В противном случае поток переводится в состояние «заблокирован» на период, равный значению тайм-аута, пока счетчик снова не увеличится. Задав значение osWaitForever, поток будет ждать бесконечно долго. osSemaphoreWait() возвращает osOK, если поток успешно получил семафор, в противном случае она возвращает osErrorOS31. Чтобы выпустить семафор мы используем функцию

osStatus osSemaphoreRelease(osSemaphoreId semaphore_id);

Семафор динамически выделяется ОС при его создании, и его необходимо явно уничтожить с помощью функции

osStatus osSemaphoreDelete(osSemaphoreId semaphore_id);

Как видно из API-интерфейсов, касающихся манипулирования очередями, FreeRTOS предоставляет два отдельных API-интерфейса для управления семафорами из потока или из ISR. Например, функция xSemaphoreTake() используется для получения семафора из потока, в то время как xSemaphoreTakeFromISR() используется для выполнения этой операции из ISR. Уровень CMSIS-RTOS, разработанный ST, предназначен для абстрагирования от этого аспекта.

В следующем примере показано, как использовать семафор в качестве примитива уведомление. Это снова классическое мигающее приложение, но на этот раз задержка blinkThread() устанавливается другим потоком delayThread(), который «разблокирует» мигающий поток, выпуская бинарный семафор.

31 Как видите, osSemaphoreWait() предназначена для возврата int32_t вместо классического значения возврата osStatus. Это связано с тем, что API-интерфейс CMSIS-RTOS устанавливает, что он должен возвращать счетчик семафора после того, как он был декментирован процедурой получения. Однако FreeRTOS не предоставляет эту возможность.

Запуск FreeRTOS

637

Имя файла: src/main-ex4.c

14 osSemaphoreId semid;

15

16int main(void) {

17HAL_Init();

18

 

19

Nucleo_BSP_Init();

20

 

21

RetargetInit(&huart2);

22

 

23osThreadDef(blink, blinkThread, osPriorityNormal, 0, 100);

24osThreadCreate(osThread(blink), NULL);

25

26osThreadDef(delay, delayThread, osPriorityNormal, 0, 100);

27osThreadCreate(osThread(delay), NULL);

28

29osSemaphoreDef(sem);

30semid = osSemaphoreCreate(osSemaphore(sem), 1);

31osSemaphoreWait(semid, osWaitForever);

32

33 osKernelStart();

34

35/* Бесконечный цикл */

36while (1);

37}

38

39void blinkThread(void const *argument) {

40while(1) {

41osSemaphoreWait(semid, osWaitForever);

42HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

43}

44osThreadTerminate(NULL);

45}

46

47void delayThread(void const *argument) {

48while(1) {

49osDelay(500);

50osSemaphoreRelease(semid);

51}

Строки [29:31] определяют и создают бинарный семафор с именем sem: семафор сразу же получается потоком, в результате чего его счетчик становится равным нулю. blinkThread() и delayThread() запланированы, но первый переводится в состояние «заблокирован», как только он достигает вызова osSemaphoreWait(): так как семафор уже «получен», поток будет заменен, пока семафор не будет выпущен потоком delayThread(), который выполняет данную операцию каждые 500 мс. Это заставит светодиод LD2 мигать с частотой 2 Гц.