Многие вещи делаются совсем не так, как в более привычных системах (вернее, в их отсутствии -- никогда раньше никакие РТОСы не использовал).
Возникла вполне практическая задачка
1) Допустим, есть некая железяка, которую все хотят использовать. В одну единицу времени железяка может обслуживать ровно один запрос. Если запросов больше одного, то каждый следующий должен подождать выполнения предыдущего -- это очень обычная ситуация, встречается сплошь и рядом.
2) Допустим, есть как минимум два процесса, которые хотят иметь доступ к этой железяке. И ещё предположим, что нет требования безусловной строгости выполнения запроса на доступ ровно в тот момент, когда зачесалось. И это, опять же, естественное ограничение - например, если мы подчинимся требованию "СРОЧНО ПРЕДОСТАВИТЬ" в случае какого-нибудь последовательного порта, то на его выходе будет каша из символов, переданных одновременно двумя процессами. Т.е. нужна некоторая более крупногабаритная гранулярность, неделимая порция данных. И пока одна порция выводится, второй процесс (в самом простейшем случае) должен подождать. Тут, канешн, могут быть разные другие тонкости - бывает так, что нам вообще не важен результат выполнения запроса, а значит, ждать вовсе не нужно его выполнения. Например, если мы просто вываливаем строчки на экран, то их достаточно напихать в какой-нибудь отдельный буфер, из которого они будут постепенно изыматься по мере сил. Но если результат выполнения операции безусловно важен для процесса, запросившего её, то надо терпеливо дожидаться завершения.
Как одно соединить с другим? Придумал вот что.
Отдельный процесс, который имеет абсолютный доступ к этой железяке - и больше никто. Назовём его "СЕРВЕР". Он получает запросы, обрабатывает их и шлёт ответы.
И пара процессов, каждому из которых позарез нужно к этой железяке обратиться и что-то от неё специфическое получить. Назовём их "КЛИЕНТ1" и "КЛИЕНТ2".
Предположим, что эта железяка - это лампочка.
Первый клиент каждую секунду хочет включить её ровно на так же одну секунду и чтобы ему оттудова пришла буква "А". А второй клиент хочет, чтобы лампочка включилась на 100 миллисекунд, причём чтобы это происходило так же каждые 100 миллисекунд, но он ждёт, что ему придёт буква "Б".
Делаем вот что:
1) выделяем по бинарному семафору на каждый из клиентов:
xSema1 = xSemaphoreCreateBinary(); xSema2 = xSemaphoreCreateBinary();
2) создаём очередь из структур такого вида:
typedef struct { SemaphoreHandle_t semaphore; /* какой семафор надо будет дёрнуть после обработки данных */ uint8_t Request; uint8_t Response; } req_t; .... xQueue = xQueueCreate(10, sizeof(req_t**));
Семафор внутри такого объекта нужен для того, чтобы сервер мог разблокировать именно тот процесс, который прислал этот конкретный запрос. Я не могу пока придумать более изящный способ (почти наверняка есть что-то готовое, а я просто тупоголовый нуб).
3) внутри каждого из клиентских процессов заводим своё собственное казино, в первом клиенте так:
req_t req; req_t *req_p = &req; req.semaphore = xSema1; // используем семафор 1 req.Request = '1';
и во втором, аналогично:
req_t req; req_t *req_p = &req; req.semaphore = xSema2; // используем семафор 2 req.Request = '2';
Указатель на указатель нужен для того, чтобы
С точки зрения всяких там секьюрностей и безопасностей это ужасно - глобальные переменные и обнажённые указатели на приватные данные, но если существует какой-то другой способ, то я о нём пока что не знаю.
Каждый из клиентов делает нечто такое:
xQueueSend(xQueue, &req_p, portMAX_DELAY); // записываем наш запрос в очередь xSemaphoreTake(xSema2, portMAX_DELAY); // берём семафор. Блокируемся здесь, пока идёт обработка! vTaskDelay(100); /* задержка в тиках ОС = 100мс*/
Итак, сервер заводит себе переменную типа "указатель на структуру" и сидит ждёт, пока в очереди не появится очередной указатель на указатель на структуру, всё просто:
req_t *req_p; ... xQueueReceive (xQueue, &req_p, portMAX_DELAY); switch (req_p->Request) { ... }
Так же сервер мигает лампочкой на специфичный для каждого типа запроса период времени. Оттуда же он вытаскивает семафоры и отдаёт их:
xSemaphoreGive (req_p->semaphore); /* отдаём семафор */
..позволяя клиентам продолжать свою важную работу.
Скомпилировал, запустил. Вижу, что лампочка включается на 1 секунду, затем моргает 5 раз (вкл на 100 мс/выкл на 100 мс), затем снова включается на 1 секунду. Как и ожидалось -- клиенты ничего не знают о существовании друг друга, но эдак вот синхронизируются через общий сервер.
А зачем такая кутерьма? А затем, что модуль, способный обслуживать только один запрос за раз, это типичное периферийное устройство типичного микроконтроллера, исключения из этого правила очень редкие, их почти что и нет вообще. Т.е. я щас накидал хоть и корявенький, но вполне рабочий скелет-драйвер для 99% всех железяк в моём любимом STM32F746.