Перейти к содержанию
    

Переключение задачи по прерыванию.

Как переключаться на другую задачу после прерывания?

 

есть фоновая задача, мигает лампочкой. есть более приоритетная задача для работы с UART. Задача для уарта ждет появления байта в очереди

 

void taskRS232(void *pvParameters)
{
    alt_u8 byte;
    for(;;)
    {
        xQueueReceive(uart232Queue, &byte, portMAX_DELAY);
        handler232();
    }
    vTaskDelete( NULL );
}

 

посылаю в устройство байт. попадаю в прерывание по приему. в конце прерывания проверяю нет ли более приоритетной задачи чем прерванная и если есть то переключаю контекст

 

void UartPC::uartInt(void *context)//прерывание по RS-232 от bawdc
{
    static alt_u8 byte;
    static portBASE_TYPE xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    alt_u16 status = IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
    if(status & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
    {//прерывание по приему
        byte = IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);
        xQueueSendFromISR(uart232Queue, &byte, &xHigherPriorityTaskWoken);
    }
    if(xHigherPriorityTaskWoken == pdTRUE)
        taskYIELD();
}

 

задача ожидавшая появление в очереди uart232Queue байта разблокируется и начинает работать. После обработки ожидает следующий байт в очереди. Почему то попав в ожидание xQueueReceive(uart232Queue, &byte, portMAX_DELAY) после taskYIELD() управление задаче по миганию лампочки не передается. выполняется пустая задача. мигание лампочки в состоянии блокировки.

 

Убрал из обработчика прерывания переключение контекста taskYIELD();. Всё заработало, но медленно. видно что после прерывания задача уарта не сразу получает управление, наверно по тикам. Дальше стал ковырять исходники и нашел vTaskSwitchContext(). Вставил в конце обработчика прерывания vTaskSwitchContext() вместо taskYIELD() - всё заработало как надо. переключение происходит сразу по выходу из обработчика-прерывания.

 

Что за функция vTaskSwitchContext()? Как правильно ею пользоваться? Почему taskYIELD() блокирует прерванную задачу?

 

Как правильно сделать так, чтоб при выходе из обработчика прерывания управление передавалось не прерванной задаче, а задаче готовой к выполнению с самым большим приоритетом?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что за функция vTaskSwitchContext()? Как правильно ею пользоваться? Почему taskYIELD() блокирует прерванную задачу?

Как правильно сделать так, чтоб при выходе из обработчика прерывания управление передавалось не прерванной задаче, а задаче готовой к выполнению с самым большим приоритетом?

taskYIELD можно вызывать только в потоках. В прерываниях нельзя.

 

vTaskSwitchContext() - на основе данных о потоках (приоритеты, блокировки) вычисляет какой поток надо запустить и помещает указатель на него в pxCurrentTCB.

В зависимости от порта ФрииРТОС, вызов vTaskSwitchContext() в обработчике прерывания тоже может быть не корректен. Вызов его осуществляется на уровне абстракции от железа (port.c и portasm.asm), там где скрещивается ФрииРТОС с конкретным железом.

 

посмотрите в файле portmacro.h , там должен быть макрос типа "portEND_SWITCHING_ISR", вот его и вызывайте.

 

P.S. Что за процессор у Вас? Какой порт ФрииРТОС используете?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что за процессор у Вас? Какой порт ФрииРТОС используете?

Nios II, порт ...\FreeRTOS\Source\portable\GCC\NiosII

 

посмотрите в файле portmacro.h , там должен быть макрос типа "portEND_SWITCHING_ISR", вот его и вызывайте.

Ага, спасибо..... нашел вот что:

extern void vTaskSwitchContext( void );
#define portYIELD()                                    asm volatile ( "trap" );
#define portEND_SWITCHING_ISR( xSwitchRequired )     if( xSwitchRequired )     vTaskSwitchContext()

Только что за xSwitchRequired? В принцепе в моем случае я уже проверил что переключение требуется и можно смело вызывать напрямую vTaskSwitchContext(). Хотя наверно элегантнее будет проверять так

void UartPC::uartInt(void *context)//прерывание по RS-232 от bawdc
{
    static alt_u8 byte;
    static portBASE_TYPE xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    alt_u16 status = IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
    if(status & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
    {//прерывание по приему
        byte = IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);
        xQueueSendFromISR(uart232Queue, &byte, &xHigherPriorityTaskWoken);
    }
   portEND_SWITCHING_ISR(xHigherPriorityTaskWoken == pdTRUE);
}

 

ps

taskYIELD можно вызывать только в потоках. В прерываниях нельзя.

Наверно для некоторых платформ нельзя. В статьях КиТ по фрииртос (№7 стр 26) сказано про переключение контекста в прерываниях....

Для принудительного переключения кон-

текста служит API-макрос portSWITCH_

CONTEXT(). Однако для других платформ

имя макроса будет иным, например, для ми-

кроконтроллеров AVR это будет taskYIELD(),

для ARM7 — portYIELD_FROM_ISR().

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Nios II, порт ...\FreeRTOS\Source\portable\GCC\NiosII

С этим портом не работал, точно не скажу.

Хотя наверно элегантнее будет проверять так

...
xQueueSendFromISR(uart232Queue, &byte, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken == pdTRUE);
...

Это наиболее правильный метод.

Наверно для некоторых платформ нельзя. В статьях КиТ по фрииртос (№7 стр 26) сказано про переключение контекста в прерываниях....

Это зависит от того, как организованы прерывания на конкретной платформе (разрешение вложенности, поддерживаются ли софтовые прерывания, сохраняется ли контекст задачи перед вызовом обработчика или нет и т.д.)

IMHO, полезно бы было ФриРТОС-овцам во всех портах реализовать макрос "portEND_SWITCHING_ISR", а уже внутри вызывать либо ТаскСвитч либо ТаскЙолд, дабы народ не путать.

Почитайте эту статью про внутреннее устройство FreeRTOS. Начинаешь лучше понимать суть проблем, когда знаешь как оно внутри работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

полезно бы было ФриРТОС-овцам во всех портах реализовать макрос "portEND_SWITCHING_ISR", а уже внутри вызывать либо ТаскСвитч либо ТаскЙолд, дабы народ не путать.
+1. Я вообще удивлён, что этого не сделано до сих пор.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...