FreeRTOS के उदाहरण का उपयोग करके RTOS में संदेश संसाधित करना

Logo_FreeRTOS नमस्ते यह आलेख थ्रेड्स के बीच संदेश भेजने के लिए FreeRTOS के लिए हैंडलर पैटर्न के एक संभावित कार्यान्वयन का वर्णन करता है। लेख मुख्य रूप से माइक्रोकंट्रोलर, DIY उत्साही और RTOS और माइक्रोकंट्रोलर का अध्ययन करने वाले लोगों के लिए परियोजनाओं में ऑपरेटिंग सिस्टम का उपयोग करने वाले लोगों के लिए है।
यह माना जाता है कि पाठक आरटीओएस से संबंधित मूल शब्दों से परिचित है, जैसे कि कतार और प्रवाह। आप FreeRTOS के बारे में अधिक सीख सकते हैं qdx पदों में FreeRTOS: परिचय और FreeRTOS: इंटरप्रोसेस संचार
जिन लोगों ने FreeRTOS का उपयोग करके माइक्रोकंट्रोलर्स के लिए परियोजनाओं में भाग लिया, वे इस तथ्य से परिचित हो सकते हैं कि मानक एपीआई बल्कि खराब है, जिससे अतिरिक्त कोड लिखने की आवश्यकता होती है, जो काफी हद तक दोहराई जाती है। मेरे मामले में, प्रवाह के बीच बातचीत के लिए उपकरणों की कमी थी, अर्थात् एकीकृत संदेश प्रणाली की कमी। आमतौर पर, कतार के एक या दूसरे रूप का उपयोग थ्रेड्स और सिंक्रनाइज़ेशन के बीच सूचनाओं के आदान-प्रदान के लिए किया जाता है। इसके अलावा, कतार में निहित जानकारी का प्रकार हर बार अलग होता है, जो कोड के पुन: उपयोग की संभावना को कम करता है।
एक एकीकृत संदेश फ़ॉर्म का उपयोग करने से आप कई थ्रेड्स को एक वर्कर थ्रेड में संयोजित कर सकते हैं, जो एक कतार में संदेशों को प्राप्त करता है।

विचार एंड्रॉइड में हैंडलर वर्ग का उपयोग करने के समान है, इसलिए नाम (वर्ग फ़ील्ड और संरचनाओं के नाम सहित) बेशर्मी से वहां से उधार लिए गए हैं।
दृष्टिकोण कई प्रकार के संदेशों को संसाधित करने के लिए एक एकल थ्रेड के उपयोग पर आधारित है, जो कतार से संदेशों को निकालता है, संबंधित हैंडलर को कॉल करता है, और अगले संदेश पर आगे बढ़ता है।
थ्रेड कतार पर अवरुद्ध है, इसलिए यदि कोई संदेश नहीं है, तो नियंत्रण अन्य थ्रेड्स में स्थानांतरित किया जाता है। जैसे ही एक नया संदेश पंक्तिबद्ध होता है, थ्रेड अनलॉक हो जाता है और संदेश संसाधित होता है। संदेशों को हैंडलर, अन्य थ्रेड्स, अन्य हैंडलर या स्वयं के द्वारा पंक्तिबद्ध किया जा सकता है।

किसी भी धागे की तरह, वर्कर थ्रेड (या लूपर) को उच्च प्राथमिकता के साथ दूसरे धागे से बदला जा सकता है। विभिन्न प्राथमिकताओं के साथ कई लूपर्स का उपयोग करना सबसे महत्वपूर्ण संदेशों के समय पर प्रसंस्करण की अनुमति देता है। आदर्श रूप से, प्रत्येक हैंडलर के लिए एक अनूठी प्राथमिकता वाला एक धागा (दुर्भाग्य से, हमेशा एक समझौता होगा)।
यह सब क्यों जरूरी है?

सबसे पहले, यह दृष्टिकोण लचीलापन प्रदान करता है। यह आपको जटिल इनकैप्सुलेटेड ऑब्जेक्ट बनाने की अनुमति देता है जो कई घटनाओं का जवाब देता है। हालिया अभ्यास का एक उदाहरण आरएफआईडी रीडर क्लास है, जिसे शुरू में केवल कमांड लाइन के साथ काम करने के लिए माना जाता था। परिणामस्वरूप, हैंडलर एक राज्य मशीन में बदल गया, और कमांड लाइन से संदेशों में फ़ोल्डर, टाइमर, मोशन सेंसर और बैटरी स्तर की निगरानी के संदेश जोड़े गए।
चार्ट


कार्यान्वयन का उदाहरण

