آردوینو آموزش آردوینو

آموزش و بررسی زمان سنجی با تابع millis در آردوینو Arduino

نوشته شده توسط معین صابری

پلتفرم آردویینو، یکی از پلتفرم های سخت افزاری/ نرم افزاری است که طی دو دهه اخیر، تحول شگرفی در زمینه الکترونیک و برنامه نویسی سیستم های سخت افزاری ایجاد کرده است. به کمک بردهای آردویینو و نرم افزار آن، روند توسعه و اجرای پروژه ها، افزایش می یابد. سهولت در کار، وجود منابع آموزشی فراوان و جامع کاربری گسترده سبب شده تمام افراد فعال در حوزه الکترونیک، چه با سطح حرفه ای و چه در سطح کار آموز، جزو مخاطبین این برد باشند. در این آموزش به بررسی یکی از توابع مهم و کاربردی آردویینو، ()millis می پردازیم. این تابع که برای کنترل زمانبندی فرایندهای مختلف به کار می رود، قادر است تا مدت زمان طی شده از زمان راه اندازی آردویینو تاکنون را اندازه گیری کرده و بر حسب میلی ثانیه، در خروجی تولید کند. اما کار با این تابع نیاز به رعایت الزامات و نکاتی است که در این آموزش به آن می پردازیم. در ادامه با مرجع تخصصی آردوینو به زبان فارسی، دیجی اسپارک همراه باشید.

 


اهمیت زمانبدی در برنامه نویسی


یکی از مهم ترین مباحث در برنامه نویسی سیستم ها، کنترل یک فراید و تعیین یک مدت زمان مشخص جهت اجرای آن است. برای روشن تر شدن موضوع، فرض کنید که قصد ارسال داده به یک سرور، از طریق ماژول های سیم کارتی را دارید. این عمل، با توجه به تاخیرات موجود در شبکه، زمانبر خواهد بود. از طرفی، ممکن است در بعضی موارد، با ارسال یک داده به سرور، پاسخی از سرور دریافت نشود. بنابراین، جهت کنترل این روال و ایجاد یک برنامه استاندارد، می بایست یک مدت زمان مشخص جهت انتظار برای دریافت پاسخ از سرور، تعیین شود. چنانچه در مدت زمان مورد نظر، پاسخی از سرور دریافت نشد، اجرای این روال متوقف شده و برنامه سایر دستورات را از سر خواهد گرفت.

اهمیت زمانبدی فرایندها در برنامه نویسی - دیجی اسپارک

 

مطابق شکل فوق، ورودی به تابع اعمل می شود. تابع شروع به اجرای فرامین و پردازش داده ها می نماید. به موازات اجرای دستورات، تایمر millis زمان را کنترل می کند. چنانچه زمان از حد انتظار، مثلا از ۱۰ ثانیه بیشتر شده و اجرای تابع خاتمه نیافته باشد، اجرا تابع پایان گرفته و روال عادی برنامه، دنبال خواهد شد. در این شرایط، با توجه به خطای صورت گرفته، تصمیم لازم، اتخاذ خواهد شد.

یکی دیگر از کاربردهای تابع millis، اجرای یک فرایند، به صورت مکرر و در زمان های خاص است. به عنوان مثال، به کمک تابع millis می توان هر ۱۵ ثانیه یکبار، اقدام به خواندن ورودی آنالوگ، نمود. از آنجاییکه کار با تابع millis بسیار ساده بوده و در خروجی خود زمان سپری شده را اعلام می نماید، بنابراین با اندازه گیری زمان فعلی و مفابسه آن با زمان اندازه گیری شده در مرحله قبل، می توان زمان سپری شده بین این دو بازه را اندازه گیری کرده و در صورت لزوم، ورودی از آنالوگ خوانده شود.

 


تعریف تابع millis


