نظام التشغيل في نهاية الأسبوع

تنويه. المؤلف لا يدافع عن استخدام أنظمة التشغيل متعددة المهام لأجهزة التحكم الدقيقة.

صورة

الحياة بلا رحمة يفرض استخدام أنظمة التشغيل (OS) لميكروكونترولر. هناك عدد هائل من هذه الأنظمة في السوق. يحاول مطورو نظام التشغيل ، المتنافسون مع بعضهم البعض ، زيادة وظائف منتجاتهم إلى الحد الأقصى. يؤدي هذا غالبًا إلى زيادة "ثقل" النظام ، وأيضًا زيادة كبيرة في "عتبة الدخول" للمبرمج الذي يطور برامج للأنظمة المدمجة.

لكي لا تعذبني اختيار نظام التشغيل لمشاريعنا ، بالإضافة إلى عدم إغراء العقل بدراسة منتج شخص آخر ، وكذلك لإتقان تقنية كتابة التطبيقات المدمجة لأنظمة التشغيل ، وكذلك لمعرفة ما يدور حوله ، قررت أن أكتب نظام التشغيل الخاص بي. ليس لها رائحة.

نظام التشغيل المقترح (OS ، اللغة لا تجرؤ على استدعاء نظام التشغيل الخاص به ، وخاصة OSRV) التعاونية مع المهام الثابتة. كما ذكر أعلاه ، لست مؤيدًا لاستخدام نظام التشغيل لأجهزة التحكم الدقيقة ، لكنني أكثر من ذلك لست مؤيدًا لاستخدام أنظمة التشغيل الوقائية في أجهزة التحكم الدقيقة. تعدد المهام الاستباقية ، مقارنة بالتعاونية ، ليس فقط إجراءات تبديل السياق المعقدة ، ولكن أيضًا تزامن سلاسل العمليات كثيفة الاستخدام للموارد. يؤدي استخدام المهام الديناميكية أيضًا إلى تعقيد نظام التشغيل بشكل كبير.

تم تطوير نظام التشغيل لمعالج عائلة 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 ، لن يعود التحكم أبدًا.

من المستحيل كتابة هذه الوظيفة في لغة C ، لذلك فإن خوارزمية تشغيلها تدمر منطق اللغة تمامًا. يحتوي الكود المصدري على اختبارات لبرامج التجميع لأنظمة البرمجة IAR و GCC. بالنسبة للمرضى ، يتم إعطاء الكود في C. لكن أود أن أشير إلى أنه قادر على التجميع بشكل صحيح مع "مراحل القمر" معينة فقط. في حالتي ، حدث هذا فقط عند استخدام المستوى المتوسط ​​من التحسين ، في المستوى المنخفض والعالي ، تم تجميع الشفرة عن طريق الخطأ.

ملف 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.

باستخدام نظام التشغيل


على سبيل المثال ، متحكم تقليدي "helword" لنظام تشغيل مطور.

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

في الختام ، أود أن أتمنى مخلصًا أن يكون هذا ، من أجل المتعة ، نظام التشغيل المطور مثيرًا للاهتمام ومفيدًا لمطوري البرامج للأنظمة المدمجة.

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


All Articles