۱-۷ آشنایی با مقدمات واحد Timer-Counter
۲۷ بهمن ۱۳۹۷۳-۷ آشنایی با مفهوم وقفه (Interrupt)
۲۷ بهمن ۱۳۹۷برای راه اندازی واحد Timer-Counter در میکروکنترلرهای AVR در نرمافزار CodeVision پس از ساخت یک پروژه در مرحلهی انتخاب ویژگیها بخش Timers را همانند زیر انتخاب کرده:
اکنون با منویی مواجه میشوید که ۴ بخش دارد:
Timer0: این بخش مربوط به واحد Timer-Counter صفر است که از یک حافظهی ۸ بیتی بهره میبرد.
Timer1: این بخش مربوط به واحد Timer-Counter یک است که از یک حافظهی ۱۶ بیتی بهره میبرد.
Timer2 : این بخش مربوط به واحد Timer-Counterدو است که از یک حافظهی ۸ بیتی بهره میبرد.
ابتدا کار خود را با Timer0 شروع میکنیم.
هر واحد Timer در داخل خود یک شمارنده (رجیستر) به نام TCNTx دارد که x شمارهی تایمر است و این شمارنده در خود تعداد پالس مربعی وارد شده به این واحد تایمر را در خود نگه میدارد. مثلا شمارندهی Timer صفرم نامش TCNT0 است که یک حافظهی ۸ بیتی است و TCNT1 مربوط به Timer دوم است که یک شمارندهی ۱۶ بیتی است. درواقع با وارد شدن هر پالس مربعی به واحد Timer0 این شمارنده یک واحد افزایش مییابد تا به عدد ۲۵۵ برسد. عدد ۲۵۵ معادل دسیمال عدد باینری 11111111 است، درواقع به این مفهوم که تمام خانههای حافظهی ۸ بیتی ما دارای عدد ۱ شدهاند.
حال اگر یک پالس مربعی جدید وارد واحد Timer0 شود عدد 11111111 با عدد ۱ جمع خواهد شد که حاصل آن یک عدد ۹ بیتی به صورت 100000000 است ولی حافظهی ما تنها ۸ بیت است، بنابراین تنها ۸ بیت اول این عدد ۹ رقمی در حافظه میماند و رقم پرارزشتر آن یعنی بیت نهم که همان ۱ است دور ریخته میشود.
به این رخداد در علم کامپیوتر سرریز (Overflow) گفته میشود. با هربار رخ دادن Overflow گویی حافظهی ۸ بیتی ما کاملا به حالت اول خود که همان 00000000 است باز میگردد. ظاهرا با این اوصاف ما با استفاده از Timer0 تنها میتوانیم ۲۵۵ پالس را بشماریم و نه بیشتر. در ادامه در صدد رفع این مشکل برمیآییم، چرا که ما اغلب نیاز داریم تعداد بسیار بیشتری از این پالسها را بشماریم.
به شکل روبرو نگاه کنید.
بخش Clock Source مشخص کنندهی منبع تولید کنندهی پالس است. تایمر کانتر صفر هم میتواند از منبع داخلی مولد کلاک استفاده کند و هم از منبع خارجی تولید کننده پالس مربعی. در واقع منبع خارجی میتواند هر وسیله ایی اعم از انکودر، اسیلاتور و غیره باشد. همانطور که در شکل ۷ میبینید در بخش Clock Source ما میتوانیم از دو منع داخلی و خارجی استفاده کنیم. از ۳ انتخابی که در این بخش داریم انتخاب اول System Clock است که معرفی کنندهی استفاده از کلاک داخلی سیستم است که در ادامه بیشتر در مورد آن صحبت خواهیم کرد. اما دو گزینهی بعدی مربوط به منابع خارجی کلاک میباشند که میتوانند به پایهای با عنوان T0 که همان PORTB.0 است وارد شوند.
دو گزینه ی مطرح شده برای کلاک خارجی به صورت زیر مطرح شده است:
- T0 pin Falling Edge
- T0 pin Rising Edge
قسمت اول در صورت انتخاب شدن پین B.0 که همان T0 است را به لبهی پایین رونده حساس میکند و با اعمال هر لبهی پایین رونده یک واحد به TCNT0 افزوده میشود. باید توجه داشته باشید که در صورت انتخاب این گزینه باید پین B.0 را قبلا Pull Up کردهباشید. قسمت دوم همچنین در صورت انتخاب شدن پین B.0 را به لبهی بالا رونده حساس میکند و با اعمال هر لبهی بالا رونده یک واحد به TCNT0 افزوده میشود. باید توجه داشته باشید که در صورت انتخاب این گزینه باید پین B.0 را قبلا Pull Down کردهباشید. حال گزینهی T0 pin Falling Edge را انتخاب میکنیم و علت اصلی آن نیز این است که ما میتوانیم به صورت داخلی پایهی B.0 را Pull Up کنیم و نیازی به سخت افزار خارجی برای عمل Pull Down کردن نداریم. در ادامه یک پروژهی عملی در این زمینه را انجام میدهیم.
شمارش تعداد پالسهای خروجی از یک منبع مولد پالس مربعی توسط تایمرکانتر صفر:
در نرم افزار Proteus همانند شکل زیر میکرو و مولد پالس مربعی به همراه یک LCD را اضافه میکنیم.
در شکل ۹ همانطور که میبینید یک منبع تولید پالس از منوی سمت راست نرم افزار Proteus بخش Generator Mode همانند شکل ۱۰ اضافه شده است.
با رفتن به این بخش و انتخاب یک مولد پالس DC و متصل کردن آن به پایهی B.0 مدار شما آماده برنامه ریزی است.
توجه داشته باشید از پایهی B.0 به این علت استفاده کردیم که این پایه منحصرا در اختیار تایمر کانتر صفر بوده و تنها برای استفاده در سیستم کلاک خارجی مورد استفاده قرار میگیرد. همچنین از پایهی دیگری برای انجام عمل شمارش توسط تایمر کانتر صفر نمیتوانیم استفاده کنیم. حال پس از Pull Up کردن پین B.0 و حساس کردن این پایه به Falling Edge کار خود را ادامه میدهیم. اگر به منویwizard Code خود پس از انجام اعمال بالا نگاه کنید با منویی شبیه زیر مواجه خواهید شد.
در منوی مقابل بخشی که با عنوان Mode مشخص شده است وضعیت شمارنده را بیان میکند و عبارت مقابل آن یعنی Normal top=0xFF به معنای آن است که شمارنده تا حداکثر مقدار خود که همان ۲۵۵ در مبنای ده دهی و 11111111 در مبنای باینری است بالا رود و سپس سرریز رخ دهد و دوباره مقدار شمارنده 00000000 شود. در بخش Output میتوانیم وضعیت برخی پایه ها را در زمانهای خاصی مثلا زمان رخداد Overflow تغییر دهیم که ما در این بخش آن را در حالت Disconnect قرار میدهیم.
پس از انجام این تنظیمات و تعیین پورت C به عنوان پورت متصل به LCD کاراکتری وارد مرحلهی کد نویسی میشویم.
حال به عنوان اولین بخش از پروژه سعی میکنیم تغییرات شمارندهی TCNT0 را در برنامهی خود نشان دهیم. لازم به ذکر است عبارت TCNT0 در محیط برنامه نویسی به عنوان یک متغیر Global میباشد و در همه جای برنامه قابل دسترسی است و همواره عددی بین ۰ تا ۲۵۵ دارد و در واقع نمایان گر وضعیت رجیستر TCNT0 که مخفف Timer Counter 0 است میباشد. لازم به یادآوری است که رجیستر TCNT0یک رجیستر ۸ بیتی بوده و تنها میتواند اعدادی بین ۰ تا ۲۵۵ را در خود جای دهد.
به قطعه کد زیر نگاه کنید:
#include <mega16.h> #include <alcd.h> #include <delay.h> #include <stdio.h> #include <stdlib.h> char str[20]; while(1) { sprintf(str,"TCNT0 = %d",TCNT0); lcd_puts(str); delay_ms(30); lcd_clear(); }
در کد بالا ابتدا با استفاده از تابع sprintf متغیر ۸ بیتی TCNT0 در داخل رشتهی str ریخته شده و سپس آمادهی چاپ بر روی LCD میشود. سپس با یک تاخیر ۳۰ میلی ثانیهای مقدار روی LCD به روز میشود. حال برای این که آن کد بر روی شبیه ساز Proteus جواب دهد نیاز به تغییراتی در نرم افزار Proteus داریم. با دوبار کلیک کردن روی مولد پالس خود و انتخاب مد Pulse با منویی شبیه زیر مواجه خواهیم شد.
در قسمت سبز رنگ با نام Initial(Low) Voltage ولتاژ پالس خود را در حالت Low-State مشخص میکنیم. در اینجا ما این مقدار را برابر ۰ ولت در نظر گرفتیم.
در قسمت Pulsed(High) Voltage میزان ولتاژ پالس خود را در حالت High-State مشخص میکنیم. همانطور که در شکل پیداست ما این مقدار را برابر +5 ولت در نظر گرفتیم. بخش سوم که با عنوان Rise Time و Fall Time مشخص شده نمایانگر همان تاخیر تغییر سطوح منطقی است که ما در ایجا آن را بدون تغییر رها کردیم. بخش بعدی بخش فرکانس است که با رنگ بنفش مشخص شده است و ما در این بخش فرکانس موج مربعی خود را تعیین میکنیم که نمایان گر تعداد پالس تولید شده در واحد زمان است. مثلا در اینجا در هر ثانیه ۱۰ پالس تولید میشود. حال پس از انجام این تنظیمات مشاهده میکنید که مقدار TCNT0 از ۰ شروع شده و تا ۲۵۵ رفته و سپس دوباره به مقدار ۰ برمیگردد، در واقع با این کار به مفهوم واقعی شمارش تعداد پالس در میکروکنترلرها به وسیلهی شمارندههایی با تعدادبیت محدود پی میبریم.
در مرحله بعد میخواهیم کاری کنیم که بتوانیم تعداد بیشتری پالس را بشماریم به این معنی که مثلا اگر ۵۰۰ پالس وارد شد ما بتوانیم این تعداد پالس را با در نظرگرفتن سرریز TCNT0 نیز بشماریم.
به قطعه کد زیر نگاه کنید.
#include <mega16.h> #include <alcd.h> #include <delay.h> #include <stdio.h> #include <stdlib.h> char str[20]; int counter=0; long int totalPulse=0; while(1) { if(TCNT0 == 255) counter++; totalPulse=counter*256+TCNT0; sprintf(str,"AllPulse = %d",totalPulse); lcd_puts(str); delay_ms(5); lcd_clear(); }
در قطعه کد بالا با تعریف دو متغیر جدید به نامهای counter که تعداد ۲۵۵ شدنهای TCNT0 را در خود ذخیره میکند و همچنین متغیر totalPulse که تعداد کل پالسهای وارد شده به واحد تایمرکانتر صفر را در خود دارد کار خود را آغاز میکنیم. در کد با هر بار ۲۵۵شدن TCNT0 یک واحد به متغیر counter اضافه میکنیم، سپس تعداد کل پالسها را از ضرب counter در ۲۵۶ و جمع آن با مقدار فعلی TCNT0 محاسبه میکنیم. همچنین مقدار تاخیر را بسیار کم و برابر با 5ms قرار میدهیم.
اگر این کد را برای یک موج مربعی با فرکانس 10Hz اجرا کنید مشاهده میکنید که کد به خوبی کار میکند ولی اگر فرکانس منبع را به 100Hz افزایش دهید مشاهده میکنید که گاها پس از رسیدن TCNT0 به مقدار ۲۵۵ مقدار پالس کلی شما کاهش پیدا کرده و دوباره از مقدار قبلی خود شروع به کار میکند.
علت این امر چیست؟
بلی، علت این امر این است که ما در این قطعه کد هر بار قبل از چک کردن شرط ۲۵۵ شدن TCNT0 به میزان 5ms تاخیر داریم و در حین این تاخیر ممکن است TCNT0 سرریز کند و ما متوجه آن نخواهیم شد. هرچه فرکانس سیگنال ورودی بیشتر باشد این امر مشهودتر است. با این اوصاف این روش نیز روش کارآمدی نخواهد بود و باید در صدد استفاده از روشهای کاملتر و بهتری باشیم. در بخش بعدی با معرفی یک قابلیت جدید در میکروکنترلرهای AVR این مبحث را ادامه میدهیم.
13 Comments
سلام . خسته نباشید مهندس
مطالبتون بسیار دقیق و قابل فهم بود.
امیدوارم بتونم از مطالب شما بهره کافی ببرم. با تشکر فراوان
ممنون از شما دوست عزیز، باعث افتخار هست که مطالب برای شما مفید واقع شده.
سلام آقای محمدی.
باتشکر از شما بابت مطالب مفید وکاملا روان،خیلی لذت بردم از اینگونه توضیح.
موفق باشید
سلام و خسته نباشید میخوام به کمک تایمر کانتر یه برنامه به زبان basic بنویسم که بتونه یه موج مربعی با فرکانس ۱۰khz و D=40% تولید کنه
و قسمت بعدش میخوام موج مربعی باشه اما فرکانس وD قابل تنظیم باشه
ممنون میشم پاسخ بدید خیلی مهمه
سلام و خسته نباشید میخوام به کمک تایمر کانتر یه برنامه به زبان basic بنویسم که بتونه یه موج مربعی با فرکانس ۱۰khz و D=40% تولید کنه
و قسمت بعدش میخوام موج مربعی باشه اما فرکانس وD قابل تنظیم باشه
ممنون میشم پاسخ بدید خیلی مهمه.
سلام
میشه راجع به کد توضیح دهید، چرا در ۲۵۶ ضرب کردید؟
بسیار عالی و روان بود
ممنون خیلی گویا جالب و خوب بود
خدا خیرتون بده
سلام ببخشید من میخوام برنامه در ۱۵ ثانیه تعداد پالس های خارجی رو شمارش کند چطور میتونم بنویسم میشه کمکم کنید عجله دارم …
عالی بود،ممنون
سلام، ممنونم، بسیار عالی بود
سلام میخوام پالس حساس به سطح با وقفه صفر بشمارم
سلام خسته نباشید مهندس ببخشید یه سوال من این برنامه رو گپی کردم و در پروتیوس شبیه سازی کردم یه مشکلی که داره بعد از عدد۲۵۰که میرسه عدد پرتاب میشه میره تا بالای ۱۰۰۰چرا این مشکل به وجود میاد؟