Jump to content

    

Несколько вопросов начинающего

Помогите пожалуйста побыстрее разобраться что к чему.

 

Вопрос 1:

Есть задача, управляющая миганием светодиода. Мигать он может с разными частотами и разным цветом. Что бы он мигал задача всё время "крутится",

а частота мигания регулируется при помощи vTaskDelay.

Мигание светодиода должно зависеть от результатов выполнения других задач. Как правильнее всего реализовать передачу данных из нескольких разных задач в эту одну ?

Пришло в голову : сделать очередь, в которую все задачи управляющие миганием будут записывать данные по мере их появления. В задаче мигания проверять наличие в очереди непрочитанного элемента. И только если он есть читать, иначе задача заблокируется и светодиод перестанет мигать совсем.

Share this post


Link to post
Share on other sites

А нельзя просто, по результату полученного сообщения менять частоту мигания?

То есть, пришло сообщение от одной задачи, мигаем пять раз в секунду, от другой - десять.

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

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

Share this post


Link to post
Share on other sites
В задаче мигания проверять наличие в очереди непрочитанного элемента. И только если он есть читать, иначе задача заблокируется и светодиод перестанет мигать совсем.

Можно при получении из очереди выставить xTicksToWait и ждать он будет не больше этого количества тиков.

Share this post


Link to post
Share on other sites
Можно при получении из очереди выставить xTicksToWait и ждать он будет не больше этого количества тиков.

Большое спасибо !

 

 

Share this post


Link to post
Share on other sites

Вопрос 2 :

 

Есть USB библиотека. К ней я обращаюсь при помощи всего трёх функций USB_INIT(), USB_RECEIVE(char *p_data), USB_TRANSMIT(char *p_data);

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

Как правильнее всего применить эту библиотеку в проекте, где я решил использовать freeRTOS ? Будет ли правильно просто вставить эти функции в какие-то задачи, чтоб они там вызывались ?

Мой вопрос не "будет ли работать ?" а "как вообще это надо сделать по всем правилам ?"

Share this post


Link to post
Share on other sites

Вопрос 3:

 

Почему 2 задачи работают, а если запускаю 3 то не работает ничего ?

Использую heap_2

 

Где взять файл Errors.h , в котором должны быть коды ошибок возвращаемые xTaskCreate ?

 

#define configUSE_PREEMPTION        1
#define configUSE_IDLE_HOOK            0
#define configUSE_TICK_HOOK            0
#define configCPU_CLOCK_HZ            ( ( unsigned long ) 16000000 )    
#define configTICK_RATE_HZ            ( ( portTickType ) 1000 )
#define configMAX_PRIORITIES        ( ( unsigned portBASE_TYPE ) 5 )
#define configMINIMAL_STACK_SIZE    ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17*128) )  
#define configMAX_TASK_NAME_LEN        ( 16 )
#define configUSE_TRACE_FACILITY    0
#define configUSE_16_BIT_TICKS        0
#define configIDLE_SHOULD_YIELD        1


/////////


void Task1 (void *pvParameters)
{
    static char x;
   while(1)
     {
      x++;
     }
}

void Task2 (void *pvParameters)
{
    static char x;
   while(1)
     {
      x++;
     }
}

void Task3 (void *pvParameters)
{
    static char x;
   while(1)
     {
      x++;
     }
}


int main()
{
    xTaskCreate(Task1,(signed char*)"Task1", configMINIMAL_STACK_SIZE,        NULL, tskIDLE_PRIORITY + 1, NULL);
      xTaskCreate(Task2,(signed char*)"Task2", configMINIMAL_STACK_SIZE,        NULL, tskIDLE_PRIORITY + 1, NULL);
     xTaskCreate(Task3,(signed char*)"Task3", configMINIMAL_STACK_SIZE,        NULL, tskIDLE_PRIORITY + 1, NULL);
        vTaskStartScheduler();

}

