RTOS ChibiOS/RT – операционная система для микроконтроллеров.
RTOS ChibiOS/RT:
CHIBIOS/RT – операционная система для микроконтроллеров
CHIBIOS/RT – операционная система для микроконтроллеров часть 2.
Очередь ввода/вывода
Очереди ввода/вывода являются общей системой кольцевых буферов в специализированных связях байт-ориентированного ввода/вывода.
Глобальные параметры
CH_CFG_USE_QUEUES | Макрос включает API очереди ввода/вывода в ChibiOS/RT. |
Описание
Очереди ввода/вывода предназначены для подключения ISR (прерывания) и потоков. Этот объект не пригоден для прямого использования, за исключением получения более специализированного объекта (рисунок 1).
Рисунок 1. Очередь ввода/вывода.
Очереди ввода/вывода имеют механизм уведомлений, который информирует ISR стороне, когда данные были считаны или записаны со стороны потока. Уведомление обратного вызова может быть использовано для повторного включения прерывания или возобновления операций на уровне hardware.
Характер очереди – асимметричный, ISR сторона всегда неблокирующая, это означает, что попытка записать в заполненную входную очередь или чтение из пустой очереди вывода завершается кодом ошибки, нет ожидания исполнения. С другой стороны, поток может быть:
- Заблокирован. Поток приостанавливаются до тех пор, пока заданный объем данных был считан из входной очереди или записан в выходную очередь.
- Заблокирован с таймаутом. Операция блокируется по спецификации таймаута.
- Не блокируемый. В операции чтения или записи только передается объемы данных, которые могут быть переданы немедленно, без блокирования.
Следующие операции определены для всех очередей ввода/вывода.
Получение размера очереди
Операция размера, возвращает размер кольцевого буфера, выделенного в очереди.
Сброс очереди
Операция сброса очищает очередь потока, ожидающие уведомление посредством сообщения MSG_RESET.
Очередь ввода
Объект очереди ввода является кольцевой буфер для записи ISR и для чтения со стороны потока (рисунок 2).
Рисунок 2. Очередь ввода.
Следующие операции определены для очередей ввода.
Получение пространства
Операция пространство возвращает количество заполненных слотов в очереди ввода.
Ввод данных
Операция ввода данных выполняется для ISR и для записи байта в очередь, если очередь заполнена, то операция не выполняется.
Получение данных
Операция получения данных возвращает один байт, взятый из вводной очереди, ожидание в случае необходимости. Может быть указан таймаут.
Чтение данных
Операция считывания получает указанный объем данных из вводной очереди, ожидание в случае необходимости. Может быть указан таймаут.
Очередь вывода
Очередь вывода, кольцевой буфер записывает со стороны потока и чтение с стороны ISR (рисунок 3).
Рисунок 3. Очередь вывода.
Следующие операции определены для очередей вывода.
Получение пространства
Операция пространство возвращает количество пустых слотов в очереди вывода.
Получение данных
Операция получения данных осуществляется для ISR и для чтения байта из очереди, если очередь пуста, то операция не выполняется.
Ввод данных
Операция ввода данных пишет один байт в очереди вывода, ожидание в случае необходимости. Может быть указан таймаут.
Чтение данных
Операция записи посылает указанный объем данных в очередь вывода, ожидание в случае необходимости. Может быть указан таймаут.
Таблица 1. API очереди ввода/вывода.
chQSizeI() | Возвращает размер очереди |
chQSpaceI() | Возвращает заполненное/пустое пространство |
chQGetLinkX() | Возвращает пользовательские данные, связанные с очередью. |
INPUTQUEUE_DECL() | Инициализация статической области ввода |
chIQObjectInit() | Инициализирует объект почтовый ящик (mail box) типа input_queue_t |
chIQResetI() | Сброс очереди ввода |
chIQPutI() | Ставит байт в очередь ввода (вариант I-класса) |
chIQGetTimeout() | Получает байт из очереди ввода с таймаутом |
chIQReadTimeout() | Читает блок данных из очереди ввода с таймаутом |
OUTPUTQUEUE_DECL() | Инициализация статической области вывода |
chOQObjectInit() | Инициализирует объект почтовый ящик (mail box) типа output_queue_t |
chOQResetI() | Сбрасывает очередь вывода |
chOQGetI() | Получает байт из очереди вывода (вариант I-класса) |
chOQPutTimeout() | Ставим байт в очередь вывода с таймаутом |
ReadTimeout() | Записывает блок данных в очередь вывода с таймаутом |
/* Пример */ /* Подключаем заголовочные файлы */ #include "ch.h" #include "hal.h" #define QUEUES_SIZE 30 static input_queue_t iq; static output_queue_t oq; static uint8_t buffIQ[QUEUES_SIZE]; static uint8_t buffOQ[QUEUES_SIZE]; /* Callback для обработки входной очереди */ void callbackbuffIQ(io_queue_t *qp){ (void)qp; } /* Callback для обработки выходной очереди */ void callbackbuffOQ(output_queue_t *qp){ (void)qp; } /* Выделаем рабочее пространство для задачи 1*/ static THD_WORKING_AREA(waThread1, 128); /* Выделаем рабочее пространство для задачи 2*/ static THD_WORKING_AREA(waThread2, 128); static bool stIQ = FALSE; static bool stOQ = FALSE; static msg_t msgIQ = 0; static msg_t msgOQ = 0; static THD_FUNCTION(Thread1, arg) { (void)arg; chRegSetThreadName("Thread1"); while (true) { /* Проверяем состояние очереди */ /* Если очередь пустая - возвращаем FALSE, иначе TRUE */ stIQ = chOQIsEmptyI(&iq); stOQ = chOQIsEmptyI(&oq); /* Добавляем элементы в очередь для ввода */ /* Переходим в состояние блокировки ядра */ chSysLock(); msgIQ = chIQPutI(&iq, 'e'); msgIQ = chIQPutI(&iq, 'l'); msgIQ = chIQPutI(&iq, 'e'); msgIQ = chIQPutI(&iq, 'c'); msgIQ = chIQPutI(&iq, 't'); msgIQ = chIQPutI(&iq, 'r'); msgIQ = chIQPutI(&iq, 'o'); msgIQ = chIQPutI(&iq, 'n'); msgIQ = chIQPutI(&iq, 'i'); msgIQ = chIQPutI(&iq, 'c'); msgIQ = chIQPutI(&iq, 'a'); /* Разблокировка ядра */ chSysUnlock(); chSysLock(); /* Читаем выходную очередь */ msgOQ = chOQGetI(&oq); chSysUnlock(); chThdSleepMilliseconds(100); } } static THD_FUNCTION(Thread2, arg) { (void)arg; chRegSetThreadName("Thread2"); while (true) { /* Время ожидания - бесконечное (TIME_INFINITE), ждем пока данные поместятся в очередь */ chOQWriteTimeout(&oq, (uint8_t *)"info", 5, TIME_INFINITE); chThdSleepMilliseconds(500); } } int main(void) { /* Инициализация HAL и ядра ОСРВ */ halInit(); chSysInit(); /* Инициализация очереди входных данных */ chIQObjectInit(&iq, buffIQ, QUEUES_SIZE, callbackbuffIQ, NULL); /* Инициализация очереди выходных данных */ chIQObjectInit(&oq, buffOQ, QUEUES_SIZE, callbackbuffOQ, NULL); chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 10, Thread1, NULL); chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO + 10, Thread2, NULL); while (true) {} }
Семафоры
Семафоры являются одним из наиболее распространенных функций во встраиваемых ОСРВ, конечно, есть различия в внутренней реализации и некоторых деталях.
В ChibiOS/RT реализовано два варианта семафоров:
- Счетные семафоры.
- Бинарные семафоры.
Пользователь может использовать оба типа семафора без ограничений.
Глобальные параметры
Семафоры используют следующие глобальные параметры:
CH_CFG_USE_SEMAPHORES | Этот переключатель позволяет использовать API семафора в ядре (макрос применяется для счетных и бинарных семафоров). |
CH_CFG_USE_SEMAPHORES_PRIORITY | Если данный параметр включен, то потоки помещаются в очередь по приоритету, а не в порядке FIFO, применяется для семафоров. По умолчанию данный параметр отключен. |
Счетные семафоры
Счетные семафоры впервые были определены голландским ученым Эдсгером Дейкстрой в начале 60 -х годов 20 века.
Счетные семафоры иметь внутренний переменный знаковый счетчик, значение переменной характеризует внутреннее состояние семафора.
Значения счетчика равно:
- N < 0. Семафор взят и есть -N потоков в очереди.
- N = 0. Семафор взят, но нет потоков в очереди.
- N > 0. Семафор не взят и потоки могут быть приняты за N раз.
В основном счетчик семафора защищает ресурс, объект ограничивающий количество потоков, которые могут войти в заданный участок кода. Для счетных семафоров характерно следующие поведение, охарактеризованное на диаграмме состояний (Рисунок 4).
Рисунок 4. Диаграмма состояний счетных семафоров.
Расширения
В ChibiOS/RT реализована расширенная версия семафора Дейкстры, есть несколько усовершенствований по сравнению с первоначальным определением:
- Операция сброса. В дополнение к классической версии семафора Дейкстры, операция сигнала и ожидания, добавлена новая операция сброса. Эта операция способна сбросить счетчик семафора в любые неотрицательные значения, все ожидающие потоки берутся из очереди, если таковые имеются.
- Таймауты. Операция ожидания имеет необязательный параметр таймаута, находясь в очереди потока может быть удалена из очереди, если сигнал сброса не выполняется в течение заданного интервала времени.
- Сообщения. Ожидание кода сообщения на операцию, указывает на место, где поток был обозначен:
- Поток занял ресурс.
- Поток был в очереди, и операция сброса была выполнена на семафоре.
- Поток был в очереди и истекло время ожидания.
- Атомарный сигнал и ожидание. Операция выполняется на семафоре, и операция ожидания производится на другом семафоре, выполнение операции атомарно.
Таблица 2. API счетных семафоров.
SEMAPHORE_DECL() | Инициализация статического семафора |
chSemObjectInit() | Инициализация объекта семафор типа semaphore_t |
chSemWait() | Выполняет операцию ожидания на семафоре |
chSemWaitTimeout() | Выполняет операцию ожидания на семафоре со спецификацией таймаута |
chSemWaitTimeoutS() | Выполняет операцию ожидания на семафоре со спецификацией таймаута (вариант S-класса) |
chSemSignal() | Выполняет операцию сигнал на семафоре |
chSemSignalI() | Выполняет операцию для сигнала на семафоре (вариант I-класса) |
chSemReset() | Выполняет операцию сброс семафора |
chSemResetI() | Выполняет операцию сброс семафора (вариант I-класса) |
chSemAddCounterI() | Добавить константу к счетчику семафора, потоки берутся из очереди (вариант I- класса) |
chSemSignalWait() | Атомарно выполняется сигнал семафора и ждем другого семафора |
chSemGetCounterI() | Вернуть текущее значение счетчика семафора (вариант I- класса) |
chSemFastWaitI() | Ультрабыстрая версия удобного ожидания в тех условиях, где известен счетчик больше нуля, операция – чисто декремент (вариант I- класса) |
chSemFastSignalI() | Ультрабыстрая версия удобного выполнения сигналов в тех условиях, где известен неотрицательный счетчик, операция – чисто инкремент (вариант I- класса) |
Бинарные семафоры
В ChibiOS/RT бинарные семафоров построены на основе счетных семафоров. Бинарные семафоры используя очень эффективный встроенный код. Счетчик бинарных семафоров не позволяют считать один за одним, таким образом, семафор имеет только два возможных состояния.
- Захвачен. Когда значение счетчика нуль или меньше нуля. Отрицательные числа указывает на число потоков в очереди в двоичном семафоре.
- Не захвачен. Когда значение счетчика равно одному.
Для бинарных семафоров характерно следующие поведение, охарактеризованное на диаграмме состояний (Рисунок 5).
Рисунок 5. Диаграмма состояний бинарных семафоров.
Расширения
В ChibiOS/RT реализована расширенная версия бинарного семафора, есть несколько усовершенствований по сравнению с первоначальным определением:
- Операция сброса. В дополнение к классической версии семафора, операция сигнала и ожидания, добавлена новая операция сброса. Эта операция способна сбросить семафор, семафор находится в состоянии захвачен или не захвачен (сводный), все ожидающие потоки берутся из очереди, если таковые имеются.
- Таймауты. Операция ожидания имеет необязательный параметр таймаута, находясь в очереди потока может быть удалена из очереди, если сигнал сброса не выполняется в течение заданного интервала времени.
- Сообщения. Ожидание кода сообщения на операцию, указывает на место, где поток был обозначен:
- Поток занял ресурс.
- Поток был в очереди, и операция сброса была выполнена на семафоре.
- Поток был в очереди и истекло время ожидания.
Таблица 3. API бинарных семафоров.
SEMAPHORE_DECL() | Инициализация статического семафора |
chBSemObjectInit() | Инициализация объекта семафор типа binary_semaphore_t |
chBSemWait() | Выполняет операцию ожидания на семафоре |
chBSemWaitS() | Выполняет операцию ожидания на семафоре (вариант S-класса) |
chBSemWaitTimeout() | Выполняет операцию ожидания на семафоре со спецификацией таймаута |
chBSemWaitTimeoutS() | Выполняет операцию ожидания на семафоре со спецификацией таймаута (вариант S-класса) |
chBSemSignal() | Выполняет операцию сигнал на семафоре |
chBSemSignalI() | Выполняет операцию для сигнала на семафоре (вариант I-класса) |
chBSemReset() | Выполняет операцию сброс семафора |
chBSemResetI() | Выполняет операцию сброс семафора (вариант I-класса) |
chBSemGetStateI() | Возвращает 1 (true), если семафор захвачен (вариант I-класса) |
/* Пример */ /* Подключаем заголовочные файлы */ #include "ch.h" #include "hal.h" /* Создаем бинарный семафор */ static binary_semaphore_t button_led_bsem; /* Выделаем рабочее пространство для задачи 1*/ static THD_WORKING_AREA(waThread1, 128); /* Задача проверяет состояние бинарного семафора */ static THD_FUNCTION(Thread1, arg) { (void)arg; /* Установка имени текущего потока "blinker1" */ chRegSetThreadName("blinker"); while (true) { msg_t msg = chBSemWait(&button_led_bsem); if (msg == MSG_OK) { chThdSleepMilliseconds(500); continue; } /* Начинаем мигать светодиодом, если бинарный семафор не захвачен, c период 500 мс */ palClearPad(GPIOG, GPIOG_LED3_GREEN); chThdSleepMilliseconds(500); palSetPad(GPIOG, GPIOG_LED3_GREEN); chThdSleepMilliseconds(500); } } /* Выделаем рабочее пространство для задачи 2*/ static THD_WORKING_AREA(waThread2, 128); /* Захватываем бинарный семафор */ static THD_FUNCTION(Thread2, arg) { (void)arg; /* Установка имени текущего потока "button click" */ chRegSetThreadName("button click"); while (true) { bool status = chBSemGetStateI(&button_led_bsem); /* Если chBSemGetStateI вернет статус TRUE, тогда мигаем красным светодиодом */ /* chBSemGetStateI возвращает текущее состояние бинарного семафор, если не занят тогда TRUE, * в противном случае FALSE. */ if(status == TRUE){ palClearPad(GPIOG, GPIOG_LED4_RED); chThdSleepMilliseconds(500); palSetPad(GPIOG, GPIOG_LED4_RED); chThdSleepMilliseconds(500); } if(palReadPad(GPIOA, GPIOA_BUTTON) == PAL_HIGH){ chBSemSignal(&button_led_bsem); } else{ chBSemReset(&button_led_bsem, true); } } } int main(void) { /* Инициализация HAL и ядра ОСРВ */ halInit(); chSysInit(); /* Инициализация объекта семафор типа binary_semaphore_t */ chBSemObjectInit(&button_led_bsem, false); /* Создаем два потока для обработки задач */ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO, Thread2, NULL); while (true) {} }