پنج شنبه , ۲ آذر ۱۳۹۶
آخرین نوشته‌ها
خانه | سیستم های نهفته | میکروکنترلر | دوره آموزشی AVR | تایمر صفر (TIMER0) در میکروکنترلر AVR (جلسه ی ۴)

تایمر صفر (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-smltn

 

توضیحات:

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

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

پسورد: www.microlearn.ir

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

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

درباره‌ی مجتبی

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

۲۰ ديدگاه

  1. سلام
    بابت مطالب دقیق تون ممنون

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

  3. #define xtal 8000000
    ینی چی؟

    • سلام
      دستور #define برای اختصاص یه مقدار خاص به یه متغیر استفاده میشه. متغیر xtal همونطور که از اسمش پیداس برای تعریف مقدار فرکانس میکروکنترلر در نظر گرفته شده و برای محاسبات تاخیر و .. استفاده میشه. اینجا مقدار فرکانس هشت مگاهرتز تعریف شده.
      این دستور ممکنه ابتدای توابع یا کتابخونه ها دیده بشه. دلیلش رو هم که گفتم چیه.

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

    • سلام به شما
      سیاست کاری میکرولرن بر اینه که تا حد ممکن آموزش هایی را ارائه کنه که مخاطب بیشتری به اون ها نیاز داره، بر این اساس از کدویژن استفاده کردیم. دلیل به کار نبردن کدهای آماده و ویزارد هم ارتقا سطح برنامه نویسی کاربران سایت هست. فعلا درخواست زیادی برای atmel studio نداشتیم. در صورتی که مخاطبان بیشتری به این نرم افزار نیاز داشتند حتما برنامه نویسی در این کامپایلر را هم برای شما قرار می دیم. 🙂

  5. تشکر می کنم.توضیحاتتون واضح و کامل بود. الان متوجه مشکلم شدم.

  6. مرسی از راهنمایی های خوبتون. می خواستم خواهش کنم اگه ممکنه در مورد همین تنظیم نرم افزاری تاخیر بیشتر توضیح بدین:
    مثلا فرض کنیم یک فلاشر ساده(مثل اولین مثال) داریم. از تاخیر ۱۰۰میلی ثانیه استفاده کرده ایم.

    اگه بخوایم فقط از اسیلاتور آر سی داخلی میکرو استفاده کنیم نحوه ی مقدار دهی رجیستری که با آن تقسیم فرکانسی انجام میدهیم(CLKPR که قبلا به آن اشاره کردین) برای دو کلاک مختلف (۱,۴مگ مثلا)با هدف یک تاخیر ۱۰۰msچطور می شود؟(طوری که برای هر دو واقعا ۱۰۰ms به صورت زمان واقعی شود هرچند نا دقیق)
    و سوال آخر اینکه CLKDIV8 را در کدام قسمت از تنظیمات پروتیوس در میکرو میشه تغییر داد؟

    • شما کار رو برای خودتون سخت کردی. برای ایجاد یه تاخیر صد میلی ثانیه ای نیاز به این همه دردسر نیست. استفاده از رجیستر CLKPR برای تغییر فرکانس کلاک هست و فقط برای موارد خاص کاربرد داره و البته فقط در سری های خاص AVR این قابلیت وجود داره.
      شما وقتی داری یه پروژه توی کدویژن ایجاد میکنی با توجه به امکاناتی که داری و با توجه به نیازت یه فرکانس ساعتی رو به عنوان منبع کلاک وارد کن. مثل شکل زیر:

      بعد هم خیلی راحت پس از ایجاد هدر delay.h در ابتدای برنامه ، هرجای برنامه که دوست داشتی تاخیر صد میلی ثانیه ای ایجاد کنی بنویس:
      delay_ms(100);
      خود کدویژن بسته به این که فرکانس چیه محاسبات رو انجام میده و تاخیر صد میلی ثانیه ای ایجاد میکنه و نیاز نیست که شما کار دیگه ای بکنی. حتی وقتی که فرکانس ساعت رو مثلا ۱ مگ تنظیم کنی باز هم کدویژن با محاسبات، تاخیر صدمیلی ثانیه ای ایجاد میکنه.
      بعد از این کار برنامه ی شما برای ایجاد تاخیر مشکلی نداره و تاخیر صد میلی ثانیه ای رو ایجاد کرده اما طبق فرکانس ساعتی که شما قبلا در ابتدای ایجاد پروژه انتخاب کردی. برای همین تا آخر کار باید فرکانس ساعتت هم همون باشه. مثلا فرض کن در ایجاد پروژه مثل عکس بالا فرکانس رو روی ۴ مگ تنظیم کردی. بعد وقتی برنامه ت رو نوشتی و کامپایل کردی، توی پروتئوس هم باید فرکانسی که انتخاب می کنی همون ۴ مگ باشه و بعد از اون در بستن مدار به طور سخت افزاری از کریستال ۴ مگ یا از منبع داخلی ۴ مگ استفاده کنی تا همه چیز طبق روال درست پیش بره.
      سوالی داشتی در خدمتم.

  7. یه سوال کلی داشتم:
    این تاخیری(delay) که گاهی استفاده می کنیم با تغییر فرکانس کلاک عوض میشه.
    یعنی میشه گفت real time نیست(اگه اشتباه نکنم)
    پس نسبت به چه چیزی سنجیده میشه کلا؟؟

    • توابع delay با توجه به مقداری که شما وارد می کنید و با توجه به فرکانس کلاک تنظیم میشن و همون مقدار تاخیری که میخواین رو تولید می کنن. فقط اگه دنبال کارهای خیلی دقیق باشین بهتره ازشون استفاده نکنین.
      البته قرار هم نیست با تغییر کلاک زمان تاخیرشون تغییر کنه، اگه اینجور بود که اصلا به درد نمیخوردن، مگه این که شما برنامه تون رو مثلا برای کلاک ۸ مگ بنویسین و بعد کریستال ۱ مگ ببندین که خب اینجوری زمانی که برای تاخیر نوشتین هشت برابر میشه.
      یادتون باشه که وقتی قصد تغییر کلاک رو دارین هم به لحاظ نرم افزاری و هم شبیه سازی و هم سخت افزاری اون رو تغییر بدین.

  8. احسنت به شما آقا مجتبی ، واقعا عالی بود

  9. سوالم اینکه شما در timre0_initi مقدار دهی اولیه کردین لزومی داره اینجوری در تابع مقداردهی کنیم بعد فراخوانی کنیم؟

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


  10. #include
    int tot=0;
    bit x=0;
    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
    tot++;
    }
    void main(void)
    {
    DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (1<<DDC0);
    PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
    TCCR0=(0<<WGM00) | (0<<COM01) | (0<<COM00) | (0<<WGM01) | (1<<CS02) | (0<<CS01) | (0<<CS00);
    TCNT0=0x00;
    OCR0=0x00;
    TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (1<=61)
    {
    if (TCNT0>=0x09)
    {
    x=~x;
    PORTC.0=x;
    TCNT0=0x00;
    tot=0;
    }
    }
    }
    }

    ببخشید ظاهرا مثله اینکه کد موقع ارسال بهم ریخته شده بود الان اشکالات این کد به نظرتون چی میتونه باشه ؟

    • دوست من این کد، با کدی که بنده به عنوان کد نهایی نوشته م هیچ تفاوتی نداره. دقیقا همونه. از کجاش سوال دارین دقیقاً؟ چون من تفاوتی نمیبینم.
      شما فقط از متغیری به اسم tot استفاده کردین و من متغیری به اسم tot_overflow. من برای تغییر وضعیت پورت C به یه نوعی عمل کردم و شما نوع دیگر. برای toggle کردن پورت میشه به روش های مختلفی کد نوشت. که باهم فرقی هم ندارن. اگه منظورتون جای دیگه ای از کد هست که بنده متوجهش نشدم، در خدمتم.

  11. ببخشید این کدی که من برای این مثال نوشتم چه فرقی با مثال شما داره میشه اینجوری بکار برد ؟ چون درست کار میکنه

    #include

    int tot=0;
    bit x=0;

    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
    tot++;
    }

    void main(void)
    {

    DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (1<<DDC0);

    PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

    TCCR0=(0<<WGM00) | (0<<COM01) | (0<<COM00) | (0<<WGM01) | (1<<CS02) | (0<<CS01) | (0<<CS00);
    TCNT0=0x00;
    OCR0=0x00;

    TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (1<=61)
    {
    if (TCNT0>=0x09)
    {
    x=~x;
    PORTC.0=x;
    TCNT0=0x00;
    tot=0;
    }
    }

    }
    }

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

    • کدی که نوشتی کد ناقصیه، و ایرادهای زیادی توش داره. ممکن نیست این کد کار کنه. شاید به هنگام کپی کردن مشکلی ایجاد شده. یک بار دیگه کدتون رو برام بفرستین. ولی در هر صورت این کد قرار نیست جواب بده. دلایل زیادی هم داره. مثلا شما اون (۶۱=>۱) رو چرا به TIMSK دادین و منظورتون از نوشتنش اصلا چی بوده؟ یا شما متغیر tot رو تعریف کردین و مقدارش رو در وقفه ها اضافه کردین و دوباره درون شرط صفر کردین. چه استفاده ای از این کار بردین؟ اصلا برای این که وقفه اتفاق بیفته TCNT0 باید ۲۵۵ بار بشمره ولی شما داخل شرط تا به ۹ میرسه دوباره اونو صفر میکنی و وقفه ای هم اتفاق نخواهد افتاد و بدتر از همه این که بیت فعال ساز عمومی رو هم یک نکردی و کلا وقفه ای نمیتونه اتفاق بیفته و خیلی دلایل دیگه.
      در رابطه با سوال بعدی عرض شود که نوشتن تابع initiation به علت اینه که کد خوانا تر باشه وگرنه لزومی نداره که جدا نوشته بشه. ولی باید حتما نوشته بشه حالا چه درون برنامه ی اصلی و چه به صورت تابعی جدا. ولی دوست من همیشه یادت باشه که زیبا نوشتن کد هم خودش هنریه 🙂
      و در مورد سوال آخر هم رجوعت میدم به لینک زیر :
      http://microlearn.ir/1008/int.html
      در وقفه های داخلی هم بیت فعال ساز وقفه عمومی صفر میشه و پس از اتمام زیرروال وقفه دوباره یک میشه.

  12. با سلام
    ممنون از پست خوبتون. فقط سوالی داشتم: چرا در مورد مد CTC چیزی نگفتین؟ من نیاز داشتم که در مورد این مد کاری تایمر هم بدونم.

    • با سلام و تشکر از لطفتان.
      در جلسات بعد در مورد مد CTC و همچنین PWM برای هر سه تایمر صحبت خواهد شد.با دنبال کردن مطالب سایت مد CTC را پیگیری فرمایید.
      همچنین می توانید از طریق لینک زیر به خبرنامه ی تلگرام ما نیز بپیوندید و از انتشار مطلب مورد نظرتان با خبر گردید.
      https://telegram.me/microlearn
      با تشکر.

پاسخ دهید

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

Time limit is exhausted. Please reload CAPTCHA.