আমি মিলিস () রোলওভারটি কীভাবে পরিচালনা করতে পারি?


73

আমাকে প্রতি পাঁচ মিনিটে সেন্সর পড়তে হবে, তবে যেহেতু আমার স্কেচটিতে অন্যান্য কাজও করা আছে, আমি কেবল পড়ার delay()মধ্যেই পারছি না । এই লাইনগুলিতে আই কোডটি দেওয়ার পরামর্শ দিয়ে দেরি না করে ব্লিঙ্ক রয়েছে:

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
এডগার বোনেট

এই জাতীয় জিনিসগুলির জন্য আমাদের সত্যিকারের একটি FAQ প্রয়োজন।

আমি যে "কৌশলগুলি" ব্যবহার করি তার মধ্যে একটি হ'ল অন্তর অন্তর্ভুক্ত ক্ষুদ্রতম ইন্ট ব্যবহার করে আরডুইনোতে বোঝা হালকা করা। উদাহরণস্বরূপ, সর্বাধিক 1 মিনিটের ব্যবধানের জন্য, আমি লিখিuint16_t previousMillis; const uint16_t interval = 45000; ... uint16_t currentMillis = (uint16_t) millis(); if ((currentMillis - previousMillis) >= interval) ...
ফ্রেগুই 87

উত্তর:


95

সংক্ষিপ্ত উত্তর: মিলিস রোলওভারটি "পরিচালনা" করার চেষ্টা করবেন না, পরিবর্তে রোলওভার-নিরাপদ কোড লিখুন। টিউটোরিয়াল থেকে আপনার উদাহরণ কোড ঠিক আছে। আপনি যদি সংশোধনমূলক পদক্ষেপগুলি প্রয়োগ করার জন্য রোলওভারটি সনাক্ত করার চেষ্টা করেন, সম্ভবত আপনি কোনও ভুল করছেন। বেশিরভাগ আরডিনো প্রোগ্রামগুলিতে কেবল এমন ইভেন্টগুলি পরিচালনা করতে হয় যা তুলনামূলকভাবে সংক্ষিপ্ত সময়সীমা বিস্তৃত হয়, যেমন 50 এমএসের জন্য একটি বোতাম ডিবাজ করা, বা 12 ঘন্টা একটি হিটার চালু করা ... তারপরেও, এবং এমনকি যদি প্রোগ্রামটি একসাথে কয়েক বছরের জন্য চালানো হয়, মিলিস রোলওভারটি উদ্বেগের বিষয় হওয়া উচিত নয়।

রোলওভার সমস্যাটি পরিচালনা করার সঠিক উপায় (বা বরং, পরিচালনা করা এড়ানো) হ'ল মডুলার গাণিতিকের ক্ষেত্রে unsigned longফিরে আসা সংখ্যাটি ভাবা । গাণিতিকভাবে ঝুঁকির জন্য, প্রোগ্রামিং করার সময় এই ধারণার সাথে কিছু পরিচিতি খুব দরকারী। আপনি নিক গ্যামনের নিবন্ধ মিলিস () উপচে পড়া ... কোনও খারাপ জিনিসটিতে অংকিত অংকটি দেখতে পাচ্ছেন ? । যারা গণনা সংক্রান্ত বিশদটি নিয়ে যেতে চান না তাদের জন্য আমি এখানে বিকল্পের (আশাবাদী সরল) এটি সম্পর্কে চিন্তাভাবনা করার প্রস্তাব দিই। এটি তাত্ক্ষণিকসময়কালের মধ্যে সাধারণ পার্থক্যের ভিত্তিতে তৈরি । যতক্ষণ আপনার পরীক্ষাগুলি কেবলমাত্র সময়কালগুলির তুলনা করে জড়িত থাকে ততক্ষণ আপনার ভাল হওয়া উচিত।millis()

মাইক্রোগুলিতে নোট () : এখানে প্রতিটি কথাই millis()সমানভাবে প্রযোজ্য micros(), ব্যতীত micros()প্রতিটি 71১. minutes মিনিটের পরে রোলগুলি তৈরি হয় এবং setMillis()নীচে প্রদত্ত ফাংশনটি প্রভাবিত করে না micros()

তাত্ক্ষণিক, টাইমস্ট্যাম্প এবং সময়কাল

