Дисклеймер. Автор не является сторонником использования многозадачных операционных систем для микроконтроллеров.

image

Жизнь нещадно заставляет применять операционные системы (ОС) для микроконтроллеров. На рынке существует немерянное количество подобных систем. Разработчики операционных систем, соревнуясь друг с другом, пытаются максимально увеличить функциональность своих продуктов. Это зачастую приводит к увеличению «тяжеловесности» системы, а также значительно повышает «порог вхождения» для программиста, разрабатывающего программное обеспечение встраиваемых систем.

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

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

Операционная система разрабатывалась под процессор семейства Cortex-M0. При небольших изменениях, касающихся правил сохранения-восстановления контекста, ее можно использовать для других типов процессоров.

Исходный код


Файл IntorOS.h
#ifndef __INTOROS_H
#define __INTOROS_H

//Операционная система IntorOS

//Константы
#define IntorOSMaxKolvoZadach (2) //максимальное количество задач (резервирование памяти)
#define IntorOSRazmerSteka (1024) //размер стека под все задачи [байты] (резервирование памяти)
#define IntorOSError (0) //Обработка ошибок 0-зависнуть !0- Сброс МК

//Функции
//инициализация задачи
//аргументы TaskPointer - указатель на задачу, точка входа 
//аргументы Stek - размер стека задачи в байтах
void InitTask(void (*TaskPointer)(void), unsigned long Stek);

//запуск операционной системы
//аргумент номер стартовой задачи
void StartOS(unsigned long Num);

//передать управление операционной системе, усыпить поток
//аргумент ms - время в миллисекундах
void Sleep(unsigned long ms);

//завершение задачи
static inline void EndTask(void){while(1)Sleep(0xFFFFFFFF);}

//остановить задачу
//аргумент - номер задачи
void StopTask(unsigned long Num);

//запустить ранее остановленную задачу
//аргумент - номер задачи
void StartTask(unsigned long Num);

#endif 



Файл IntorOS.c
#define _INTOROS_C

#include "stm32l0xx.h"
#include "IntorOS.h"

//тип данных Параметры Задачи
typedef struct
  {
  unsigned long TaskSleep;//время до запуска задачи в мС
  unsigned long* SP; //указатель стека задачи
  }
  Task_t;

unsigned long KolvoTask;//количество задач
unsigned long KolvoTaskStek;//использование стека
unsigned long TaskNum;//номер текущей исполняемой задачи
Task_t TaskList[IntorOSMaxKolvoZadach];//список задач
unsigned long TaskStek[IntorOSRazmerSteka/4];//резервирование пямяти под стеки задач

//инициализация задачи
//аргументы TaskPointer - указатель на задачу, точка входа 
//аргументы Stek - размер стека задачи
void InitTask(void (*TaskPointer)(void), unsigned long Stek)
  {
  //инициализация параметров задачи  
  TaskList[KolvoTask].TaskSleep=0;//время через которое произойдет возврат управления в задачу
  TaskList[KolvoTask].SP=&(TaskStek[IntorOSRazmerSteka/4-1-KolvoTaskStek]);//указатель стека задачи
    
  //инициализация стека задачи
  //записать в стек точку входа в задачу (регистр LR) 
  TaskList[KolvoTask].SP--;
  (*(TaskList[KolvoTask].SP))=(unsigned long)(TaskPointer);
  
  TaskList[KolvoTask].SP--;//записать в стек R4
  TaskList[KolvoTask].SP--;//записать в стек R5
  TaskList[KolvoTask].SP--;//записать в стек R6
  TaskList[KolvoTask].SP--;//записать в стек R7
  TaskList[KolvoTask].SP--;//записать в стек R8
  TaskList[KolvoTask].SP--;//записать в стек R9
  TaskList[KolvoTask].SP--;//записать в стек R10
  TaskList[KolvoTask].SP--;//записать в стек R11
  TaskList[KolvoTask].SP--;//записать в стек R12  
    
  KolvoTask++;//инкремент количества задач (для следующего вызова)
  KolvoTaskStek=KolvoTaskStek+Stek/4;//инкремент использование стека
  //Проверяем распределение стека
  if(KolvoTaskStek>(IntorOSRazmerSteka/4))
#if IntorOSError==0  
  while(1);//если ошибка в указании размера стека - зависнуть
#else
  NVIC_SystemReset();//если ошибка в указании размера стека - Сброс МК
#endif  
  return;
  }

