人生は容赦なく、マイクロコントローラーにオペレーティングシステム(OS)の使用を強制します。 市場には非常に多くのそのようなシステムがあります。 互いに競合するオペレーティングシステム開発者は、製品の機能を最大限に活用しようとしています。 これはしばしばシステムの「重さ」の増加につながり、組み込みシステム用のソフトウェアを開発するプログラマーの「エントリーしきい値」も大幅に増加します。
私のプロジェクトでOSの選択に悩まされたり、他人の製品の研究に頭を悩ませたり、オペレーティングシステム用の組み込みアプリケーションを書く技術を習得したり、それが何であるかを理解したりするために、私は自分のOSを書くことにしました。 その匂いはありません。
提案されたOS(OS、言語はあえてOS、特にOSRVと呼ぶことを敢えてしない)は、静的タスクと協力します。 上記のように、私はマイクロコントローラー用のOSの使用を支持していませんが、マイクロコントローラーでプリエンプティブオペレーティングシステムを使用することの支持者でもありません。 協調的と比較して、プリエンプティブマルチタスクは、複雑なコンテキストスイッチング手順だけでなく、スレッドのリソース集約型の同期でもあります。 動的タスクを使用すると、オペレーティングシステムが大幅に複雑になります。
オペレーティングシステムは、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
OSコンパイル定数
#define IntorOSMaxKolvoZadach (2) // ( ) #define IntorOSRazmerSteka (1024) // [] ( )
宗教的な理由から、動的なメモリ割り当てを使用できないため、必要なメモリの量はコンパイル段階で指定する必要があります。
OS OSサービス
void InitTask(void (*TaskPointer)(void), unsigned long Stek);
タスクの初期化。 タスクは関数の形式で実行され、関数へのポインターが初期化プロシージャに渡されます。 初期化中に、タスクに割り当てられたスタックサイズを指定する必要があります。 タスクが初期化される順序によって識別子が決まります。 最初に初期化されるタスクの識別子は0です。予約されたスタックサイズより大きい合計スタックサイズを指定すると、エラーが発生します。 タスクが初期化されると、タスクスタックへのポインターが設定され、スタックはタスクコンテキストによってロードされます。
void StartOS(unsigned long Num);
オペレーティングシステムの起動。 関数の引数として、実行を開始するタスクの識別子が渡されます。 オペレーティングシステムが起動すると、システムタイマーは1ミリ秒のクォンタムに設定されます。 コンテキストは、起動されたタスクのスタックから書き出され、タスクが呼び出されます。
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です。
OSを使用する
たとえば、開発されたオペレーティングシステム用の従来のマイクロコントローラ「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); }
結論として、私はこれが、楽しみのために、開発されたOSが組み込みシステム用ソフトウェアの開発者にとって興味深く有用であることを心から望みます。