RTOSについて少し
マイクロコントローラで複数のアクション(プロセス/タスク)を同時に実行する必要がある場合、通常、RTOS(リアルタイムオペレーティングシステム)の使用を検討します。RTOSは通常、数キロバイト余分にメモリを消費します。同時に、RTOSアプリケーションは、デバッグ時を含め、さらに複雑になる可能性があります。
ほとんどのRTOSは、プロアクティブなスケジューリングアルゴリズムを使用します。割り込みを使用すると、現在実行中のプロセスが一時停止され、タスクスケジューラが呼び出されて、次に実行するプロセスが決定されます。プロセスは、小さなチャンクである程度のCPU時間を受け取ります。プロセスにかかる合計時間は、プロセスの優先度によって異なります。通常、すべてのプロセスは無限のサイクルです。
1つのジョブが中断され、保存とコンテキスト切り替えが発生します。ジョブ切り替え操作には、オペレーティングシステムからのいくつかの追加操作が必要です。
マルチタスクを実行しながら、RTOSなしで処理する方法はありますか?
RTOSに頼らずに、単純なマイクロコントローラーで数十の異なるタスクを同時に実行することは可能ですか?今日は、非常に少量のマイクロコントローラメモリを追加で使用しながら、複数のタスクを同時に実行できるようにするアプローチを検討します。アプローチ(私は「北韓それを呼び出すBezRTOSを、 私はNoRTOSそれを呼び出すために望んでいた、 しかし、この名前がすでにされているテキサス・インスツルメンツが使用する迅速かつ簡単かつ透明な方法でマルチタスクと同じ時間制御で割り込みルーチンを維持します別のもの)。
このアプローチの使用を提案するいくつかの重要なポイント:
リアルタイム応答がオプションであり、数マイクロ秒またはミリ秒遅れる場合があります。ほとんどのタスクでは、イベントへの即時応答は大きな役割を果たしません。たとえば、ドアが1ミリ秒後に開き始めたり、LEDが1ミリ秒後にオンになったり、ボタンの押下が1ミリ秒後にトリガーされたりした場合、これはそれほど重要ではありません。逆に、ボタンが押されたときのコンタクトバウンスの場合など、遅延イベントの読み取りの信頼性が高い場合もあります。
プロセスの継続性は、いわゆるプロセスよりも重要です。別のプロセスにタイムスロットを与えるために、プロセスがかなりの時間突然中断されたくない場合のリアルタイムの反応。
たとえば、メモリが少量で安価なマイクロコントローラー(古いものも)を備えたコントローラーの場合、RTOSはプラットフォームに移植されません。
RTOS
RTOS
: ( ), ( ) (, - ).
( /).
"" / "" .
.
, ( FIFO):
, :
,
:
, , polling, SPI, I2C, UART… (, , ... )
(, )
:
, ,
-
WiFi
C .
#define Q_SIZE_FAST 16
volatile int F1_last; //
int F1_first; //
void (*F1_Queue[Q_SIZE_FAST])(); // ()
void DummyF(void){;}
void F1_QueueIni(void){ //
F1_last = 0;
F1_first = 0;
}
int F1_push(void (*pointerQ)(void)){ //
if ((F1_last+1)%Q_SIZE_FAST == F1_first)return 1;
F1_Queue[F1_last++] = pointerQ;
F1_last %= Q_SIZE_FAST;
return 0;
}
void (*F1_pull(void))(void){ // -
//
void (*pullVar)(void);
if (F1_last == F1_first)return DummyF;
pullVar = F1_Queue[F1_first++];
F1_first %= Q_SIZE_FAST;
return pullVar;
}
, , , -, . , , . , .
, . :
void DelayOnF1(uint64_t delay){
uint64_t targetTime = delay + millis();
while(millis() < targetTime) F1_pull()();
}
millis() (volatile uint64_t, 1 ).
, . .
main.c:
F1_QueueIni();
F2_QueueIni();
F3_QueueIni();
F4_QueueIni();
while(1){
F1_pull()();
F2_pull()();
F3_pull()();
F4_pull()();
}
:
F1_push(LED_On_Off);
F1_push(ReadChannelsVoltage);
:
F2_push(CalculateTemperatureMiddleValue);
F2_push(CalculateHumidityMiddleValue);
:
F3_push(Display_ScreenInfo);
F3_push(ResetSensor);
:
F4_push(ScanKeyBoard);
F4_push(ReadTouchScr);
(not nested, tail chaining interrupts) - .
( ) .
:
struct fParams { //
int IntVar;
float FloatVar;
};
volatile int FP_last; //
int FP_first; //
void (*FP_Queue[Q_SIZE_FAST])(struct fParams *); //
//
struct fParams PARAMS_array[Q_SIZE_FAST]; //
void FP_QueueIni(void){ //
FP_last = 0;
FP_first = 0;
}
int FP_push(void (*pointerQ)(struct fParams *), struct fParams * parameterQ){ //
if ((FP_last+1)%Q_SIZE_FAST == FP_first)return 1;
FP_Queue[FP_last] = pointerQ;
PARAMS_array[FP_last++] = *parameterQ;
FP_last %= Q_SIZE_FAST;
return 0;
}
void FP_pull(void){ // ,
void (*pullVar)(struct fParams *);
struct fParams * Params;
if (FP_last == FP_first)return;
Params = &PARAMS_array[FP_first];
pullVar = FP_Queue[FP_first++];
FP_first %= Q_SIZE_FAST;
pullVar(Params);
}
:
main.c:
FPQueueIni();
while(1){
FPpull();
}
:
FP_push(ApmControl,&(struct fParams){1,7.18}); // AmpContol // 1 7.18
, “” :
void SIM800_IniCMD(void) {
__HAL UART_ENABLE_IT(shuart2, UART_IT_IDLE); // IDLE
__HAL UART_ENABLE_IT(shuart2, UART_IT_RXNE); // IDLE
ResParse.bytes = 3; // .
Delay_ms_OnMediumQ(32); // "" 32
SIM800_AddCMD((char *) GSM_ATcmd, sizeof (GSM_ATcmd)); // SIM800 DMA
Delay_ms_OnMediumQ(4000); // "" 4c
SIM800_AddCMD((char *) GSM_ATcmd_Disable_Echo, sizeof (GSM_ATcmd_Disable_Echo));
...
, ! , , .
BezRTOS :
void blink(int count) { // LED
while (count--) {
HAL _GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // .1
HAL Delay(100);// 100 .
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);// .0
}
}
:
void blink(int count) { // LED
while (count--) {
HAL _GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // .1
Delay_ms_OnMediumQ(100); // 100 .
// ""
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // .0
}
}
:
:
while(SignalNotStable);
:
while(SignalNotStable){
S1_pull()(); // S1
}
C++
++ , .
typedef void(*fP)(void);
class fQ {
private:
int first;
int last;
fP * fQueue;
int lengthQ;
public:
fQ(int sizeQ);
~fQ();
int push(fP); //
int pull(void); //
};
fQ::fQ(int sizeQ){ //
fQueue = new fP[sizeQ];
last = 0;
first = 0;
lengthQ = sizeQ;
}
fQ::~fQ(){ //
delete [] fQueue;
}
int fQ::push(fP pointerF){ //
if ((last+1)%lengthQ == first){
return 1;
}
fQueue[last++] = pointerF;
last = last%lengthQ;
return 0;
}
int fQ::pull(void){ //
if (last != first){
fQueue[first++]();
first = first%lengthQ;
return 0;
}
else{
return 1;
}
}
:
main.cpp:
fQ F1(16); //
fQ F2(12);
fQ A1(8);
int main(){
for(;;){
A1.pull();
usleep(10000); // 10
}
return 0;
}
- №1:
if(pin10.in == 1) F1.push(SwithOnRelay);
else F1.push(SwithOffRelay);
- №2:
F2.push(ToggleLED);
- - 100 :
A1.push(UpdateUI);
UpdateUI:
void UpdateUI(void){
pin_CS_DISP.out = 0;
Delay_ms_OnF1(2); // 2 . - F1
DispLCD(Voltage);
Delay_ms_OnF2(2); // 2 . - F2
pin_CS_DISP.out = 1;
}
, “”
,
, “”
-
( )
, -
, , ,
“”
, , , . , “” “” / “”, “” “”
:
, : “”, “’, “”, “”, “ ”. .
ジョブが失われないように、キューを十分に長くします。
ネストされた割り込みの場合、そのような割り込みハンドラから別のキューにジョブをドロップします。
BezRTOSは、完全なRTOSを統合することが困難または不可能な場合でも機能します(例:ATTINYxx、PIC16FXXX)。
このアプローチを数年間うまく適用してきたので、私はそれについての記事を書かざるを得ませんでした。
BezRTOSはRTOSではなく、マルチタスクの効果と待機時間の効率的な利用を提供する方法であり、割り込みハンドラーを高速に保つことができます(すぐに実行する必要がある短い命令のみがあり、メインの「タスク」は待機しますキューに少し...)。