تایمر صفر (TIMER0) در میکروکنترلر AVR (جلسه ی ۱۳)

در جلسه قبل با مفاهیم تایمرها در میکروکنترلر AVR آشنا شدیم. حالا وقت اینه که کمی عملی تر وارد مبحث تایمرها بشیم و کمی با رجیسترهای اونا آشناتر بشیم و کمی هم کد بنویسیم و با شبیه ساز اجراشون کنیم تا بهتر بحث تایمر رو لمس کنیم. اما در این جلسه در مورد TIMER0 و اون هم فقط در مد نرمال صحبت می کنیم. امیدوارم مفید واقع بشه.
  • توضیحات
  • کدنویسی
  • شبیه سازی

در جلسه ی قبل مفاهیم پایه ی تایمرها در میکروکنترلرهای AVR را بیان کردیم. به طور خلاصه در جلسه ی قبل:

timer0 title

۱- دیدیم که تایمرها از رجیسترهایی ساخته شده اند که مقادیرشان به طور خودکار افزایش یا کاهش می یابد. و در نتیجه اصطلاحات تایمر و کانتر می توانند به جای یکدیگر مورد استفاده قرار گیرند.

۲- یاد گرفتیم که در AVR سه نوع تایمر وجود دارد: TIMER1 ، TIMER0 و TIMER2. در بین این سه تایمر، TIMER1 شانزده بیتی می باشد در حالی که دیگر تایمرها هشت بیتی هستند.

۳- با تقسیم کننده های فرکانس آشنا شدیم.

۴- نحوه ی انتخاب مقسم فرکانس مناسب را یاد گرفتیم.

۵- همچنین دیدیم که می توان از وقفه ی تایمرها نیز استفاده نمود.

۶- همچنین یاد گرفتیم که هر یک از تایمرهای موجود در AVR می تواند در سه مد کاری نرمال، CTC و PWM کار کند.

در این پست نحوه ی استفاده از TIMER0 در مد نرمال را یاد خواهیم گرفت. چون تایمر یکی از اجزای خارجی میکروکنترلر AVR است باید با نوشتن “۱” بر روی رجیسترهایی خاص، آن را فعال کرد. دو رجیستری که باید در ابتدا بشناسیم عبارتند از:

رجیستر TCNT0

شمارش هشت بیتی TIMER0 در این رجیستر انجام می شود. مقدار شمارشگر در این رجیستر ذخیره می شود و به طور خودکار افزایش یا کاهش می یابد. این رجیستر هم خواندنی و هم نوشتنی است. شکل زیر نشان دهنده ی بیت های این رجیستر می باشد:

tcnt0

حالا می دانیم که مقدار شمارشگر در کجا ذخیره می شود. اما تا وقتی که تایمر را فعال نکنیم، این رجیستر نیز فعال نیست. بنابراین نیاز است که ابتدا تایمر را تنظیم کنیم. اما چگونه؟ در ادامه متوجه خواهید شد:

رجیستر TCCR0

این رجیستر به صورت زیر می باشد:

tccr0

در این رجیستر فعلا بر روی بیت هایی تمرکز می کنیم که هایلایت شده اند. دیگر بیت های این رجیستر را در پست های آتی بررسی خواهیم کرد. این بیت ها که CS00 و CS01 و CS02 نام دارند، بیت های انتخاب ساعت (Clock Select) هستند. با این بیت ها می توانیم انتخاب کنیم که از کدام تقسیم کننده ی فرکانس استفاده شود. حالت های مختلفی که برای این سه بیت ممکن است، در جدول زیر لیست شده است:

cs0 table

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

مثال ۱ :

فرض کنید که می خواهیم یک سیگنال پالس با عرض پالس های ۶ میلی ثانیه ای تولید کنیم (یعنی ۶ میلی ثانیه یک باشد و ۶ میلی ثانیه صفر باشد). همچنین فرکانس منبع ساعت را ۳۲ کیلوهرتز در نظر می گیریم. قصد داریم که با استفاده از تایمر صفر و بدون استفاده از مقسم فرکانس این پالس را تولید کنیم. با استفاده از فرمولی که در جلسه ی قبل یاد گرفتیم، برای تاخیر ۶ میلی ثانیه ای و فرکانس ساعت ۳۲ کیلوهرتز نیاز به ۱۹۱ شمارش داریم. با توجه به این که حداکثر مقدار شمارش برای تایمر صفر ۲۵۵ است پس این امر با استفاده از این تایمر میسر خواهد بود.

برای ایجاد چنین پالسی کافی است که مقدار شمارشگر را دنبال کنیم و هنگامی که به مقدار آن به ۱۹۱ رسید، پین خروجی مورد نظر را تغییر وضعیت دهیم (اگر “۰” است آن را “۱” کنیم و اگر “۱” است آن را “۰” کنیم) و سپس تایمر را ریست نماییم. با توجه به این که در این مثال می خواهیم از هیچ مقسم فرکانسی استفاده نکنیم باید CS00 را یک کرده و دیگر بیت های رجیستر TCCR0 را در حالت صفر باقی بگذاریم. (ردیف دوم جدول بالا که با رنگ زرد مشخص شده است). PIN.C0 را به عنوان پین خروجی مورد نظر انتخاب می کنیم. برنامه ای که در محیط کدویژن می نویسیم در تب کد نویسی و تحت عنوان کد مثال شماره ی ۱ آمده است.

برنامه ی نوشته شده در یک حلقه ی while به طور مدام مقدار کانتر را چک می کند تا وقتی که به مقدار ۱۹۱ برسد. هنگامی که مقدار رجیستر تایمر به ۱۹۱ رسید پین PIN.C0 تغییر وضعیت می دهد. علت اینکه شرط عبارت if به صورت TCNT0 >= 191 نوشته شده و از شرط TCNT0 == 191 استفاده نکردیم این است که ممکن است در برخی موارد چک کردن شرط آنقدری طول بکشد که مقدار تایمر از ۱۹۱ گذر کند و با این که مقدار تایمر از ۱۹۱ گذر کرده، برخلاف انتظارمان شرط ارضا نشود و خطا داشته باشیم.

مثال ۲ :

با توجه به این که فرکانس داخلی میکروکنترلر نمی تواند ۳۲ کیلوهرتز باشد، مثال بالا را با حالتی واقعی تر بیان می کنیم و در عوض از تقسیم کننده های فرکانس استفاده می نماییم. فرض می کنیم که فرکانس داخلی برابر با ۱ مگاهرتز باشد و بخواهیم سیگنال پالسی تولید کنیم که عرض پالس های آن در حدود ۲۵۰ میلی ثانیه باشد (مقدار دقیق آن برایمان مهم نیست). با توجه به این که فرکانس داخلی ۱ مگاهرتز است و دوره ی زمانی آن خیلی کم می شود، تایمر هشت بیتی برای تولید تاخیر ۲۵۰ میلی ثانیه قابل استفاده نیست. برای رفع این مشکل از تقسیم کننده ی فرکانس استفاده می کنیم. به جدول زیر دقت کنید:

prescaler choosing table

با استفاده از فرمولی که در جلسه ی قبل ارائه شد، تعداد شمارش های مورد نیاز برای تاخیر ۲۵۰ میلی ثانیه ای و با استفاده از تقسیم کننده های فرکانس مختلف محاسبه شده اند. مشاهده می شود که مقسم ۱۰۲۴ برای کار ما مناسب است چون تعداد شمارش مورد نیاز از حداکثر شمارش ممکن در تایمر صفر (۲۵۵) کمتر است. یعنی با استفاده از مقسم ۱۰۲۴ ، تایمر باید ۲۴۴٫۱۴۰۶۲۵ بار بشمارد که این مقدار به دلیل غیرصحیح بودن ممکن نیست. اما در اینجا چون دقیق بودن زمان تاخیر برایمان مهم نیست از مقادیر اعشاری آن صرف نظر کرده و با تعداد ۲۴۴ شمارش (یعنی از صفر تا ۲۴۳) به تاخیر ۲۴۸/۸۳۲ میلی ثانیه می رسیم که برای کار ما مناسب است.

برای انتخاب مقسم فرکانس ۱۰۲۴ ، رجیستر TCCR0 را طبق ردیف ششم جدول انتخاب مقسم های فرکانس مقداردهی می کنیم، یعنی CS00=1 و CS01=0 و CS02=1 . بقیه ی کد مانند کد قبلی باقی می ماند، فقط به جای ۱۹۱ بار، این دفعه ۲۴۳ را قرار می دهیم. در تب کد نویسی به کد مثال شماره ی ۲ دقت کنید.