সময়ের সাথে মোকাবিলা করার সময়, আমাদের কমপক্ষে দুটি পৃথক ধারণার মধ্যে তফাত তৈরি করতে হবে: তাত্ক্ষণিকসময়কাল । তাত্ক্ষণিক সময় অক্ষের একটি বিন্দু। একটি সময়কাল হ'ল সময় ব্যবধানের দৈর্ঘ্য, অর্থাত্ তাত্ক্ষণিকের মধ্যে সময়ের দূরত্ব যা অন্তরালের শুরু এবং শেষকে সংজ্ঞায়িত করে। প্রতিদিনের ভাষায় এই ধারণাগুলির মধ্যে পার্থক্য সর্বদা খুব তীক্ষ্ণ হয় না। উদাহরণস্বরূপ, আমি যদি বলি যে " আমি পাঁচ মিনিটের মধ্যে ফিরে আসছি ", তবে " পাঁচ মিনিট " হ'ল আমার অনুপস্থিতির আনুমানিক সময়কাল , যখন " পাঁচ মিনিটের মধ্যে " তাত্ক্ষণিক আমার পূর্বাভাস ফিরে আসছে। পার্থক্য মাথায় রাখা গুরুত্বপূর্ণ, কারণ এটি সম্পূর্ণরূপে রোলওভার সমস্যা এড়ানো সহজ উপায়।

এর রিটার্ন মানটি millis()একটি সময়কাল হিসাবে ব্যাখ্যা করা যেতে পারে: প্রোগ্রামের শুরু থেকে এখন অবধি সময় অতিবাহিত। এই ব্যাখ্যাটি অবশ্য মিলিসের উপচে পড়ার সাথে সাথেই ভেঙে যায়। millis()একটি টাইমস্ট্যাম্প ফিরে আসার কথা চিন্তা করার জন্য সাধারণত আরও বেশি কার্যকর , যেমন একটি নির্দিষ্ট তাত্ক্ষণিক শনাক্তকরণের একটি "লেবেল"। এটি যুক্তিযুক্ত হতে পারে যে এই ব্যাখ্যাটি এই লেবেলগুলি অস্পষ্ট হওয়ার কারণে ভোগ করে, কারণ প্রতি 49.7 দিন এগুলি পুনরায় ব্যবহার করা হয়। এটি অবশ্য খুব কমই সমস্যা: বেশিরভাগ এম্বেড থাকা অ্যাপ্লিকেশনগুলিতে, 49.7 দিন আগে যা ঘটেছিল তা প্রাচীন ইতিহাস যা আমরা যত্ন করি না। সুতরাং, পুরানো লেবেলগুলি পুনর্ব্যবহার করা কোনও সমস্যা হওয়া উচিত নয়।

টাইমস্ট্যাম্প তুলনা করবেন না

দুটি টাইমস্ট্যাম্পগুলির মধ্যে কোনটি অপরটির চেয়ে বেশি এটি সন্ধান করার চেষ্টা করা অর্থহীন নয়। উদাহরণ:

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

উদাসীনভাবে, একজনের শর্তটি if ()সর্বদা সত্য হওয়ার প্রত্যাশা করবে । মিলিসের সময় ওভারফ্লো হয়ে গেলে এটি আসলে মিথ্যা হবে delay(3000)। টি -1 এবং টি 2 কে পুনর্ব্যবহারযোগ্য লেবেল হিসাবে ভাবা ত্রুটিটি এড়ানোর সহজতম উপায়: লেবেল টি 1 টি 2 এর আগে একটি তাত্ক্ষণিকভাবে স্পষ্টভাবে বরাদ্দ করা হয়েছে, তবে 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 দিনেরও কাছাকাছি closer হ্যাঁ, আমাদের সর্বোচ্চ 49,7 দিনের ব্যবস্থাপত্র বিলম্ব অর্ধেক কেটে গেছে।

