Ein bisschen über RTOS
Wenn auf dem Mikrocontroller mehrere Aktionen (Prozesse / Aufgaben) gleichzeitig ausgeführt werden müssen, wird normalerweise die Verwendung von RTOS (Real Time Operating System) in Betracht gezogen. RTOS belegt normalerweise einige zusätzliche Kilobyte Speicher. Gleichzeitig können RTOS-Anwendungen die Komplexität erhöhen, auch beim Debuggen.
Die meisten RTOS verwenden einen proaktiven Planungsalgorithmus. Durch die Verwendung eines Interrupts wird der aktuell ausgeführte Prozess angehalten und der Taskplaner wird aufgerufen, um zu bestimmen, welcher Prozess als Nächstes ausgeführt werden soll. Prozesse erhalten eine gewisse CPU-Zeit in kleinen Blöcken. Die Gesamtzeit, die ein Prozess benötigt, hängt von seiner Priorität ab. Alle Prozesse sind normalerweise endlose Zyklen.
Ein Job wird unterbrochen, gespeichert und der Kontext wechselt. Jobwechselvorgänge erfordern mehrere zusätzliche Vorgänge vom Betriebssystem.
Gibt es eine Möglichkeit, ohne RTOS auszukommen, während Sie noch Multitasking können?
Ist es möglich, Dutzende verschiedener Aufgaben gleichzeitig auf einfachen Mikrocontrollern auszuführen, ohne auf RTOS zurückzugreifen? Heute werden wir einen Ansatz betrachten, mit dem Sie mehrere Aufgaben gleichzeitig ausführen können, während Sie zusätzlich eine sehr kleine Menge an Mikrocontrollerspeicher verwenden. Ein Ansatz (ich werde ihn BezRTOS nennen , ich wollte ihn NoRTOS nennen, aber dieser Name wird bereits von Texas Instruments für einen anderen verwendet), der Interrupt-Routinen schnell hält und gleichzeitig Multitasking auf einfache und transparente Weise steuert.
Einige wichtige Punkte, die die Verwendung dieses Ansatzes nahe legen:
. . , , 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;
}
, “”
,
, “”
-
( )
, -
, , ,
“”
, , , . , “” “” / “”, “” “”
:
, : “”, “’, “”, “”, “ ”. .
Stellen Sie Warteschlangen lang genug, damit keine Jobs verloren gehen.
Legen Sie bei verschachtelten Interrupts Jobs von solchen Interrupt-Handlern in verschiedenen Warteschlangen ab.
BezRTOS funktioniert dort, wo es schwierig oder sogar unmöglich ist, ein vollständiges RTOS zu integrieren (z. B. ATTINYxx, PIC16FXXX).
Nachdem ich diesen Ansatz mehrere Jahre erfolgreich angewendet hatte, konnte ich nicht anders, als einen Artikel darüber zu schreiben.
BezRTOS ist kein RTOS, sondern eine Methode, die den Effekt von Multitasking und effizienter Nutzung von Wartezeiten bietet und es Ihnen ermöglicht, Interrupt-Handler schnell zu halten (es gibt nur kurze Anweisungen, die eine sofortige Ausführung erfordern, die Haupt- "Aufgabe" wartet a wenig in der Warteschlange ...).