📜 ⬆️ ⬇️

Weekend OS

Disclaimer The author is not a supporter of the use of multi-tasking operating systems for microcontrollers.

image

Life mercilessly forces to use operating systems (OS) for microcontrollers. There are unmeasured number of such systems on the market. The developers of operating systems, competing with each other, are trying to maximize the functionality of their products. This often leads to an increase in the "heaviness" of the system, and also significantly increases the "threshold of entry" for a programmer who develops software for embedded systems.

In order not to suffer from the choice of OS for my projects, and not to cloud the mind by studying someone else's product, and also to master the technique of writing embedded applications for operating systems, and also to figure out what it is all about, I decided to write my OSku. Its does not smell.
')
The proposed OSKA (it is OSKA, the language does not turn to call it OS, and even more so RTOS) cooperative with static tasks. As noted above, I am not a supporter of using an OS for microcontrollers, but even more I am not a supporter of using displacing operating systems in microcontrollers. Preemptive multitasking, as compared with cooperative, is not only complex context switching procedures, but also resource-intensive synchronization of threads. The use of dynamic tasks also significantly weights the operating system.

The operating system was developed under the processor of the Cortex-M0 family. If there are minor changes regarding the context save-restore rules, it can be used for other types of processors.

Source


IntorOS.h file
#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 file
 #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 file
 #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 file
 .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 


OSKA compilation constants


 #define IntorOSMaxKolvoZadach (2) //   ( ) #define IntorOSRazmerSteka (1024) //     [] ( ) 

For religious reasons, I cannot use dynamic memory allocation, so the amount of required memory must be specified at the compilation stage.

OSKA services


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

Initialize the task. The task is designed as a function, the pointer to the function is passed to the initialization procedure. During initialization, you must specify the size of the stack allocated to the task. The order of task initialization determines their identifiers. The task initialized first has the identifier 0. If you specify the total stack size larger than the reserved, an error will occur. When the task is initialized, the task stack pointer is configured, the stack is loaded with the task context.

 void StartOS(unsigned long Num); 

Start the operating system. As an argument of the function, the identifier of the task with which to start execution is passed. When the operating system starts, the system timer is configured for a quantum of one millisecond. The context is written off from the stack of the task being started and the task is called.

 void Sleep(unsigned long ms); 

Scheduler. When this function is called from a task, control is transferred to the operating system. The operating system from the list selects the task ready for execution and transfers control to it. The function argument is the time in milliseconds, after which it is necessary to return control to the current task. If you call a function with argument 0xFFFFFFFF, the control will never return.

This function cannot be written in C language, since the algorithm of its work completely destroys the logic of the language. The source codes are tests of programs in assembly language for programming systems IAR and GCC. For the afflicted, the code in the C language But I would like to note that it can compile correctly only under certain "phases of the moon." In my case, this happened only when using the average level of optimization; at the low and high levels the code was compiled erroneously.

Sleep.c file
 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); 

Completing the task. As noted above, the tasks are static, uploading the task is impossible. If you need to complete a task, you can use this function. The task remains on the list, but control is not transferred to it.

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

Stop or run a task. The argument is a task identifier. These functions allow you to implement a task manager. It is worth noting that you can only start a previously stopped task, the time to start which is 0xFFFFFFFF.

Using OSYA


For example, the traditional microcontroller "Helvord" under the developed operating system.

 #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); } 

In conclusion, I want to sincerely hope that this, for fun, developed OSKA will be interesting and useful to developers of software for embedded systems.

Source: https://habr.com/ru/post/447302/


All Articles