Un peu sur RTOS
Lorsqu'il est nécessaire d'effectuer plusieurs actions (processus / tâches) en même temps sur le microcontrôleur, on pense généralement à utiliser RTOS (Real Time Operating System). RTOS occupe généralement quelques kilo-octets supplémentaires de mémoire. Dans le même temps, les applications RTOS peuvent ajouter plus de complexité, y compris lors du débogage.
La plupart des RTOS utilisent un algorithme de planification proactif. En utilisant une interruption, le processus en cours d'exécution est suspendu et le planificateur de tâches est appelé pour déterminer le processus à exécuter ensuite. Les processus reçoivent une certaine quantité de temps CPU par petits morceaux. La durée totale d'un processus dépend de sa priorité. Tous les processus sont généralement des cycles sans fin.
Un travail est interrompu, l'enregistrement et le changement de contexte se produisent. Les opérations de changement de tâche nécessitent plusieurs opérations supplémentaires du système d'exploitation.
Existe-t-il un moyen de se passer de RTOS tout en étant capable d'effectuer plusieurs tâches à la fois?
Est-il possible d'effectuer des dizaines de tâches différentes simultanément sur de simples microcontrôleurs sans recourir à RTOS? Aujourd'hui, nous allons envisager une approche qui vous permettra d'effectuer plusieurs tâches en même temps, tout en utilisant en plus une très petite quantité de mémoire du microcontrôleur. Une approche (je l' appellerai BezRTOS , je voulais l'appeler NoRTOS, mais ce nom est déjà utilisé par Texas Instruments pour un autre) qui gardera les routines d'interruption rapides et en même temps contrôlera le multitâche de manière simple et transparente.
Quelques points clés qui suggèrent d'utiliser cette approche:
. . , , 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;
}
, “”
,
, “”
-
( )
, -
, , ,
“”
, , , . , “” “” / “”, “” “”
:
, : “”, “’, “”, “”, “ ”. .
Faites des files d'attente suffisamment longues pour ne pas perdre les travaux.
Pour les interruptions imbriquées, déposez les travaux de ces gestionnaires d'interruptions dans différentes files d'attente.
BezRTOS fonctionnera là où un RTOS complet est difficile voire impossible à intégrer (par exemple ATTINYxx, PIC16FXXX).
Ayant appliqué cette approche avec succès pendant plusieurs années, je n'ai pas pu m'empêcher d'écrire un article à ce sujet.
BezRTOS n'est pas un RTOS, c'est une méthode pour fournir l'effet du multitâche et une utilisation efficace des temps d'attente, vous permettant de garder les gestionnaires d'interruption rapides (il n'y aura que des instructions courtes qui nécessitent une exécution immédiate, la "tâche" principale attendra un peu dans la file d'attente ...).