Visits: 3616
با توجه به سوالات مکرر کاربران در مورد تایمرها و ایجاد زمان های دقیق، در این مطلب تعداد زیادی مثال با تایمرهای صفر (8 بیتی) و یک (16 بیتی) جهت ایجاد زمان دقیق یک ثانیه برنامه نویسی شده اند. در این مثال ها همه به صورت وقفه و همه به صورت چک مداوم برنامه ها را نوشته ایم، بنابراین با مطالعه این مطلب و مشاهد برنامه ها دیگر نباید مشکلی در ایجاد تاخیر های دقیق با تایمر وجود داشته باشد. در ادامه ما با استفاده از یک میکروکنترلر atmega16 و یک عدد LED به همراه یک اسیلوسکوپ برای تک تک حالت ها برنامه های جداگانه با فایل های شبیه سازی جدا گانه ایجاد کرده ایم، تا این مسائل را به روشنی حل کنیم.
اولین نکته مهم در ایجاد تاخیرهای دقیق انتخاب مقسم فرکانسی تایمر یا همان Perscale می باشد.
یعنی باید به گونه انتخاب شود که با استفاده از آن به توان اعداد صحیح و روندی برای شمارنده تایمر انتخاب کرد. برای انجام این کار باید دقت و حوصله زیادی به خرج داد تا Perscale خوبی انتخاب کرد، که خود این مسئله به فرکانس کاری میکرو وابستگی شدیدی دارد، پس برای ایجاد یک زمان دقیق ما نباید در محاسبات حتی از یک ده هزارم اعشار هم صرف نظر کنیم که برای اینکار توصیه میکنیم از ماشین حساب استفاده کنید. (در این صورت شما تاخیر های دقیقی خواهد داشت)
در تمامی مثال ها ما فرکانس کاری میکرو را 4000000 هرتز انتخاب میکنیم و محاسبات را بر این اساس انجام می دهیم. F=4MHz همچنین برای راحتی کار از Codewizard نرم افزار استفاده میکنیم.
مثال های تایمر 8 بیتی صفر
ایجاد تاخیر 1 ثانیه با استفاده از وقفه تایمر صفر محتوای پوشه (TimersTimer 0 (8bit)1sec delay [INT])
Perscale=64 انتخاب میکنیم و مد تایمر هم شمارش تا FF می باشد، همچنین وقفه تایمر صفر و وقفه سراسری را هم فعال میکنیم. بنابراین TCCR0=0x03 خواهد شد. حال محاسبات تایمر:
Ft0= 4MHz/64=62500Hz فرکانس کاری تایمر
مدت زمان شمارش هر پله تایمر Tt0=1/62500=16us
رجیستر تایمر صفر 8 بیتی است بنابراین 250 پله شمارش نیاز می باشد تا 4ms ایجاد شود 16us*250=4ms
پس اگر 250 مرتبه تایمر وقفه صادر کند ما 1 ثانیه تاخیر دقیق ایجاد خواهیم کرد 1sec=1000ms ⇒ 1000/4ms=250
عدد 250 به وجود آمده در مرحله 3 و 4 از نظر مقدار هیچ ربطی به هم ندارند و در این مثال به طور کامل تصادفی شبیه هم شده اند. در مرحله سوم گفتیم که برای ایجاد تاخیر 4ms باید 250 پله شمارش شود و همانطور که میدانیم این رجیستر از 0 تا FF را شمارش می کند بنابراین ما باید در هر بار ایجاد وقفه مقدار اولیه شمارش را 6 وارد کنیم تا شمارش از این مقدار تا 256 ادامه یابد و در نهایت 250 پله شمارش شود. تا این جا ما 4ms تاخیر ایجاد کردیم و همانطور که مشاهد میکنید این عدد صحیح و بدون اعشار می باشد. حال با استفاده از یک متغییر به نام c تعداد وقفه های تایمر را شمارش می کنیم. بنابراین برای ایجاد 1 ثانیه تاخیر باید 250 بار وقفه رخ دهد. به عنوان مثال برای ایجاد نیم ثانیه این عدد به 125 تغییر می یابد.
while (1){ if(c>=250) { led=~led; c=0; } }
بنابراین در یک حلقه بینهایت مقدار c چک می شود تا در صورتی که به 250 رسیده است وضعیت پورت A.0 را برعکس کند. یعنی هر ثانیه یک بار چشمک خواهد زد. همچنین می توانید با استفاده از اسیلوسکوپ این زمان دقیق را مشاهده نمائید.
ایجاد تاخیر 1 ثانیه با استفاده از چک مداوم در تایمر صفر محتوای پوشه (TimersTimer 0 (8bit)1sec delay chek)
تمامی مراحل مربوط به محاسبات تایمر دقیقا شبیه به مثال قبلی می باشد. اما منظور از چک مداوم چیست؟ در اینجا چون ما از وقفه استفاده نکرده ایم باید میکروکنترلر را وارد یک حلقه بینهایت کنیم و تنها شرط خروج از این حلقه را یک شدن پرچم سریز، تایمر صفر قرار دهیم. اما در این حلقه بینهایت چه مسائلی باید توسط برنامه نویس رعایت شود!
void delay_1s(){ for(c=0; c<=250; c++){ CCR0=0x03; TCNT0=0x06; OCR0=0x00; while(!(TIFR & 0x02)){//wait for 1s //other program with out delay }; TCCR0=0x00; //timer0 off TIFR|=0x02; //default flag timer0 ==> change to 1 } }
برای راحتی کار ما مراحل ایجاد مدت زمان 1 ثانیه را در یک تابع به نام delay_1s برنامه نویسی میکنیم. در مثال قبلی ما با استفاده از دستور if مقدار c را برای 250 شدن برسی می کردیم ولی در اینجا با کمک یک حلقه for این شمارش را انجام می دهیم. ابتدا تایمر را روشن میکنیم و بعد از اتمام نیز آن را خاموش می کنیم، تنها نکته آن در درحلقه بینهایت می باشد یعنی TIFR، همانطور که می دانیم این رجیستر مربوط به پرچم های تایمرهای 0 و 1 و2 میکرو می باشد، یعنی در هر کدارم از تایمر ها که سریز شمارش رخ دهید در این رجیستر بیت مربوطه آن یک می شود. حال در تایمر صفر پرچم سرریز تایمر صفر بیت دوم می باشد که مقدار آن 02 می شود. با استفاده از دستور AND بیتی یعنی TIFR & 0x02 تنها بیت دوم رجیستر TIFR یعنی (TOV0) مورد برسی قرار میگیرد و در صورتی که این بیت یک شود طبق شرط حلقه ! مخالف آن یعنی صفر است و حلقه شکسته خواهد شد. اما در صورتی که بیت TOV0 صفر باشد ! مخالف آن یک می شود و حلقه ادامه می یابد. بعد از حلقه بینهایت تایمر را خاموش می کنیم و رجیستر TIFRرا OR بیتی می کنیم یعنی تنها بیت TOV0 صفر خواهد شد و دستور بر روی مابقی بیت ها تاثیری نخواهد داشت.
نکته: بیت TOV0 در هنگام فعال بودن وقفه بعد از رخ دادن وقفه و یک شدن آن به صورت اتوماتیک صفر خواهد شد. اما در حالت چک مداوم باید توسط برنامه نویس این بیت صفر شود تا تایمر برای سرریز بعدی آماده باشد.
در حلقه بینهایت چه مسایلی را باید رعایت کرد؟ در این حلقه ما می توانیم توابع یا سایر دستورات دیگر را فراخوانی کنیم یعنی در همان مدتی که داریم عمل زمان سنجی را انجام می دهیم کار دومی هم به میکرو محول کنیم. در این حلقه نباید از دستورات یا توابعی که مدت زمان اجرای آنها بیشتر یا مساوی 4ms است استفاده نمود، زیرا باعث گذر زمان شده و میکرو شرط حلقه را با تاخیر چک خواهد نمود، بنابراین مدت زمان 1 ثانیه ما دقیق نخواهد شد.با توجه به نکته فوق ما می توانیم در این مدت زمان برای کار دوم میکرو مسائلی همچون: اسکن صفحه کلید، خواندن یا نوشتن بر روی پورتها، استفاده از مبدل آنالوگ به دیجیتال و سایر عملیاتی که مدت زمان اجرای آنها توسط CPU کمتر از 4ms باشد استفاده نمود. توصیه میکنم که از دستور sprintf و ریفرش LCD توسط دستور lcd_puts استفاده نکنید چون مدت زمان زیادی طول خواهد کشید تا اطلاعات برای LCD ارسال شود در نتیجه زمان سنجی ما دچار اختلال خواهد شد.
در هر صورت توصیه من این است که از روش اول یعنی وقفه استفاد کنید، چون در این مد هیچ گونه محدویتی وجود ندارد و کارها با سرعت بیشتری پیش خواهند رفت. (مگر در شرایط خاص)
مثال های تایمر 16بیتی یک
محاسبات این تایمر نیز شبیه تایمر صفر می باشد با این تفاوت که بجای عدد 256 باید از عدد 16 بیتی 65536 استفاده کرد.
ایجاد تاخیر 1 ثانیه با استفاده از وقفه تایمر یک محتوای پوشه (TimersTimer 1 (16bit)1sec delay [INT])
چون شمارنده تایمر در اینجا 16 بیتی می باشد بنابراین قارد است از 0 تا FFFF یعنی 0 تا 65535 را بشمارد. از آنجایی که فرکانس تایمر 62500 هرتز شد و مدت زمان شمارش هر پله هم 16us شد بنابراین با تقسیم عدد 1 بر 16us حاصل 62500 پله شمارش خواهد شد پس 65536-62500=3036 خواهد شد یعنی شروع شمارنده تایمر باید از عدد 30 36 باشد. همانطور که ملاحضه می کنید در اینجا چون تایمر یک 16 بیتی می باشد و گستره شمارش بالایی دارد دیگر نیازی به یک متغیر c برای شمارش نیست و به راحتی خود تایمر این کار را انجام خواهد داد.
interrupt [TIM1_OVF] void timer1_ovf_isr(void){ TCNT1H=0xBDC >> 8; TCNT1L=0xBDC & 0xff; led=~led; }
در روتین وقفه تایمر یک هم باید مجددا مقدار شمارنده برای شمارش بعدی با عدد 3036 بارگذاری شود. همچنین در همین روتین ما عمل معکوس سازی پین A.0 را انجام می دهیم.
ایجاد تاخیر 1 ثانیه با استفاده از چک مداوم در تایمر یک محتوای پوشه (TimersTimer 1 (16bit)1sec delay chek)
در این جا هم مانند مثال شماره 2 می باشد با این تفاوت که بیت TOV1 برابر با 04 می باشد یعنی بیت سوم رجیستر TIFR و مابقی محاسبات نیز مانند حلت قبل می باشد.
void delay_1s(){ TCCR1A=0x00; TCCR1B=0x03; TCNT1H=0x0B; TCNT1L=0xDC; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; while(!(TIFR & 0x04)){//wait for 1s //other program with out delay }; TCCR1B=0x00; //timer0 off TIFR|=0x04; //default flag timer1 ==> change to 1 }
امیدوارم که توضیحات کامل بوده باشد و این مقاله مورد رضایت دوستان و کاربران محترم picpars.com واقع شده باشد. باز هم می توانید سوالات و مشکلات خود را در قسمت نظرات مطرح نمائید. با تشکر سید محسن قاسمیان
این مقاله تنها در وب سایت برنامه نویسی میکروکنترلرها منتشر شده است و هر گونه کپی برداری از مطالب و فایل ها تنها با درج منبع و نام سایت مجاز می باشد!