همان طور که دیده شد، زمان تاخیری که در بالا تولید شد دقیقا برابر با ۲۵۰ میلی ثانیه نشد، حال سوال اینجاست که اگر دقیق بودن این مقدار برایمان مهم بود چه باید می کردیم؟ در جلسه ی قبل گفتیم که برای دقیق تر بودن زمان تاخیر ایجاد شده، باید از تقسیم کننده های فرکانسی پایین تر استفاده کنیم. با استفاده از فرمول ارائه شده در پست قبل، در این جا اگر بخواهیم از تقسیم کننده ی فرکانس ۲۵۶ استفاده کنیم برای ایجاد تاخیر ۲۵۰ میلی ثانیه ای و با فرکانس داخلی ۱ مگاهرتز، تایمر باید در حدود ۹۷۶ بار بشمارد. این مقدار از ۲۵۵ بالاتر بوده و این امکان برایمان وجود ندارد. از طرفی استفاده از فرکانس داخلی ۱ مگاهرتز دقت مورد نظر را ندارد (چون توسط مدار RC داخلی تولید می شود.) و ما همیشه دوست داریم فرکانس CPU در حد امکان بالاتر باشد تا میکروکنترلر دستورات خود را سریع تر انجام دهد. برای همین معمولا از فرکانس خارجی و دقیق ۱۶ مگاهرتز استفاده می کنیم که این خود باعث بدتر شدن شرایط مساله می شود. اما باز هم راه حلی وجود دارد. برای یادگیری آن مساله را دوباره تعریف می کنیم:

مثال ۳ :

فرض کنیم که قصد تولید سیگنال پالسی با عرض پالس های ۲۵۰ میلی ثانیه ای داریم. CPU دارای فرکانس خارجی ۱۶ مگاهرتز بوده و می خواهیم که از تقسیم کننده ی فرکانس ۲۵۶ نیز استفاده کنیم.

برای حل این مساله از وقفه ی تایمر استفاده می کنیم. می دانیم که هر بار که تایمر سرریز شود، میکروکنترلر قادر به ایجاد یک وقفه است. با توجه به این که تاخیری که می خواهیم ایجاد کنیم از حداکثر تاخیر ممکن بیشتر است، تایمر مطمئنا سرریز خواهد کرد. و همچنین هربار که تایمر سرریز شود یک وقفه اتفاق می افتد. درون ISR وقفه دستوری می نویسیم که تعداد سرریزها را در متغیری ذخیره کند و با این کار با تعداد سرریز لازم به زمانی که می خواهیم دست می یابیم. حالا می خواهیم بدانیم که برای ایجاد این تاخیر تایمر چند بار سرریز می شود و چند بار وقفه رخ می دهد؟

برای این کار، نیاز به محاسبات داریم. اگر از تقسیم کننده ی فرکانس ۲۵۶ استفاده کنیم، تایمر در هر ۴/۰۹۶ میلی ثانیه سرریز خواهد شد. حالا به محض این که تایمر سرریز شود وقفه رخ می دهد و میکروکنترلر دستورات داخل ISR تایمر را اجرا خواهد کرد. داریم:

 ۲۵۰ms ÷ ۴٫۰۹۶ =۶۱٫۰۳۵۲

بنابراین هنگامی که تایمر ۶۱ مرتبه سرریز شود ۲۴۹٫۸۵۶ میلی ثانیه زمان گذشته است. پس از آن در حلقه ی ۶۲ ام تایمر به زمان ۲۵۰ میلی ثانیه می رسد و از آن می گذرد. بنابراین در حلقه ی ۶۲ام نیاز به ایجاد یک تاخیر ۰٫۱۴۴ = ۲۴۹٫۸۵۶ – ۲۵۰ داریم. با توجه به این که فرکانس خارجی ۱۶ مگا هرتز بوده و از مقسم ۲۵۶ استفاده می کنیم فرکانس تایمر برابر با ۶۲٫۵ KHz می باشد. و هر شمارش ۰٫۰۱۶ میلی ثانیه زمان می برد. از این رو برای دست یابی به تاخیر ۰٫۱۴۴ میلی ثانیه باید تایمر ۹ بار بشمارد (  ۹x  ۰٫۰۱۶ = ۰٫۱۴۴). پس در حلقه ی ۶۲ام فقط اجازه می دهیم که تایمر ۹ بار بشمارد و سپس آن را ریست می کنیم. در این صورت دقیقا به زمان ۲۵۰ میلی ثانیه ای دست یافته ایم. برنامه ای که در محیط کد ویژن می نویسیم در تب کدنویسی تحت عنوان کد مثال ۳ آمده است.

توجه کنید که کد بالا تا وقتی که ندانید چگونه وقفه ی تایمر را فعال کنید کامل نیست. برای این کار نیاز است تا با رجیسترهای زیر آشنا شوید:

رجیستر TIMSK

این رجیستر مانند شکل زیر می باشد:.

TIMSK0