//запуск операционной системы
//аргумент номер стартовой задачи
void StartOS(unsigned long Num)
  {
  SysTick_Config(SystemCoreClock/1000);//запускаем таймер задержки вызова задач квант 1мС
  
  TaskNum=Num;//номер стартовой задачи
  
  //Деинициализация стека стартовой задачи
  TaskList[TaskNum].SP++;//списать со стека R12
  TaskList[TaskNum].SP++;//списать со стека R11
  TaskList[TaskNum].SP++;//списать со стека R10
  TaskList[TaskNum].SP++;//списать со стека R9
  TaskList[TaskNum].SP++;//списать со стека R8
  TaskList[TaskNum].SP++;//списать со стека R7
  TaskList[TaskNum].SP++;//списать со стека R6
  TaskList[TaskNum].SP++;//списать со стека R5
  TaskList[TaskNum].SP++;//списать со стека R4
  TaskList[TaskNum].SP++;//списать со стека LR
  
  __set_SP((unsigned long)TaskList[TaskNum].SP);//установить указатель стека запускаемой задачи
  (*((void (*)(void))(*(TaskList[TaskNum].SP-1))))();//передаем управление в задачу 
  //если произошло завершение задачи
#if IntorOSError==0  
  while(1);//зависнуть
#else
  NVIC_SystemReset();//Сброс МК
#endif  
  }

//остановить задачу
//аргумент - номер задачи
void StopTask(unsigned long Num)
  {
  TaskList[Num].TaskSleep=0xFFFFFFFF;
  return;
  }

//запустить ранее остановленную задачу
//аргумент - номер задачи
void StartTask(unsigned long Num)
  {
  if((~(TaskList[Num].TaskSleep))==0)
    {//если задача была остановлена, запустить 
    TaskList[Num].TaskSleep=0x00000000;
    }
  return;
  }

//прерывание системного таймера
void SysTick_Handler(void);
void SysTick_Handler(void)
  {
  TimingDelay++;//инкремент переменной системного таймера
  for(int i=0;i<KolvoTask;i++)
    {//перебираем задачи
    if(((TaskList[i].TaskSleep)!=0) && ((~(TaskList[i].TaskSleep))!=0))
      {//если время до запуска не 0 и не 0xFFFFFFFF
      (TaskList[i].TaskSleep)--;//уменьшаем время до запуска
      }
    }
  return;
  }



Файл IntorOSSleepIAR.s
#define SHT_PROGBITS 0x1

        EXTERN KolvoTask
        EXTERN TaskList
        EXTERN TaskNum
        PUBLIC Sleep
        SECTION `.text`:CODE:NOROOT(2)
        THUMB
//    8 //передать управление операционной системе
//    9 //аргумент время в лимлисекундах
//   10 void Sleep(unsigned long ms)
Sleep:
//   11   { 
//   12   //сохраняем контекст
//   13   __asm("PUSH {R4-R7,LR}");        
        PUSH {R4-R7,LR}
//   14   __asm("MOV R4,R8");
        MOV R4,R8
//   15   __asm("MOV R5,R9");
        MOV R5,R9
//   16   __asm("MOV R6,R10");
        MOV R6,R10
//   17   __asm("MOV R7,R11");
        MOV R7,R11
//   18   __asm("PUSH {R4-R7}");
        PUSH {R4-R7}
//   19   __asm("MOV R4,R12");
        MOV R4,R12
//   20   __asm("PUSH {R4}");
        PUSH {R4}
