मैं मिलिस () रोलओवर को कैसे संभाल सकता हूं?


73

मुझे हर पांच मिनट में एक सेंसर पढ़ने की जरूरत है, लेकिन चूंकि मेरे स्केच में भी अन्य कार्य करने हैं, इसलिए मैं सिर्फ delay()रीडिंग के बीच नहीं रह सकता । इन लाइनों के साथ I कोड का सुझाव दिए बिना देरी के ट्यूटोरियल के पलक है :

void loop()
{
    unsigned long currentMillis = millis();

    // Read the sensor when needed.
    if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        readSensor();
    }

    // Do other stuff...
}

समस्या यह है कि millis()लगभग 49.7 दिनों के बाद वापस शून्य पर लुढ़कने वाली है। चूंकि मेरा स्केच उससे अधिक समय तक चलने का इरादा है, मुझे यह सुनिश्चित करने की आवश्यकता है कि रोलओवर मेरे स्केच को विफल न करें। मैं आसानी से रोलओवर स्थिति ( currentMillis < previousMillis) का पता लगा सकता हूं , लेकिन मुझे यकीन नहीं है कि तब क्या करना है।

इस प्रकार मेरा प्रश्न: millis()रोलओवर को संभालने का उचित / सरल तरीका क्या होगा ?


5
संपादकीय नोट: यह वास्तव में मेरा प्रश्न नहीं है, बल्कि प्रश्न / उत्तर प्रारूप में एक ट्यूटोरियल है। मैंने इस विषय के बारे में इंटरनेट (यहाँ सहित) पर बहुत भ्रम देखा है, और यह साइट उत्तर खोजने के लिए स्पष्ट स्थान की तरह प्रतीत होती है। यही कारण है कि मैं इस ट्यूटोरियल को यहां प्रदान कर रहा हूं।
एडगर बोनट

2
मैं previousMillis += intervalइसके बजाय previousMillis = currentMillisअगर मैं परिणामों की एक निश्चित आवृत्ति चाहता था।
जसन

4
@ जैसन: यह सही है! previousMillis += intervalयदि आप निरंतर आवृत्ति चाहते हैं और सुनिश्चित हैं कि आपका प्रसंस्करण कम से कम लेता है interval, लेकिन previousMillis = currentMillisन्यूनतम विलंब की गारंटी देता है interval
एडगर बोनट

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

"ट्रिक्स" में से एक मैं सबसे छोटे इंट का उपयोग करके arduino पर लोड को हल्का करने के लिए है जिसमें अंतराल शामिल है। उदाहरण के लिए, अधिकतम 1 मिनट के अंतराल के लिए, मैं लिखता हूंuint16_t previousMillis; const uint16_t interval = 45000; ... uint16_t currentMillis = (uint16_t) millis(); if ((currentMillis - previousMillis) >= interval) ...
frarugi87

जवाबों:


95

संक्षिप्त उत्तर: मिलिस रोलओवर को "हैंडल" करने की कोशिश न करें, इसके बजाय रोलओवर-सुरक्षित कोड लिखें। ट्यूटोरियल से आपका उदाहरण कोड ठीक है। यदि आप सुधारात्मक उपायों को लागू करने के लिए रोलओवर का पता लगाने की कोशिश करते हैं, तो संभावना है कि आप कुछ गलत कर रहे हैं। अधिकांश Arduino कार्यक्रमों को केवल उन घटनाओं का प्रबंधन करना होता है, जो अपेक्षाकृत कम अवधि की होती हैं, जैसे कि 50 ms के लिए एक बटन को डीब्यू करना, या 12 घंटे के लिए हीटर चालू करना ... फिर, और यहां तक ​​कि अगर प्रोग्राम को एक समय में चलाने के लिए है, मिलिस रोलओवर एक चिंता का विषय नहीं होना चाहिए।

