Компоненты и технологии • №2 '2011 компоненты


КОМПОНЕНТЫ И ТЕХНОЛОГИИ • № 7 '2011



Pdf көрінісі
бет70/129
Дата28.09.2023
өлшемі4.1 Mb.
#478975
1   ...   66   67   68   69   70   71   72   73   ...   129
Kurniz

30
КОМПОНЕНТЫ И ТЕХНОЛОГИИ • № 7 '2011
микроконтроллеры
3. pxTaskWoken — значение *pxTaskWoken 
устанавливается равным pdTRUE, если су-
ществует задача, которая «хочет» записать 
данные в очередь, и приоритет у нее выше, 
чем у задачи, выполнение которой прервало 
прерывание. Если таковой задачи нет, то зна-
чение *pxTaskWoken остается неизменным. 
Проанализировав значение *pxTaskWoken 
после выполнения xQueueReceiveFromISR()
можно сделать вывод о необходимости 
принудительного переключения контекста 
в конце обработчика прерывания. В этом 
случае управление сразу перейдет разбло-
кированной высокоприоритетной задаче.
4. Возвращаемое значение — может прини-
мать 2 значения:
pdTRUE — означает, что данные успеш-
но прочитаны из очереди.
pdFALSE — означает, что данные не про-
читаны, так как очередь пуста.
Следует обратить внимание, что в отличие 
от версий API-функций для работы с очередя-
ми, предназначенными для вызова из тела за-
дачи, описанные выше API-функции не име-
ют параметра portTickType xTicksToWait
который задает время ожидания задачи в бло-
кированном состоянии. Что и понятно, так 
как обработчик прерывания — это не задача, 
и он не может переходить в блокированное 
состояние. Поэтому если чтение/запись из/в 
очередь невозможно выполнить внутри об-
работчика прерывания, то соответствующая 
API-функция вернет управление сразу же.
Эффективное
использование очередей
Б
óльшая часть демонстрационных про-
ектов из дистрибутива FreeRTOS содержит 
пример работы с очередями, в котором оче-
редь используется для передачи каждого от-
дельного символа, полученного от универ-
сального асинхронного приемопередатчика 
(UART), где символ записывается в очередь 
внутри обработчика прерывания, а считыва-
ется из нее в теле задачи.
Передача сообщения побайтно при помощи 
очереди — это очень неэффективный метод 
обмена информацией (особенно на высоких 
скоростях передачи) и приводится в демонстра-
ционных проектах лишь для наглядности.
Гораздо эффективнее использовать один 
из следующих подходов:
1. Внутри обработчика прерывания поме-
щать каждый принятый символ в простой 
буфер, а когда сообщение будет принято 
полностью или обнаружится окончание 
передачи, использовать двоичный семафор 
для разблокировки задачи-обработчика, 
которая произведет интерпретацию при-
нятого сообщения.
2. Интерпретировать сообщение внутри об-
работчика прерывания, а очередь использо-
вать для передачи интерпретированной ко-
манды (как показано на рис. 5, КиТ № 6`2011,
стр. 102). Такой подход допускается, если 
интерпретация не содержит сложных алго-
ритмов и занимает немного процессорного 
времени.
Рассмотрим учебную программу № 2, в ко-
торой продемонстрировано применение 
API-функций xQueueSendToBackFromISR() 
и xQueueReceiveFromISR() внутри обработ-
чика прерываний. В программе реализована 
задача — генератор чисел, которая отвечает 
за генерацию последовательности целых чи-
сел. Целые числа по 5 штук помещаются в оче-
редь № 1, после чего происходит программное 
прерывание (для простоты оно генерируется 
из тела задачи — генератора чисел). Внутри 
обработчика прерывания происходит чте-
ние числа из очереди № 1 с помощью API-
функции xQueueReceiveFromISR(). Далее это 
число преобразуется в указатель на строку, ко-
торый помещается в очередь № 2 с помощью 
API-функции xQueueSendToBackFromISR()
Задача-принтер считывает указатели из очере-
ди № 2 и выводит соответствующие им строки 
на экран (рис. 10).
Текст учебной программы № 2:
#include
#include
#include
#include
#include “FreeRTOS.h”
#include “task.h”
#include “queue.h”
#include “portasm.h”
/* Дескрипторы очередей – глобальные переменные */
xQueueHandle xIntegerQueue;
xQueueHandle xStringQueue;
/*-----------------------------------------------------------*/
/* Периодическая задача — генератор чисел */
static void vIntegerGenerator(void *pvParameters) {
portTickType xLastExecutionTime;
unsigned portLONG ulValueToSend = 0;
int i;
/* Переменная xLastExecutionTime нуждается в инициализации 
текущим значением счетчика квантов.
Это единственный случай, когда ее значение задается явно.
В дальнейшем ее значение будет автоматически 
модифицироваться API-функцией vTaskDelayUntil(). */
xLastExecutionTime = xTaskGetTickCount();
for (;;) {
/* Это периодическая задача. Период выполнения – 200 мс. */
vTaskDelayUntil(&xLastExecutionTime, 200 / portTICK_RATE_MS);
/* Отправить в очередь № 1 5 чисел от 0 до 4. Числа будут 
считаны из очереди в обработчике прерывания.
Обработчик прерывания всегда опустошает очередь, поэтому 
запись 5 элементов будет всегда возможна – в переходе 
в блокированное состояние нет необходимости */
for (i = 0; i < 5; i++) {
xQueueSendToBack(xIntegerQueue, &ulValueToSend, 0);
ulValueToSend++;
}
/* Принудительно вызвать прерывание. Отобразить 
сообщение до его вызова и после. */
puts(“Generator task - About to generate an interrupt.”);
__asm {int 0x82} /* Эта инструкция сгенерирует прерывание. */
puts(“Generator task - Interrupt generated.\r\n”);
}
}
/*-----------------------------------------------------------*/
/* Обработчик прерывания */
static void __interrupt __far vExampleInterruptHandler( void )
{
static portBASE_TYPE xHigherPriorityTaskWoken;
static unsigned long ulReceivedNumber;
/* Массив строк определен как static, значит, память для его 
размещения выделяется как
для глобальной переменной (он хранится не в стеке). */
static const char *pcStrings[] =
{
“String 0”,
“String 1”,
“String 2”,
“String 3”
};
/* Аргумент API-функции xQueueReceiveFromISR(), который 
устанавливается в pdTRUE, если операция с очередью 
разблокирует более высокоприоритетную задачу.
Перед вызовом xQueueReceiveFromISR() должен 
принудительно устанавливаться в pdFALSE */
xHigherPriorityTaskWoken = pdFALSE;
/* Считывать из очереди числа, пока та не станет пустой. */
while( xQueueReceiveFromISR( xIntegerQueue,
&ulReceivedNumber,
&xHigherPriorityTaskWoken ) != errQUEUE_EMPTY )
{
/* Обнулить в числе все биты, кроме последних двух.
Таким образом, полученное число будет принимать 
значения от 0 до 3. Использовать полученное число 
как индекс в массиве строк. Получить таким образом 
указатель на строку, который передать в очередь № 2 */
ulReceivedNumber &= 0x03;
xQueueSendToBackFromISR( xStringQueue,
&pcStrings[ ulReceivedNumber ],
&xHigherPriorityTaskWoken );
}
/* Проверить, не разблокировалась ли более высокоприоритетная 
задача при записи в очередь. Если да, то выполнить 
принудительное переключение контекста. */
if( xHigherPriorityTaskWoken == pdTRUE )
{
/* Макрос, выполняющий переключение контекста.
На других платформах имя макроса может быть другое! */
portSWITCH_CONTEXT();
}
}
/*-----------------------------------------------------------*/
/* Задача-принтер. */
static void vStringPrinter(void *pvParameters) {
char *pcString;
/* Бесконечный цикл */
for (;;) {
/* Прочитать очередной указатель на строку из очереди № 2.
Находится в блокированном состоянии сколь угодно долго, 
пока очередь № 2 пуста. */
xQueueReceive(xStringQueue, &pcString, portMAX_DELAY);
/* Вывести строку, на которую ссылается указатель на дисплей. */
puts(pcString);
}
}
/*-----------------------------------------------------------*/
/* Точка входа. С функции main() начнется выполнение 
программы. */
int main(void) {
/* Как и другие объекты ядра, очереди необходимо создать 
до первого их использования. Очередь xIntegerQueue будет 
хранить переменные типа unsigned long. Очередь 


Достарыңызбен бөлісу:
1   ...   66   67   68   69   70   71   72   73   ...   129




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет