Implémentation du multitâche sur les files d'attente fonctionnelles (pas de RTOS)

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





  1. : (   ), ( ) (, - ).





  2. ( /).





  3. "" / "" .





  4. .





, ( 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 ...).








All Articles