Думаю каждый при разработке более или менее сложной программы для МК сталкивался с ситуацией когда скорость выполнения программы меньше чем нужно. И дело даже не в производительности МК, а в алгоритме программы. Скажем при опросе кнопок для защиты от дребезга использованы функции задержки выполняя которые МК зря расходует энергию. А ведь вместо этой задержки можно выполнить другой код, что позволит более рационально использовать ресурсы МК. Но такой подход может значительно усложнить код сделав его запутанным. Видимо это обстоятельство привело к появлению RTOS - операционных систем реального времени для МК. Они представляют из себя планировщик задач, т. е. позволяют реализовать многозадачность, которая может быть нескольких видов, основные из которых это кооперативная и вытесняющая. В первом случае, активная задача сама решает когда она ей больше не нужно процессорное время и она может отдать его другим задачам, а во втором случае, задачу "никто не спрашивает" выполнила она все или нет, ее работу "наглым образом" :D прерывает планировщик и отдает процессорное время другой задаче. По такому принципу реализована многозадачность в винде. :) Такой подход хорош тем что если зависнет одна задача, другие будут нормально работать и более или менее гарантируется периодичность выполнения задач, а не как в корпоративной многозадачности, в которой одна задача может выполняться сколько угодно долго в то время как другие будут ждать пока она отдаст им процессорное время.
ОСь FreeRTOS позволяет реализовать как корпоративную, так и вытесняющую многозадачность и она поддерживает многие модели МК среди которые есть AVR и STM32.
В сети можно найти много статей о использовании FreeRTOS с STM32.

Итак, для того чтобы добавить FreeRTOS в проект, ее нужно скачать с официального сайта на котором сейчас доступная версия 9.0.0. В архиве много файлов и папок, но нам понадобится всего несколько.
Нужно скопировать все *.c файлы и папку include из папки "FreeRTOS\Source".
Также нужно скопировать папку "FreeRTOS\Source\portable\GCC\ARM_CM3".
Нужно из папки "FreeRTOS\Source\portable\MemMang" скопировать один из *.c файлов в зависимости от ситуации. Обычно хватает файла heap_1.c.

Отличия этих файлов

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

Также понадобится файл конфигурации ОС FreeRTOSConfig.h который можно взять скажем из папки "FreeRTOS\Demo\CORTEX_STM32F103_Primer_GCC".

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

Код
Код:
#include "stm32f10x_conf.h"

#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t Task_1_Handle;
TaskHandle_t Task_2_Handle;
TaskHandle_t Task_3_Handle;
TaskHandle_t Task_4_Handle;

void Task_1(void const *argument) // Задача 1.
{
  while(1)
  {
    GPIO_SetBits(GPIOA, GPIO_Pin_4);
    vTaskDelay(200);
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);
    vTaskDelay(100);
  }
}

void Task_2(void const *argument) // Задача 2.
{
  while(1)
  {
    GPIO_SetBits(GPIOA, GPIO_Pin_5);
    vTaskDelay(50);
    GPIO_ResetBits(GPIOA, GPIO_Pin_5);
    vTaskDelay(400);
  }
}

void Task_3(void const *argument) // Задача 3.
{
  while(1)
  {
    GPIO_SetBits(GPIOA, GPIO_Pin_6);
    vTaskDelay(100);
    GPIO_ResetBits(GPIOA, GPIO_Pin_6);
    vTaskDelay(500);
  }
}

void Task_4(void const *argument) // Задача 4.
{
  while(1)
  {
    GPIO_SetBits(GPIOA, GPIO_Pin_7);
    vTaskDelay(200);
    GPIO_ResetBits(GPIOA, GPIO_Pin_7);
    vTaskDelay(200);
  }
}


void vApplicationIdleHook(void) // Функция вызывается когда МК делать нечего, т. е. все задачи неактивны.
{
   __WFI();  // Отправляем МК спать до ближайшего прерывания.
}

int main(void)
{

    {
        GPIO_InitTypeDef GPIO_InitStructure; // Настройка портов A4, A5, A6 и A7 на выход

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }

    // Создание задач.

    xTaskCreate((TaskFunction_t) Task_1, "Blinker 1",
                configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, Task_1_Handle);

    xTaskCreate((TaskFunction_t) Task_2, "Blinker 2",
                configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, Task_2_Handle);

    xTaskCreate((TaskFunction_t) Task_3, "Blinker 3",
                configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, Task_3_Handle);

    xTaskCreate((TaskFunction_t) Task_4, "Blinker 4",
                configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, Task_4_Handle);


    // Запуск планировщика задач.
    vTaskStartScheduler();

    while(1)
    {
    }
}

Сами по себе задачи (потоки) это обычные функции код которых регулярно выполняется по мере переключения задач и в функции должен быть бесконечный цикл.
Задача создается функцией xTaskCreate.

Описание аргументов функции

Task_1 - это имя функции которая будет задачей.
"Blinker 1" - текстовая строка для отладки. Может быть любой, но длина ограничена в конфиге ОС.
configMINIMAL_STACK_SIZE - размер стека для задачи. Определяется опытным путем. В данном случае стоит системный минимум, т. к. задачи используют очень мало памяти.
NULL - передаваемый argument в задачу. В данном случае не нужен, потому NULL.
tskIDLE_PRIORITY + 1 - приоритет задачи. Выбран минимально возможный. На 1 пункт выше чем IDLE.
Task_1_Handle - адрес переменной типа xTaskHandle в которую запишется дескриптор задачи. Зная этот дескриптор мы можем ею управлять, например приостановить или завершить ее работу.

Функция vTaskStartScheduler() запускает планировщик задач и с этого момента на ОСи начинают крутиться задачи. :D

Скриншот

Среда EmBitz при отладке обнаружила использование FreeRTOS в проекте и показала все задачи и некоторую информацию о них. :)

http://s3.uploads.ru/aHzTi.png

Теперь о том что наверное интересует многих - сколько ресурсов требует ОСь. Как оказалось, сравнительно не много. Нужно примерно 1.5 КБ flash и по 602 байта ОЗУ на каждую задачу. Учитывая что в МК 20 КБ ОЗУ, это позволит запустить до 30 задач, что на мой взгляд больше чем достаточно. :) А ведь есть МК с гораздо большим объемом ОЗУ... :) Например STM32F03RET6 содержит 512 КБ flash и 64 КБ ОЗУ. :D

Файлы примера. http://pure-basic.narod.ru/forum_files/ … askLed.zip
Светодиоды нужно подключить к выводам A4, A5, A6 и A7.