प्रबंधन करने का सही तरीका (या बल्कि, प्रबंधन करने से बचना) रोलओवर समस्या को मॉड्यूलर अंकगणित के संदर्भ में दिए unsigned longगए संख्या के बारे में सोचना है । गणितीय रूप से झुकाव के लिए, इस अवधारणा के साथ कुछ परिचित प्रोग्रामिंग करते समय बहुत उपयोगी होते हैं। आप निक गैमन के लेख मिलिस () अतिप्रवाह ... एक बुरी चीज में कार्रवाई में गणित देख सकते हैं ? । जो लोग कम्प्यूटेशनल विवरणों के माध्यम से नहीं जाना चाहते हैं, मैं उनके बारे में सोचने का एक वैकल्पिक (उम्मीद से सरल) तरीका पेश करता हूं। यह इंस्टेंट और ड्यूरेशन के बीच सरल अंतर पर आधारित है । जब तक आपके परीक्षणों में केवल अवधि की तुलना शामिल होती है, आपको ठीक होना चाहिए।millis()

माइक्रोस पर ध्यान दें () : यहां कहा गया सब कुछ millis()समान रूप से लागू होता है micros(), इस तथ्य को छोड़कर कि micros()हर 71.6 मिनट में रोल setMillis()करता है , और नीचे दिए गए फ़ंक्शन को प्रभावित नहीं करता है micros()

इंस्टेंट, टाइमस्टैम्प और अवधि

समय के साथ काम करते समय, हमें कम से कम दो अलग-अलग अवधारणाओं के बीच अंतर करना होगा: उदाहरण और अवधि । एक समय अक्ष पर एक बिंदु है। एक अवधि एक समय अंतराल की लंबाई होती है, यानी उन अंतरालों के बीच की दूरी जो अंतराल की शुरुआत और अंत को परिभाषित करते हैं। इन अवधारणाओं के बीच का अंतर हमेशा रोजमर्रा की भाषा में बहुत तेज नहीं होता है। उदाहरण के लिए, यदि मैं कहता हूं कि " मैं पांच मिनट में वापस आ जाऊंगा ", तो " पांच मिनट " मेरी अनुपस्थिति की अनुमानित अवधि है, जबकि " पांच मिनट में " तत्काल है मेरे आने की भविष्यवाणी की। भेद को ध्यान में रखना महत्वपूर्ण है, क्योंकि यह रोलओवर समस्या से पूरी तरह से बचने का सबसे सरल तरीका है।

वापसी मूल्य millis()को एक अवधि के रूप में व्याख्या किया जा सकता है: कार्यक्रम की शुरुआत से अब तक का समय। यह व्याख्या, हालांकि, मिलीसेक ओवरफ्लो होते ही टूट जाती है। यह millis()एक टाइमस्टैम्प को वापस करने के बारे में सोचने के लिए आम तौर पर कहीं अधिक उपयोगी है , अर्थात एक विशेष लेबल की पहचान करने वाला "लेबल"। यह तर्क दिया जा सकता है कि यह व्याख्या इन लेबल से अस्पष्ट है, क्योंकि वे हर 49.7 दिनों में पुन: उपयोग किए जाते हैं। हालांकि, यह एक समस्या है, शायद ही कभी: ज्यादातर एम्बेडेड अनुप्रयोगों में, 49.7 दिन पहले जो कुछ भी हुआ वह प्राचीन इतिहास है जिसकी हमें परवाह नहीं है। इस प्रकार, पुराने लेबल को पुनर्चक्रित करना एक मुद्दा नहीं होना चाहिए।

टाइमस्टैम्प की तुलना न करें

यह पता लगाने की कोशिश की जा रही है कि दो टाइमस्टैम्प्स में से कौन सा अन्य की तुलना में अधिक है, इसका कोई मतलब नहीं है। उदाहरण:

unsigned long t1 = millis();
delay(3000);
unsigned long t2 = millis();
if (t2 > t1) { ... }

ईमानदारी से, एक की स्थिति if ()हमेशा सच होने की उम्मीद होगी । लेकिन यह वास्तव में असत्य होगा यदि मिल के दौरान बहुत अधिक हो delay(3000)। T1 और t2 को रिसाइकिल लेबल के रूप में सोचना त्रुटि से बचने का सबसे सरल तरीका है: लेबल t1 को स्पष्ट रूप से t2 से पहले इंस्टेंट को सौंपा गया है, लेकिन 49.7 दिनों में इसे भविष्य के इंस्टेंट पर फिर से असाइन किया जाएगा। इस प्रकार, टी 1 टी 2 से पहले और बाद में दोनों होता है । यह स्पष्ट करना चाहिए कि अभिव्यक्ति का t2 > t1कोई मतलब नहीं है।

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

  1. later_timestamp - earlier_timestampएक अवधि की पैदावार, अर्थात् पहले के तात्कालिक और बाद के तात्कालिक के बीच का समय समाप्त हो जाता है। यह टाइमस्टैम्प से जुड़े सबसे उपयोगी अंकगणितीय ऑपरेशन है।
  2. timestamp ± durationएक टाइमस्टैम्प की पैदावार करता है जो प्रारंभिक टाइमस्टैम्प के बाद (यदि +) का उपयोग करने से पहले या (यदि -) है। यह उतना उपयोगी नहीं है जितना लगता है, क्योंकि टाइमस्टैम्प का उपयोग केवल दो प्रकार की गणनाओं में किया जा सकता है ...

मॉड्यूलर अंकगणित के लिए धन्यवाद, इन दोनों को मिलिस रोल ओवर में ठीक काम करने की गारंटी है, कम से कम जब तक शामिल विलंब 49.7 दिनों से कम हो।

तुलना करना ठीक है

एक अवधि कुछ समय के अंतराल के दौरान केवल मिलीसेकंड की मात्रा होती है। जब तक हमें 49.7 दिनों से अधिक अवधि को संभालने की आवश्यकता नहीं है, तब तक कोई भी ऑपरेशन जो शारीरिक रूप से समझ में आता है, उसे भी समझ में आना चाहिए। हम, उदाहरण के लिए, एक आवृत्ति को कई बार प्राप्त करने के लिए एक आवृत्ति से गुणा कर सकते हैं। या हम यह जानने के लिए दो अवधि की तुलना कर सकते हैं कि कौन अधिक लंबी है। उदाहरण के लिए, यहां दो वैकल्पिक कार्यान्वयन हैं delay()। सबसे पहले, छोटी गाड़ी एक:

void myDelay(unsigned long ms) {          // ms: duration
    unsigned long start = millis();       // start: timestamp
    unsigned long finished = start + ms;  // finished: timestamp
    for (;;) {
        unsigned long now = millis();     // now: timestamp
        if (now >= finished)              // comparing timestamps: BUG!
            return;
    }
}

और यहाँ सही है:

void myDelay(unsigned long ms) {              // ms: duration
    unsigned long start = millis();           // start: timestamp
    for (;;) {
        unsigned long now = millis();         // now: timestamp
        unsigned long elapsed = now - start;  // elapsed: duration
        if (elapsed >= ms)                    // comparing durations: OK
            return;
    }
}

अधिकांश सी प्रोग्रामर ऊपर दिए गए लूपों को एक टेस्टर रूप में लिखते हैं, जैसे

while (millis() < start + ms) ;  // BUGGY version

तथा

while (millis() - start < ms) ;  // CORRECT version

हालांकि वे भ्रामक रूप से समान दिखते हैं, टाइमस्टैम्प / अवधि भेद स्पष्ट करना चाहिए कि कौन से छोटी गाड़ी है और कौन सी सही है।

क्या होगा अगर मुझे वास्तव में टाइमस्टैम्प की तुलना करने की आवश्यकता है?

बेहतर है कि स्थिति से बचने की कोशिश करें। यदि यह अपरिहार्य है, तो अभी भी उम्मीद है यदि यह ज्ञात है कि संबंधित इंस्टेंट काफी करीब हैं: 24.85 दिनों की तुलना में करीब। हां, 49.7 दिनों की हमारी अधिकतम प्रबंधनीय देरी सिर्फ आधे में कट गई।

स्पष्ट समाधान हमारी टाइमस्टैम्प तुलना समस्या को अवधि तुलना समस्या में बदलना है। कहें कि हमें यह जानना चाहिए कि तत्काल t1 t2 से पहले या बाद में है। हम उनके सामान्य अतीत में कुछ संदर्भ तुरंत चुनते हैं, और इस संदर्भ से अवधि की तुलना t1 और t2 दोनों तक करते हैं। संदर्भ तत्काल को t1 या t2 में से एक लंबी पर्याप्त अवधि घटाकर प्राप्त किया जाता है:

unsigned long reference_instant = t2 - LONG_ENOUGH_DURATION;
unsigned long from_reference_until_t1 = t1 - reference_instant;
unsigned long from_reference_until_t2 = t2 - reference_instant;
if (from_reference_until_t1 < from_reference_until_t2)
    // t1 is before t2

इसे सरल बनाया जा सकता है:

if (t1 - t2 + LONG_ENOUGH_DURATION < LONG_ENOUGH_DURATION)
    // t1 is before t2

यह और सरल बनाने के लिए लुभावना है if (t1 - t2 < 0)। जाहिर है, यह काम नहीं करता है, क्योंकि t1 - t2, एक अहस्ताक्षरित संख्या के रूप में गणना की जा सकती है, नकारात्मक नहीं हो सकती है। यह, हालांकि, पोर्टेबल नहीं है, काम करता है:

if ((signed long)(t1 - t2) < 0)  // works with gcc
    // t1 is before t2

signedऊपर दिया गया कीवर्ड निरर्थक है (एक प्लेन longहमेशा हस्ताक्षरित है), लेकिन यह इरादे को स्पष्ट करने में मदद करता है। एक हस्ताक्षरित लंबे समय तक परिवर्तित करना LONG_ENOUGH_DURATION24.85 दिनों के बराबर स्थापित करने के बराबर है। चाल पोर्टेबल नहीं है, क्योंकि सी मानक के अनुसार, परिणाम कार्यान्वयन परिभाषित है । लेकिन चूंकि gcc कंपाइलर सही काम करने का वादा करता है, इसलिए यह Arduino पर मज़बूती से काम करता है। यदि हम कार्यान्वयन परिभाषित व्यवहार से बचना चाहते हैं, तो ऊपर हस्ताक्षरित तुलना गणितीय रूप से इसके समकक्ष है:

#include <limits.h>

if (t1 - t2 > LONG_MAX)  // too big to be believed
    // t1 is before t2

एकमात्र समस्या है कि तुलना पीछे की ओर दिखती है। यह समतुल्य है, जब तक कि 32-बिट्स हैं, इस एकल-बिट परीक्षण के लिए:

if ((t1 - t2) & 0x80000000)  // test the "sign" bit
    // t1 is before t2

पिछले तीन परीक्षण वास्तव में एक ही मशीन कोड में gcc द्वारा संकलित किए गए हैं।

मैं मिलिस रोलओवर के खिलाफ अपने स्केच का परीक्षण कैसे करूं

यदि आप ऊपर दिए गए उपदेशों का पालन करते हैं, तो आपको सभी अच्छे होने चाहिए। यदि आप फिर भी परीक्षण करना चाहते हैं, तो इस फ़ंक्शन को अपने स्केच में जोड़ें:

#include <util/atomic.h>

void setMillis(unsigned long ms)
{
    extern unsigned long timer0_millis;
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
        timer0_millis = ms;
    }
}

और अब आप अपने कार्यक्रम को कॉल करके समय-यात्रा कर सकते हैं setMillis(destination)। यदि आप चाहते हैं कि यह बार-बार मिलिस ओवरफ्लो से गुज़रता है, तो फिल कोनहॉग डे को जीते हुए फिल कॉनर्स की तरह, आप इसे अंदर रख सकते हैं loop():

// 6-second time loop starting at rollover - 3 seconds
if (millis() - (-3000) >= 6000)
    setMillis(-3000);

ऊपर (-3000) ऋणात्मक टाइमस्टैम्प को संकलक द्वारा 3000 मिलीसेकंड से पहले एक अहस्ताक्षरित लंबे समय तक रोलओवर (इसे 4294964296 में बदल दिया जाता है) से बदल दिया जाता है।

क्या होगा यदि मुझे वास्तव में बहुत लंबी अवधि को ट्रैक करने की आवश्यकता है?

यदि आपको तीन महीने बाद एक रिले को चालू करने और इसे बंद करने की आवश्यकता है, तो आपको वास्तव में मिली ओवरफ्लो को ट्रैक करने की आवश्यकता है। ऐसा करने के कई तरीके हैं। सबसे सरल समाधान millis() 64 बिट्स का विस्तार करना हो सकता है :

uint64_t millis64() {
    static uint32_t low32, high32;
    uint32_t new_low32 = millis();
    if (new_low32 < low32) high32++;
    low32 = new_low32;
    return (uint64_t) high32 << 32 | low32;
}

यह अनिवार्य रूप से रोलओवर घटनाओं की गिनती कर रहा है, और 64 बिट मिलीसेकंड गिनती के 32 सबसे महत्वपूर्ण बिट्स के रूप में इस गिनती का उपयोग कर रहा है। इस गिनती को ठीक से काम करने के लिए, फ़ंक्शन को हर 49.7 दिनों में कम से कम एक बार बुलाया जाना चाहिए। हालांकि, अगर इसे केवल 49.7 दिनों के लिए एक बार कहा जाता है, तो कुछ मामलों में यह संभव है कि चेक (new_low32 < low32)विफल हो जाता है और कोड की एक गिनती याद आती है high32। मिलिस (एक विशिष्ट 49.7 दिन की खिड़की) के एकल "रैप" में इस कोड को एकमात्र कॉल करने के लिए निर्णय लेने के लिए मिलिस () का उपयोग करना बहुत खतरनाक हो सकता है, यह इस बात पर निर्भर करता है कि समय सीमा कैसे रेखाबद्ध होती है। सुरक्षा के लिए, यदि केवल कॉल करने के लिए मिलिस () का उपयोग करके मिलिस 64 () में एकमात्र कॉल करना है, तो प्रत्येक 49.7 दिन की विंडो में कम से कम दो कॉल होनी चाहिए।

हालांकि, ध्यान रखें कि Arduino पर 64 बिट अंकगणित महंगा है। यह 32 बिट्स पर रहने के लिए समय रिज़ॉल्यूशन को कम करने के लिए लायक हो सकता है।


2
तो, आप कह रहे हैं कि प्रश्न में लिखा कोड वास्तव में सही ढंग से काम करेगा?
जसन

3
@ जैसन: बिल्कुल! मैंने एक बार से अधिक लोगों को समस्या को "ठीक" करने की कोशिश की है जो पहले से मौजूद नहीं था।
एडगर बोनट

2
मुझे खुशी है कि मैंने यह पाया। मैंने पहले भी यह सवाल किया है।
सेबेस्टियन फ्रीमैन

1
StackExchange पर सबसे अच्छे और सबसे उपयोगी उत्तरों में से एक! आपका बहुत बहुत धन्यवाद! :)
फाल्को

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

17

TL; DR लघु संस्करण:

एक unsigned long0 से 4,294,967,295 (2 ^ 32 - 1) है।

तो कहने दें previousMillisकि 4,294,967,290 (रोलओवर से पहले 5 एमएस), और currentMillis10 (रोलओवर के बाद 10ms) है। फिर currentMillis - previousMillisवास्तविक 16 है (-4,294,967,280 नहीं) क्योंकि परिणाम की गणना एक अहस्ताक्षरित लंबी (जो नकारात्मक नहीं हो सकती है, इसलिए खुद ही चारों ओर लुढ़क जाएगी)। आप इसे बस द्वारा देख सकते हैं:

Serial.println( ( unsigned long ) ( 10 - 4294967290 ) ); // 16

तो उपरोक्त कोड पूरी तरह से ठीक काम करेगा। चाल हमेशा समय के अंतर की गणना करने के लिए होती है, न कि दो समय मूल्यों की तुलना करने के लिए।


रोलओवर से पहले कैसे 15 सेंटीमीटर और रोलओवर के बाद 10ms (यानी 49.7 दिन बाद )। 15> 10 , लेकिन 15ms की मुहर लगभग डेढ़ महीने पुरानी है। 15-10> 0 और 10-15> 0 unsigned तर्क, ताकि यहाँ कोई फायदा न हो!
ps95

@ prakharsingh95 10ms-15ms ~ 49.7 दिन - 5ms हो जाएगा, जो कि सही अंतर है। गणित millis()दो बार रोल करने तक काम करता है , लेकिन यह प्रश्न में कोड होने की संभावना नहीं है।
ब्रेटम

मुझे rephrase करते हैं। मान लीजिए कि आपके पास दो टाइमस्टैम्प 200ms और 10ms हैं। आप कैसे बताते हैं कि कौन सा (हैं) लुढ़का हुआ है?
ps95

@ prakharsingh95 पहले संग्रहित previousMillisकिया गया है जिसे पहले मापा गया है currentMillis, इसलिए यदि कोई रोलओवर हुआ currentMillisहै तो वह छोटा है previousMillis। गणित से पता चलता है कि जब तक दो रोलओवर नहीं हुए हैं, तब तक आपको इसके बारे में सोचने की जरूरत नहीं है।
ब्रेटम

1
आह अच्छा। यदि आप करते हैं t2-t1, और यदि आप गारंटी दे सकते हैं t1कि पहले मापा गया है t2तो यह हस्ताक्षरित के बराबर है (t2-t1)% 4,294,967,295, इसलिए ऑटो रैपराउंड। नाइस !. लेकिन क्या होगा अगर दो रोलओवर हों, या interval> 4,294,967,295 हो?
ps95

1

millis()एक कक्षा में लपेटें !

तर्क:

  1. millis()सीधे के बजाय आईडी का उपयोग करें ।
  2. आईडी के उपयोग से रिवर्सल की तुलना करें। यह स्वच्छ और रोलओवर स्वतंत्र है।
  3. विशिष्ट अनुप्रयोगों के लिए, दो आईडी के बीच सटीक अंतर की गणना करने के लिए, रिवर्सल और टिकटों का ट्रैक रखें। अंतर की गणना करें।

उलट-पलट का ध्यान रखना:

  1. समय-समय पर स्थानीय स्टाम्प को अपडेट करें millis()। यह पता लगाने में आपकी मदद करेगा कि millis()क्या ओवरफ्लो हुआ है।
  2. टाइमर की अवधि सटीकता निर्धारित करती है
class Timer {

public:
    static long last_stamp;
    static long *stamps;
    static int *reversals;
    static int count;
    static int reversal_count;

    static void setup_timer() {
        // Setup Timer2 overflow to fire every 8ms (125Hz)
        //   period [sec] = (1 / f_clock [sec]) * prescale * (255-count)
        //                  (1/16000000)  * 1024 * (255-130) = .008 sec


        TCCR2B = 0x00;        // Disable Timer2 while we set it up

        TCNT2  = 130;         // Reset Timer Count  (255-130) = execute ev 125-th T/C clock
        TIFR2  = 0x00;        // Timer2 INT Flag Reg: Clear Timer Overflow Flag
        TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable
        TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal
        TCCR2B = 0x07;        // Timer2 Control Reg B: Timer Prescaler set to 1024

        count = 0;
        stamps = new long[50];
        reversals = new int [10];
        reversal_count =0;
    }

    static long get_stamp () {
        stamps[count++] = millis();
        return count-1;
    }

    static bool compare_stamps_by_id(int s1, int s2) {
        return s1 > s2;
    }

    static long long get_stamp_difference(int s1, int s2) {
        int no_of_reversals = 0;
        for(int j=0; j < reversal_count; j++)
        if(reversals[j] < s2 && reversals[j] > s1)
            no_of_reversals++;
        return stamps[s2]-stamps[s1] + 49.7 * 86400 * 1000;       
    }

};

long Timer::last_stamp;
long *Timer::stamps;
int *Timer::reversals;
int Timer::count;
int Timer::reversal_count;

ISR(TIMER2_OVF_vect) {

    long stamp = millis();
    if(stamp < Timer::last_stamp) // reversal
        Timer::reversals[Timer::reversal_count++] = Timer::count;
    else 
        ; // no reversal
    Timer::last_stamp = stamp;    
    TCNT2 = 130;     // reset timer ct to 130 out of 255
    TIFR2 = 0x00;    // timer2 int flag reg: clear timer overflow flag
};

