تایمر و وقفه های تایمر ESP32 (آموزش جامع)
محتویات
هنگامی که یک تسک یا کار می بایست سر موقع انجام بگیرد، به تایمرها و وقفه های تایمر نیاز پیدا میکنیم. تایمر، نوعی وقفه محسوب شده و مانند یک ساعت ساده است که برای اندازه گیری و کنترل رویدادهای زمانی استفاده میشود و یک تاخیر زمانی دقیق ایجاد میکند. اغلب میکروکنترلرها درون خود یک تایمر داخلی دارند که نه تنها برای تولید تاخیرهای زمانی که به عنوان کانتر هم استفاده میشود. این ویژگی تایمر، کاربردهای گوناگونی دارد. تایمرهای میکروکنترلر، توسط رجیسترهای کاربردی خاصی که به عملیات تایمر اختصاص داده شده اند، کنترل میشوند.
تراشه ESP32 دو گروه تایمر سخت افزاری دارد و هر گروه هم شامل دو تایمر سخت افزاری همه منظوره است. همه این تایمرها، تایمرهای معمولی 64 بیتی هستند. این تایمر ها مبتنی بر prescaler یا همان پیش تقسیم کننده فرکانسی 16 بیتی و کانترهای صعودی و نزولی (بالا یا پایین شمار) 64 بیتی که قابلیت بارگذاری مجدد دارند، هستند.
وقفه های تایمر
وقفه های تایمر، یک روش موثر و مفید برای اطمینان حاصل کردن از به موقع رخ دادن ایونت ها با دقت میلی ثانیه، تنظیم دقیق کلاک یا عملیات PWM و یا صرفا اعمال یک پالس قابل اعتماد به یک LED است. وقفه های تایمر، همان وقفه های سخت افزاری تولیدشده توسط تایمر هستند. به کمک وقفه های تایمر، میتوان یک تسک را با فواصل زمانی بسیار خاص و دقیق و صرف نظر از اینکه چه عملیات دیگری در کد در حال اجراست، انجام دهیم. این نوع وقفه ها، به وقفه های خارجی شباهت دارند. اما به جای اینکه توسط یک ایونت خارجی تریگر شوند، تایمر آن ها را تریگر میکند. هنگام تریگر شدن وقفه ها، ابتدا دستور فعلی به طور کامل اجرا میشود. پس از اجرا شدن کامل دستور فعلی، وقفه ها از اجراشدن ادامه کد جلوگیری کرده و آن را به وقفه انداخته و قطع میکند. پس از فراخوانی ISR (روتین سرویس وقفه که وظیفه آن چک کردن درست اجرا شدن یا نشدن وقفه هاست)، به سراغ دستور بعدی، از آنجایی که مانده بود میروند و اجرای کد اصلی را ادامه میدهند. دقیقا مانند وقفه سخت افزاری یا وقفه خارجی.
در تصویر زیر، مفهوم تایمر نشان داده شده است.
از آنجایی که این تایمرها، مبتنی بر تایمرهای سخت افزاری هستند، تمام زمان بندی هایشان به کلاک تایمر مربوط است. سرعت تایمر را با فرمول زیر تعیین میکنیم.
timer speed (Hz) = Timer clock speed (Mhz) / prescaler
برای مثال، طبق این فرمول سرعت یک تایمر در ESP32 که با فرکانس کلاک 80 مگاهرتز کار میکند، به ازای مقدار پیش تقسیم کننده فرکانسی 8، 80 مگاهرتز یا 8000000 هرتز بوده و به ازای prescaler = 80، ا مگاهرتز یا 1000000 هرتز میشود.
فرکانس کلاک به مقدار prescaler یا پیش تقسیم کننده فرکانسی، تقسیم شده تا یک “تیک” تایمر ساخته شود. با هر تیک تایمر، یک واحد به کانتر آن افزوده میشود.
سپس ISR برای تریگر کردن تعداد مشخصی از این تیک ها، فراخوانی میشود.
در تصویر زیر، این فرآیند در وقفه تایمر را مشاهده میکنید.
وقفه های تایمر هم دقیقا مانند وقفه های سخت افزاری بهترین روش اجرای توابع non-blocking با یک تاخیر زمانی خاص هستند.
برای اینکار، در تابع ()setup یک وقفه تایمر مخصوص را پیکربندی کرده و آن را به یک ISR خاص متصل میکنیم. میکروکنترلر حلقه اصلی را به طور مداوم اجرا خواهد کرد. زمانی که یک وقفه تایمر تولید شد، میکروکنترلر اجرای حلقه اصلی را متوقف کرده و به اجرای ISR یا وقفه های سخت افزاری داخلی میپردازد. هنگامی که اجرای روتین سرویس وقفه به پایان رسید، میکروکنترلر حلقه اصلی را از جایی که مانده بود، اجرا میکند. این پروسه برای هر وقفه تایمر تکرار میشود.
سوالات متداول درباره وقفه های تایمر ESP32
1–ESP32 چند تایمر دارد؟
ESP32 چهار تایمر 64 بیتی دارد.
2-چگونه از وقفه تایمر در ESP32 استفاده کنیم؟
میتوانید تایمر موردنظر را به یک وقفه وصل کرده و یک ISR مخصوص هم به آن نسبت دهید.
3- تایمر ESP32 چگونه کار میکند؟
تایمر از یک کانتر استفاده میکند. این کانتر با توجه به مقدار فرکانس کلاک و مقدار پیش تقسیم کننده، شمارش را با یک سرعت مشخص انجام میدهد. این کانتر شمارش را تا مقدار حداکثری که برای آن تعیین شده انجام داده، سپس ریست شده و یک وقفه را تریگر میکند. ما میتوانیم با تغییر این مقدار تعیین شده، تاخیرهای زمانی تایمر را تغییر دهیم.
وقفه تایمر ESP32 باپروژه LED چشمک زن
در این مثال، برنامه LED چشمک زن را تست میکنیم. اما این بار به جای استفاده از تاخیر و تابع ()delay، از وقفه تایمر استفاده خواهیم کرد.
ابتدا اتصالات را مانند تصویر زیر انجام داده و مدار خود را روی بردبورد ببندید.
در تصویر زیر، مدار واقعی بسته شده روی بردبورد را مشاهده میکنید.
پایه آند یا مثبت LED را به پایه GPIO21 میکروکنترلر و پایه کاند یا منفی آن را به کمک یک مقاومت 220 اهمی به پایه GND میکروکنترلر متصل کنید.
کد LED چشمک زن با وقفه تایمر ESP32
کد موجود در انتهای این مطلب را دانلود کرده و آن را روی ESP32 آپلود کنید. مشاهده خواهید کرد که LED با فرکانس 1 هرتز یا هر ثانیه یکبار چشمک میزند. حال بیایید هر بخش از کد را با هم بررسی کنیم.
#define LED 21 hw_timer_t *My_timer = NULL;
در قسمت متغیر سراسری، پایه GPIO21 را به عنوان پایه LED تعریف کرده و سپس برای پیکربندی تایمر، یک متغیر پوینتر یا اشاره گر به نام My_timer از نوع hw_timer_t ایجاد کردیم.
void IRAM_ATTR onTimer(){ digitalWrite(LED, !digitalRead(LED)); }
در ادامه، برای وقفه تایمر یک روتین سرویس وقفه یا ISR ایجاد کردیم. در ISR، یک تابع برای تغییر یا معکوس کردن وضعیت پایه GPIO21 که همان پایه متصل شده به LED است، تعریف کردیم. هنگامی که وقفه تایمر رخ دهد، این روتین اجرا میشود.
void setup() { pinMode(LED, OUTPUT) My_timer = timerBegin(0, 80, true); timerAttachInterrupt(My_timer, &onTimer, true); timerAlarmWrite(My_timer, 1000000, true); timerAlarmEnable(My_timer); } void loop() { }
در تابع ()setup، به کمک تابع ()pinMode پایه GPIO21 را به عنوان پایه خروجی مقداردهی کردیم.
My_timer = timerBegin(0, 80, true);
برای راه اندازی و مقداردهی تایمر، از تابع ()timerbegin با آرگومان هایی که در ادامه توضیح میدهیم استفاده کردیم. اولین آرگومان این تابع، شماره تایمری است که میخواهیم از آن استفاده کنیم. ( از آنجایی که چهار وقفه سخت افزاری داریم، این قسمت میتواند از 0 تا 3 مقداردهی شود.)
دومین آرگومان این تابع، مقدار پیش تقسیم کننده و سومین آرگومان یا پرامتر آن هم یک پرچم است که نشان میدهد کانتر باید رو به بالا بشمرد (true) و یا روبه پایین (false).
در این مثال، ما از تایمر شماره 0 با مقدار prescaler = 80 و کانتری که صعودی است و روبه بالا میشمرد استفاده میکنیم.
timerAttachInterrupt(My_timer, &onTimer, true);
قبل از فعالسازی تایمر، باید آن را به یک روتین سرویس وقفه یا ISR وصل کنیم تا در زمان رخ دادن وقفه، اجرا شود. این کار را با فراخوانی تابع timerAttatchInterrupt انجام میدهیم. در این مثال، تابع ISR که onTimer نام دارد را به وقفه تایمر وصل کردیم.
timerAlarmWrite(My_timer, 1000000, true);
تابع timerAlarmWrite برای مشخص کردن مقدار کانتر ( که وقفه تایمر با این فرکانس تولید خواهد شد) استفاده میشود. در اینجا، فرض میکنیم که میخواهیم هر ثانیه یکبار وقفه تولید کنیم. پس باید اجازه دهیم زمانی به اندازه 1000000 میکروثانیه یا همان 1 ثانیه بگذرد. آرگومان سوم را true میکنیم، یعنی میخواهیم که کانتر شمارش روبه بالا انجام دهد. سپس کانتر دوباره بارگذاری شده و وقفه به صورت متناوب تولید میشود.
timerAlarmEnable(My_timer);
در نهایت هم وقفه تایمر را با استفاده از تابع timerAlarmEnable فعال میکنیم.
موارد موجود در فایل : سورس کامل
برای دانلود فایل ها باید حساب کاربری داشته باشید ثبت نام / ورود