Инструменты пользователя

Инструменты сайта


prog:spec:proj_rtos_base

Начальные cведения о Keil RTOS

RTOS (real-time operating system) - многозадачная операционная система реального времени (ОСРВ) RTX, интегрированная в среду Keil. ОСРВ выполняет важное дело – реализует вытесняющую многозадачность. Применение ОСРВ позволяет улучшить управление проектами и облегчает повторное использование кода. Обратной стороной медали является использование повышенного объёма памяти и увеличение времени реакции на прерывания. Однако сейчас, когда объём ОЗУ в МК составляет 32 Кб и более, а размер ОСРВ составляет до 5 Кб, возможностей для внедрения ОСРВ более чем достаточно. Поэтому на вопрос: «Зачем использовать ОСРВ?» можно ответить просто: «Потому что мы можем!».

На сегодняшний день, Keil предоставляет две версии своей ОСРВ: RTOS v1 (Keil RTX 4) и RTOS v2 (Keil RTX 5). Мы будем использовать RTOS v2. Основным «строительным материалом» в обычной Си-программе являются функции, которые мы вызываем для выполнения определённых операций и которые затем передают управление в вызывающую их функцию. В ОСРВ такими базовыми исполнительными элементами являются потоки (процессы). Поток очень похож на Си-функцию, но в то же время имеет несколько принципиальных отличий.

unsigned int function (void)
{

       ……
       return();
}	
void thread (void)
{
         while(1)
     {
        ……
     }
}

Если из функции мы рано или поздно возвращаемся, то поток, запущенный однажды, не завершится никогда, так как в его теле имеется бесконечный цикл while(1). ОСРВ состоит из набора таких потоков, выполнением которых управляет специальный модуль – планировщик. Этот планировщик представляет собой обработчик прерываний от таймера, предоставляющий каждому процессу некий интервал времени для управления. Таким образом, например, «процесс 1» будет выполняться в течение 100 мс, затем управление передаётся на такое же время «процессу 2», после чего происходит переход к «процессу 3», и в конце концов возвращается обратно к «процессу 1». Циклически предоставляя каждому процессу «кусочки» времени, мы получаем иллюзию их одновременного выполнения. Время, предоставляемое на выполнение функции потока, является настраиваемым параметром. Его мы рассмотрим позже.

А пока, рассмотрим некоторые свойства потока, позволяющие планировщику выстраивать логику управления. Поток может находиться в одном из трёх состояний:

Running Поток выполняется
Ready Поток готов к запуску
Wait Поток заблокирован, ожидает события от ОС

Запуск потоков осуществляется исходя из определения их приоритетов. Если несколько потоков находятся в состоянии готовности к запуску «Ready», то в сначала выполняются процессы с наибольшим приоритетом, либо при равенстве приоритетов в порядке очереди, или в так называемом «карусельном» режиме. Таблица приоритетов в порядке возрастания приведена ниже:

RTOS: уровни приоритета
osPriorityIdle
osPriorityLow
osPriorityBelowNormal
osPriorityNormal
osPriorityAboveNormal
osPriorityHigh
osPriorityRealTime
osPriorityError

При создании потока ему присваивается приоритет osPriorityNormal и уникальный идентификатор, который используется при установке определённых параметров потоку. Управление ОСРВ осуществляется с помощью специальных функций, каждая из которых начинается с приставки os и, далее, название самой функции, например, osKernelStart () – запуск планировщика ядра ОСРВ, по сути, запуск самой ОС. Полный перечень функций RTOS2 можно посмотреть на официальном сайте.

Создание проекта

Создаём новый проект, как описано в статье Создание проекта. Выбираем микроконтроллер 1986ВЕ92 и подключаем разделы библиотеки: Startup_MDR1986BE9x, PORT, RST_CLK. Для настройки проекта можно воспользоваться Настройки проекта для 1986ВЕ9х».

Теперь подключаем в наш проект саму ОСРВ. Конечно, это можно было сделать и на этапе создания проекта, но так мы выделим отдельные шаги (или шаг) для подключения только ОСРВ.

Переходим в окно «Manage Run-Time Environment», кликнув на соответствующий значок

rtos_manage.jpg

В окне «Manage Run-Time Environment» выбираем: CMSIS :: CORE и CMSIS :: RTOS2 (API) :: Keil RTX5. Можно добавить RTX в качестве библиотеки (Library), либо добавить полный исходный код (Source). Мы выберем Library.

Нажимаем «ОК». В окне «Project» можно увидеть файлы, которые были автоматически добавлены в проект: RTX_Config.h, RTX_Config.c, библиотека или файлы исходного кода.

rtos_project.jpg

Программа мигания светодиодами

#include <MDR32F9Qx_port.h>
#include <MDR32F9Qx_rst_clk.h>

// Библиотека для работы с RTOS
#include <rtx_os.h>

//  Прототипы функций мигания светодиодами, реализованные ниже
void THREAD_LED0 (void *argument);
void THREAD_LED1 (void *argument);
void Delay(int waitTicks);