// Usage

void setup () {
    Timer::setup_timer();

    long s1 = Timer::get_stamp();
    delay(3000);
    long s2 = Timer::get_stamp();

    Timer::compare_stamps_by_id(s1, s2); // true

    Timer::get_stamp_difference(s1, s2); // return true difference, taking into account reversals
}

टाइमर क्रेडिट


9
मैंने Maaaany त्रुटियों को दूर करने के लिए कोड को संपादित किया जो इसे संकलन करने से रोकता है। इस सामान की कीमत आपको 232 बाइट्स रैम और दो पीडब्लूएम चैनलों से चुकानी पड़ेगी। यह get_stamp()51 बार आपके बाद स्मृति को दूषित करना भी शुरू कर देगा । टाइमस्टैम्प के बजाय देरी की तुलना करना निश्चित रूप से अधिक कुशल होगा।
एडगर बोनट

1

मुझे यह सवाल बहुत अच्छा लगा, और इससे उत्पन्न शानदार जवाब। पिछले उत्तर पर पहले एक त्वरित टिप्पणी (मुझे पता है, मुझे पता है, लेकिन मेरे पास अभी तक टिप्पणी करने के लिए प्रतिनिधि नहीं है। :-)

एडगर बोनेट का जवाब आश्चर्यजनक था। मैं 35 साल से कोडिंग कर रहा हूं, और मैंने आज कुछ नया सीखा है। धन्यवाद। उस ने कहा, मुझे विश्वास है कि "क्या होगा यदि मुझे वास्तव में बहुत लंबी अवधि को ट्रैक करने की आवश्यकता है?" तोड़ता है जब तक कि आप मिलिस 64 () को कम से कम एक बार रोलओवर अवधि कहते हैं। वास्तव में नाइटपिकी, और वास्तविक दुनिया के कार्यान्वयन में एक मुद्दा होने की संभावना नहीं है, लेकिन आप वहां जाते हैं।

अब, यदि आप वास्तव में किसी भी समय सीमा को कवर करने वाले टाइमस्टैम्प चाहते हैं (मेरे रेकॉन्डिंग से मिलिसकंड का 64-बिट्स लगभग आधा बिलियन वर्ष है), तो यह मौजूदा मिलिस () कार्यान्वयन को 64 बिट्स तक विस्तारित करने के लिए सरल दिखता है।

एटीन्यूकोर / वायरिंग.सी (मैं एटीटीनी 85 के साथ काम कर रहा हूं) में ये बदलाव काम करने लगते हैं (मैं मान रहा हूं कि अन्य AVR के लिए कोड बहुत समान है)। // BFB टिप्पणियों और नई मिलिस 64 () फ़ंक्शन वाली पंक्तियों को देखें। स्पष्ट रूप से यह दोनों बड़ा (98 बाइट्स कोड, 4 बाइट्स ऑफ़ डेटा) और धीमे होने वाला है, और जैसा कि एडगर ने बताया, आप लगभग निश्चित रूप से अहस्ताक्षरित पूर्णांक गणित की बेहतर समझ के साथ अपने लक्ष्यों को पूरा कर सकते हैं, लेकिन यह एक दिलचस्प अभ्यास था ।

volatile unsigned long long timer0_millis = 0;      // BFB: need 64-bit resolution

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long long m = timer0_millis;       // BFB: need 64-bit resolution
    unsigned 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++;
}

// BFB: 64-bit version
unsigned long long millis64()
{
    unsigned long long m;
    uint8_t oldSREG = SREG;

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
    m = timer0_millis;
    SREG = oldSREG;

    return m;
}

1
आप सही हैं, मेरा millis64()एकमात्र काम है अगर इसे रोलओवर अवधि से अधिक बार कहा जाता है। मैंने इस सीमा को इंगित करने के लिए अपना उत्तर संपादित किया। आपके संस्करण में यह समस्या नहीं है, लेकिन यह एक और दोष है: यह रुकावट के संदर्भ में 64-बिट अंकगणित करता है , जो कभी-कभी अन्य व्यवधानों के जवाब में विलंबता को बढ़ाता है।
एडगर बोनट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.