//   21   TaskList[TaskNum].TaskSleep=ms;//сохраняем время через которое произойдет возврат управления
        LDR      R1,Sleep_0
        LDR      R2,Sleep_0+0x4
        LDR      R3,[R1, #+0]
        LSLS     R3,R3,#+3
        STR      R0,[R2, R3]
//   22   TaskList[TaskNum].SP =__get_SP();//сохраняем SP
        MOV      R0,SP
        LDR      R3,[R1, #+0]
        LSLS     R3,R3,#+3
        ADDS     R3,R2,R3
        STR      R0,[R3, #+4]
//   23   //выбор задачи для исполнения 
//   24   while(1)
//   25     {
//   26     TaskNum++;if(TaskNum==KolvoTask)TaskNum=0;//инкрементируем номер текущей задачи
Sleep_1:
        LDR      R0,[R1, #+0]
        ADDS     R0,R0,#+1
        LDR      R3,Sleep_0+0x8
        LDR      R3,[R3, #+0]
        CMP      R0,R3
        BNE      Sleep_2
        MOVS     R0,#+0
Sleep_2:
        STR      R0,[R1, #+0]
        LSLS     R0,R0,#+3
        ADDS     R0,R2,R0
        LDR      R3,[R0, #+0]
        CMP      R3,#+0
        BNE      Sleep_1
//   27     //проверяем готовность задачи к выполнению
//   28     if(TaskList[TaskNum].TaskSleep==0)
//   29       {//задача готова к выполнению
//   30       //востанавливаем контекст
//   31       __set_SP(TaskList[TaskNum].SP);//востанавливаем SP
        LDR      R0,[R0, #+4]
        MOV      SP,R0
//   32       __asm("POP {R4}");
        POP {R4}
//   33       __asm("MOV R12,R4");
        MOV R12,R4
//   34       __asm("POP {R4-R7}");
        POP {R4-R7}
//   35       __asm("MOV R11,R7");
        MOV R11,R7
//   36       __asm("MOV R10,R6");
        MOV R10,R6
//   37       __asm("MOV R9,R5");
        MOV R9,R5
//   38       __asm("MOV R8,R4");
        MOV R8,R4
//   39       __asm("POP {R4-R7,PC}");
        POP {R4-R7,PC}
//   40             
//   41       //The End
//   42       return;
        NOP       
//   43       }
//   44     }
//   45   }
        DATA
Sleep_0:
//      extern unsigned long TaskNum;//номер текущей задачи
        DC32     TaskNum
//      extern Task_t TaskList[IntorOSMaxKolvoZadach];//список задач
        DC32     TaskList
//      extern unsigned long KolvoTask;//количество задач
        DC32     KolvoTask        
        
        SECTION `.iar_vfe_header`:DATA:NOALLOC:NOROOT(2)
        SECTION_TYPE SHT_PROGBITS, 0
        DATA
        DC32 0

        SECTION __DLIB_PERTHREAD:DATA:REORDER:NOROOT(0)
        SECTION_TYPE SHT_PROGBITS, 0

        SECTION __DLIB_PERTHREAD_init:DATA:REORDER:NOROOT(0)
        SECTION_TYPE SHT_PROGBITS, 0

        END


Файл IntorOSSleepGCC.s
.cpu cortex-m0
  .text
  .cfi_sections  .debug_frame
  .section  .text.Sleep,"ax",%progbits
  .align  1
  .global  Sleep
  .syntax unified
  .thumb
  .thumb_func
  .type  Sleep, %function

  .extern KolvoTask
  .extern TaskList
  .extern TaskNum
  
  .cfi_startproc
  
//    8 //передать управление операционной системе
//    9 //аргумент время в лимлисекундах
//   10 void Sleep(unsigned long ms)
Sleep:
//   11   { 
//   12   //сохраняем контекст
//   13   __asm("PUSH {R4-R7,LR}");        
        PUSH {R4-R7,LR}
//   14   __asm("MOV R4,R8");
        MOV R4,R8
//   15   __asm("MOV R5,R9");
        MOV R5,R9
//   16   __asm("MOV R6,R10");
        MOV R6,R10
//   17   __asm("MOV R7,R11");
        MOV R7,R11
//   18   __asm("PUSH {R4-R7}");
        PUSH {R4-R7}
//   19   __asm("MOV R4,R12");
        MOV R4,R12
//   20   __asm("PUSH {R4}");
        PUSH {R4}
//   21   TaskList[TaskNum].TaskSleep=ms;//сохраняем время через которое произойдет возврат управления
        LDR      R1,Sleep_0
        LDR      R2,Sleep_0+0x4
        LDR      R3,[R1, #+0]
        LSLS     R3,R3,#+3
        STR      R0,[R2, R3]
//   22   TaskList[TaskNum].SP =__get_SP();//сохраняем SP
        MOV      R0,SP
        LDR      R3,[R1, #+0]
        LSLS     R3,R3,#+3
        ADDS     R3,R2,R3
        STR      R0,[R3, #+4]
//   23   //выбор задачи для исполнения 
//   24   while(1)
//   25     {
//   26     TaskNum++;if(TaskNum==KolvoTask)TaskNum=0;//инкрементируем номер текущей задачи
Sleep_1:
        LDR      R0,[R1, #+0]
        ADDS     R0,R0,#+1
        LDR      R3,Sleep_0+0x8
        LDR      R3,[R3, #+0]
        CMP      R0,R3
        BNE      Sleep_2
        MOVS     R0,#+0
Sleep_2:
        STR      R0,[R1, #+0]
        LSLS     R0,R0,#+3
        ADDS     R0,R2,R0
        LDR      R3,[R0, #+0]
        CMP      R3,#+0
        BNE      Sleep_1
//   27     //проверяем готовность задачи к выполнению
//   28     if(TaskList[TaskNum].TaskSleep==0)
//   29       {//задача готова к выполнению
//   30       //востанавливаем контекст
//   31       __set_SP(TaskList[TaskNum].SP);//востанавливаем SP
        LDR      R0,[R0, #+4]
        MOV      SP,R0
//   32       __asm("POP {R4}");
        POP {R4}
//   33       __asm("MOV R12,R4");
        MOV R12,R4
//   34       __asm("POP {R4-R7}");
        POP {R4-R7}
//   35       __asm("MOV R11,R7");
        MOV R11,R7
//   36       __asm("MOV R10,R6");
        MOV R10,R6
//   37       __asm("MOV R9,R5");
        MOV R9,R5
//   38       __asm("MOV R8,R4");
        MOV R8,R4
//   39       __asm("POP {R4-R7,PC}");
        POP {R4-R7,PC}
//   40             
//   41       //The End
//   42       return;
        NOP       
//   43       }
//   44     }
//   45   }

        .align  2
Sleep_0:
//      extern unsigned long TaskNum;//номер текущей задачи
        .word TaskNum
//      extern Task_t TaskList[IntorOSMaxKolvoZadach];//список задач
        .word TaskList
//      extern unsigned long KolvoTask;//количество задач
        .word KolvoTask        

  .cfi_endproc


Константы компиляции ОСьки


#define IntorOSMaxKolvoZadach (2) //максимальное количество задач (резервирование памяти)
#define IntorOSRazmerSteka (1024) //размер стека под все задачи [байты] (резервирование памяти)

По религиозным причинам я не могу использовать динамическое выделение памяти, поэтому объем требуемой памяти необходимо указывать на этапе компиляции.

Сервисы ОСьки


void InitTask(void (*TaskPointer)(void), unsigned long Stek);

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

void StartOS(unsigned long Num);

Старт операционной системы. В качестве аргумента функции передается идентификатор задачи, с которой необходимо начать выполнение. При старте операционной системы настраивается системный таймер на квант одна миллисекунда. Со стека запускаемой задачи списывается контекст и вызывается задача.

void Sleep(unsigned long ms);

Планировщик. При вызове этой функции из задачи, управление передается операционной системе. Операционная система из списка выбирает задачу готовую для исполнения и передает ей управление. Аргумент функции – время в миллисекундах, через которое необходимо вернуть управление текущей задаче. При вызове функции с аргументом 0xFFFFFFFF возврат управления не произойдет никогда.

Данную функцию невозможно написать на языке Си, так алгоритм ее работы полностью разрушает логику языка. В исходных кодах приведены тесты программ на языке ассемблера для систем программирования IAR и GCC. Для страждущих приведен код на языке Си. Но хотелось бы отметить, что правильно скомпилироваться он способен только при определенных «фазах луны». В моем случае это произошло только при использовании среднего уровня оптимизации, на низком и на высоком уровне код компилировался ошибочно.

Файл Sleep.c
extern Task_t TaskList[IntorOSMaxKolvoZadach];//список задач
extern unsigned long TaskNum;//номер текущей задачи
extern unsigned long KolvoTask;//количество задач
//передать управление операционной системе
//аргумент время в лимлисекундах
#pragma optimize=medium
void Sleep(unsigned long ms)
  { 
  //сохраняем контекст
  __asm("PUSH {R4-R7,LR}");
  __asm("MOV R4,R8");
  __asm("MOV R5,R9");
  __asm("MOV R6,R10");
  __asm("MOV R7,R11");
  __asm("PUSH {R4-R7}");
  __asm("MOV R4,R12");
  __asm("PUSH {R4}");
  TaskList[TaskNum].TaskSleep=ms;//сохраняем время через которое произойдет возврат управления
  TaskList[TaskNum].SP =(unsigned long*)__get_SP();//сохраняем SP
  //выбор задачи для исполнения 
  while(1)
    {
    TaskNum++;if(TaskNum==KolvoTask)TaskNum=0;//инкрементируем номер текущей задачи
    //проверяем готовность задачи к выполнению
    if(TaskList[TaskNum].TaskSleep==0)
      {//задача готова к выполнению
      //востанавливаем контекст
      __set_SP((unsigned long)TaskList[TaskNum].SP);//востанавливаем SP
      __asm("POP {R4}");
      __asm("MOV R12,R4");
      __asm("POP {R4-R7}");
      __asm("MOV R11,R7");
      __asm("MOV R10,R6");
      __asm("MOV R9,R5");
      __asm("MOV R8,R4");
      __asm("POP {R4-R7,PC}"); //return
      }
    }
  }


void EndTask(void);

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

void StopTask(unsigned long Num);
void StartTask(unsigned long Num);

Остановить или запустить задачу. Аргумент – идентификатор задачи. Эти функции позволяют реализовать диспетчер задач. Стоить отметить, что запустить можно только ранее остановленную задачу, время до запуска которой равно 0xFFFFFFFF.

Использование ОСьки


Для примера традиционный микроконтроллерный «хелворд» под разработанную операционную систему.

#include "stm32l0xx.h"
#include "stm32l0xx_ll_gpio.h"
#include "IntorOS.h"
//Задача 0
void Task0(void)
  {
  LL_GPIO_InitTypeDef GPIO_InitStruct;
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  while(1)
    {
    GPIOB->BRR=LL_GPIO_PIN_0;
    Sleep(1000);
    GPIOB->BSRR=LL_GPIO_PIN_0;
    Sleep(1000);
    }
  }
//Задача 1
void Task1(void)
  {
  LL_GPIO_InitTypeDef GPIO_InitStruct;
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  while(1)
    {
    GPIOB->BRR=LL_GPIO_PIN_1;
    Sleep(500);
    GPIOB->BSRR=LL_GPIO_PIN_1;
    Sleep(500);
    }
  }

void main(void)
  {
  // MCU Configuration
  SystemClock_Config();
  //Инициализация задач  
  InitTask(Task0, 512);
  InitTask(Task1, 256);
  //Запуск ОС
  StartOS(0);
  }

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

Комментарии (18)


  1. lamerok
    08.04.2019 20:50
    +4

    В качестве тренировки написания своего это хорошо!
    Но еще раз убеждаюсь, что электронщик и программист в микроконтроллерах это должны быть два разных человека.
    Вот проще не бывает, порт для кооперативного super simple tasker. И проще и линтом проверен и кода меньше.
    github.com/QuantumLeaps/qpc/tree/master/ports/arm-cm/qv/iar

    www.state-machine.com/qpc/api.html#api_qv


    1. Koyanisqatsi
      08.04.2019 22:49

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

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


      1. nightmare-1966
        09.04.2019 01:45
        +1

        Да уж, сказано сильно — только вот работодатели про это обычно не знают :-)…
        Помнится, ходил на собеседку в «Электроприбор», хорошую для Питера зарплату предлагали, вполне на уровне для «Инженер, регулировщик РЭА».
        В результате выяснилось, что они от работника хотят и программирование АРМ на С++, и для ПЛИС Ксайлинкс/Альтера на Верилог чтоб ошибки находил в коде…
        Ну чтобы минимум за пару квалифицированных работников работал за одну зарплату.
        Вот такие «скромные» хотелки у работодателей нередко встречаются.


        1. lamerok
          09.04.2019 07:38

          Мое мнение, что хороший продукт при этом получится может с вероятностью 50 на 50. Может получится, а может нет. Если делается поделка в двух экземплярах, то да так можно делать. Но если, делается электроника и софт для производства 10000 приборов в год. То боже упаси совмещать две эти роли. Один баг в софте или ошибка проектирования электроники и компания теряет 1 000 000 долларов при цене прибора 100 баксов. И кого вы думаете назначаи козлом отпущения?


    1. I_Soba
      09.04.2019 10:58

      Не могу полностью согласится, я считаю, что тут дело в опыте и квалификации, и всех грести под одну гребенку я бы не стал. Если условный «Эмбеддер», к примеру, может понимать ассемблер для отлова багов, знать си для средних и мелких проектов, а также уметь в с++ для крупных (то бишь разрабатываемыми приличной командой), знает особенности архитектуры контроллера и именно встраиваемого программирования, понимает принципы RTOS(по необходимости), разбирается как в схемотехнике, так и в корректной трассировке плат, то почему бы и не совмещать эти роли. Тогда, это совсем не обязательно будет мелкая поделка. Очевидно, вышеописанный специалист всегда найдет себе работу с адекватными требованиями и соответствующей оплатой труда.


  1. jcmvbkbc
    08.04.2019 23:06
    +2

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

    Да ладно, на раз такая функция пишется на чистом С, без ассемблера и без фаз луны:

    void Sleep(unsigned long ms)
    {
        TaskList[TaskNum].TaskSleep = ms;
        if (setjmp(TaskList[TaskNum].context) == 0)
        {
            while(1)
            {
                ....
                if(TaskList[TaskNum].TaskSleep==0)
                    longjmp(TaskList[TaskNum].context, 1);
                ...
        }
    }


    1. IBAH_II Автор
      09.04.2019 11:10

      Не хиляет…
      Как найти точку входа в задачу?


      1. jcmvbkbc
        09.04.2019 12:17

        Как найти точку входа в задачу?

        Есть варианты. Один — руками заполнить jmp_buf в InitTask. Для этого надо знать его структуру. Другой — в InitTask сделать что-то такое:

        static void InitTask1(jmp_buf *buf, void (*TaskPointer)(void), unsigned long stack)
        {
            set_stack_pointer(stack); // установить указатель стека созданной задачи
            if (setjmp(TaskList[TaskNum].context) == 0) {
                longjmp(*buf, 1);
            } else {
                TaskPointer();
                KillTask();
            }
        }
        
        void InitTask(void (*TaskPointer)(void), unsigned long stack)
        {
            jmp_buf buf;
            if (setjmp(buf) == 0) {
                InitTask1(&buf, TaskPointer, stack);
            }
        }


        Возможно лучше сделать это через третью промежуточную функцию на ассемблере, которая только и делает, что устанавливает указатель стека.


        1. IBAH_II Автор
          10.04.2019 14:20

          Возможно лучше сделать это через третью промежуточную функцию на ассемблере, которая только и делает, что устанавливает указатель стека.


          Что я и делаю без этих хлопот…


          1. jcmvbkbc
            10.04.2019 20:29

            Что я и делаю без этих хлопот…

            Я не сомневаюсь. Мой комментарий был о
            Данную функцию невозможно написать на языке Си
            .


  1. poulch
    08.04.2019 23:33

    лет 10 назад для AVR была похожая оська YAVRTOS. сейчас сайт ее мертв, но архивы все еще находятся гуглом. в свое время оказалась очень полезной для меня к изучению…


    1. IBAH_II Автор
      09.04.2019 11:15

      1. poulch
        09.04.2019 20:08

        в веб архиве лучше наверное искать. те ссылки что находят сейчас — не последняя версия. Если нужно, то могу в почту прислать сохраненную в свое время последнюю версию yavrtos (version 1.11, 2009 Apr 02)


        1. jaiprakash
          09.04.2019 21:55

          Почему бы не на файлоомбенник, а то и на гитхаб.


          1. poulch
            10.04.2019 13:38
            +1

            положил на github github.com/poulch74/yavrtos


  1. Polaris99
    09.04.2019 14:09

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


    1. IBAH_II Автор
      09.04.2019 17:27

      Мне ОС нужна для возможности использования чужих исходников, написанных в блокирующем стиле.


  1. strvv
    10.04.2019 12:54

    OSA для исходно пиков аналогичная.