Share this post


Link to post
Share on other sites

Вопрос 4:

 

Почему в файле FreeRtosConfig.h нету параметров configUSE_STATS_FORMATTING_FUNCTIONS и configCHECK_FOR_STACK_OVERFLOW ?

configUSE_STATS_FORMATTING_FUNCTIONS вписал туда руками что бы начать использовать отладочную функцию vTaskList

Share this post


Link to post
Share on other sites
Вопрос 2 :

 

Есть USB библиотека. К ней я обращаюсь при помощи всего трёх функций USB_INIT(), USB_RECEIVE(char *p_data), USB_TRANSMIT(char *p_data);

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

Как правильнее всего применить эту библиотеку в проекте, где я решил использовать freeRTOS ? Будет ли правильно просто вставить эти функции в какие-то задачи, чтоб они там вызывались ?

Мой вопрос не "будет ли работать ?" а "как вообще это надо сделать по всем правилам ?"

Можно прям так и использовать, только преимуществ от rtos вы не получите. Обрамляете вызовы своих функций (порт USB наверно один) в семафоры или мутексы, чтобы не было двойных вызов (хотя в вытесняющем режиме этого может и не произойти) и обращений к одному железу.

 

В №3 надо попробовать увеличить configTOTAL_HEAP_SIZE.

В №4 http://microsin.ru/content/view/1309/44/ по умолчанию никому не надо.

 

Share this post


Link to post
Share on other sites
Можно прям так и использовать, только преимуществ от rtos вы не получите. Обрамляете вызовы своих функций (порт USB наверно один) в семафоры или мутексы, чтобы не было двойных вызов (хотя в вытесняющем режиме этого может и не произойти) и обращений к одному железу.

Спасибо. Преимущества получу. У меня же не одна USB задача , и работа с USB занимает малую часть все логики программы.

 

С вопросом 3 разобрался сам. Не хватает размера кучи. #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) означает стек глубиной 128 4х байтных слов, т.к. процессор 32х разрядный. А #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17*128) ) означает размер кучи всего 17 * 128 = 2176 байт. Т.е. хватит на 4 задачи, 2 из которых это задача таймера(точно не знаю, по-моему она создаётся после запуска шедуллера и сразу удаляется) и задача бездействия.

 

Вопрос 5:

Я правильно понимаю, что при создании каждой задачи правильнее указывать какой-то определённый размер стека, который на самом деле нужен, а не одно и то же значение configMINIMAL_STACK_SIZE ?

Share this post


Link to post
Share on other sites
Спасибо. Преимущества получу. У меня же не одна USB задача , и работа с USB занимает малую часть все логики программы.

 

Вопрос 5:

Я правильно понимаю, что при создании каждой задачи правильнее указывать какой-то определённый размер стека, который на самом деле нужен, а не одно и то же значение configMINIMAL_STACK_SIZE ?

Высокоприоритетные задачи не дадут выполняться менее приоритетным, и когда высокоприоритетные "простаивают" (без оптимизации) ожидание в цикле то все остальные тоже ждут, а если оптимизировать и сказать например vTaskDelay то выполняться будет менее приоритетная задача.

 

http://microsin.net/programming/ARM/freertos-part1.html

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

 

Значение usStackDepth указывает количество слов, которое можно сохранить в стеке, а не количество байт. Например, если стек имеет ширину 32 бита, и переданное значение usStackDepth равно 100, то под стек будет выделено 400 байт (100 * 4 байт). Глубина стека, умноженная на его ширину, не должна превышать максимальное значение, которое может содержать переменная типа size_t.

 

Размер стека, используемого для задачи ожидания (idle task, об этой задаче подробнее говорится далее), задается константой configMINIMAL_STACK_SIZE. Значение, назначенное этой константе в демо-приложении FreeRTOS (для определенной архитектуры микроконтроллера) может быть использовано как минимально рекомендованное для любой задачи. Если Ваша программа использует пространство в стеке, то нужно указать для константы configMINIMAL_STACK_SIZE увеличенное значение.

 

