Содержание

Подключаем FreeRTOS

Для реализации "многопоточных" программ удобно пользоваться системами реального времени. Одна из которых это FreeRTOS. При открытии портала FreeRTOS.org поражает обилие и качество предоставленной документации. За что хочется сказать разработчикам продукта огромное спасибо!

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

  1. Создание проекта - что подключать.

Внутри FreeRTOS уже есть порт под все Cortex-M ядра, которые используются в Миландр. Для 1986ВЕ1Т подходит профиль от Cоrtex-M0. Для отсчетов времени, при работе планировщика задач, используется встроенный в ядро системный таймер. Поэтому вне зависимости от периферии, которую каждый производитель добавляет к ядру ARM, порт от FreeRTOS уже является рабочим.

Для удобства использования FreeRTOS, эта система была добавлена в состав Pack_v6. Теперь для подключения FreeRTOS достаточно выставить пару галок в Keil. Выглядит это так:

Для простейшей проверки работы FreeRTOS был написан проект мигания светодиодами из нескольких потоков - GitHub. Проект создан сразу под несколько микроконтроллеров - переключается в выпадающем списке SelectTarget. (Но проверен пока только на 1986ВЕ3Т, остальные будут проверены после снятия карантина.)

Как видно по картинке, для минимального использования FreeRTOS необходимо подключить файлы, которые представлены в группе FreeRTOS в дереве проекта. При использовании Pack это происходит автоматически при установке галочки Core. При установке галки необходимо так-же выбрать вариант реализации менеджера памяти - heap_0 … heap_5. Описание вариантов доступно по этой ссылке. Попасть на страницу можно так-же кликнув на гиперссылку, которая выделена синим цветом справа от heap_3 на картинке. Это мое первое знакомство с FreeRTOS, поэтому рекомендаций по выбору менеджера памяти дать не могу.

Для запуска простого проекта с несколькими задачами, подключения галочки "Core" достаточно. Остальные файлы, которые были в составе FreeRTOS вынесены отдельными пунктами. Их можно подключить при необходимости. (На данный момент в Pack реализовано вот такое подключение исходников. Возможно позднее что-то будет изменено при возникновении потребности или большего осознания особенностей работы FreeRTOS).

По картинке так-же видно, что в дереве проекта подключено несколько файлов - port.c и FreeRTOSConfig.h. Это потому что проект реализован сразу для нескольких микроконтроллеров, в данном случае - четырех. Это все разные файлы, отличаются пути.

Подключение MDR_Timer вместо SysTimer для 1986ВЕ1Т/1986ВЕ3Т

Как известно из errata, в микроконтроллерах 1986ВЕ1Т и 1986ВЕ3Т системный таймер не считает, пока ядро останавливается для отработки EEPROM_Delay - задержки доступа к Flash памяти (Потому что память не умеет работать так быстро, как ядро). По этой причине, системный таймер не дает точных отсчетов времени - он считает медленнее чем положено. Чтобы избавится от данной проблемы надо вместо системного таймера, подключить к FreeRTOS любой из аппаратных таймеров доступных в микроконтроллере. Делается это все в том-же файле port.c.

Чтобы не сильно менять файл исходный port.c (на тот случай, если его придется обновлять из первоисточника или подключать другую реализацию) я реализовал подключение аппаратного таймера в отдельном файле port_timer_mdr.h и подключил его к port.c. Файл port_timer_mdr.h преднамеренно не имеет защиты от двойного подключения, как это принято в заголовочных файлах, чтобы его вторичное подключение сразу давало ошибку на этапе компиляции. Этот файл подключается только в port.c и нигде больше подключаться не должен!

(Реализовывать код в заголовочном файле не красиво, но хотелось оставить возможность выбирать вариант с системным таймером и с аппаратным таймером. А кроме этого иметь возможность по быстрому подключить реализацию с аппаратным таймером к другому port.c, например для того же GCC, не сильно меняя исходный файл. Так проще отслеживать изменения, если они будут происходить при обновлении FreeRTOS от первоисточника.)

В port.c получилось так:

#if (configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0)
  ...                            // исходный код работы с SysTimer
#else
  #include "port_timer_mdr.h"    // код с MDR_Timer
#endif // configOVERRIDE_DEFAULT_TICK_CONFIGURATION

В файле FreeRTOSConfig.h назначается новое название обработчика, которое будет использоваться вместо SysTick_Handler:

#include <MDR_Config.h>

#if( configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 )
  // Исходный обработчик
  #define xPortSysTickHandler SysTick_Handler
  
