Implementación de multitarea en colas funcionales (sin RTOS)

Un poco sobre RTOS

Cuando es necesario realizar varias acciones (procesos / tareas) al mismo tiempo en el microcontrolador, solemos pensar en utilizar RTOS (Real Time Operating System). RTOS suele ocupar algunos kilobytes extra de memoria. Al mismo tiempo, las aplicaciones RTOS pueden agregar más complejidad, incluso al depurar.





La mayoría de los RTOS utilizan un algoritmo de programación proactivo. Al usar una interrupción, el proceso que se está ejecutando actualmente se suspende y se llama al programador de tareas para determinar qué proceso debe ejecutarse a continuación. Los procesos reciben una cierta cantidad de tiempo de CPU en pequeños fragmentos. La cantidad total de tiempo que tarda un proceso depende de su prioridad. Todos los procesos suelen ser ciclos sin fin.





Se interrumpe un trabajo, se guarda y se cambia de contexto. Las operaciones de cambio de trabajo requieren varias operaciones adicionales del sistema operativo.





¿Hay alguna manera de arreglárselas sin RTOS sin dejar de poder realizar múltiples tareas?

¿Es posible realizar decenas de tareas diferentes simultáneamente en microcontroladores simples sin recurrir a RTOS? Hoy consideraremos un enfoque que le permitirá realizar varias tareas al mismo tiempo, mientras que además utiliza una cantidad muy pequeña de memoria del microcontrolador. Un enfoque (lo llamaré BezRTOS , quería llamarlo NoRTOS, pero Texas Instruments ya está usando este nombre para otro) que mantendrá las rutinas de interrupción rápidas y al mismo tiempo controlará la multitarea de una manera simple y transparente.





Algunos puntos clave que sugieren utilizar este enfoque:





  • . . , , 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;
}
      
      



  • , “”





  • ,





  • , “”





  • -









  • ( )





  • , -





  • , , ,





  • “”





  • , , , . , “” “” / “”,  “” “”





:





, : “”, “’, “”, “”, “ ”. .





Cree colas lo suficientemente largas para que no se pierdan trabajos.





Para las interrupciones anidadas, suelte los trabajos de dichos controladores de interrupciones en diferentes colas.





BezRTOS funcionará donde un RTOS completo sea difícil o incluso imposible de integrar (por ejemplo, ATTINYxx, PIC16FXXX).





Después de haber aplicado este enfoque con éxito durante varios años, no pude evitar escribir un artículo al respecto.





BezRTOS no es un RTOS, es un método para proporcionar el efecto de la multitarea y la utilización eficiente de los tiempos de espera, lo que le permite mantener rápidos los manejadores de interrupciones (solo habrá instrucciones breves que requieren ejecución inmediata, la "tarea" principal esperará un poco en la cola ...).








All Articles