एक साधारण C ++ प्रोग्राम के उदाहरण पर उपरोक्त पर विचार करें। मैं थ्रेड क्लास का विवरण नहीं दूंगा, यह उल्लेख करने के लिए पर्याप्त है कि थ्रेड के वंशज रन () विधि को ओवरराइड करना चाहिए, जो थ्रेड का शरीर है।

प्रत्येक संदेश एक संरचना है:

struct MESSAGE { /** Handler responsible for handling this message */ Handler *handler; /** What message is about */ char what; /** First argument */ char arg1; /** Second argument */ char arg2; /** Pointer to the allocated memory. Handler should cast to the proper type, * according to the message.what */ void *ptr; }; 

लूप स्ट्रीम कार्यान्वयन उदाहरण:

 Looper::Looper(uint8_t messageQueueSize, const char *name, unsigned short stackDepth, char priority): Thread(name, stackDepth, priority) { messageQueue = xQueueCreate(messageQueueSize, sizeof(Message)); } void Looper::run() { Message msg; for (;;) { if (xQueueReceive(messageQueue, &msg, portMAX_DELAY)) { // Call handleMessage from the handler msg.handler->handleMessage(msg); } } } xQueueHandle Looper::getMessageQueue(){ return messageQueue; } 

सार हैंडलर के कार्यान्वयन का एक उदाहरण (सभी विधियाँ नहीं):

 Handler::Handler(Looper *looper) { messageQueue = looper->getMessageQueue(); } bool Handler::sendMessage(char what, char arg1, char arg2, void *ptr) { Message msg; msg.handler = this; msg.what = what; msg.arg1 = arg1; msg.arg2 = arg2; msg.ptr = ptr; return xQueueSend(messageQueue, &msg, 0); } 

हैंडलर कार्यान्वयन उदाहरण:

आपको एक आभासी विधि को ओवरराइड करने की आवश्यकता है, जिसे लूपर कॉल करेगा।
 void ExampleHandler::handleMessage(Message msg) { #ifdef DEBUG //      ,    debugTx->putString("ExampleHandler.handleMessage("); debugTx->putInt(msg.what, 10); debugTx->putString(")\n"); #endif TxBuffer *responseTx; switch (msg.what) { case EVENT_RUN_SPI_TEST: responseTx = (TxBuffer*)msg.ptr; testSpi(); //     responseTx->putString("Some response\n"); break; case EVENT_BLINK: //     led->blink(msg.arg1, msg.arg2); break; } } 

मुख्य का एक उदाहरण कार्यान्वयन:

मुख्य का उपयोग थ्रेड्स, हैंडलर और अन्य इनिशियलाइज़ेशन बनाने के लिए किया जाता है।
 int main( void ) { //   Looper looper = Looper(10, "LPR", 500, configNORMAL_PRIORITY); //     ExampleHandler exampleHandler = ExampleHandler(&looper); //    CommandInterpreter interpreter = CommandInterpreter(); //  .    //   Strings_SpiExampleCmd,    //     EVENT_RUN_SPI_TEST interpreter.registerCommand(Strings_SpiExampleCmd, Strings_SpiExampleCmdDesc, &exampleHandler, EVENT_RUN_SPI_TEST); interpreter.registerCommand(Strings_BlinkCmd, Strings_BlinkCmdDesc, &exampleHandler, EVENT_BLINK); vTaskStartScheduler(); /* Should never get here, stop execution and report error */ while(true) ledRGB.set(PINK); return 0; } 


नमूना स्रोतों
निष्कर्ष

इस दृष्टिकोण का उपयोग करने के कई फायदे हैं:

संदेश संचालकों (हैंडलर) की कुछ सीमाएँ हैं:

बेशक, सभी धागे प्रस्तावित मॉडल का उपयोग नहीं कर सकते हैं। यदि आपको कठिन वास्तविक समय प्रदान करने की आवश्यकता है, तो आप एक ही धागे (एक संभव है) पर कई हैंडलर नहीं कर पाएंगे। हालांकि, अभ्यास से पता चलता है कि अन्य सभी प्रवाह काफी सरल हैं और व्यावहारिक रूप से अन्य प्रवाह के साथ बातचीत की आवश्यकता नहीं है। ये या तो धाराएँ हैं जो कुछ पढ़ती हैं (एक धारावाहिक बंदरगाह या यूएसबी से) और जिम्मेदार प्रोसेसर को संदेश भेजते हैं, या धाराएं जो समय लेने वाले संचालन (प्रदर्शन) करती हैं। फर्मवेयर के मुख्य तर्क को हैंडलर की मदद से सफलतापूर्वक वर्णित किया जा सकता है।
आपका ध्यान देने के लिए धन्यवाद।

Source: https://habr.com/ru/post/In143979/


All Articles