یکی از قابلیت های بردهای آردویینو، به عنوان مثال برد آردویینو UNO، وجود تایمرهای داخلی است. به لطف وجود همین تایمر، تابع millis که جزو یکی از پایه ای ترین توابع آردویینو  به شمار می رود، می تواند مدت زمان سپری شده از لحظه شروع به کار برد تاکنون را محاسبه کند. خروجی این تابع از نوع unsigned long int بوده که در معماری بردهای آردویینو ۸ بیتی نظیر برد آردویینو UNO، به اندازه ۳۲ بیت خواهد بود. نحوه استفاده از این تابع، به شکل زیر است.

unsigend long int x=millis();

به عنوان مثالی دیگر، عبارت زیر، مدت زمان طی شده از لحظه راه اندازی برد آردویینو تاکنون را نشان می دهد.

Serial.println(millis());

خروجی تابع فوق را در سریال مانیتور، به صورت زیر می توان مشاهده نمود.

تابع millis، تعریف و توضیح در آردوینو - دیجی اسپارک

 

مطابق تصویر فوق، مدت زمان سپری شده بر حسب میلی ثانیه، در خروجی سریال مانیتور به نمایش در می آید. برای تبدیل این مقدار به ثانیه، کافیست خروجی تابع millis، بر ۱۰۰۰ تقسیم گردد.

 


اجرای یک نمونه کد با تابع millis


در این قسمت به بررسی یکی از مهم ترین کاربردهای تابع miliis، زمانبندی اجرای فرایندها، می پردازیم. برای روشن تر شدن موضوع، فرض کنید که هر ده ثانیه یکبار، قصد خواندن ورودی از آنالوگ را دارید. به این منظور، قطعه برنامه زیر را در نظر بگیرید.

unsigned long int t1=0;
int interval=10000;
int x=0;
void setup() {
}

void loop() { 
 if(millis() > t1+ interval){
  t1=millis();
  x=analogRead(0);
 }
}

 مطابق برنامه فوق، هر ۱۰ ثانیه یکبار، تابع analogRead فراخوانی میشود. اما اندازه گیری زمان ۱۰ ثانیه، به کمک تابع millis صورت می گیرد. در ابتدا، در منغیر interval، عدد ۱۰۰۰۰(ده هزار) ذخیره می گردد. همچنین، مقدار اولیه متغیر t1، برابر با صفر، فرض می شود. بنابراین، در لحظه اول، حاصل جمع متغیر t1 و interval، ده هزار خواهد بود. در طرف دیگر، مقدار تابع millis(مدت زمان سپری شده از ابتدای راه اندازی برد آردویینو) با این حاصل جمع مقایسه شده و هرگاه از مقدار ۱۰ هزار بیشتر شود، وارد شرط شده و مقدار آنالوگ، خوانده می شود. در نظر داشته باشید که مقدار ۱۰ هزار، همان ۱۰ هزار میلی ثانیه و یا ۱۰ ثانیه است.

یکی دیگر از نکات مهم کد فوق، ذخیره زمان فعلی در متغیر t1 است. به عبارت دیگر، با وارد شدن به شرط، مقدار زمان فعلی در این متغیر، ذخیره میگردد. دلیل این امر، افزایش مقدار خروجی تابع millis است. از آنجاییکه مقدار خروجی تابع millis(زمان طی شده پس از راه اندازی آردویینو) در حال افزایش است، بنابراین، با ورود به شرط،  زمان فعلی در متغیر t1 ذخیره گردیده تا در مراحل بعدی، بتوان جهت مقایسه با خروجی تابع millis، از آن استفاده نمود.

برای درک بهتر موضوع فوق، فرض کنید پس از اولین ورود به شرط، مقدار زمان فعلی به صورت عدد ۱۰۰۰۰(ده هزار) در متغیر t1 ذخیره می گردد. در این قسمت نیز، خروجی تابع millis در حال افزایش نسبت به عدد ۱۰ هزار است. چراکه با رسیدن به عدد ۱۰ هزار در ابتدای اجرای برنامه، شرط اجرا می شود. در این حالت، حاصل جمع مقدار ذخیره شده در متغیر t1 و عدد interval، مقدار ۲۰ هزار خواهد بود. در این حالت، تا زمانیکه مقدار خروجی تابع millis بیشتر از ۱۰ هزار نشود، شرط مجددا اجرا نخواهد شد. بدین ترتیب، هر ده ثانیه یکبار، شرط اجرا شده و با ذخیره زمان فعلی، ورودی آنالوگ خوانده خواهد شد.

 


Overflow در تابع millis و خطرات آن


در قسمت قبلی، به بررسی نحوه استفاده از تابع millis، خروجی آن و استفاده از این تابع در زمانبندی فرآیندها، استفاده کردیم. نکته مهم در به کارگیری این تابع، بحث بیشینه مقدار خروجی آن و سرریز(Oveflow) است. همانطورکه در بخش پیشین نیز گفته شد، این تابع مقدار خود را به صورت  unsigned long int تولید می کند. در بردهای آردویینو مبتنی بر تراشه AVR که معماری آن ها ۸ بیتی است، بیشنه مقدار برای نوع داده unsigned long int برابر با ۴۲۹۴۹۶۷۲۹۵ خواهد بود. تابع millis، پس از رسیدن به این مقدار، سر ریز کرده و روال شمارش خود را از ۰ آغاز خواهد کرد. به عبارتی دیگر، این تابع، پس از ۴۹ روز، سر ریز کرده و با پاک شدن مقادیر قبلی، شمارش از مقدار ۰ آغاز خواهد شد.

Overflow در تابع millis و خطرات آن - دیجی اسپارک

بدین ترتیب، با سر ریز شدن خروجی تابع millis، شمارش از صفر شروع خواهد شد. بدین ترتیب، اجرای فرایندها در زمان های مشخص، با این وجود، پس از ۵۰ روز غیرممکن خواهد بود. به عنوان مثال، در مورد خواندن هر ۱۰ ثانیه یکبار ورودی آنالوگ، این مقدار غیر ممکن خواهد بود. چراکه ممکن است در بعضی موارد، ۴۹ روز زمان نیاز باشد تا ورودی آنالوگ مجددا خوانده شود! اما غلبه بر این مشکل، راهکاری خلاقانه و البته ساده دارد که پیش از آن، می بایست به مطالعه برخی مباحث پایه در محاسبات کامپیوتری بپردازیم. پس از این مرحله، به ارائه و بررسی راهکار خواهیم پرداخت.

 


مکمل دو و نحوه اجرای عمل تفریق در کامپیوتر


در این قسمت به بررسی یکی از مباحث بسیار مهم در محاسبات کامپیوتری می پردازیم. از آنجاییکه راهکار غلبه بر مشکل تابع millis arduino نیاز به تسلط بر محاسبات کامپیوتری دارد، لذا در ابتدا به تشریح نحوه اجرای عمل تفریق در کامپیوترها می پردازیم. فرض کنید قصد اجرای عمل تفریق بین دو عدد a و b را به شکل زیر دارید.

c=a-b;

در قطعه کد فوق، عدد a از عدد b کم شده و حاصل در c ذخیره می گردد. در سیستم های کامپیوتری، عملی به اسم تفریق وجود ندارد! در حقیقت عمل تفریق، با ساختن مکمل دو ساخته می شود. در مثال فوق، عدد a با مکمل ۲ عدد b جمع شده و حاصل در عدد c ذخیره می شود. ذکر این نکته ضروریست که در داده های علامت دار(signed)، آخرین بیت عدد، علامت آن را مشخص میکند. چنانچه این بیت یک شود، عدد مورد نظر منفی خواهد بود. به عنوان مثال، برنامه زیر را در نظر بگیرید.

void setup() {
    Serial.begin(115200);
 
    unsigned long a = 1;
    unsigned long b = 4294967295; //unsigned long maximum value
 
    Serial.println(a-b);     
}
  
void loop() {
}

با اجرای برنامه فوق، مشاهده خواهید کرد که حاصل تفریق، یک عدد منفی نبوده و در سریال مانیتور، عدد ۲ نمایش داده خواهد شد. همانطور که بیان شد، برای اجرای عمل تفریق، از مکمل دو عدد استفاده می شود. در مثال فوق، عدد a برابر با ۱ و عدد b برابر با ۴۲۹۴۹۶۷۲۹۵ و هر دو آن ها از نوع unsigned long int هستند. مطابق آنچه که که پیشتر گفته شد، در معماری بردهای آردویینو ۸ بیتی، نوع داده unsogned long int به میزان ۳۲ بیت است. برای اجرای عمل تفریق، ابتدا عدد b را به باینری تبدیل می کنیم. معادل باینری این عدد به صورت زیر است.

۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱۱

برای ساخت مکمل دو عددb، می بایست ابتدا بیت های آن را معکوس کنیم. بنابراین عدد فوق به صورت زیر تبدیل می شود.

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰

سپس عدد فوق را با یک جمع می کنیم تا مکمل دو آن ساخته شود. با جمع عدد فوق با یک، عدد زیر حاصل می شود.

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۱

در نهایت می بایست عدد a با مقدار ۱، با مکمل دو عدد b جمع کنیم. حاصل این جمع به صورت باینری، برابر با عدد زیر است.

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۱۰

معادل عدد فوق در مبنای ۱۰، عدد ۲ است! به عبارت دیگر، شما با تفریق یک عدد کوچکتر از یک عدد بزرگتر، نه تنها عدد منفی کسب نمی کنید، بلکه یک عدد مثبت بدست خواهید آورد. البته چنانچه که تعداد بیت های این دو عدد بیشتر می بود، مثلا ۶۴ بیت، عدد منفی به راحتی تولید می شد. از آنجاییکه سر ریز رخ داده و جایی برای بیت علامت وجود نداشت، بنابراین یک عدد مثبت حاصل شده است. از دیگر دلایل ایجاد عدد مثبت، بدون علامت بودن آن است. در حقیقت در سیستم اعداد بودن علامت، نتیجه محاسبات هیچ گاه منفی نخواهد شد. چراکه بیت علامت، همواره برابر با ۰ است. از این موضوع می توان در غلبه بر مشکل تابع millis، استفاده نمود. این موضوع در قسمت بعدی، مورد بحث و بررسی قرار می گیرد.

 


غلبه بر مشکل Overflow در تابع millis


در قسمت قبلی، در رابطه با بحث تفریق دو عدد علامت دار و تولید یک عدد مثبت، صحبت کردیم. در این قسمت، با توجه به مطالب گفته شده در قسمت قبلی، به جای اینکه مقدار t1 را با interval جمع کرده و خروجی تابع millis با آن مقایسه شود، از تفریق استفاده می کنیم. برای این منظور، کد زیر را در نظر بگیرید.

unsigned long int t1 = 0;
int interval = 10000;
int x = 0;
void setup() {
}
void loop()
{
  if (millis() - t1 > interval)
  {
    t1 = millis(); 
    x = analogRead(0);
  }
}

در کد فوق، تا پیش از سر ریز شدن تابع millis()، همه چیز عادی و مثل گذشته پیش می رود. اما زمانیکه تابع millis arduino سرریز کرده و مقدار آن صفر می شود، کد فوق بر مشکل سر ریز، غلبه می کند. برای روشن تر شدن موضوع، فرض کنید که در روز چهل و نهم، مقدار ۴۲۹۴۹۶۷۲۹۵ در متغیر t1 ذخیره می شود. پس از این مرحله، تابع millis سر ریز کرده و شمارش خود را از صفر آغاز می نماید. همانطور که پیشتر گفتیم، برای تفریق از مکمل دو استفاده می شود. به عبارت دیگر، مکمل دو عدد ۴۲۹۴۹۶۷۲۹۵، برابر با یک خواهد بود. در طرف دیگر، تابع millis پس از سر ریز، مقدار خود را از ۴۲۹۴۹۶۷۲۹۵ یا همان یک کسر کرده و تا زمانیکه حاصل این تفریق بیشتر از ۱۰ هزار(۱۰ثانیه) نشود، وارد شرط نخواهد شد. بدین ترتیب با راهکاری ساده، بر مشکل سر ریز این تابع غلبه شده و دیگر نگرانی وجود نخواهد داشت.

 


جمع بندی


در این آموزش به بررسی تابع millis و نقش آن در کنترل فرایندها، پرداختیم. این تابع میتواند مدت زمان طی شده از ابتدای راه اندازی برد آردویینو را اندازه بگیرد. بدین ترتیب به کمک این تابع می توانیم اجرای برخی از قسمت های برنامه را زمانبندی کرده و در بازه های زمانی مشخص، آن ها را اجرا کنیم. در کنار این مزایا، تابع millis بعد از ۴۹ روز مداوم کاری، دچار سر ریز شده و مقدار آن از صفر شروع خواهد شد. این موضوع می تواند سبب ایجاد اختلال در برنامه و عدم عملکرد صحیح قسمت های زمان بندی شده باشد. در این نوشتار، به این مشکل و نحوه غلبه بر آن به طور مفصل صحبت شده است.

 

چنانچه در مطالب گفته شده ابهامی می بینید و یا اینکه احساس می کنید که بعضی از مطالب برای شما گنگ است، بدون هیچ نگرانی در انتهای همین پست، به صورت ثبت نظر سوالتان را مطرح کنید. من در سریع‌ترین زمان ممکن پاسخ رفع مشکل شما را خواهم داد. همچنین اگر ایرادی در کدها و یا مراحل اجرایی وجود دارند می‌توانید از همین طریق اطلاع رسانی کنید

 

درباره نویسنده

معین صابری

کارشناسی ارشد رشته معماری سیستم های کامپیوتری

مالي که ز تو کس نستاند، علم است
حرزي که تو را به حق رساند، علم است
جز علم طلب مکن تو اندر عالم
چيزي که تو را ز غم رهاند، علم است
(شیخ بهایی)

تبادل نظر و رفع عیب با ثبت دیدگاه

۳۰ دیدگاه

  • درود، خسته نباشید، عالی بود جناب صابری عزیز.
    آیا میشه با همین تابع millis یک ساعت دیجیتال ۲۴ ساعته شش رقمی با سون سگمنت و آردوینو ساخت ؟
    من توی دیجی اسپارک آموزشی در موردش ندیدم. میشه لطفا بهم کمک کنید ؟

    • درود و سپاس از لطف شما
      بله می شود ساخت، اما کار بسیار سختی خواهد بود، چراکه تابع millis همانطور که در مطلب به آن اشاره شد، بعد از ۵۰ روز، سر ریز کرده و مجددا از صفر شروع به شمارش می کند. بهترین راه استفاده از آی سی های ساعت نظیر DS1307 است. آموزش زیر را مطالعه نمایید:
      لینک آموزش ساعت دیجیتال با سون سگمنت، کلیک کنید

      • بله درسته، اما همانطور که در جریان هستید، ساعت دیجیتالی هر ۲۴ ساعت یکبار صفر میشه، در حالی که گفتید تابع millis بعد از ۵۰ روز سر ریز میکنه. بنابراین اصلا کار به اونجاها نمیکشه و ما میتونیم هر یک روز یکبار اینو صفر کنیم. به نظرتون چجوری میشه اینکار رو انجام داد ؟؟
        در ضمن، آموزشی که لینکشو برام گذاشته بودید، ساعت دیجیتال ۴ رقمی بود، در صورتی که من میخواستم ۶ رقمی بسازم. برای ۶ رقمی آموشی هست توی دیجی اسپارک ؟

        • ساعت دیجیتالی بعد از ۲۴ ساعت صفر نمی شود. ماژول های ساعت برای همیشه ساعت را در خود ذخیره دارند. علاوه بر این، روی این ماژول ها باتری قرار داشته که می تواند تا ده ها سال زمان را در خود ذخیره کند. دو رقم اضافه بر ۴ رقم را برای ثانیه می خواهید؟ آموزشی که لینک آن را قرار دادم را کامل تر مطالعه کنید تا کتابخانه ماژول ساعت را بیشتر آشنا شوید. در این کتابخانه می توانید ثانیه را هم علاوه بر دقیقه و ساعت اندازه گیری نمایید. در ادامه اطلاعات تکمیلی را می توانید در انتهای همان پست از نویسنده ان سوال کنید تا سریعتر و بهتر شما را راهنمایی کند.
          با سپاس از شما

      • سوال دیگه که داشتم در مکمل ۲ وقتی به اون عدد ۳۲ بیتی ، عدد ۱ رو اضافه میکنی میشه ۳۳ بیت چه طوری همچین چیزی برای آردوینو ۳۲ بیتی ممکنه؟؟

        • خیر، زمانیکه عدد ۱ به یک عدد ۳۲ بیتی که تمام بیت های آن ۱ است اضافه می شود، نتیجه ۳۳ بیت خواهد شد که چون ۳۳ بیت در ۳۲ بیت جا نمی شود، سر ریز اتفاق خواهد افتاد.
          در رابطه با ۳۲ بیت در آردوینو، در حقیقت ۳۲ بیت با قراردادن ۴ حافظه(رجیستر) ۸ بیتی کنار هم، ساخته می شود.

  • عالی عالی
    درود بر شما که به پیشرفت علمی ایران کمک میکنید
    دمتون گرم کیف کردم از نحوه توضیح دادنتون

  • سلام خیلی آموزش کاربردی بود مچکرم
    سوالی که دارم:
    برای غلبه بر مشکل تابع بعد از ۴۹ روز ، خب صفر منهای اون عدد بزرگ که بعد ۴۹ روز به دست میاد عددش کمتر ۱۰ هزار هست پس وارد حلقه if نمیشه چه طور میشه که عدد منفی بیشتر از ۱۰ هزار میشه؟؟؟

    • سلام و ممنونم از شما
      در آموزش، قسمت “مکمل دو نحوه اجرای عمل تفریق در کامپیوتر ” را بار دیگر مطالعه کنید. در رابطه با نحوه عمل تفریق در سیستم های کامپیوتری صحبت کرده ایم. اگر سوالی بود مجددا بپرسید

      • سلام استاد صابری عزیز
        بنده به پیشنهاد یکی از دوستان این مطلب شما رو بررسی کردم، واقعا مطلب رو خوب توضیح داده بودید. خیلی عالی بود
        ولی در کل فکر کنم خودتون این برنامه رو ۵۰ روز تست نکرده اید تا ببینید که قسمتی از توضیحات شما درست نیستند.

        ببینید اولا متغیر شما long نیست که عدد منفی توش باشه یا بیت علامت داشته باشه. متغیر شما از نوع unsigned long هست و موقعی که عدد ۴۲۹۴۹۶۷۲۹۵ داخلش هست معادل ۱- نیست همون ۴۲۹۴۹۶۷۲۹۵ هست، و بعد از ۱ میلی ثانیه که یک واحد بهش اضافه میشه تبدیل میشه به ۰ چرا چون یک بیت کری داریم و داخل این متغیر ۰ میمونه.
        حالا اگه همون حالت if(millis() > t1+ interval) را در نظر بگیریم، و فرض کنیم هم t1 = 4294967295 و هم millis() = 4294967295 حالا چه اتفاقی می افته؟؟؟
        یک میلی ثانیه بعد، millis میشه صفر ولی طرف راست چی داریم؟؟؟ ۴۲۹۴۹۶۷۲۹۵ + ۱۰۰۰۰ درسته؟؟؟
        خب پس در کل داریم: if (0 > 4294967295 + 10000) و به نظر میاد که cpu باید ۵۰ روز منتظر بمونه…
        ولی اینطور نیست. چون اگه عدد ۴۲۹۴۹۶۷۲۹۵ با ۱ جمع بشه میشه ۰ چه برسه به ۱۰۰۰۰ … چرا ؟؟؟
        چون آردوینو جواب ها رو تا ۴ بایت بررسی میکنه نه بیشتر. مگر اینکه خودمون بیایم و بگیم uint64_t حساب کن.
        و نتیجه ۴۲۹۴۹۶۷۲۹۵ + ۱۰۰۰۰ میشه ۹۹۹۹ پس داریم : if (0 > 9999) که باز هم همون ۱۰ ثانیه تاخیر رو ایجاد میکنه و هیچ ایراد و اشکالی توی برنامه پیش نمیاد.

        ممنون که وقت گذاشتید و این مطلب رو تا آخر مطالعه و منتشر کردید.
        با سپاس

        • سلام و از لطف و دقت نظر شما بی نهایت سپاسگزارم
          نخست اینکه طبعا نمی توانیم ۵۰ روز یا به عبارتی ۲ ماه منتظر اجرا و نتیجه یک کد باشیم! پس با محاسبات مورد را بررسی می کنیم.
          دوم اینکه اگر به مطالب درج شده، به خصوص قسمت “مکمل ۲ و نحوه اجرای عمل تفریق در کامپیوتر” دقت بفرمایید، گفته ایم که حاصل تفریق عدد ۱ از ۴۲۹۴۹۶۷۲۹۵ منفی نخواهد شد و مثبت خواهد بود. از این ویژگی در کد جهت اجرای صحیح زمانبندی استفاده می کنیم.
          اما در رابطه با محاسباتی که فرمودید، فرض کنید که خروجی millis برابر با ۴۲۹۴۹۶۶۲۹۵ باشد. طبعا جمع این مقدار با ۱۰۰۰ برابر با ۴۲۹۴۹۶۷۲۹۵ خواهد شد. در این حالت برنامه وارد شرط نخواهد شد. چرا که خروجی تابع millis از این مقدار کمتر است. با گذشت کمی زمان، خروجی millis صفرشده(بر اثر overflow) و از اول شروع به شمارش می کند. با توجه به شرایط رخ داده(مقدار ذخیره شده در t1 و interval که جمع آن ها ۴۲۹۴۹۶۷۲۹۵ می شود)، شرط هیچ گاه اجرا نخواهد شد! در این حالت است که عمل تفریق به نجات شرط و اجرای صحیح برنامه می آید.
          امیدوارم مطالب را واضح بیان کرده باشم. اگر ابهامی وجود دارد و یا فکر می کنید همچنان مطالب ایراد دارند، با ردیگر لطف شما را جهت پاسخ گویی و ادامه این بحث جذاب، خواستارم.

          • سلام مهندس جان
            ممنون که انتقاد ها رو چاپ میکنید.
            نیازی به ۵۰ روز نیست، شما این برنامه رو تست کنید متوجه عرایض بنده می شوید:

            unsigned long MILLIS = 4294966295;
            unsigned long int t1 = MILLIS;

            int interval = 1000;
            int x = 0;
            void setup()
            {
            Serial.begin(9600);
            }

            void loop()
            {
            Serial.print(F(“mullis: “));
            Serial.println(MILLIS);
            Serial.print(F(“t1 + interval: “));
            Serial.println(t1 + interval);

            if (MILLIS >= t1 + interval)
            {
            t1 = MILLIS;
            Serial.println(F(“—————————————“));
            }

            MILLIS++;
            delay(100);
            }

            البته نباید در شرط از < استفاده کنید، باید از =< استفاده کنید.
            ولی روش تفریق همیشه درسته ولی اونجا هم باید از =< استفاده کنید.تا ۱ میلی ثانیه اضافه تر شمارش نشه.
            توی برنامه ای که من برای شبیه سازی تابع millis نوشتم عدد سرریز رو ۱۰۰۰ تا کمتر نوشتم تا کمی فرصت مشاهده خروجی رو داشته باشیم.
            حالا اگه شرایطی پیش بیاد که millis قبل از رسیدن به شرط if صفر بشه دیگه این روش کار نمیکنه ولی با استفاده از روش تفریق همیشه جواب میده.
            ممنون از آموزش های خوبتون.

            • متشکرم از لطف شما
              بله، منظور من هم در کامنت قبلی و هم اصلا موضوع این آموزش، اینست که تفریق همواره پاسخ خواهد داد. باز هم از لطف شما بی نهایت ممنون و سپاسگزارم. لطفا ارگ باز هم ابهامی وجود دارد یا مطالب باز هم نیازی به اصلاح دارند، مورد را حتما اعلام نمایید. بار دیگر از لطف و توجه شما صمیمانه سپاسگزارم.

  • سلام
    خیلی ممنون بابت آموزش، میخواستم بدونم برای تبدیل میلی ثانیه به ثانیه باید چه اقدامی انجام بدیم؟ کلا اینکه بخوایم تبدیل میلی ثانیه به ثانیه را انجام بدیم باید از همین تابع استفاده کنیم؟

    • سلام و ممنونم از لطف شما
      کافیست خروجی تابع millis را بر ۱۰۰۰ تقسیم کنید تا زمان بر حسب ثانیه بدست آید.

  • سلام ممنون از آموزشی که گذاشتید.
    میخواستم ببینم میشه با این تابع و تابع if طوری برنامه نوشت که به طور مثال با ۱۰ ثانیه نگه داشتن پوش باتن یک ال ای دی رو روشن و خاموش کنه . ممنون میشم راهنمایی کنید

    • سلام و خواهش میکنم
      ابتدا بابت تاخیر در پاسخگویی عذرخواهی می کنم
      بله این امکان وجود دارد:
      unsigned long x;
      while(millis()-x 10000)
      toggle_led();

  • سلام و تشکر از آموزش کاربردی و مفیدتون
    یه سوال داشتم ، اگر در یک ربات با برد آردوینو که برای خاموش کردن آتش از سنسور آتش و پره استفاده کردیم بخواهیم بعد از روشن شدن سنسور پره شروع به کار کند و تا ۵ ثانیه بعد از خاموش شدن سنسور پره به کارش ادامه بدهد ، از چه تابعی باید استفاده کرد؟
    در حال حاضر با روشن شدن سنسور پره شروع به کار و با خاموش شدن سنسور پره خاموش میشه.
    تشکر

    • سلام و متشکرم از شما
      برای این مورد باید از تایمر داخلی میکروکنترلر آردوینو یا سیستم عامل استفاده کنید. این دو آموزش در حال انتشار هستند. در صفحه اصلی سایت روزانه قسمت انتشار روز را کنترل کنید تا آموزش منتشر شود.

  • با سلام و عرض ادب
    ممنون بابت توضیحات کاملی که ارائه فرمودید.
    یک سوال داشتم
    ایا میتوان در یک برنامه برای چند متغیر چند بار از MILLIS استفاده کرد ، یعنی MILLIS های تو در تو .
    سپاس

  • سلام و وقت بخیر. آیا با اضافه کردن شرط زیر به آخر کد نیز میتوان مشکل سرریز شدن millis() را حل کرد؟
    } (if(millis() == 0
    t1 = 0;
    {

    • سلام و متشکرم
      نه لزوما!
      چون به سرعت مقدار millis تغییر می کند. مقدار millis در یک روال موازی با برنامه و در وقفه های تایمر مشغول تغییر است!

  • سلام یه کد ساده میخوام ولی نمیتونم بنویسم اگه میشه برام بنویسید.
    میخوام وقتی پایه ۲ آردوینو HIGH شد ، پایه ۳ روشن بشه به مدت دو دقیقه و بعد از دو دقیقه خاموش بشه.
    و اگه در طول مدت روشن بودن پایه ۳ ، پایه ۴ HIGH شد ، سریعا پایه ۳ خاموش بشه حتی اگه دو دقیقه به پایان نرسیده باشه.