সুস্পষ্ট সমাধান হ'ল আমাদের টাইমস্ট্যাম্প তুলনা সমস্যাটিকে একটি সময়কাল তুলনামূলক সমস্যায় রূপান্তর করা। বলুন আমাদের তাত্ক্ষণিক t1 টি 2 এর আগে বা পরে আছে কিনা তা জানতে হবে। আমরা তাদের সাধারণ অতীতে কিছু রেফারেন্স তাত্ক্ষণিক চয়ন করি এবং 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 দিনের সমান সেটিংয়ের সমান। কৌশলটি পোর্টেবল নয় কারণ সি স্ট্যান্ডার্ড অনুযায়ী ফলাফলটি বাস্তবায়িত সংজ্ঞায়িত । তবে যেহেতু জিসিসি সংকলক সঠিক কাজটি করার প্রতিশ্রুতি দিয়েছে , তাই এটি আরডুইনোর উপর নির্ভরযোগ্যভাবে কাজ করে। যদি আমরা বাস্তবায়িত সংজ্ঞায়িত আচরণ এড়াতে চাই তবে উপরের স্বাক্ষরিত তুলনাটি গাণিতিকভাবে এর সমান:

#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

শেষ তিনটি পরীক্ষা প্রকৃতপক্ষে একই মেশিন কোডে জিসিসি দ্বারা সংকলিত হয়।

মিলিস রোলওভারের বিপরীতে আমি কীভাবে আমার স্কেচটি পরীক্ষা করব

আপনি যদি উপরের আদেশগুলি অনুসরণ করেন তবে আপনার ভাল হওয়া উচিত। তবুও আপনি যদি পরীক্ষা করতে চান তবে আপনার স্কেচে এই ফাংশনটি যুক্ত করুন:

#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 দিনের উইন্ডোটিতে কমপক্ষে দুটি কল থাকা উচিত।

তবে মনে রাখবেন যে, আরডুইনোতে bit৪ বিটের গাণিতিক ব্যয়বহুল। 32 বিট এ থাকার জন্য সময় রেজোলিউশনটি হ্রাস করার উপযুক্ত হতে পারে।


2
সুতরাং, আপনি বলছেন যে প্রশ্নে লিখিত কোডটি আসলে সঠিকভাবে কাজ করবে?
জেসেন

3
@ জেসেন: ঠিক! আমি একাধিকবার মনে করেছি যে লোকেরা প্রথম স্থানে বিদ্যমান সমস্যাটি "সমাধান" করার চেষ্টা করেছিল।
এডগার বোনেট

2
আমি খুজে পেয়েছি আমি আগে এই প্রশ্ন ছিল।
সেবাস্তিয়ান ফ্রিম্যান

1
স্ট্যাক এক্সচেঞ্জের সেরা এবং সবচেয়ে দরকারী উত্তরগুলির মধ্যে একটি! অনেক ধন্যবাদ! :)
ফালকো

এটি প্রশ্নের এমন আশ্চর্যজনক উত্তর is আমি মূলত বছরে একবার এই উত্তরে ফিরে আসি কারণ আমি গণ্ডগোলের রোলওভারগুলি নিয়ে ভীতু।
জেফ্রি নগদ

17

টি এল; ডিআর সংক্ষিপ্ত সংস্করণ:

একটি unsigned long0 থেকে 4,294,967,295 (2 ^ 32 - 1)।

সুতরাং আসুন ধরা যাক previousMillis4,294,967,290 (রোলওভারের আগে 5 এমএস), এবং currentMillisএটি 10 ​​(রোলওভারের পরে 10 মিমি)। তারপরে currentMillis - previousMillisপ্রকৃত 16 (ফলাফল -4,294,967,280 নয়) যেহেতু ফলাফলটি স্বাক্ষরবিহীন দীর্ঘ হিসাবে গণনা করা হবে (যা নেতিবাচক হতে পারে না, তাই এটি নিজেই চারপাশে ঘুরবে )। আপনি কেবল এটি দ্বারা পরীক্ষা করতে পারেন:

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

সুতরাং উপরের কোডটি পুরোপুরি সূক্ষ্মভাবে কাজ করবে। কৌশলটি সর্বদা সময় পার্থক্য গণনা করা, এবং দুটি সময়ের মান তুলনা করা হয়।


কিভাবে একটি সম্পর্কে 15ms রোলওভার আগে ও একটি 10ms রোলওভার পর (অর্থাত 49,7 দিন পর )। 15> 10 , তবে 15 মিমি স্ট্যাম্পটি প্রায় দেড় মাস পুরানো। 15-10> 0 এবং 10-15> 0 unsigned যুক্তি, যাতে এখানে কোন ব্যবহার হয় না!
PS95