این رجیستر بین هر سه تایمر مشترک بوده و فقط بیت های ۰ و ۱ مربوط به TIMER0 می باشند. در حال حاضر ما با بیت ۰ یعنی TOIE0 کار داریم. یک کردن این بیت، وقفه ی سرریز تایمر صفر را فعال می کند.

رجیستر TIFR

بیت های این رجیستر در شکل زیر آمده اند:

TIFR0

هر چند در این جا ما از این رجیستر استفاده نمی کنیم اما لازم است تا در مورد آن بدانید. این رجیستر نیز در بین هر سه تایمر AVR مشترک است. بیت ۰ و ۱ این رجیستر مربوط به تایمر صفر می باشند. در حال حاضر نیاز است که با بیت TOV0 آشنا شویم. این بیت هنگامی که سرریز رخ می دهد یک می شود و هنگامی که ISR اجرا شد دوباره صفر می گردد. اگر ISR برای اجرا وجود نداشته باشد با نوشتن یک بر روی این بیت آن را صفر می کنیم. در این جا چون از ISR استفاده می کنیم نیازی به استفاده از این بیت نداریم.

فعال کردن وقفه ی عمومی:

در میکروکنترلرهای AVR یک بیت به تنهایی همه ی وقفه ها را فعال یا غیر فعال می کند. برای استفاده از وقفه ی تایمر باید ابتدا این بیت را فعال کنیم. این کار با نوشتن دستور sei امکان پذیر است. البته نیازی به نگرانی نیست چون در این جا فقط یک بار از این دستور استفاده خواهیم نمود.

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

کد مثال ۱ :

توضیحات کد:

خط شماره ی ۳ : تابعی را برای راه اندازی تایمر صفر تعیین می کنیم که نام آن timer0_init می باشد.

خط شماره ی ۵ : مقدار CS00 در رجیستر TCCR0 را یک می کنیم تا تایمر آغاز به شمارش کند.

خط شماره ی ۶ : مقدار رجیستر TCNT0 را صفر می کنیم تا تایمر از صفر شروع به شمارش کند.

خط شماره ی ۱۱ : پین PINC.0 را خروجی می کنیم.

خط شماره ی ۱۲ : تایمر را راه اندازی می کنیم.

خط شماره ی ۱۵: تا وقتی که مقدار تایمر به ۱۹۱ برسد صبر کرده و مقدار تایمر بررسی می کنیم.

خط شماره ی ۱۷: خروجی PINC.0 را تغییر وضعیت می دهیم.

خط شماره ی ۱۸: مثدار تایمر را صفر می کنیم.

 ***********************************************

 کد مثال ۲ :

توضیحات کد:

خط شماره ی ۵ : مقدار CS00  و CS02 در رجیستر TCCR0 را یک می کنیم تا تایمر با مقسم فرکانس ۱۰۲۴ آغاز به شمارش کند.

خط شماره ی ۱۵: تا وقتی که مقدار تایمر به ۲۴۳ برسد صبر کرده و مقدار تایمر بررسی می کنیم.

بقیه ی قسمت های کد مانند کد قبل می باشد.

 ***********************************************

کد مثال ۳ :

توضیحات:

خط شماره ی ۳ : در محیط کد ویژن برای فراخوانی روال وقفه ی سرریز تایمر صفر باید به این صورت عمل کرد.

خط شماره ی ۵ : پس از هر بار سرریز تایمر ۰ مقدار متغیر tot_overflow یکی افزایش می یابد.

خط شماره ی ۹ : فعال کردن تایمر صفر با تقسیم کننده ی ۲۵۶٫

خطوط ۱۶ تا ۲۴ : در این حلقه ابتدا صبر می کنیم تا تایمر ۶۱ بار سرریز شود. سپس صبر می کنیم تا تایمر ۹ تا بشمارد. در این صورت طبق محاسباتی که انجام شد ۲۵۰ میلی ثانیه سپری می شود. پس از گذشت این زمان پین PINC.0 را تغییر وضعیت می دهیم و تایمر و متغیر tot_overflow را دوباره صفر می کنیم.

بقیه ی دستورات مانند کدهای قبل است.

 ***********************************************

کد نهایی:

توضیحات: خط شماره ی ۱۲ : وقفه ی مربوط به سرریز تایمر صفر را فعال می کنیم.

خط شماره ۱۳ : وقفه ی عمومی را فعال می کنیم. بقیه ی دستورات مانند کدهای قبل است.

شبیه سازی کد نهایی در محیط پروتئوس به صورت زیر اجرا می شود:

timer0-smltn

 

