AVR के लिए कुशल उलटा (1 / x)


12

मैं एक AVR पर एक व्युत्क्रम की गणना करने का एक कुशल तरीका खोजने की कोशिश कर रहा हूं (या यह अनुमान लगा रहा हूं)।

मैं एक स्टेपर मोटर के लिए पल्स अवधि की गणना करने की कोशिश कर रहा हूं ताकि मैं गति को रैखिक रूप से भिन्न कर सकूं। अवधि गति के व्युत्क्रमानुपाती ( p = K/v) है, लेकिन मैं इसे मक्खी पर गणना करने का एक अच्छा तरीका नहीं सोच सकता।

मेरा सूत्र है

p = 202/v + 298; // p in us; v varies from 1->100

Arduino पर परीक्षण, ऐसा लगता है कि विभाजन को पूरी तरह से छोड़ने pपर अनदेखा किया गया है 298(हालांकि शायद यह avr-gcc में अलग होगा)। मैंने vएक लूप में योग करने की कोशिश की है जब तक कि यह अधिक नहीं हो जाता है 202, और छोरों की गिनती करता है, लेकिन यह काफी धीमा है।

मैं एक लुकअप टेबल जेनरेट कर सकता था और इसे फ्लैश में स्टोर कर सकता था, लेकिन मैं सोच रहा था कि क्या कोई और तरीका है।

संपादित करें : शायद शीर्षक "कुशल विभाजन" होना चाहिए ...

अपडेट : जैसा कि पिंगसवेप्ट बताता है, वेग की अवधि को मैप करने का मेरा फॉर्मूला गलत है। लेकिन मुख्य समस्या विभाजन ऑपरेशन है।

संपादित करें 2 : आगे की जांच पर, डिवाइड arduino पर काम कर रहा है, समस्या दोनों गलत सूत्र ऊपर और एक अंतर अतिप्रवाह दोनों के कारण था।


2
क्या v पूर्णांक या फ्लोटिंग पॉइंट है?
mjh2007

एक पूर्णांक, लेकिन जैसा कि यह हम में एक अवधि देता है, पूर्णांक विभाजन यहां पर्याप्त सटीक है।
पीटर गिब्सन

यदि आप वास्तव में गति से संबंधित हैं, तो आप 100 पूर्णांकों के मान को रोक सकते हैं और गुणन के लिए पूर्व स्केलरों की एक लुकअप तालिका बना सकते हैं। बेशक वहाँ एक स्मृति व्यापार बंद है।
आरवाईएस

जवाबों:


7

विभाजन के बारे में एक अच्छी बात यह है कि कमोबेश हर कोई इसे कर रहा है। यह सी भाषा की एक सुंदर मूल विशेषता है, और AVR-GCC (जिसे Arduino IDE द्वारा कहा जाता है) जैसे कंपाइलर उपलब्ध सर्वश्रेष्ठ डिवीजन एल्गोरिदम को चुनेंगे, जब माइक्रोकंट्रोलर में हार्डवेयर डिवीजन इंस्ट्रक्शन नहीं होता है।

दूसरे शब्दों में, आपको इस बारे में चिंता करने की ज़रूरत नहीं है कि जब तक आपके पास बहुत ही अजीब विशेष मामला नहीं है तब तक विभाजन कैसे लागू किया जाता है।


यदि आप चिंता करते हैं, तो आपको Atmel के आधिकारिक सुझाए गए डिवीजन एल्गोरिदम (कोड आकार के लिए अनुकूलित और निष्पादन की गति के लिए एक, न तो कोई डेटा मेमोरी नहीं लेना) पढ़ने में आनंद आ सकता है। वे अंदर हैं:

http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf

जो अनुप्रयोग नोट "AVR200: मल्टीप्ली और डिवाइड रूटीन" है इसके (यथोचित बड़े) Atmega प्रोसेसर के लिए Atmel पृष्ठ पर सूचीबद्ध Atmega 168 और Atmega 328 जैसे मानक Arduinos में उपयोग किया गया है। डेटा-शीट और एप्लिकेशन नोटों की सूची इस प्रकार है:

http://www.atmel.com/dyn/products/product_card.asp?part_id=4720


4

मुझे लगता है कि आप सभी की जरूरत है एक 100-प्रविष्टि लुकअप तालिका है। इससे ज्यादा तेज नहीं मिलता है।

#define VALUE_FOR_V_EQUALS_ZERO 0
uint16_t formula_lookup[100] = {VALUE_FOR_V_EQUALS_ZERO, 500, 399, 365, 348, ..., 300};

