Menerapkan multitasking pada antrian fungsional (tanpa RTOS)

Sedikit tentang RTOS

Ketika perlu melakukan beberapa tindakan (proses / tugas) secara bersamaan pada mikrokontroler, biasanya kita berpikir untuk menggunakan RTOS (Sistem Operasi Waktu Nyata). RTOS biasanya membutuhkan beberapa kilobyte memori tambahan. Pada saat yang sama, aplikasi RTOS dapat menambahkan lebih banyak kerumitan, termasuk saat melakukan debug.





Kebanyakan RTOS menggunakan algoritma penjadwalan proaktif. Dengan menggunakan interupsi, proses yang sedang berjalan ditangguhkan dan penjadwal tugas dipanggil untuk menentukan proses mana yang harus dijalankan selanjutnya. Proses menerima sejumlah waktu CPU dalam potongan kecil. Jumlah total waktu yang dibutuhkan suatu proses tergantung pada prioritasnya. Semua proses biasanya merupakan siklus tanpa akhir.





Satu pekerjaan terputus, penyimpanan dan pengalihan konteks terjadi. Operasi pengalihan pekerjaan memerlukan beberapa operasi tambahan dari sistem operasi.





Apakah ada cara untuk bertahan tanpa RTOS sambil tetap dapat melakukan banyak tugas?

Apakah mungkin untuk melakukan lusinan tugas berbeda secara bersamaan pada mikrokontroler sederhana tanpa menggunakan RTOS? Hari ini kami akan mempertimbangkan pendekatan yang memungkinkan Anda melakukan beberapa tugas pada saat yang sama, sementara juga menggunakan memori mikrokontroler dalam jumlah yang sangat kecil. Sebuah pendekatan (saya akan menyebutnya BezRTOS , saya ingin menyebutnya NoRTOS, tetapi nama ini sudah digunakan oleh Texas Instruments untuk yang lain) yang akan menjaga rutinitas interupsi cepat dan pada saat yang sama mengontrol multitasking dengan cara yang sederhana dan transparan.





Beberapa poin penting yang menyarankan penggunaan pendekatan ini:





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



  • , β€œβ€





  • ,





  • , β€œβ€





  • -









  • ( )





  • , -





  • , , ,





  • β€œβ€





  • , , , . , β€œβ€ β€œβ€ / β€œβ€,  β€œβ€ β€œβ€





:





, : β€œβ€, β€œβ€™, β€œβ€, β€œβ€, β€œ ”. .





Buat antrian yang cukup panjang agar pekerjaan tidak hilang.





Untuk interupsi bersarang, lepaskan tugas dari penangan interupsi tersebut ke antrean yang berbeda.





BezRTOS akan bekerja jika RTOS lengkap sulit atau bahkan tidak mungkin untuk diintegrasikan (mis. ATTINYxx, PIC16FXXX).





Setelah berhasil menerapkan pendekatan ini selama beberapa tahun, saya mau tidak mau menulis artikel tentangnya.





BezRTOS bukanlah RTOS, ini adalah metode yang memberikan efek multitasking dan penggunaan waktu tunggu yang efisien, memungkinkan Anda untuk menjaga penangan interupsi dengan cepat (hanya akan ada instruksi singkat yang memerlukan eksekusi segera, "tugas" utama akan menunggu sedikit dalam antrian ...).








All Articles