Опять к вопросу об ОС для МК. Чтобы понять для чего нужны ОС для МК, решил написать собственную кооперативную оську.
Краткое описание
Каждой задаче выделяется область сохранения контекста и стек. Определяются константами MaxKolvoZadach и RazmerSteka
Задачи не загружаются и не выгружаются! При попытке выгрузки случится катастрофия! Если задачу надо завершить, использовать функцию EndTask()
Интерфейсы ОС
#define MaxKolvoZadach (5) //максимальное количество задач
#define RazmerSteka (32) //размер стека
//инициализация задачи
void InitTask(void (*TaskPointer)(void));
//инициализация операционной системы
void InitOS(void);
//передать управление операционной системе
//аргумент время в мимлисекундах
void Sleep(unsigned long ms);
//завершение задачи
static inline void EndTask(void){while(1)Sleep(0xFFFFFFFF);}
Функции
//тип данных задача
typedef struct
{
unsigned long TaskSleep;//время до запуска задачи в мС
unsigned long R4;
unsigned long R5;
unsigned long R6;
unsigned long R7;
unsigned long R8;
unsigned long R9;
unsigned long R10;
unsigned long R11;
unsigned long R12;
unsigned long SP;
void (*TaskPointer)(void);//указатель на задачу
}Task_t;
unsigned long KolvoTask;//количество задач
unsigned long TaskNum;//номер текущей задачи
Task_t TaskList[MaxKolvoZadach];//список задач
unsigned long TaskStek[MaxKolvoZadach][RazmerSteka];//стек
//инициализация задачи
void InitTask(void (*TaskPointer)(void))
{
TaskList[KolvoTask].TaskSleep=0;
TaskList[KolvoTask].TaskPointer=TaskPointer;
TaskList[KolvoTask].SP=(unsigned long)(&(TaskStek[KolvoTask][RazmerSteka-1]));
KolvoTask++;
return;
}
//инициализация операционой системы
void InitOS(void)
{
TaskNum=0;//номер стартовой задачи
SysTick_Config(SystemCoreClock/1000);//запускаем таймер задержки вызова задач квант 1мС
__set_SP(TaskList[TaskNum].SP);//установить указатель стека
(*(TaskList[TaskNum].TaskPointer))();//передаем управление в задачу TaskNum
//если произошло завершение задачи
Delay(1000);//ожидаем секунду
NVIC_SystemReset();//ресет
}
//преравание системного таймера
void SysTick_Handler(void);
void SysTick_Handler(void)
{
TimingDelay++;
for(int i=0;i<KolvoTask;i++)
{
if((TaskList[i].TaskSleep)!=0)
{
(TaskList[i].TaskSleep)--;
}
}
return;
}
Планировщик
#define SHT_PROGBITS 0x1
EXTERN KolvoTask
EXTERN TaskList
EXTERN TaskNum
PUBLIC Sleep
SECTION `.text`:CODE:NOROOT(2)
THUMB
// 5 //передать управление операционной системе
// 6 //аргумент время в лимлисекундах
// 7 void Sleep(unsigned long ms)
// 8 {
Sleep:
PUSH {R4}
// 9 //сохраняем контекст
// 10 TaskList[TaskNum].TaskSleep=ms;//сохраняем время через которое произойдет возврат управления
LDR R1,??Sleep_0
LDR R2,??Sleep_0+0x4
LDR R3,[R1, #+0]
MOVS R4,#+48
MULS R3,R4,R3
STR R0,[R2, R3]
// 11 TaskList[TaskNum].R4 =__get_R4();//сохраняем R4
POP {R0}
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+4]
// 12 TaskList[TaskNum].R5 =__get_R5();//сохраняем R5
MOV R0,R5
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+8]
// 13 TaskList[TaskNum].R6 =__get_R6();//сохраняем R6
MOV R0,R6
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+12]
// 14 TaskList[TaskNum].R7 =__get_R7();//сохраняем R7
MOV R0,R7
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+16]
// 15 TaskList[TaskNum].R8 =__get_R8();//сохраняем R8
MOV R0,R8
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+20]
// 16 TaskList[TaskNum].R9 =__get_R9();//сохраняем R9
MOV R0,R9
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+24]
// 17 TaskList[TaskNum].R10=__get_R10();//сохраняем R10
MOV R0,R10
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+28]
// 18 TaskList[TaskNum].R11=__get_R11();//сохраняем R11
MOV R0,R11
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+32]
// 19 TaskList[TaskNum].R12=__get_R12();//сохраняем R12
MOV R0,R12
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+36]
// 20 TaskList[TaskNum].SP =__get_SP();//сохраняем SP
MOV R0,SP
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+40]
// 21 TaskList[TaskNum].TaskPointer=((void(*)(void))__get_LR());//сохраняем адрес возврата
MOV R0,LR
LDR R3,[R1, #+0]
MULS R3,R4,R3
ADDS R3,R2,R3
STR R0,[R3, #+44]
// 22
// 23 while(1)
// 24 {
// 25 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]
MOVS R3,#+48
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R3,[R0, #+0]
CMP R3,#+0
BNE ??Sleep_1
// 26 //проверяем готовность задачи к выполнению
// 27 if(TaskList[TaskNum].TaskSleep==0)
// 28 {//задача готова к выполнению
// 29 //востанавливаем контекст
// 30 __set_R4(TaskList[TaskNum].R4);//востанавливаем R4
LDR R0,[R0, #+4]
MOV R4,R0
// 31 __set_R5(TaskList[TaskNum].R5);//востанавливаем R5
LDR R0,[R1, #+0]
MOVS R3,#+48
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+8]
MOV R5,R0
// 32 __set_R6(TaskList[TaskNum].R6);//востанавливаем R6
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+12]
MOV R6,R0
// 33 __set_R7(TaskList[TaskNum].R7);//востанавливаем R7
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+16]
MOV R7,R0
// 34 __set_R8(TaskList[TaskNum].R8);//востанавливаем R8
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+20]
MOV R8,R0
// 35 __set_R9(TaskList[TaskNum].R9);//востанавливаем R9
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+24]
MOV R9,R0
// 36 __set_R10(TaskList[TaskNum].R10);//востанавливаем R10
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+28]
MOV R10,R0
// 37 __set_R11(TaskList[TaskNum].R11);//востанавливаем R11
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+32]
MOV R11,R0
// 38 __set_R12(TaskList[TaskNum].R12);//востанавливаем R12
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+36]
MOV R12,R0
// 39 __set_SP(TaskList[TaskNum].SP);//востанавливаем SP
LDR R0,[R1, #+0]
MULS R0,R3,R0
ADDS R0,R2,R0
LDR R0,[R0, #+40]
MOV SP,R0
// 40 __set_LR((unsigned long)TaskList[TaskNum].TaskPointer);//установить адрес возврата
LDR R0,[R1, #+0]
MOVS R1,#+48
MULS R0,R1,R0
ADDS R0,R2,R0
LDR R0,[R0, #+44]
MOV LR,R0
// 41 //The End
// 42 return;
BX LR
Nop
DATA
??Sleep_0:
DC32 TaskNum
DC32 TaskList
DC32 KolvoTask
// 43 }
// 44 }
// 45 }
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
Тест
void Task0(void);
void Task0(void)
{
int i=0;
while(1)
{
PutDecuFifo0(i++);
putstrfifo0(" Task0 0.5sek\r\n");
Sleep(500);
if(i==5)break;
}
EndTask();
}
void Task1(void);
void Task1(void)
{
int i=0;
while(1)
{
PutDecuFifo0(i++);
putstrfifo0(" Task1 1sek\r\n");
Sleep(1000);
}
}
void Task2(void);
void Task2(void)
{
putstrfifo0("Task2 Запущена\r\n");
for(int i=10;i>0;i--)
{
putstrfifo0("Task2 2sek. До останова ");
PutDecuFifo0(i);
putstrfifo0(" выполнений\r\n");
Sleep(2000);
}
putstrfifo0("Task2 Закончена\r\n");
//The End
EndTask();
}
int main(void)
{
// MCU Configuration
//Reset of all peripherals, Initializes the Flash interface and the Systick.
LL_Init();
//Configure the system clock
SystemClock_Config();
InitTask(Task0);
InitTask(Task1);
InitTask(Task2);
InitOS();
}
Выводы.
Главный вывод.
Операционные системы нужны тем кто не умет писать неблокирующий код и конечные автоматы!
Хня все эти ОС, не знаю как вытесняющие, кооперативные точно хня! Зачем тратить ресурсы на сохранение-востановление контекста, если можно прекрасно обойтись без этих хлопот.
Вытесняющие ОС требуют ресурсов на переключение еще больше.
Стек каждой задаче?!!! А не жирно ли?
Планировщик можно написать только на ассемблере. Как написать на чистом Си не представляю.