...

//"calculate" formula
p = formula_lookup[v > 67 ? 67 : v];

संपादित करें क्योंकि आप वास्तव में केवल ६ evaluate से अधिक के मान का मूल्यांकन करते हैं, ६ evaluate से अधिक v का मान हमेशा ३०० से अधिक होता है।


जैसा कि मैंने सवाल में कहा था, मैं सोच रहा था कि क्या कोई और तरीका है
पीटर गिब्सन

3

हेनरी वारेन और उनकी वेबसाइट hackersdelight.org पर "हैकर्स डिलाइट द्वारा " पुस्तक में कुछ बहुत अच्छी तकनीकों का उल्लेख किया गया है । एक ऐसी तकनीक के लिए जो छोटे माइक्रोकंट्रोलरों के साथ अच्छी तरह से काम करती है जब स्थिरांक द्वारा विभाजित किया जाता है, तो इस फाइल पर एक नज़र है ।


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

यह एक उत्कृष्ट पुस्तक है!
विंडेल ऑस्के

3

आपका फ़ंक्शन ऐसा नहीं लगता है कि यह आपको इच्छित परिणाम देगा। उदाहरण के लिए, मूल्य 50 मोटे तौर पर 302 रिटर्न देता है, जबकि 100 मोटे तौर पर 300 रिटर्न देता है। उन दो परिणामों के कारण मोटर की गति में लगभग कोई बदलाव नहीं होगा।

अगर मैं आपको सही ढंग से समझता हूं, तो आप वास्तव में 1-100 से 300-500 की रेंज (लगभग) की संख्या को मैप करने का एक तेज़ तरीका ढूंढ रहे हैं, जैसे कि 1 मैप्स 500 और 100 मैप्स 300 तक।

शायद कोशिश करें: p = 500 - (2 * v)

लेकिन मुझे गलतफहमी हो सकती है - क्या आप एक निरंतर आवृत्ति वर्ग तरंग के समय की गणना करने की कोशिश कर रहे हैं? 298 क्या है?


हां धन्यवाद, सूत्र गलत है। बिंदु को स्टेपर के आउटपुट से रैखिक त्वरण प्राप्त करना है, प्रत्येक बार अंतराल द्वारा लक्ष्य की गति को अलग करके (गति ++ कहना)। इस अवधि (आवृत्ति) के लिए मैप किया जाना चाहिए कि एक + ve बढ़त स्टेपर मोटर नियंत्रक को भेजी जाती है - इसलिए उलटा संबंध (पी = 1 / v)।
पीटर गिब्सन

क्या आप निरंतर त्वरण का मतलब है, यानी एक रैखिक रूप से बढ़ते वेग?
pingswept

आह हाँ, निरंतर त्वरण, मैंने सवाल किया कि मूल रूप से प्रश्न लिखते समय और उसे ठीक करने के लिए भी याद रखना चाहिए
पीटर गिब्सन

3

लगभग विभाजित करने के लिए एक कुशल तरीका पाली द्वारा है। जैसे अगर x = y / 103; 103 से विभाजित करना 0.0097087 से गुणा करने के समान है, इसलिए यह अनुमानित करने के लिए पहली बार एक 'अच्छा' शिफ्ट नंबर (यानी आधार -2 नंबर, 2,4,8,16,32 और इसी तरह) का चयन करें

इस उदाहरण के लिए 1024 एक अच्छा फिट है जैसा कि हम कह सकते हैं कि 10/1024 = 0.009765 इसका कोड करना संभव है:

x = (y * 10) >> 10;

चर को सुनिश्चित करने के लिए निश्चित रूप से याद रखना कि गुणा होने पर अपने प्रकार को ओवरफ्लो नहीं करता है। यह सटीक नहीं है, लेकिन इसकी जल्दी है।


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

3

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

http://en.wikipedia.org/wiki/Multiplicative_inverse

केवल गुणन और घटाव का उपयोग करके, x के पारस्परिक को अनुमानित करने के लिए, कोई संख्या y का अनुमान लगा सकता है, और फिर बार-बार y को 2y - xy2 से बदल सकता है। एक बार जब y में परिवर्तन (और रहता है) पर्याप्त रूप से छोटा हो जाता है, तो y, x के पारस्परिक का एक अनुमान है।


दिलचस्प है, मुझे आश्चर्य है कि यह उल्लेखित अन्य तरीकों की तुलना कैसे करता है
पीटर गिब्सन