Нет простого способа узнать, какого размера стек нужен для задачи. Этот размер можно вычислить, но в большинстве случаев можно просто назначить подходящее значение, подобранное опытным путем, либо взятое приблизительно. Подобрать правильный размер стека важно, чтобы обеспечить адекватное использование RAM без ненужных затрат. Часть 6 содержит информацию о том, как запросить размер стека, используемого задачей.

Share this post


Link to post
Share on other sites

6 ) Вопрос по структуре программы :

 

Допустим есть задача v_Start_File_Write_Task, которая начинает запись файла на SD карту. Эта задача запускает какие-то ещё задачи, а те в свою очередь возможно запускают ещё задачи.

Как остановить запись файла и удалить все связанные с этим задачи ? Сделать задачу v_Stop_File_Write_Task в которой все связанные с файловой системой задачи будут удалятся ?

Более изящного пути нет ?

Share this post


Link to post
Share on other sites
...Более изящного пути нет ?

 

не знаю как на счёт изяществ, но... в серверном мире(как пример) просматриваются следующие модели:

1) когда логика отрабатывает по запросам пользователя. Этот вариант смахивает на Ваш. У Вас пользователь = это обращение к карточке.

2) когда есть поток(и) обрабатывающие запросы от любых пользователей к определённым ресурсам.

3) смешенная.

 

1 вариант не плохо применять в разрезе секьюрности(можно потоку(ам) наследовать уровень доступа самого залогиневшегося пользователя).

минусы - вы тратите доп. ресурсы на подъём и порождения необходимых дочерних потоков, если такие требуются. слабая диспетчеризация

доступа к ресурсам и т.п..

 

2 вариант просче масштабировать от нагрузке. можно это делать на автоматике даже. при этом можно время подготовки выделяемых

ресурсов убрать в фоновый режим.

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

 

возвращаясь к вопросу.

кмк можно породить необходимое кол-во потоков для обработки ваших нужд, и передавать в поток конкретную очередь команд обращений

к данному девайсу. При этом, если задач нет - потоки ожидают на семафорах(либо других элементах синхронизации). При поступлении задачи -

необходимый(ые) потоки просыпаются, делают работу и опять уходят на ожидание.

 

ЗЫ

Пример:

на одной из серверных осей(НЕ форточки), разграничение доступа к БД было на уровне потоков. Доступ к БД, по условию задачи, возможен

был только со стороны сервера. Обращение к БД было инкапсулировано в один поток. Код обращения к БД резко сократился - механизмы

транзакций(сервер считался защищён с точки зрения отказов железа) и блокировки не использовались. Быстродействие повысилось. Т.к. читала

и писала одна и та-же "правая рука", то целостность базы была обеспечена априори. Т.е. на лету можно было делать бэкапы в фоновом режиме,

заливку данных, сжатие данных, печать любых отчётов за 0 минут 0 секунд.

Edited by kolobok0

Share this post


Link to post
Share on other sites

Ещё вопрос, №7

 

Правильно ли я понимаю, что любой переход задачи в блокированное состояние, будь то вызов vTaskDelay() , попытка взять симофор, взять элемент из пустой очереди и т.п. эквивалентен форсированному переключению контекста ?

Иными словами, Если у меня во всех задачах либо форсированное переключение контекста, либо какие угодно переводы задачи в блокированное состояние, то я могу быть уверен в том что моя программа "крутится" с максимально возможной скоростью при данной частоте процессора, и системный квант времени фактически ни на что не влияет ?

Share this post


Link to post
Share on other sites
...что любой переход задачи...Если у меня во всех задачах либо форсированное

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

частоте процессора, и системный квант времени .. ни на что не влияет ?

 

т.к. фриртос вытесняющая, если мне не изменяет память по поводу потрохов, то да. При обращении к ядру оси по делам диспетчиризации, будет произведён

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

следующий в очереди ожидания поток.

 

я так понимаешь, что не было попытки заглянуть внутрь оси и разобраться :) самое лучшее именно та ки поступить...

по поводу максимально возможного - тут всё верно, но немножко оговорочка: теоретически можно поставить колом и даннй механизм,

который будет конечно же дышать, но будет бОльшее выполнение кол-во кода при одном и том же конечном функционале. Но думаю

1) накладные расходы (по сравнению с полезным выполнением, будут минимальны)

2) вопрос спускается к устройству оси. И если Вы ей "доверяете" - то оставте это дело ей. Если нет - переписывайте, либо изучайте вопрос

уже более глубже(сравнивая оси, выбирая и корректируя на более оптимальный олгаритм шедуллер и иже).

3) ИМХО - достаточно при написании кода относится к оси почти как к НЕ вытесняющей. Т.е. помнить о шедуллере и писать код

менее "жадным" к выполнению. Т.е. там где нет необходимости постоянно поллить - перейти на прерывания (идеально чтоб всё было

на прерываниях. от аппаратуры и при передачи самих данных.)

 

По поводу таймера:

Рекомендую посчитать в тактах МК время кванта тиканья системного таймера. если у Вас всё по умолчанию, тиканье с частотой 1ms

и частота ядра 168Мгц, то на один квант приходится около 168000 тактов МК. Что более чем достаточно для многих задач, с

"бесконечными циклами выполняющихся за 10 секунд" :) т.е. вероятность того, что Вы получите прерывание потока именно таким

способом сводится к минимуму, по большому счёту... хотя зависит от задач :)

Edited by kolobok0

Share this post


Link to post
Share on other sites
Правильно ли я понимаю, что любой переход задачи в блокированное состояние, будь то вызов vTaskDelay() , попытка взять симофор, взять элемент из пустой очереди и т.п. эквивалентен форсированному переключению контекста ?

Нет ни какого форсированного переключения контекста. Есть переключение контекста. При вызове vTaskDelay() ( или попытка взять семафор, взять элемент из пустой очереди и т.п.) планировщик проверит нет ли менее приоритетных задач, готовых к выполнению. Если есть, то будет обычное переключение контекста на др. задачу. Если нет задач, готовых к выполнению, то произойдет переключение контекста на пустую(фоновую) задачу (idle task). Пустую задачу можно определить самому.

 

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

 

я так понял что вы беспокоитесь о том, что переключение контекста может происходить только при взове планировщика во время системного кванта. Да... во FreeRTOS так и делается.

 

Допустим системный квант будет 1 раз в сек. При вызове vTaskDelay() переключение контекста произойдет сразуже, не дожидпясь ситемного тика (конца секунды). При попытке захватить семафор.... если семафор занят.... то сразу будет вызван планировщик, т.е. сразу произойдет переключение контекста. Т.е. если пренебречь временем обработки прерывания системного кванта, то системный квант времени фактически ни на что не влияет.

 

НО! Есть маленький нюанс: Допустим у вас 2 полезные задачи. обе заблокированны. одна ждёт семафор1, другая семафор2. Выполняется idle задача. Если семафор1 выдается в прерывании по приходу, например байта по УАРТ, то в обработчике прерывания УАРТ нужно руками прописывать переключение контекста. см. portEND_SWITCHING_ISR(). Если этого не сделать, то по приходу байта будет выдан семафор, но переключения контекста с задачи idle на задачу 1 не произойдет. Переключение контекста случится по системному тику (в конце секунды). Вот в таком случает системный квант будет влиять на скорость работы программы.

 

ps почитайте http://kit-e.ru/assets/listalka/Kurniz/Kurniz.pdf там все азы и буки по фриртос да и вообще по ртос, всё разжованно и по полочкам.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this