#else
  // Обработчик аппаратного прерывания от MDR_Timer.
  // MDR_FREE_RTOS_TIMER_HANDLER выбирается в MDR_Config.h
  #define xPortSysTickHandler MDR_FREE_RTOS_TIMER_HANDLER

  // Компенсация времени обработки в режиме configUSE_TICKLESS_IDLE == 1
  #if configUSE_TICKLESS_IDLE	
    #define portMISSED_COUNTS_FACTOR  MDR_FREE_RTOS_TIMER_MISSED_FACTOR
  #endif
#endif

Файл <MDR_Config.h> подключает файл для конкретного МК (например <MDR_ConfigVE3.h>), где задается какой таймер будет использован вместо SysTimer:

//===========================  FreeRTOS (for FreeRTOSConfig.h)  ===========================
//  Выбор таймера для отсчета configTICK_RATE_HZ, по умолчанию - системный таймер SysTimer
#define configOVERRIDE_DEFAULT_TICK_CONFIGURATION     1 

#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION != 0
  // Выбор аппаратного таймера для FreeRTOS (вместо SysTimer который в ВЕ1 и ВЕ3 имеет ошибку в errata)
  #define MDR_FREE_RTOS_TIMER                MDR_TIMER4ex
  #define MDR_FREE_RTOS_TIMER_HANDLER        TIMER4_IRQHandler
  
  // Подстройка отсчетов времени при configUSE_TICKLESS_IDLE = 1
  // !!! Значение неправильное (взято наглаз по скорости мигания светодиода), перемерить осциллографом и поменять!!!
  // см функцию - vPortSuppressTicksAndSleep() файл FreeRTOS/port.c
  #define MDR_FREE_RTOS_TIMER_MISSED_FACTOR  445
#endif

Если configOVERRIDE_DEFAULT_TICK_CONFIGURATION = 1, то вместо SysTimer будет использоваться таймер MDR_FREE_RTOS_TIMER с прерыванием MDR_FREE_RTOS_TIMER_HANDLER. В случае 1986ВЕ1 и 1986ВЕ3Т удобно назначит на эти цели таймер MDR_TIMER4, потому что он по регистрам управляется отдельно от первых трех. В частности его нельзя запустить / остановить синхронно подав частоту через регистр TIM_CLOCK.

Опция configUSE_TICKLESS_IDLE

Таймер обычно отсчитывает для ОС периоды в 1мс. Но возможен случай, когда все задачи приостановлены или находятся в режиме ожидания, например в функции sleep(). Если время простоя оставляет величину много большую чем 1мс, то имеет смысл не генерить прерывания каждую 1мс, а выставит таймер сразу на ожидаемое время бездействия. Такой режим работы FreeRTOS включается в FreeRTOSConfig.h параметром configUSE_TICKLESS_IDLE = 1, а отрабатывается функцией:

void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime);

Функция настраивает таймер на ожидаемое время простоя xExpectedIdleTime, а затем переводит ядро в режим SLEEP функцией __wfi(). Ядро выйдет из режима сна когда возникнет какое-нибудь прерывание. После выхода из сна функция vPortSuppressTicksAndSleep() проверяет по таймеру ли было пробуждение, или по какому то другому прерыванию и затем перенастраивает таймер для продолжения нормального отсчета 1мс. Данный режим снижает энергопотребление, но приводит к тому что есть небольшая задержка на время пока таймер останавливается и перенастраивается. Данная задержка введена параметром portMISSED_COUNTS_FACTOR в файле port.c:

/* A fiddle factor to estimate the number of SysTick counts that would have
 occurred while the SysTick counter is stopped during tickless idle
 calculations. */
#ifndef portMISSED_COUNTS_FACTOR
	#define portMISSED_COUNTS_FACTOR	( 45UL )
#endif

Эта задержка определена для системного таймера. При использовании режима configUSE_TICKLESS_IDLE с аппаратным таймером нужна подобная задержка. Полагаю она будет зависеть от опций оптимизации компилятора, поэтому данная задержка вынесена в MDR_ConfigVE3.h под именем MDR_FREE_RTOS_TIMER_MISSED_FACTOR и должна подстраиваться под конкретную сборку. Сейчас значение выставлено наугад, подбирая чтобы светодиод мигал приблизительно раз в секунду. Лучше всего было бы подключить осциллограф к светодиоду и выставить данный параметр так, чтобы на осциллографе период необходимы период.

Особенности текущей реализации

ИТОГО

Для того чтобы использовать FreeRTOS c Pack_v6 необходимо :

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