ماژول های تایمر در آردوینو عملکرد زمان بندی دقیقی را ارائه می دهند. آنها به ما اجازه می دهند کارهای مختلفی را انجام دهیم، مانند ایجاد تأخیرهای دقیق، ایجاد رویدادهای دوره ای، اندازه گیری فواصل زمانی و برآوردن نیازهای زمانی برنامه مورد نظر.

هر برد آردوینو میکروکنترلر مربوط به خودش را دارد که مجموعه ای از تایمرهای سخت افزاری خاص خود را دارد. بنابراین، همیشه باید به دیتاشیت مربوط به میکروکنترلر مورد نظر مراجعه کنیم تا بیشتر با قابلیت های سخت افزاری آن و نحوه استفاده بهینه از آن آشنا شویم.

آردوینو UNO  که از میکروکنترلر Atemga328p  استفاده می کند دارای 3 تایمر سخت افزاری است که عبارتند از:

     Timer0: تایمر 8 بیتی

     Timer1: تایمر 16 بیتی

     Timer2: تایمر 8 بیتی

این ماژول‌های تایمر برای تولید سیگنال‌های خروجی PWM و ارائه قابلیت‌های زمان‌بندی و ایجاد تاخیر استفاده می‌شوند، و همچنین می‌توانیم از آنها برای اجرا در هر حالتی برای دستیابی به عملکرد مورد نظر استفاده کنیم.

هر تایمر سخت افزاری یک رجیستر شمارنده دیجیتال در هسته خود دارد که بر اساس سیگنال کلاک ورودی شمارش می کند. اگر سیگنال ساعت از یک منبع داخلی با فرکانس ثابت بیاید، گفته می شود که در حالت تایمر کار می کند. اما اگر ورودی ساعت به صورت خارجی از یک پایه IO یا هر منبع نا همزمان تغذیه شود، گفته می شود که به عنوان شمارنده ای کار می کند که پالس های دریافتی را می شمارد.

چرا از تایمر استفاده کنیم؟

استفاده از تایمرهای سخت‌افزاری به جای راه‌حل‌های نرم‌افزاری، به‌ویژه مواردی مانند تابع () delay، مدیریت رویدادهای دوره‌ای، تولید سیگنال PWM و بسیاری از برنامه‌های کاربردی بسیار کارآمدتر است. ماژول‌های تایمر سخت‌افزاری می‌توانند در یکی از حالت‌های متعدد کار کنند، که هر کدام کاربردها و موارد استفاده خاص خود را دارند.

Prescaler در تایمر آردوینو

prescaler در ماژول تایمر سخت‌افزاری یک مدار دیجیتالی است که برای تقسیم فرکانس سیگنال ساعت بر یک عدد قابل تنظیم استفاده می‌شود تا نرخ ساعت تایمر کاهش یابد و رسیدن به سرریز overflow بیشتر طول بکشد.

اجرای ماژول تایمر در فرکانس سیستم برای رزولوشن  خوب است، اما وقفه های سرریز تایمر زیادی ایجاد می کند که نیاز به رسیدگی بیشتری در برنامه شما دارد. از این رو، استفاده از یک prescaler می تواند برای جلوگیری از این مفید باشد.

در حالت تایمر، ماژول تایمر کلاک داخلی سیستم را به عنوان منبع کلاک خواهد داشت و مانند شکل زیر از prescaler عبور می کند. شما می توانید نسبت تقسیم prescaler را تنظیم کنید تا بتوانید فرکانس کلاک ورودی تایمر را کنترل نمایید.

مقادیر تقسیم کننده prescaler تایمر از یک ماژول تایمر به ماژول دیگر متفاوت است و در دیتاشیت برای هر ماژول تایمر 0 ، 1 و 2 بیان شده است.

وقفه های تایمر آردوینو

تایمرهای آردوینو سیگنال های وقفه متفاوتی را برای رویدادهای مختلف ارائه می دهند. زمانی که یک تایمر به حداکثر مقدار شمارش خود می رسد (255 برای تایمرهای 8 بیتی و 65535 برای تایمرهای 16 بیتی) یک وقفه سرریز ایجاد می کند، صفر می شود و دوباره شروع به شمارش می کند.

همچنین دو رجیستر مقایسه خروجی در هر تایمر وجود دارد (COMPA و COMPB). هنگامی که مقدار شمارنده رجیستر تایمر به مقدار COMPA می‌رسد، پین OCnA را بسته به تنظیمات شما HIGH یا LOW می‌کند و همچنین یک وقفه COMPA (در صورت فعال بودن) ایجاد می‌کند. و همین امر در مورد COMPB نیز صدق می کند.

هر وقفه تایمر آدرس برداری وقفه خاص خود را دارد و می توان به صورت جداگانه هر وقفه را فعال یا غیرفعال کرد.

استفاده از حالت تایمر

در آردوینو UNO تایمرهای 0 و 2 ، 8 بیتی و تایمر 1 ، 16 بیتی هستند. همچنین در آردوینو از تایمر 0 در توابع delay و millis و micros استفاده شده است و باید موقع دستکاری این تایمر به این مساله توجه شود.

رابطه کلی تعداد پالس که تیک هم گفته می شود و زمان مورد نظر به این صورت است:

timerformula

تغییر prescaler به ما این امکان را می دهد که حداکثر بازه زمانی تولید شده مجاز را با ماژول تایمر قبل از سرریز شدن آن افزایش دهیم.

بهترین راه برای نشان دادن محاسبات ماژول تایمر این است که یک مثال کاربردی را در نظر بگیریم و مراحل محاسبه را مرحله به مرحله طی کنیم.

فرض کنید می خواهیم هر 200 میلی ثانیه یک وقفه تایمر دوره ای ایجاد کنیم و از آن به عنوان پایه زمانی برای برنامه خود استفاده کنید. ما از ماژول Timer1 استفاده می کنیم که در بردهای آردوینو UNO با فرکانس 16 مگاهرتز کار می کند.

تایمر 1 می تواند مقادیر prescaler زیر را داشته باشد:

1:1

1:8

1:64

1:256

1:1024

با توجه به اینکه زمان مورد نظر ما 200 میلی ثانیه است prescale را طوری انتخاب کنیم که T بزرگتر از 200 میلی ثانیه شود. با انتخاب 64 برای prescale و در نظر گرفتن 65535 برای حداکثر تعداد تیک و فرکانس 16 مگاهرتز زمان حداکثر T برابر خواهد بود با 262 میلی ثانیه.

بنابراین prescale 64 برای کار ما مناسب است.

حالا این بار دوباره از فرمول استفاده می کنیم و زمان T را برابر 100 میلی ثانیه در نظر می گیریم تا بتوانیم تعداد دقیق تیک های مورد نیاز را پیدا کنیم. با جاگذاری 200 میلی ثانیه و فرکانس 16 مگاهرتز و 64 تعداد تیک برابر خواهد بود با 50000.

پس از محاسبه تیک های مورد نیاز برای رسیدن به زمانی 200 میلی ثانیه مورد نظر برای رویدادهای وقفه تایمر، می‌توانیم به برنامه‌نویسی ماژول تایمر آردوینو بپردازیم.

رجیستر تایمر (TCNTx) را با مقداری از قبل بارگذاری می کنیم به گونه ای که پس از 50000 تیک به سرریز (65535) برسد. برای زمان 200 میلی‌ثانیه مقدار پیش بارگذاری تایمر باید برابر باشد با :

65535 – 50000 = 15535

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

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

int led = 3;
byte state = LOW;
ISR(TIMER1_OVF_vect)
{
  TCNT1 = 15535;
  state=!state;
  
}
 
void setup()
{
  TCCR1A = 0;           
  TCCR1B = 0;          
  TCCR1B |= B00000011;  
  TCNT1 = 15535;        
  TIMSK1 |= B00000001;  
  pinMode(led, OUTPUT); 
}
void loop()
{
    digitalWrite(led, state); 
}

توضیح برنامه:

int led = 3; 

این خط یک متغیر عدد صحیح به نام led را اعلام می کند و آن را به مقدار 3 مقداردهی اولیه می کند. این مقدار نشان دهنده پین آردوینو متصل به LED است.

byte state = LOW;

این خط یک متغیر از نوع بایت به نام state را اعلام می کند و آن را با مقدار LOW مقداردهی اولیه می کند. این متغیر وضعیت LED را ذخیره می کند.

ISR(TIMER1_OVF_vect)

تابع ISR یک تابع ویژه است که هنگام وقوع یک رویداد خاص، در این مورد، زمانی که تایمر 1 سرریز می شود، راه اندازی می شود. TIMER1_OVF_vect بردار وقفه برای رویداد سرریز تایمر 1 است.

در داخل تابع ISR دو عمل انجام می شود:

TCNT1 = 15535;

این خط شمارنده تایمر 1 را به مقدار 15535 تنظیم می کند.

state=!state;

این خط مقدار متغیر state را تغییر می دهد. این بدان معنی است که با هر بار سرریز شدن شمارنده تایمر 1وضعیت LED در فرکانس مشخص شده به طور متناوب بین روشن و خاموش خواهد بود.

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

در این تابع، اقدامات زیر انجام می شود:

TCCR1A = 0;
TCCR1B = 0;

این دو خط رجیستر کنترل  A و B تایمر 1 را پاک می کنند و حالت تایمر 1 را روی CTC تنظیم می نمایند.

TCCR1B |= B00000011;

این خط prescale را معادل 64 قرار می دهد. در زیر مقادیر این رجیستر برای هر prescale را مشاهده می کنید:

TCCR1B |= B00000001;    1:1
TCCR1B |= B00000010;    1:8
TCCR1B |= B00000011;    1:64
TCCR1B |= B00000100;    1:256
TCCR1B |= B00000101;    1:1024

خط بعد:

TCNT1 = 15535;

این خط مقدار اولیه شمارنده تایمر 1 را تعیین می کند.

TIMSK1 |= B00000001;

این خط وقفه سرریز تایمر 1 را فعال می کند.

pinMode(led, OUTPUT);

این خط پین متصل به LED را به عنوان پایه خروجی تنظیم می کند و به آردوینو اجازه می دهد تا وضعیت LED را کنترل کند.

در حلقه اصلی برنامه یعنی loop این دستور مشاهده می شود:

digitalWrite(led, state);

این خط مقدار فعلی متغیر state را روی پین led می نویسد. این بدان معناست که اگر state برابر HIGH باشد LED روشن می شود و اگر حالت LOW باشد خاموش می شود.با اجرای برنامه خواهید دید که led شروع به چشمک زدن خواهد کرد. برای دیدن دقت تایمر باید از اسیلسکوپ استفاده نمود.

استفاده از تایمرها در حالت شمارنده

ماژول تایمر در حالت شمارنده یا کانتر طوری پیکربندی می شود که یک پین خارجی به عنوان منبع کلاک با چندین گزینه prescaler داشته باشد. معمولاً برای شمارش پالس‌های ورودی خارجی یا اندازه‌گیری یک سیگنال ورودی با شمارش لبه‌های بالارونده یا پایین رونده آن در یک بازه زمانی ثابت استفاده می‌شود. برنامه ها و موارد استفاده زیادی برای ماژول های تایمر در حالت شمارنده وجود دارد.

در آردوینو UNO پایه شماره 4 به عنوان ورودی تایمر 0 یا T0 استفاده می شود و پایه شماره 5 نیز به عنوان ورودی تایمر 1 یا T1 به کار می رود.

تایمر 2 پایه ورودی ندارد.

برای تنظیم یک ماژول تایمر آردوینو برای عملکرد در حالت شمارنده، از بیت های انتخاب ساعت در رجیستر TCCRxB استفاده می کنیم. این بیتها سه بیت کم ارزش یعنی سه بیت سمت راست این رجیستر هستند. به طور مثال برای اینکه تایمر 1 در حالت کانتر با لبه بالارونده قرار گیرد رجیستر را به این صورت مقداردهی می کنیم:

TCCR1B |= B00000111;

و برای اینکه در حالت کانتر با لبه پایین رونده قرار گیرد رجیستر را به این صورت تنظیم می نماییم:

TCCR1B |= B00000110;

پالس ورودی باید به پایه شماره 5 آردوینو UNO اعمال شود.

برنامه زیر را ببینید:

void setup()
{
  TCCR1A = 0;           
  TCCR1B = 0;           
  TCCR1B |= B00000110;  
  TCNT1 = 0;
  Serial.begin(115200);
}
 
void loop()
{
  Serial.print("Pulses = ");
  Serial.println(TCNT1);
  delay(1000);
}

در تابع setup با استفاده از این خط:

TCCR1B |= B00000110;

تایمر 1 را در حالت شمارنده با لبه پایین رونده تنظیم کرده ایم.

یک ارتباط سریال با مانیتور آردوینو ایجاد می کنیم و در حلقه اصلی برنامه هر 1 ثانیه 1یک بار مقدار TCNT1 که تعداد پالسهای کلاک را نگه می دارد در پنجره مانیتور سریال نمایش می دهیم.