توضیحات:

 همان طور که مشاهده می شود اسیلوسکوپ نیز نشان می دهد که زمانی که ایجاد کرده ایم دقیقا برابر با ۲۵۰ میلی ثانیه است. مقیاس زمانی برابر با ۲۵۰ میلی ثانیه بوده و هر پالس نیز دقیقا یک واحد زمانی را طی می کند.

 فایل های مربوطه شامل فایل hex و فایل سی و همچنین فایل شبیه سازی را از لینک زیر دانلود کنید:

پسورد: www.microlearn.ir

دانلود فایل های تایمر صفر

خب این هم از مد نرمال تایمر ۰ امیدوارم که خسته نشده باشید. در جلسه بعد در مورد تایمر ۱ صحبت خواهیم کرد. یا حق.

درباره‌ی مجتبی حکیمیان

Avatar
کارشناس الکترونیک و کارشناس ارشد مخابرات سیستم | زمینه ی تحقیقاتی : پردازش تصویر و تعقیب اشیاء متحرک |‌فعال در حوزه ی الکترونیک مبتنی بر میکروکنترلر | برنامه نویس و طراح بردهای الکترونیکی مبتنی بر میکروکنترلر

۳۱ نظر

  1. Avatar
    محمد صادق جزی

    باسلام و درود؛
    مطالب آموزشی که ارائه کردید بسیار روان و قابل فهم و مفید و عالی بودند.
    بسیار بسیار سپاس گذارم.
    مشکل بنده حل شد.ممنونم.

  2. Avatar
    محمد رضا نعمت اللهی

    سلام و روزتون بخیر
    من مثاله شماره دو را چندین و چندبار تست کردم و بخوببی عمل کرد اما یک بار بطور اتفاقی ورودی vcc که باید به پایه ۱۰ میکرو وصل کنم به اشتباه به پایه شماره ۱ (PB0) وصل کردم و برنامه کار کرد و LED شروع به چشمک زدن کرد!
    برای تست به پایه های دیگر پورت B هم وصل کردم و برنامه کار کرد !
    دلیل این موضوع چی میتونه باشه ؟
    ممنون میشم اگر توضیح بدید.

  3. Avatar

    سلام
    واقعا نمیدونم چطوری از شما تشکر کنم !
    من بطور خوداموز دارم الکترونیک میخونم و به مباحث میکرو خیلی علاقه دارم. این مبحث تایمر و کانتر مدتها بود منو به خودش مشغول کرده بود و هر جا مطلبی پیدا میکردم و میخوندم اما متوجه نمیشدم تا اینکه با سایت شما اشنا شدم و بالاخره مشکلم حل شد. واقعا ممنونم.
    خیلی ساده و واضح توضیح دادید . تقسیم بندی هاتون عالیه و مثال ها هم حرف نداره .انشالله که موفق و پیروز باشید

  4. Avatar

    خیلی عالی بود.ممنونم.

  5. Avatar

    سلام وقت بخیر مرسی از توضیحات خوبتون
    اگه فرکانس داخل ۱مگاهرتز و تاخیر ۱۰۰میلی ثانیه باشه و پیش تقسیم کننده ۸
    او محاسبه تاخیر اخر ک برای شما عدد ۹ در اومد
    برای من توی این مثال من یک عدد۳٫۹۸۷۵ درمیاد
    راهنمایی میکنین؟

  6. Avatar

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

  7. Avatar

    سلام ، میشه نمونه برنامه را با اتمل استودیو هم بگین ، این نمونه برا کدویژن هست و منم اصلا نمیخوام با کدویژن کار کنم … تشکر از وقتی ک تا همین الان گذاشته اید ، تنها کاری ک فعلا میتونم بکنم دعا برا سلامتی تون و توفیق روزافزون هست .

    • احسان عبداللهی

      با سلام به شما دوست عزیز
      نکته اول اینه که نسبت به IDE ها حساسیت نداشته باشید با کدویژن هم میشه خیلی کارهای حرفه ای انجام داد بدون اینکه هیچ مشکلی پیش بیاد.
      اما در مورد کدی که فرمودید. مثال یک و دو عینا در اتمل قابل پیاده سازیه. مثال ۳ و مثال ۴ فقط باید تابع معرفی وقفه را با syntax موجود در اتمل تعویض بشه که کار سختی نیست.
      موفق باشید

  8. Avatar

    با سلام
    عالی بود ولی فایل c نداشت

  9. Avatar

    سلام
    بابت مطالب دقیق تون ممنون
    برای تولید فرکانس در بازه مشخص چیکار باید کرد؟

  10. Avatar

    سلام،ممنون بابت اموزش هاتون،اگر براتون میسر در مورد مد اسنکرون و سنکرون در تایمرها توضیح بدین.

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *