Установка и конфигурация FreeRTOS

На самом деле это скорей интеграция ее в проект. С технической точки зрения выглядит как подключение библиотек. Как той же CMSIS или SPL. Добавляем инклюдники, добавляем файлы в проект и все. Можно взять готовый пример и переколхозить, но в этом случае есть шанс прозевать какие-нибудь детали и получить странные эффекты. Поэтому начну с нуля, в качестве основы будет модуль Pinboard STM32F103C8T6 и Keil uVision. Под него все мы и соберем.
 

▌Качаем ОС
Тащим архив с freertos.org. Это довольно толстая солянка где 99% занимают примеры под разные архитектуры. Вот его примерная структура:

 

Вся ОС по большей части прячется вот в этих нескольких файлах:

  • queue.c — функции очередей и мутексов
  • tasks.c — функции работы с задачами
  • timers.c — функции работы с таймерами
  • croutine.c — функции работы с сопрограммами
  • event_groups.c — функции работы с флагами
  • list.c — тут все для отладки
  • port.c — платформозависимые параметры. У каждого МК этот файл свой
  • portmacro.h — настройки платформы. Тоже индивидуальный для каждого типа МК
  • FreeRTOSConfig.h — настройки ОС. Платформозависимо, а еще зависит от целей и проекта

 

▌Создаем проект в Keil
Запускаем Keil и там NewProject:

 

Создаем каталог под проект:

 

Выбираем проц:

 

Соглашаемся добавить стартап файлы:

 

Лезем в настройки проекта:

 

Выставляем там частоту:

 

Создаем три группы исходников. Для нашего кода, для CMSIS и FreeRTOS. Если юзаете SPL, то и под него можно. Это опционально конечно, но так порядку больше.

 


 

Все это сохраняем и начинаем набивать наш проект файлами:
 

▌Файло
Идем в каталог нашего проекта, видим там помойку служебных файлов проекта Keil и создаем три папки под сорцы, в соответствии с нашими группами:

 

В CMSIS кидаем файлы CМSIS для нашего проекта. Как подцеплять CMSIS я уже расписывал однажды.
 

В MAIN кладем пустые пока что main.c
 

Начинаем копировать необходимые файлы FreeRTOS в ее каталог. Открываем дистрибутив FreeRTOS и тащим все исходники туда, кроме папки Portable:

 

Заходим в Portable и смотрим папку со своей средой. У нас Keil.
 


 

Там нас ждет файл пустышка, намекающий, что все необходимое лежит в папке RVDS. Идем туда, а там лежат папки рассортированные по архитектурам. Нам нужна ARM Cortex M3. Т.е. папка ARM_CM3 и оттуда мы возьмем файлы portmacro.h и port.c. portmacro.h лучше скопировать в папку include, в кучу ко всем заголовкам.
 


 

Также надо не забыть скопировать себе файлы управления памятью heap_*.c . Они лежат в Portable/MemMang
 


 

В результате у вас будет вот такой набор файлов.
 


 

Осталось где-нибудь стырить конфиг :))) Предлагаю утащить его из папки Demo. Лезем в дистрибутив и в папке \Demo\CORTEX_STM32F103_Keil\ находим файл FreeRTOSConfig.h его мы копируем куда-нибудь себе. Например в папку MAIN. Все же это уже больше к нашей проге относится чем к ОС. Правда я бы рекомендовал сходить на официальный сайт и добавить в него недостающих строчек. Т.к. он там какой то кастрированный, видимо таскается с дремучих времен, а ОС развивается и появляются новые параметры и их в конфиге может и не быть, а они могут быть критичными или нужными.
 

▌FreeRTOSConfig.h — Конфигруция ОС
 

Открываем этот хидер… Там будет достаточно много комментов, я их вырежу, оставлю только мясо:
 

Использовать вытесняющую многозадачность?

1
#define configUSE_PREEMPTION	1

Если этот параметр поставить в 0, то получим кооперативку, с требованием вызывать taskYIELD() для отдачи управления диспетчеру.
 

1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0

Использовать ли платформенно зависимые способы переключения задач. Некоторые контроллеры имеют разные хитрые аппаратные механизмы заточенные под работу с ОС и ОС может их использовать, но для этого ей надо это разрешить.
 

1
#define configUSE_TICKLESS_IDLE	0

Использовать ли IDLE без тиков. В этом режиме таймер в IDLE глушится и не генерирует тики зря, полезно малопотребляющих решений.
 

Есть ли функция перехватывающая IDLE?

1
#define configUSE_IDLE_HOOK	0

В FreeRTOS мы можем поставить перехват IDLE, т.е. при попадании в Idle будет выполнена некая функция. Например загон проца в спячку или еще какая нужна вещь. Если Hook на IDLE не используется, то 0.
 

1
#define configUSE_TICK_HOOK	0

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

1
#define configCPU_CLOCK_HZ	( ( unsigned long ) 72000000 )

Частота на которой будет тикать проц. Нужна для вычисления задержек. Я разгоняю до 72МГЦ потому выставляю тут такое значение.
 

1
#define configTICK_RATE_HZ	( ( TickType_t ) 1000 )

Частота системных тиков ОС.
 

1
#define configMAX_PRIORITIES	( 5 )

Количество приоритетов задач
 

1
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )

Минимальный размер стека. Вообще он не влияет ни на что кроме тех мест где мы создаем задачи. Это просто макроопределение.
 

1
#define configTOTAL_HEAP_SIZE	( ( size_t ) ( 17 * 1024 ) )

Размер суммарной кучи. Области памяти задачи, где лежит и стек и локальные переменные какие то. Т.е. тут мы укзываем сколько вообще у нас места. Зависит от доступной оперативки. У нас в STM32F103C8T6 ее 20кБ, но может быть некий запас под глобальные переменные какие. Потому 3кБ оставляем себе, а остальное отдаем ОС.
 

1
#define configMAX_TASK_NAME_LEN	( 16 )

Для отладочных целей можно задавать каждой задаче символическое имя. Это имя естественно ест немного памяти. Вот тут можно указать строку какой длины мы допускаем для этого безобразия.
 

1
#define configUSE_TRACE_FACILITY	0

Используем ли мы отладочные примочки ОС.
 

1
#define configUSE_16_BIT_TICKS	0

Разрядность таймера ОС. 1—16 бит, 0—32 бита. У STM32 SysTick 24 разрядный, поэтому оставляем тут 0.
 

1
#define configIDLE_SHOULD_YIELD	1

Этот параметр определяет поведение задач с приоритетом IDLE болтающихся одновременно с IDLE ядра. Если стоит 1, то ядро будет отдавать управление сразу же как только задача станет готовой к запуску. А если оставить 0, то время будет равномерно делиться между задачами с приоритетом IDLE и IDLE ядра.
 

1
2
3
#define configUSE_MUTEXES		1
#define configUSE_RECURSIVE_MUTEXES	1
#define configUSE_COUNTING_SEMAPHORES	1

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

1
2
#define configUSE_CO_ROUTINES 			0
#define configMAX_CO_ROUTINE_PRIORITIES 	( 2 )

Используются ли сопрограммы и приоритеты для них.
 

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

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

Но есть нюанс. Допустим диспетчер что-то делает с очередью, смотрит кого там можно разблокировать по ее состоянию, пишет туда что-то свое, а тут опа прерывание и в нем некая [****]FromISR записала в ту самую очередь. Что у нас будет? У нас будет лажа. Т.к. на выходе из прерывания диспетчер похерит все что предыдущее прерывание туда писало — он то запись до конца не провел. Т.е. налицо классическое нарушение атомарности.
Чтобы этого не было, диспетчер на время критических записей запрещает прерывания. Чтобы ему никто не мог помешать делать свое дело. Но запрещает он не все прерывания, а только определенную группу. Скажем все прерывания с приоритетом (меньше число, старше приоритет) 15 по 11, а 1 по 10 нет. В результате на 1 по 10 мы можем вешать что то ну очень сильно важное и никакой диспетчер это не перебьет. Но пользоваться API RTOS в этих (1-10) прерываниях ни в коем случае уже нельзя — они могут диспетчер скукожить. Для настройки этих групп есть конфиг специальный. Для STM32 он выглядит так:
 

1
2
3
4
5
6
7
8
9
/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255 (lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
 
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */
 
/* This is the value being used as per the ST library which permits 16 priority values, 0 to 15.  This must correspond to the configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest NVIC value of 255. 
Шняга для SPL, короче. Эти утырки там приоритеты по другому обозначают*/
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

 

Мы указываем приоритет ядра KERNEL = 255. И задаем планку максимального приоритета для прерываний которые имеют право юзать [****]FromISR API функции = 191. В статье о NVIC я писал, что у STM32 в байте с приоритетом играет роль только старшая тетрада, т.е. у нас есть 16 уровней приоритетов от старшего к младшему: 0х00, 0х10, 0х20, 0х30…0xF0
 

Т.е. 255 это уровень 0xF0 — самый младший, а 191 уровень 0xB0 и, таким образом, все прерывания в которых мы можем использовать API фукнции должны быть сконфигурированы с приоритетом от 0xF0 до 0xB0, не старше. Иначе будет трудноуловимый глюк. Прерывания же не использующие API могут быть с каким угодно приоритетом от самого низкого до самого старшего.
 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete			1
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_xResumeFromISR 			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay			1
#define INCLUDE_xTaskGetSchedulerState		1
#define INCLUDE_xTaskGetCurrentTaskHandle	1
#define INCLUDE_uxTaskGetStackHighWaterMark	0
#define INCLUDE_xTaskGetIdleTaskHandle		0
#define INCLUDE_xTimerGetTimerDaemonTaskHandle	0
#define INCLUDE_pcTaskGetTaskName		0
#define INCLUDE_eTaskGetState			0
#define INCLUDE_xEventGroupSetBitFromISR	1
#define INCLUDE_xTimerPendFunctionCall		0

 

И, напоследок, мы впишем в конфиг следующие строки:

1
2
3
#define xPortSysTickHandler SysTick_Handler
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler

Тем самым, мы привяжем вектора прерываний на диспетчер ОС. Как вы можете видеть
SysTick используется как таймер службы диспетчера. У другого МК тут можно повесить
Другой таймер.

 

Более подробно можно прочесть в официальной доке, правда на варварском наречии, но нам не привыкать.
 

Вот что получилось в итоге:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
    FreeRTOS V8.0.1 - Copyright (C) 2014 Real Time Engineers Ltd. 
    All rights reserved
    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
    1 tab == 4 spaces!
*/
 
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
 
 
#define configUSE_PREEMPTION			1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION	0
#define configUSE_TICKLESS_IDLE			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES			5
#define configMINIMAL_STACK_SIZE		128
#define configTOTAL_HEAP_SIZE			( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN			16
#define configUSE_16_BIT_TICKS			0
#define configIDLE_SHOULD_YIELD			1
#define configUSE_MUTEXES			1
#define configUSE_RECURSIVE_MUTEXES		1
#define configUSE_COUNTING_SEMAPHORES		1
#define configUSE_ALTERNATIVE_API		0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE		10
#define configUSE_QUEUE_SETS			0
#define configUSE_TIME_SLICING			0
#define configUSE_NEWLIB_REENTRANT		0
#define configENABLE_BACKWARD_COMPATIBILITY	0
 
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCHECK_FOR_STACK_OVERFLOW		0
#define configUSE_MALLOC_FAILED_HOOK		0
 
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS		0
#define configUSE_TRACE_FACILITY		0
#define configUSE_STATS_FORMATTING_FUNCTIONS	0
 
/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES			0
#define configMAX_CO_ROUTINE_PRIORITIES	2
 
/* Software timer related definitions. */
#define configUSE_TIMERS			1
#define configTIMER_TASK_PRIORITY		3
#define configTIMER_QUEUE_LENGTH		10
#define configTIMER_TASK_STACK_DEPTH		configMINIMAL_STACK_SIZE
 
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY		255
#define configMAX_SYSCALL_INTERRUPT_PRIORITY	191
 
 
 
 
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
 
#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete			0
#define INCLUDE_vTaskCleanUpResources		1
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay			1
#define INCLUDE_xResumeFromISR			1
#define INCLUDE_xTaskGetSchedulerState		1
#define INCLUDE_xTaskGetCurrentTaskHandle	0
#define INCLUDE_uxTaskGetStackHighWaterMark	0
#define INCLUDE_xTaskGetIdleTaskHandle		0
#define INCLUDE_xTimerGetTimerDaemonTaskHandle	0
#define INCLUDE_pcTaskGetTaskName		0
#define INCLUDE_eTaskGetState			0
#define INCLUDE_xEventGroupSetBitFromISR	1
#define INCLUDE_xTimerPendFunctionCall		1
 
 
 
 
#define xPortSysTickHandler SysTick_Handler
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
 
 
#endif /* FREERTOS_CONFIG_H */

 

▌Выбор модели памяти
Помните мы там копировали несколько файлов heap_*.c? Я еще писал, что они определяют управление памятью. Там у нас четыре возможные модели:
 

  • heap_1.c — собственный хитрый Malloc без Free. Т.е. мы можем создавать задачи (семафоры, очереди и т.д.), но не можем удалять их, чтобы освободить память. В принципе, для не слишком замороченных программ его хватает чуть более чем всегда. Задачи/очереди/семафоры всякие обычно создаются единожды и редко уничтожаются.
  • heap_2.c — динамичная фрагментированная память. Т.е. память динамически выделяется, освобождается, но при этом участки памяти получаются фрагментированными, с дырками на месте освободившейся памяти. И мы не сможем выделить большой кусок памяти, пусть даже у нас будет куча маленьких свободных секторов суммарно нужного обьема. А если новая задача укладывается в какой-либо из этих просветов, то будет выбран наиболее подходящий по размеру свободный кусок и задача развернется там.
  • heap_3.c — использован классический для Си Malloc/Free механизм с небольшой допилкой для невозожности запуска его из двух разных потоков.
  • heap_4.c — Динамическое выделение памяти, может дефрагментировать память, чтобы получить нужный кусок. Разумеется за все приходится платить. Временем в первую очередь.
  •  

    Подробней читайте у Курница и в официальной доке.
     

    ▌Сборка проекта
    Конфигурация завершена. Теперь давайте соберем наш проект в компилируемый вид.
     

    В кейле тыкаем на наши группы в дереве каталогов и добавляем все *.С файлы которые у нас есть. Получится должно примерно так:
     


     

    Обратите внимание на то, что я добавил только один heap_1.c файл. Т.е. я, тем самым, выбрал 1-ю модель распределения памяти. Следующим шагом добавляем пути:
     


     
    Это в опциях проекта.
     


     

    Теперь открываем наш Main.c и копируем туда код простейшей моргалки, но уже на FreeRTOS
     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    #define F_CPU 8000000UL
    #include "stm32f10x.h"
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"
     
    // Отдадочная затычка. Сюда можно вписать код обработки ошибок.
    #define	ERROR_ACTION(CODE,POS)		do{}while(0)
     
     
    // Задача моргалка. Просто так. Мигает диодиком на порту LED3
    void vBlinker (void *pvParameters)
    {
     while(1)
     {
    	GPIOB->BSRR = GPIO_BSRR_BR5;		// Сбросили бит.
    	vTaskDelay(600);			// Выдержка 600мс
    	GPIOB->BSRR = GPIO_BSRR_BS5;		// Установили бит.
    	vTaskDelay(20);				// Выдержка 20мс
     }
    }
     
     
    int main(void)
    {
    // Инициализация всего. Ну почти всего, тут только всякие таймеры стартуют, а всякую периферию я последнее время
    // предочитаю инициализировать непосредственно в задачах где она используется. При старте. 	
    SystemInit();
    RCC->APB2ENR 	|= RCC_APB2ENR_IOPBEN;
     
    // Конфигурируем CRL регистры. 
    GPIOB->CRL	&= ~GPIO_CRL_CNF5;	// Сбрасываем биты CNF для бита 5. Режим 00 - Push-Pull 
    GPIOB->CRL 	|= GPIO_CRL_MODE5_0;	// Выставляем бит MODE0 для пятого пина. Режим MODE01 = Max Speed 10MHz
     
     
     
     
    if(pdTRUE != xTaskCreate(vBlinker,"Blinker", 	configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL)) ERROR_ACTION(TASK_NOT_CREATE,0);	
    // Создаем задачи. Если при создании задачи возвращенный параметр не TRUE, то обрабатываем ошибку.
    // vBlinker 			- это имя функции которая будет задачей. 
    // "Blinker" 			- текстовая строка для отладки. Может быть любой, но длина ограничина в конфиге ОС. Ну и память она тоже ест немного.
    // configMINIMAL_STACK_SIZE 	- размер стека для задачи. Определяется опытным путем. В данном случае стоит системный минимум ,т.к. 
    // 				Задачи используют очень мало памяти. Должно хватить. 
    // NULL 			- передаваемый pvParameters в задачу. В данном случае не нужен, потому NULL. Но можно задаче при создании передевать
    // 				Разные данные, для инициализации или чтобы различать две копии задачи между собой. 
    // tskIDLE_PRIORITY + 1		- приоритет задачи. В данный момент выбран минимально возможный. На 1 пункт выше чем IDLE
    // NULL 			- тут вместо NULL можно было вписать адрес переменной типа xTaskHandle в которую бы упал дескриптор задачи. 
    //				Например так: xTaskCreate(vBlinker,"Blinker",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &xBlinkerTask); 
    //				где xBlinkerTask это глобальная переменная обьявленная как xTaskHandle xBlinkerTask; глобальная - чтобы ее везде
    //				было видно. Отовсюду она была доступна. Но можно и как static обьявить или еще каким образом передать хэндл.
    //				И зная эту переменную и то что там дескриптор этой задачи мы могли бы из другой задачи ее грохнуть, поменять
    //				приоритет или засуспендить. В общем, управлять задачей. Но в моем примере это не требуется. 
    // 				Остальные аналогично.
     
    // Запускаем диспетчер и понеслась. 	
    vTaskStartScheduler();
    }

     

    После чего все можно скомпилировать и запустить в отладчике-симуляторе. Сами увидите как оно все крутится и вертится. Но это не интересно, лучше прогнать в железе.
     

    ▌Конфигурируем отладчик
    Осталась самая малось — сконфигурировать JTAG отладчик, который у вас поставляется вместе с Pinboard II + STM32. Если забыли как как это делается, то я напомню:
     

    Открываете свойства проекта. Идете в раздел Debug:

     

    Там выбираете CooCox Debugger и жмете Settings:

     

    Выставляете адаптер CoLink (не путать с CoLinkEX).
     

    Следующим шагом надо указать программатор. Для этого идем в Utilites и выставляем CooCox Debugger и там. Если галочка будет серой, типо неактивная, то ничего страшного. Надо по ней кликнуть и ее разблокирует. Это глюк.

     

    Следующим этапом нужно зайти в Settings и настроить там алгоритм прошивки.

     

    Добавив нужную модель памяти контроллера из списка:

     

    После чего жмем заливку и наблюдаем результат:

     

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

49 thoughts on “Установка и конфигурация FreeRTOS”

        1. Мне пока хватало этих 32кб под все. А в случае чего можно и на GCC перескочить. Просто в Keil инструментарий удобный для работы.

              1. В плане отладки, для меня, Keil’y аналогов нет. А как редактор кода — это всё субъективщина. Мне лично очень нравится Qt Creator; как его подцепить — есть статья в сообществе
                Поэтому мне и кажется разумным к Keil IDE вместо Keil MDK ARM подцепить ARM GCC.

                1. А толку то? MDK ARM имхо получше GCC будет для ARM. Как никак авторский компилятор. А ограничения на 30 килобайт это никак не снимет. Это же не компилятор ограничивает, а IDE.

                  1. Когда код проекта выйдет за 32 Кб и придётся переходить на другую IDE и gcc (вариант ломать мы же не рассматриваем?) могут возникнуть проблемы при портировании. Говорю как человек, который портировал (конкретно было разногласие в обработке SVC и пришлось лезть в дизасм).
                    Имхо, лучше сразу работать с бесплатным компилером. На всякий случай.

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

      1. Да, я про SPL и код типа
        TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
        TIM_Cmd(TIM3, ENABLE);
        TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
        ADC_ExternalTrigConvCmd(ADC1, ENABLE);

        1. Меня это ересь тоже бесит. Ладно бы это макросы были, а то исполняемый же код, да еще и кривой до ужаса.

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

      1. Поддерживает, но хреновенько. А как сделать статическое распределение памяти для задач — без heap вообще ?

        1. Куча в программе и куча операционки — разные вещи. В зависимости от выбранного heap_X.c будет различная реализация кучи операционки, как и pvPortMalloc() и vPortFree(). Для случаев heap_1, heap_2 весь стек операционки заранее зарезервирован в один большой массив размера configTOTAL_HEAP_SIZE.

          1. В любом режиме heap он выделяет динамически и надо сначала посчитать заранее руками и поставить необходимый configTOTAL_HEAP_SIZE. Я имел ввиду возможность статического определения стеков и других объектов. Что то типа такого

            static uint32_t task1Stack[128];

            xTaskCreate(… task1Stack..);

            или может какой-то другой вызов

            1. Не встречал такой возможности. Но думаю, что вполне можно реализовать руками. Другое дело — нужно ли? Какие плюсы это даёт по сравнению с простыми реализациями heap.c, где время динамического выделения памяти не стоит ничего?

            2. Как вариант — смотреть в сторону других ОСРВ. Например tn_kernel, ChibiOs. На самом деле я бы даже рекомендовал это сделать :)

  1. #define configMINIMAL_STACK_SIZE
    Очень важно не забывать, что это размер стека, выделяемый под idle task. При создании пользовательских задач совершенно нет необходимости его использовать и можно выделять под задачи память меньшего размера.
    Также очень важный момент: указывается в cловах, т.е. для 32-разрядных контроллеров это будет выделять под idle task (4 * configMINIMAL_STACK_SIZE) байт памяти. Тоже самое относится и к usStackDepth в xTaskCreate — это слова. Хотя configTOTAL_HEAP_SIZE задаётся в байтах.

      1. Если погуглить, то можно увидеть, что этот тип определяется инструкциями push/pop стека микроконтроллера.
        Вообще очень плохая идея лезть в ядро. Есть API — им и нужно пользоваться в соответствии с документацией.

        1. Всё можно поменять — API это не догма
          Добавил статические определения для TCB и stackalloc — ядро на 2Кб меньше стало из-за всяких heap и прочих malloc’ов. да и проверок меньше.

  2. Вот мне всегда интересно было. Программы непосредственно компилируются вместе с FreeRTOS. А как бы вот так сделать, что бы программы загружались во время работы системы? Например, кинул прогу на SD-карту, подключил к контроллеру и запустил. Или по USART загрузить например.
    В чем здесь сложность и есть ли вменяемые проекты, которые реализуют эти возможности?

    1. Их тогда надо в RAM грузить и оттуда запускать. По идее может получиться. Вот только надо будет как то адреса передавать, они же динамические будут.

      1. Да не проблема загрузить — только грузить надо туда где они при компиляции предполагались находится. К сожалению не все компиляторы делают relocable код. а так то не проблема. считал, положил и перешел на начало — а там что будет то и будет. Проверить очень легко — берете любую плату dev от stm (10$ максимум) — делаете программу — а потом записываете её в другое место — и получаете хрен на палке. Код не перемещаемый — или процессор с MMU берите — или сами это ручками делайте. Другого выбора нет.
        No fate

        1. Говорите линкеру, что код будет из оперативки исполняться и все будет ок. У меня был проект, где код находился во внешней памяти, в стартапе копировался в оперативку и оттуда выполнялся. При чем достаточно все правильно рассказать линкеру про ваши хотелки и процесс переноса происходит чудным образом сам собой — линкер добавляет свои куски кода, ответственные за перенос программы из внешней паяти в оперативу, делает перевод верторов прерываний, програм каунтера и т.д., и т.п. Единственный минус — этот внедренный код не дебажится в симуляторе.

          Кейл такое умеет точно, GCC тоже. Какой линкер такого не умеет?

          1. А в динамике? Т.е. это загрузил, поюзал, выгрузил, загрузил то, поюзал, выгрузил и так далее.

            1. В динамике тоже работает. У меня коллега разрабатывал гитарную примочку, работающую на STM32F4, если не ошибаюсь, где пресеты обработки звука динамически подгружались в оперативку. Писалось это дело под GCC. От кейла в пользу GCC он отказался из-за более простого на его взгляд синтаксиса файла линкера.

    1. Принципиально думаю вряд ли. А вот нюансы могут быть критичны. Но это сильно сильно зависит от задачи. FreeRTOS например есть на любом чайнике, А Coocox-rtos поддерживает куда меньшее количество контроллеров.

  3. 1) «#define configMAX_PRIORITIES ( 5 )

    Количество приоритетов задач»

    Не совсем так. Допустим при таком конфиге используем пять приоритетов (как в др ОС): приоритеты №№ 2,7,9,18,19. Всего 5 приоритетов. Но FreeRTOS работать не будет. configMAX_PRIORITIES — есть максимально доступный приоритет задаче. Т.е. при таком конфиге самый низкий приоритет будет 0, а самый высоки 4 (configMAX_PRIORITIES -1). При этом задач может быть больше 5, Несколько задач могут использовать один и тот же приоритет.

    2) «Иначе будет трудноуловимый глюк. » Всё правильно…. это очень нехорошее место в настройке FreeRTOS и здесь очень легко отстрелить сене ногу (с stm32). Но… такой глюк ловиться на раз-два, если в конце файла FreeRTOSConfig.h включить дефайн

    #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

    Если будет что-то не так с приоритетами, или с конфигурацией ос, то вы свалитесь в этот вечный вайл. После того, как ваша ос задышит, заработают все прерывания, мютексы, и т.п., в релизе этот дефайн можно (даже нужно) закоментировать. Этот макрос не сильно много ресурсов отъедает, но всё же позволит вам сэкономить много времени в поиске «трудноуловимого» бага.

    1. 2) Воистину так!
      >>дефайн можно (даже нужно) закоментировать
      Лучше окружить этот дефайн строками

      #if MY_DEBUG_LEVEL
      #define configASSERT
      #endif

      , где MY_DEBUG_LEVEL — дефайн задаваемый в настройках компилятора, и который Вы используете в своих debug, assert целях.

  4. #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
    Лучше воопще убрать эту строку. По крайней мере с 8ой версии. portmacro.h лучше знает как лучше.

    #define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
    если у вас STM32, то лучше ( SystemCoreClock )

  5. помогите новичку, залил в кейл ахив из статьи. Не получается скомпилировать. Ошибка.
    .\FreeRTOS_Demo_ee.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
    Not enough information to list image symbols.
    Finished: 1 information, 0 warning and 1 error messages.
    «.\FreeRTOS_Demo_ee.axf» — 1 Error(s), 15 Warning(s).

    1. если ещё актуально, то в файле FreeRTOSConfig.h найдите строку
      #define configUSE_TICK_HOOK 1
      и замените 1 на 0

  6. Добрый день!
    Интересует такой вопрос. А как посчитать: сколько памяти отвести под задачу? Например для записи на карту памяти по SDIO?

Добавить комментарий

Ваш e-mail не будет опубликован.

Перед отправкой формы:
Human test by Not Captcha