@ প্রখরসিংহ 95 ms 49.7 দিন - 5 মিমি হয়ে যাবে যা সঠিক পার্থক্য। গণিতটি millis()দু'বার রোল না হওয়া পর্যন্ত কাজ করে, তবে কোডটিতে প্রশ্নটি হওয়ার সম্ভাবনা খুব কম।
ব্রেটাম

আমাকে পুনরায় চাপ দিন। ধরুন আপনার কাছে দুটি টাইমস্ট্যাম্প 200ms এবং 10 মিমি রয়েছে। আপনি কীভাবে বলবেন যা কোনটি ঘূর্ণিত হয়েছে?
PS95

@ prakharsingh95 এতে সঞ্চিত একটিকে previousMillisআগে পরিমাপ করা উচিত currentMillis, তাই যদি কোনও রোলওভারের currentMillisচেয়ে ছোট হয় তবে previousMillis। এই গণিতটি কার্যকর হতে পারে যে দুটি রোলওভার না ঘটলে আপনার এ সম্পর্কে ভাবার দরকার নেই।
ব্রেটাম

1
আহ, ঠিক আছে. যদি আপনি এটি করেন t2-t1, এবং যদি আপনি গ্যারান্টি দিতে পারেন তবে t1এটি আগে স্বাক্ষরিতt2 সমতুল্য , অতএব স্বয়ংক্রিয় মোড়কের সমান meas নিস !. তবে সেখানে যদি দুটি রোলওভার থাকে, বা > 4,294,967,295 হয়? (t2-t1)% 4,294,967,295interval
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
আমি কোডটি সম্পাদনা করে maaaaany ত্রুটিগুলি মুছে ফেলার জন্য যা এটি সংকলন করতে বাধা দিয়েছে। এই স্টাফটির জন্য আপনার প্রায় 232 বাইট র‌্যাম এবং দুটি পিডব্লিউএম চ্যানেল ব্যয় করতে হবে। এটি get_stamp()51 বার আপনার পরে স্মৃতিটিকে দূষিত করতে শুরু করবে । টাইমস্ট্যাম্পের পরিবর্তে বিলম্বের তুলনা করা আরও কার্যকর হবে।
এডগার বোনেট

1

আমি এই প্রশ্নটি পছন্দ করেছিলাম এবং এটি উত্পন্ন দুর্দান্ত উত্তরগুলি। পূর্ববর্তী উত্তরের উপর প্রথম তাত্ক্ষণিক মন্তব্য (আমি জানি, আমি জানি, তবে এখনও মন্তব্য করার মতামত আমার কাছে নেই। :-)।

এডগার বোনেটের উত্তরটি ছিল আশ্চর্যজনক। আমি 35 বছর ধরে কোডিং করছি, এবং আমি আজ নতুন কিছু শিখেছি। ধন্যবাদ. এটি বলেছিল, "আমি যদি খুব দীর্ঘ সময়সীমার সত্যতা অবলম্বন করতে চাই তবে কিসের কোডটি আমি বিশ্বাস করি ?" বিরতি আপনি যদি রোলওভার সময়কালে একবারে মিলিস 64 () কল না করেন। সত্যই নিটপিকি এবং বাস্তব-বাস্তবায়নের ক্ষেত্রে সমস্যা হওয়ার সম্ভাবনা নেই তবে আপনি সেখানে যান।

এখন, যদি আপনি সত্যিই কোনও বুদ্ধিমান সময়সীমা (covering৪-বিট মিলিসেকেন্ডের আমার গণনা দ্বারা প্রায় অর্ধ বিলিয়ন বছর) আচ্ছাদিত টাইমস্ট্যাম্পগুলি চান, তবে বিদ্যমান মিলিস () বাস্তবায়নটি b৪ বিটের মধ্যে প্রসারিত করা সহজ বলে মনে হচ্ছে।

অ্যাটিইনিকোর / ওয়্যারিং.কমের এই পরিবর্তনগুলি (আমি এটিটিইএন 85 এর সাথে কাজ করছি) মনে হচ্ছে কাজ করছে (আমি ধরে নিচ্ছি যে অন্যান্য এভিআরের জন্য কোডটি খুব একই রকম)। // বিএফবি মন্তব্যগুলি এবং নতুন মিলিস 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৪-বিট পাটিগণিত করে , যা মাঝে মাঝে অন্যান্য বাধাগুলির প্রতিক্রিয়া জানাতে বিলম্বিতা বাড়ে।
এডগার বোনেট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.