1

यह प्रक्रिया यहाँ mcu के अनुकूल लगती है, हालाँकि इसमें पोर्टिंग की थोड़ी आवश्यकता हो सकती है।

हालांकि ऐसा लगता है कि LUT आसान होगा। आपको केवल 100 बाइट्स की आवश्यकता होगी, यदि आपने कुछ प्रक्षेप का उपयोग किया है, और चूंकि LUT निरंतर है, तो कंपाइलर डेटा क्षेत्र के बजाय कोड क्षेत्र में भी इसका पता लगा सकता है।


मैंने डिवाइडर को संक्षेप में समान करने की कोशिश की जब तक कि यह लाभांश के बराबर या उससे अधिक न हो जाए, लेकिन यह काफी धीमा पाया गया। ऐसा लगता है कि LUT जाने का रास्ता होगा - avr-gcc का उपयोग करके आपको फ्लैश में स्टोर करने के लिए <avr / progmem.h> में विशेष मैक्रो की आवश्यकता होगी।
पीटर गिब्सन

1

यह सुनिश्चित करने के लिए जांचें कि फ़्लोटिंग पॉइंट के रूप में विभाजन का प्रदर्शन किया जा रहा है। मैं माइक्रोचिप का उपयोग नहीं करता AVR, लेकिन C18 का उपयोग करते समय आपको अपने शाब्दिक को फ्लोटिंग पॉइंट के रूप में व्यवहार करने के लिए मजबूर करने की आवश्यकता होती है। उदाहरण के लिए। अपना सूत्र बदलने का प्रयास करें:

p = 202.0/v + 298.0;


1

आप उपवास चाहते हैं इसलिए यहाँ जाता है ..... चूंकि एवीआर कैंट सामान्य रूप से सामान्यीकरण करता है (जब तक आप अब शिफ्ट नहीं होते हैं तब तक छोड़ दिया जाता है), किसी भी छद्म फ्लोटिंग पॉइंट एल्गोरिदम को अनदेखा करें। एक AVR में बहुत सटीक और सबसे तेज़ पूर्णांक विभाजन के लिए सबसे सरल तरीका एक पारस्परिक लुक-अप तालिका है। तालिका एक बड़ी संख्या (2 2 32) द्वारा प्राप्त पारस्परिक स्टोर करेगी। आप तब एक अहस्ताक्षरित x x अहस्ताक्षरित = असेंबल किए गए 64 गुणन को असेंबलर में लागू करते हैं, इसलिए उत्तर = (अंशांक * व्युत्क्रम q32 [हर]] >> 32।
मैंने इनलाइन असेंबलर का उपयोग करके गुणा फ़ंक्शन को लागू किया, (एसी फ़ंक्शन में लिपटे)। जीसीसी 64-बिट "लंबे समय तक" का समर्थन करता है, हालांकि, परिणाम प्राप्त करने के लिए आपको 64 बिट्स से 64 बिट्स को गुणा करना होगा, 32x32 = 64 नहीं, 8-बिट आर्किटेक्चर पर सी भाषा सीमाओं के कारण ......

यदि आप 1 से 4096 तक पूर्णांकों द्वारा विभाजित करना चाहते हैं, तो इस विधि के नीचे आप 4K x 4 = 16K फ्लैश का उपयोग करेंगे।

बहुत सटीक अहस्ताक्षरित विभाजन अब सी में लगभग 300 चक्रों में हासिल किया गया है।

आप अधिक गति, कम सटीकता के लिए 24 बिट या 16 बिट स्केल किए गए पूर्णांक का उपयोग करने पर विचार कर सकते हैं।


1
p = 202/v + 298; // p in us; v varies from 1->100

आपके समीकरण का रिटर्न वैल्यू पहले से ही है p=298क्योंकि कंपाइलर पहले विभाजित होता है, फिर पूर्णांक muldiv रिज़ॉल्यूशन का उपयोग करें:

p = ((202*100)/v + (298*100))/100 

इसका उपयोग करना a*fएक = पूर्णांक f = भिन्न के साथ समान रूप से गुणा है ।

वह उपज r=a*fलेकिन f=b/cतब r=a*b/cलेकिन यह अभी तक काम नहीं करता है क्योंकि ऑपरेटरों की स्थिति, अंतिम r=(a*b)/cया muldiv फ़ंक्शन, केवल पूर्णांक का उपयोग करके अंश संख्याओं की गणना करने का एक तरीका है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.