// Точка входа
int main (void)
{
  // Заводим структуру конфигурации вывода(-ов) порта GPIO
  PORT_InitTypeDef GPIOInitStruct;
  
  //	Тактирование для PORTC
  RST_CLK_PCLKcmd (RST_CLK_PCLK_PORTC, ENABLE);
	
  //  Конфигурация на вывод
  PORT_StructInit(&GPIOInitStruct);
  GPIOInitStruct.PORT_Pin        = PORT_Pin_0 | PORT_Pin_1;
  GPIOInitStruct.PORT_OE         = PORT_OE_OUT;
  GPIOInitStruct.PORT_SPEED      = PORT_SPEED_SLOW;
  GPIOInitStruct.PORT_MODE       = PORT_MODE_DIGITAL;
	
  PORT_Init(MDR_PORTC, &GPIOInitStruct);

	// Инициализация RTOS
	osKernelInitialize();
	
	// Создание потоков LED0 и LED1
	osThreadNew(THREAD_LED0, NULL, NULL); 
	osThreadNew(THREAD_LED1, NULL, NULL); 
	
	// Запуск RTOS
  osKernelStart();
}


void THREAD_LED0 (void *argument)
{
  // Основной цикл
  while(1)
  {
		PORT_ResetBits (MDR_PORTC, PORT_Pin_0); // Выключаем светодиод 0
		Delay (195000); // Функция задержки
	
		PORT_SetBits (MDR_PORTC, PORT_Pin_0); // Включаем светодиод 0
		Delay (195000);
	}
}

void THREAD_LED1 (void *argument)
{
	// Основной цикл
  while(1)
  {
		PORT_ResetBits (MDR_PORTC, PORT_Pin_1); // Выключаем светодиод 1
		Delay (195000);

		PORT_SetBits (MDR_PORTC, PORT_Pin_1); // Включаем светодиод 1
		Delay (195000);
	}
}
void Delay(int waitTicks)
{
int i;
	 for (i = 0; i <waitTicks; i++)
	{
		__NOP();
	}
}

Рассмотрим процесс запуска RTOS. После конфигурации выводов и задания тактирования, в main.c необходимо произвести инициализацию RTOS и создать потоки, которые описываются как обычные функции в СИ. После же, запустить планировщик ядра ОСРВ функцией osKernelStart (), который и начнёт запускать наши потоки.

В функциях потоков LED0 и LED1 используется функция простоя Delay (N_тактов). Намного удобнее для задания задержки использовать стандартную функцию ОСРВ osDelay (N_ms), однако в нашем проекте она не подходит. При вызове функции osDelay () поток, вызывающий функцию переводится в режим ожидания Wait на указанное время и запускается поток ожидания osRtxIdleThread, в составе которого просто бесконечный цикл. При желании в него можно прописать свою функцию. Приоритет у этого потока наименьший osPriorityIdle, поэтому после его запуска при следующем тике таймера системы (его частота по умолчанию 1000 Гц) запускается поток с наибольшим приоритетом, находящийся в состоянии готовности «Ready». Это очень удобно, т.к пока запустивший функцию задержки поток находится в режиме ожидания, будут выполняться другие потоки. В нашем проекте она не подходит лишь потому, что с её помощью нельзя наглядно показать увеличение времени выполнения потока. Так, выделяя на поток определённое время, например 100 мс, оно в полном объёме использоваться не будет, так как всегда при выполнении мы будем натыкаться на функцию osDelay (), переводящую поток в режим ожидания и передающее управление другому потоку. Поэтому будем использовать обычную функцию задержки.

Ну что ж, пора переходить к практике. Компилируем наш проект и зашиваем в микроконтроллер. Сейчас светодиоды мигают «одновременно», т.к. ОСРВ делит процессорное время между двумя потоками LED0 и LED1, выделяя на каждый из них по 5 мс (по умолчанию). Чтобы своими глазами увидеть, как ОСРВ разделяет процессорное время, увеличим время выполнения каждого потока. Для этого откроем файл «RTX_Config.h», определяющий параметры конфигурации RTOS2. Слева, под окном с кодом нашей программы, выбираем вкладку «Configuration Wizard», представляющую более удобное представление настроек. Далее выбираем «System Configuration» → «Round-Robin Thread switching» и в «Round-Robin Timeout» ставим значение 1000.

Этот параметр задаётся в тиках таймера системы, что по умолчанию соответствует миллисекундам. Немного выше располагается параметр «Kernel Tick Frequency», который как раз и задаёт частоту тиков системного таймера, по умолчанию это 1000 Гц, т.е. один тик за одну миллисекунду.

Сохраним RTX_Config.h, скомпилируем наш проект ещё раз и зашьём в МК. Теперь светодиоды мигают по очереди, по 1 секунде на каждый светодиод.

Для отладки программы с ОСРВ в Keil предусмотрен ряд функций, позволяющих просматривать выполнение потоков во времени, кумулятивное время выполнения функций потоков и многое другое. Но только в версиях Keil 5.24 и позже. У меня же версия 5.23 и всех прелестей отладки RTOS я не увижу. Однако, разработчики всё же добавили несколько функций и в мою версию Keil, что ж, воспользуемся ими!

Запускаем режим отладки CTRL+F5 и в тулбаре выбираем RTX RTOS, как показано на рисунке ниже. Слева откроется встроенное окошечко с основной информацией о настройках RTOS и текущем состоянии потоков.

Нас интересует вкладка Threads. Как и следует из названия, здесь представлены потоки, используемые в данном проекте: два наших потока LED0 и LED1, а также поток простоя osRtxIdle. В поле «Value» напротив каждого из потоков написано их текущее состояние и приоритет. Кликнув на плюсик слева от названия потока, можно увидеть более подробную информацию о состоянии потока, например, выставленные флаги.

prog/spec/proj_rtos_base.txt · Последнее изменение: 2022/04/03 23:09 (внешнее изменение)