Arduino इतना धीमा क्यों है और इसके बारे में क्या किया जा सकता है

लोगो


एक बार मैं एक उत्कृष्ट लेख ( tyk ) में आया - इसमें लेखक ने स्पष्ट रूप से Arduino फ़ंक्शन का उपयोग करने और रजिस्टरों के साथ काम करने के बीच अंतर दिखाया। बहुत सारे लेख लिखे गए हैं, दोनों अर्दुइनो की प्रशंसा करते हुए और यह तर्क देते हुए कि यह सब तुच्छ है और आम तौर पर बच्चों के लिए है, इसलिए हम इसे नहीं दोहराएंगे, लेकिन यह पता लगाने की कोशिश करेंगे कि उस लेख के लेखक ने क्या परिणाम प्राप्त किए हैं। और, महत्वपूर्ण रूप से, हम सोच सकते हैं कि हम क्या कर सकते हैं। जो कोई भी दिलचस्पी है, मैं बिल्ली के नीचे पूछता हूं।


भाग 1 "प्रश्न"


इस लेख के लेखक का उद्धरण:


यह इस मामले में एक प्रदर्शन हानि का पता लगाता है - 28 बार। बेशक, इसका मतलब यह नहीं है कि आर्डिनो 28 बार धीमी गति से काम करता है, लेकिन मुझे लगता है कि स्पष्टता के लिए, यह सबसे अच्छा उदाहरण है कि क्यों Arduino को पसंद नहीं किया जाता है।

चूंकि लेख अभी शुरू हुआ है, हम इसे अभी तक नहीं समझेंगे, लेकिन दूसरे वाक्य को अनदेखा करें और मान लें कि नियंत्रक गति पिन स्विचिंग आवृत्ति के लगभग बराबर है। यानी हमारे पास जो हमारे पास है उसकी उच्चतम आवृत्ति के जनरेटर को बनाने के कार्य के साथ सामना किया जाता है। पहले, आइए देखें कि सब कुछ कितना बुरा है।


हम arduino के लिए एक सरल कार्यक्रम लिखेंगे (वास्तव में, बस कॉपी ब्लिंक)।


void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, 1); // turn the LED on (HIGH is the voltage level) digitalWrite(13, 0); // turn the LED off by making the voltage LOW } 

नियंत्रक में सीना। चूंकि मेरे पास एक आस्टसीलस्कप नहीं है, लेकिन केवल एक चीनी तर्क विश्लेषक, इसे सही ढंग से कॉन्फ़िगर करने की आवश्यकता है। अधिकतम विश्लेषक आवृत्ति 24 मेगाहर्ट्ज है, इसलिए, इसे नियंत्रक आवृत्ति के साथ बराबर किया जाना चाहिए - 16 मेगाहर्टज पर सेट। हम देखते हैं ...


Test_1


... बहुत समय से। हम यह याद रखने की कोशिश कर रहे हैं कि नियंत्रक की गति किस पर निर्भर करती है - वास्तव में, आवृत्ति। हम arduino.cc में देखते हैं। क्लॉक स्पीड 16 मेगाहर्ट्ज है, और यहां हमारे पास 145.5 किलोहर्ट्ज़ है। क्या करें? आइए इसे माथे में हल करने का प्रयास करें। उसी arduino.cc पर हम बाकी के बोर्ड को देखते हैं:


  • लियोनार्डो - उपयुक्त नहीं - 16 मेगाहर्ट्ज भी है
  • मेगा - भी - 16 मेगाहर्ट्ज
  • 101 - करेंगे - 32 मेगाहर्ट्ज
  • कारण - इससे भी बेहतर - 84 मेगाहर्ट्ज

यह माना जा सकता है कि यदि आप नियंत्रक की आवृत्ति 2 गुना बढ़ाते हैं, तो एलईडी की निमिष आवृत्ति भी 2 गुना बढ़ जाएगी, और यदि 5 से, तो 5 गुना।


Test_2


हमें वांछित परिणाम नहीं मिले। और जनरेटर एक मेन्डंडर की तरह कम और कम होता है। हम आगे सोचते हैं - अब, शायद, भाषा खराब है। ऐसा लगता है कि यह c, c ++ के साथ है, लेकिन यह मुश्किल है ( Dunning-Krueger प्रभाव के अनुसार, हम महसूस नहीं कर सकते कि हम पहले से ही c ++ में लिख रहे हैं), इसलिए हम विकल्पों की तलाश कर रहे हैं। एक छोटी खोज हमें BASCOM-AVR की ओर ले जाती है (यह इसके बारे में यहाँ बुरा नहीं बताया गया है ), इसे डालें, कोड लिखें:


 $Regfile="m328pdef.dat" $Crystal=16000000 Config Portb.5 = Output Do Toggle Portb.5 Loop 

हमें मिलता है:


Test_3


परिणाम बहुत बेहतर है, इसके अलावा, हमें सही मेन्डंडर मिला, लेकिन ... 2018 में बुनियादी रूप से, गंभीरता से? शायद हम इसे अतीत में छोड़ देंगे।


भाग 2 "उत्तर"


ऐसा लगता है कि मूर्खों को खेलना बंद करना और समझना शुरू करना है (और सी और एसेंबलर को भी याद रखना)। लूप () की शुरुआत में उल्लिखित लेख से "उपयोगी" कोड को कॉपी करें।


यहां, मेरा मानना ​​है कि, स्पष्टीकरण की आवश्यकता है: सभी कोड को arduino प्रोजेक्ट में लिखा जाएगा, लेकिन Atmel स्टूडियो 7.0 वातावरण में (वहाँ एक सुविधाजनक disassembler है), स्क्रीनशॉट इससे होगा।


 void setup() { DDRB |= (1 << 5); // PB5 } void loop() { PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON } 

परिणाम:


Test_4


यहाँ यह है! लगभग आपको जो चाहिए। केवल फॉर्म विशेष रूप से मेन्डंडर और आवृत्ति के समान नहीं है, हालांकि यह पहले से ही करीब है, लेकिन अभी भी समान नहीं है। हम हर मिलीसेकंड में सिग्नल को ज़ूम इन करने और खोजने की कोशिश करते हैं।


Test_5


यह मिलिस () के लिए जिम्मेदार टाइमर से इंटरप्ट के ट्रिगर के कारण है। इसलिए हम क्या करेंगे बस डिस्कनेक्ट है। हम आईएसआर (इंटरप्ट हैंडलर फंक्शन) की तलाश में हैं। हम पाते हैं:


 ISR(TIMER0_OVF_vect) { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = timer0_millis; nsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; } 

हमारे लिए बहुत सारे बेकार कोड। आप टाइमर ऑपरेशन मोड को बदल सकते हैं या व्यवधान को अक्षम कर सकते हैं, लेकिन यह हमारे उद्देश्यों के लिए अनावश्यक है, इसलिए केवल cli () कमांड के साथ सभी व्यवधानों को अक्षम करें। बस हमारे कोड को देखो:


 PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON 

बहुत सारे ऑपरेटर, एक असाइनमेंट को कम करते हैं।


 PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON 

हां, और लूप पर स्विच करना () बहुत सारी कमांड लेता है, क्योंकि यह मुख्य लूप में एक अतिरिक्त फ़ंक्शन है।


 int main(void) { init(); // ... setup(); for (;;) { loop(); if (serialEventRun) serialEventRun(); } return 0; } 

तो बस सेटअप में एक अंतहीन लूप बनाएं ()। हम निम्नलिखित प्राप्त करते हैं:


 void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON } } 

Test_6


61 ns नियंत्रक की आवृत्ति के अनुरूप अधिकतम है। क्या यह तेजी से संभव है? Spoiler - नहीं। आइए समझने की कोशिश करें कि क्यों - इसके लिए हम अपने कोड को अलग करते हैं:


Code_asm_1


जैसा कि स्क्रीन से देखा जा सकता है, पोर्ट 1 या 0 लिखने के लिए, ठीक 1 घड़ी चक्र खर्च होता है, लेकिन एक संक्रमण होता है जिसे एक घड़ी चक्र से कम में पूरा नहीं किया जा सकता है (RJMP दो घड़ी चक्रों में किया जाता है, और, उदाहरण के लिए, JMP तीन में )। और हम लगभग वहाँ हैं - एक मेन्डियर प्राप्त करने के लिए, आपको उस समय को बढ़ाने की आवश्यकता होती है जब 0 को दो उपायों द्वारा दिया जाता है। इस दो कोडांतरक एनओपी कमांड के लिए जोड़ें जो कुछ भी नहीं करते हैं लेकिन 1 घड़ी चक्र लेते हैं:


 void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF asm("nop"); asm("nop"); PORTB = 0b11111111; //ON } } 

Test_end


भाग 3 "निष्कर्ष"


दुर्भाग्य से, हमने जो कुछ भी किया वह व्यावहारिक दृष्टिकोण से बिल्कुल बेकार था, क्योंकि हम अब किसी भी कोड को निष्पादित नहीं कर सकते। साथ ही, 99.9% मामलों में, पोर्ट स्विचिंग आवृत्तियों किसी भी उद्देश्य के लिए पर्याप्त हैं। हां, और अगर हमें वास्तव में एक चिकनी मेयर उत्पन्न करने की आवश्यकता है, तो आप dm या NE555 जैसी बाहरी टाइमर चिप के साथ stm32 ले सकते हैं। यह आलेख सामान्य रूप से मेगा 328 पी और आर्डिनो उपकरणों को समझने के लिए उपयोगी है।


फिर भी, 8-बिट मान PORTB = 0b11111111; digitalWrite(13, 1); तुलना में बहुत तेज digitalWrite(13, 1); लेकिन आपको अन्य बोर्डों में कोड स्थानांतरित करने में असमर्थता के लिए इसके लिए भुगतान करना होगा, क्योंकि रजिस्टरों के नाम भिन्न हो सकते हैं।


केवल एक ही प्रश्न शेष है: तेज पत्थरों के उपयोग ने परिणाम क्यों नहीं दिए? उत्तर बहुत सरल है - जटिल प्रणालियों में, जीपीओ आवृत्ति कोर आवृत्ति से कम है। लेकिन कितना कम है और इसे कैसे सेट किया जा सकता है यह हमेशा एक विशिष्ट नियंत्रक पर डेटाशीट में देखा जा सकता है।


प्रकाशन ने लेखों का हवाला दिया:



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


All Articles