Implementando multitarefa em filas funcionais (sem RTOS)

Um pouco sobre RTOS

Quando é necessário realizar várias ações (processos / tarefas) ao mesmo tempo no microcontrolador, costumamos pensar em utilizar RTOS (Sistema Operacional em Tempo Real). O RTOS geralmente ocupa alguns kilobytes extras de memória. Ao mesmo tempo, os aplicativos RTOS podem adicionar mais complexidade, inclusive durante a depuração.





A maioria dos RTOS usa um algoritmo de agendamento proativo. Ao usar uma interrupção, o processo atualmente em execução é suspenso e o agendador de tarefas é chamado para determinar qual processo deve ser executado a seguir. Os processos recebem uma certa quantidade de tempo de CPU em pequenos pedaços. O tempo total que um processo leva depende de sua prioridade. Todos os processos são geralmente ciclos infinitos.





Um trabalho é interrompido, o salvamento e a troca de contexto ocorrem. As operações de troca de trabalho requerem várias operações adicionais do sistema operacional.





Existe uma maneira de sobreviver sem RTOS e ainda ser capaz de realizar várias tarefas?

É possível realizar dezenas de tarefas diferentes simultaneamente em microcontroladores simples sem recorrer ao RTOS? Hoje vamos considerar uma abordagem que permitirá que você execute várias tarefas ao mesmo tempo, usando adicionalmente uma quantidade muito pequena de memória do microcontrolador. Uma abordagem ( vou chamá-la de BezRTOS , eu queria chamá-la de NoRTOS, mas esse nome já está sendo usado pela Texas Instruments para outra) que manterá rotinas de interrupção rápidas e ao mesmo tempo controlará multitarefa de forma simples e transparente.





Alguns pontos-chave que sugerem o uso dessa abordagem:





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



  • , “”





  • ,





  • , “”





  • -









  • ( )





  • , -





  • , , ,





  • “”





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





:





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





Faça filas longas o suficiente para que os trabalhos não sejam perdidos.





Para interrupções aninhadas, solte os trabalhos de tais manipuladores de interrupção em filas diferentes.





O BezRTOS funcionará onde um RTOS completo for difícil ou mesmo impossível de integrar (por exemplo, ATTINYxx, PIC16FXXX).





Tendo aplicado essa abordagem com sucesso por vários anos, não pude deixar de escrever um artigo sobre ela.





BezRTOS não é um RTOS, é um método que proporciona o efeito de multitarefa e utilização eficiente dos tempos de espera, permitindo que você mantenha os manipuladores de interrupção rápidos (haverá apenas instruções curtas que requerem execução imediata, a "tarefa" principal irá esperar um pouco na fila ...).








All Articles