Sistema operativo de fin de semana

Descargo de responsabilidad. El autor no aboga por el uso de sistemas operativos multitarea para microcontroladores.

imagen

La vida obliga sin piedad al uso de sistemas operativos (SO) para microcontroladores. Hay una inmensa cantidad de tales sistemas en el mercado. Los desarrolladores de sistemas operativos, que compiten entre sí, intentan maximizar la funcionalidad de sus productos. Esto a menudo conduce a un aumento en el "peso" del sistema, y ​​también aumenta significativamente el "umbral de entrada" para un programador que desarrolla software para sistemas integrados.

Para no ser atormentado con la elección del sistema operativo para nuestros proyectos, así como para no nublar la mente con el estudio del producto de otra persona, así como para dominar la técnica de escribir aplicaciones integradas para sistemas operativos, así como para descubrir de qué se trata, decidí escribir mi sistema operativo. No es olor.

El sistema operativo propuesto (sistema operativo, el lenguaje no se atreve a llamar a su sistema operativo, y especialmente OSRV) cooperativo con tareas estáticas. Como se señaló anteriormente, no soy partidario del uso del sistema operativo para microcontroladores, pero aún más no soy partidario del uso de sistemas operativos preventivos en microcontroladores. La multitarea preventiva, en comparación con la cooperativa, no es solo procedimientos complejos de cambio de contexto, sino también una sincronización de subprocesos intensiva en recursos. El uso de tareas dinámicas también complica significativamente el sistema operativo.

El sistema operativo fue desarrollado para el procesador de la familia Cortex-M0. Con cambios menores en las reglas para guardar y restaurar el contexto, se puede usar para otros tipos de procesadores.

Código fuente


Archivo 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 


Archivo 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; } 


Archivo 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 


Archivo 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 


Constantes de compilación del sistema operativo


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

Por razones religiosas, no puedo usar la asignación dinámica de memoria, por lo que la cantidad de memoria requerida debe especificarse en la etapa de compilación.

OS OS Services


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

Inicialización de tareas. La tarea se ejecuta en forma de una función, se pasa un puntero a una función al procedimiento de inicialización. Durante la inicialización, debe especificar el tamaño de pila asignado a la tarea. El orden en que se inicializan las tareas determina sus identificadores. La tarea que se inicializa primero tiene el identificador 0. Si especifica un tamaño de pila total mayor que el reservado, se producirá un error. Cuando se inicializa la tarea, se establece el puntero a la pila de tareas, la pila se carga por el contexto de la tarea.

 void StartOS(unsigned long Num); 

El inicio del sistema operativo. Como argumento para la función, se pasa el identificador de la tarea con la que comenzar la ejecución. Cuando se inicia el sistema operativo, el temporizador del sistema se establece en un cuanto de un milisegundo. El contexto se borra de la pila de la tarea iniciada y se llama a la tarea.

 void Sleep(unsigned long ms); 

Planificador Cuando se llama a esta función desde una tarea, el control se transfiere al sistema operativo. El sistema operativo selecciona una tarea lista para la ejecución de la lista y le transfiere el control. El argumento de la función es el tiempo en milisegundos después del cual es necesario devolver el control a la tarea actual. Cuando se llama a una función con el argumento 0xFFFFFFFF, el control nunca volverá.

Es imposible escribir esta función en C, por lo que el algoritmo de su funcionamiento destruye por completo la lógica del lenguaje. El código fuente contiene pruebas de programas en lenguaje ensamblador para los sistemas de programación IAR y GCC. Para los enfermos, se proporciona el código en C. Pero me gustaría señalar que solo puede compilar correctamente con ciertas "fases de la luna". En mi caso, esto sucedió solo cuando se usaba el nivel medio de optimización, en los niveles bajo y alto el código se compilaba erróneamente.

Archivo 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); 

Finalización de la tarea. Como se señaló anteriormente, las tareas son estáticas, la descarga de tareas es imposible. Si necesita completar la tarea, puede usar esta función. La tarea permanece en la lista, pero el control no se transfiere a ella.

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

Parar o comenzar una tarea. El argumento es el identificador de la tarea. Estas funciones le permiten implementar el administrador de tareas. Debe tenerse en cuenta que solo puede iniciar una tarea detenida anteriormente, el tiempo hasta que se inicia es 0xFFFFFFFF.

Usando OS


Por ejemplo, un microcontrolador tradicional "helword" para un sistema operativo desarrollado.

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

En conclusión, me gustaría sinceramente esperar que esto, por diversión, el SO desarrollado sea interesante y útil para los desarrolladores de software para sistemas integrados.

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


All Articles