পাঠ ১৮ · ৫৭-এর মধ্যে · মডিউল ৫
Home / Courses / Microprocessors, Embedded Systems & IoT / টাইমার ও কাউন্টার

টাইমার ও কাউন্টার

Timers and Counters
৯ মিনিট পড়া মধ্যম · Intermediate Python সিমুলেশনসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ফ্রি-রানিং কাউন্টার ও reload/compare রেজিস্টারের কাজ
  • প্রিস্কেলার কেন লাগে এবং ক্লক ফ্রিকোয়েন্সি কমাতে কীভাবে কাজ করে
  • ইন্টারাপ্ট পিরিয়ড গণনার সূত্র এবং কয়েকটি বাস্তব কনফিগারেশনে তার প্রয়োগ
  • একটি সত্যিকারের tick-by-tick কাউন্টার সিমুলেশন দিয়ে সূত্রের গণনা সরাসরি যাচাই করা

১ · টাইমার/কাউন্টার আসলে কী

টাইমার/কাউন্টারTimer/Counterএকটি হার্ডওয়্যার রেজিস্টার যা প্রতি ক্লক পালসে নিজে থেকে বাড়ে বা কমে — CPU-কে কোনো ইনস্ট্রাকশন চালাতে হয় না। হলো মাইক্রোকন্ট্রোলারের ভেতরে বসানো একটি সাধারণ কিন্তু অত্যন্ত গুরুত্বপূর্ণ পেরিফেরাল — একটি রেজিস্টার যা প্রতি ক্লক সাইকেলে (বা প্রিস্কেল করা ক্লকে) নিজে থেকে ১ করে বাড়ে, সম্পূর্ণ স্বয়ংক্রিয়ভাবে, CPU-এর কোনো হস্তক্ষেপ ছাড়াই। রেজিস্টার আর্কিটেকচারের সাধারণ ধারণার জন্য Computer Architecture কোর্স দেখুন — এই পাঠ ধরে নিচ্ছে সেই ভিত্তি আছে এবং সরাসরি টাইমার পেরিফেরালের আচরণে যাচ্ছে।

কাউন্টার একটি নির্দিষ্ট সর্বোচ্চ মান (যেমন ৮-বিট হলে ২৫৫, ১৬-বিট হলে ৬৫৫৩৫) পর্যন্ত বেড়ে তারপর ওভারফ্লো করে ০-তে ফিরে যায় — অথবা একটি প্রোগ্রামযোগ্য reload/compare রেজিস্টার-এ পৌঁছালেই ম্যাচ ইভেন্ট ঘটিয়ে নিজে থেকে রিসেট হয়ে আবার গোনা শুরু করে। প্রতিটি ওভারফ্লো/ম্যাচ ইভেন্ট একটি হার্ডওয়্যার ইন্টারাপ্ট জেনারেট করতে পারে — অর্থাৎ একবার কনফিগার করে দিলে টাইমার নিজে থেকেই নিয়মিত বিরতিতে CPU-কে "জাগিয়ে দেয়", পোলিং লুপের প্রয়োজন ছাড়াই (L17-এর পোলিং বনাম ইন্টারাপ্ট তুলনার মতোই — এখানে ইভেন্টটা সময়-ভিত্তিক)।

২ · প্রিস্কেলার — ক্লককে ধীর করা

মাইক্রোকন্ট্রোলারের মূল ক্লক সাধারণত মেগাহার্টজ রেঞ্জে (যেমন ১৬ MHz) — সরাসরি এই গতিতে একটি ৮/১৬-বিট কাউন্টার চালালে তা প্রতি সেকেন্ডে লক্ষ লক্ষ বার ওভারফ্লো করবে, যা বেশিরভাগ বাস্তব-জীবনের টাইমিং কাজের (যেমন ১ সেকেন্ড পরপর একটি LED ব্লিংক করানো) জন্য অপ্রয়োজনীয়ভাবে দ্রুত এবং ইন্টারাপ্ট ওভারহেড বাড়িয়ে দেবে। এই সমস্যা সমাধানের জন্য থাকে প্রিস্কেলার — একটি বিভাজক (divider) যা মূল ক্লককে N দিয়ে ভাগ করে কাউন্টারে পাঠায়, ফলে কাউন্টার প্রতি N ক্লক সাইকেলে একবার বাড়ে।

ক্লক (f_clock) প্রিস্কেলার ÷N (কাউন্টার-ক্লক কমায়) কাউন্টার (০ থেকে বাড়ে) reload ম্যাচ হলে = ইন্টারাপ্ট + রিসেট
প্রিস্কেলার মূল ক্লককে ধীর করে কাউন্টারে পাঠায়; কাউন্টার reload ভ্যালুতে পৌঁছালেই ইন্টারাপ্ট ফায়ার হয়ে কাউন্টার আবার ০ থেকে শুরু হয়।

৩ · ইন্টারাপ্ট পিরিয়ড গণনার সূত্র

প্রিস্কেলার ও reload ভ্যালু জানা থাকলে ইন্টারাপ্ট ঠিক কত সেকেন্ড পরপর ফায়ার হবে তা নির্ভুলভাবে গণনা করা যায়:

$$ T_{period} = \dfrac{N_{prescaler} \times N_{reload}}{f_{clock}} $$

এখানে $N_{prescaler}$ হলো প্রিস্কেলার ডিভিশন ফ্যাক্টর, $N_{reload}$ হলো reload/compare রেজিস্টারে সেট করা টার্গেট ভ্যালু, এবং $f_{clock}$ হলো মূল ক্লক ফ্রিকোয়েন্সি (Hz)। নিচের কোড সেলে কয়েকটি বাস্তব কনফিগারেশনের জন্য এই সূত্র সরাসরি প্রয়োগ করে পিরিয়ড গণনা করা হয়েছে।

Python
class Timer:
    def __init__(self, clock_hz, prescaler, reload_value):
        self.clock_hz = clock_hz
        self.prescaler = prescaler
        self.reload_value = reload_value

    def formula_period_s(self):
        # সূত্র: T = (prescaler * reload) / clock_hz
        return (self.prescaler * self.reload_value) / self.clock_hz


configs = [
    ("কনফিগ A", Timer(clock_hz=16_000_000, prescaler=1, reload_value=16_000)),
    ("কনফিগ B", Timer(clock_hz=16_000_000, prescaler=64, reload_value=25_000)),
    ("কনফিগ C", Timer(clock_hz=8_000_000, prescaler=1024, reload_value=7_813)),
]

print("ক্লক + প্রিস্কেলার + reload থেকে ইন্টারাপ্ট পিরিয়ড:")
for name, t in configs:
    period_s = t.formula_period_s()
    print(f"  {name}: clock={t.clock_hz:>10,} Hz, prescaler={t.prescaler:>5}, "
          f"reload={t.reload_value:>6} -> period={period_s*1000:.4f} ms "
          f"(~{1/period_s:.2f} Hz ইন্টারাপ্ট রেট)")

    
লক্ষ্য করুন — কনফিগ A আর কনফিগ B সম্পূর্ণ ভিন্ন প্রিস্কেলার ও reload ব্যবহার করেও কাছাকাছি রেঞ্জের পিরিয়ড দিতে পারে, কারণ প্রিস্কেলার আর reload একসাথে গুণ হয় — একই পিরিয়ড পাওয়ার একাধিক উপায় থাকে। কনফিগ C দেখাচ্ছে বড় প্রিস্কেলার (১০২৪) দিয়ে ধীর ক্লক থেকেও প্রায় ১ সেকেন্ডের মতো দীর্ঘ পিরিয়ড তৈরি করা যায় — ছোট রেজিস্টার-প্রস্থ (যেমন ১৬-বিট) থাকলেও বড় প্রিস্কেলার দিয়ে দীর্ঘ পিরিয়ড পাওয়ার এটাই মূল কৌশল।

৪ · সিমুলেশন দিয়ে সূত্র যাচাই

উপরের সূত্র নির্ভুল কিনা তা যাচাই করার সবচেয়ে ভালো উপায় হলো কাউন্টারকে সত্যিই tick-by-tick গণনা করিয়ে দেখা — প্রতিটি tick-এ কাউন্টার ১ করে বাড়বে, যতক্ষণ না reload ভ্যালুতে পৌঁছায়। নিচের কোড সেলে এই সিমুলেশন চালিয়ে গণনা করা tick সংখ্যা দিয়ে বের করা পিরিয়ডকে সূত্রের পিরিয়ডের সাথে সরাসরি তুলনা করা হয়েছে।

Python
class SimulatedTimer:
    def __init__(self, clock_hz, prescaler, reload_value):
        self.clock_hz = clock_hz
        self.prescaler = prescaler
        self.reload_value = reload_value

    def formula_period_s(self):
        return (self.prescaler * self.reload_value) / self.clock_hz

    def run_until_overflow(self):
        # সত্যিকারের tick-by-tick গণনা: কাউন্টার ০ থেকে শুরু করে
        # প্রতি tick-এ ১ করে বাড়ে, reload_value-এ পৌঁছালে থামে
        count = 0
        ticks_counted = 0
        while count < self.reload_value:
            count += 1
            ticks_counted += 1
        return ticks_counted


t = SimulatedTimer(clock_hz=16_000_000, prescaler=64, reload_value=25_000)  # কনফিগ B

ticks_counted = t.run_until_overflow()
simulated_period_s = (ticks_counted * t.prescaler) / t.clock_hz
formula_period_s = t.formula_period_s()

print(f"reload_value          = {t.reload_value}")
print(f"সিমুলেশনে গোনা ticks   = {ticks_counted}")
print(f"সিমুলেশন থেকে পিরিয়ড   = {simulated_period_s*1000:.4f} ms")
print(f"সূত্র থেকে পিরিয়ড       = {formula_period_s*1000:.4f} ms")
print(f"দুটো মেলে?              = {abs(simulated_period_s - formula_period_s) < 1e-9}")

    
মূল কথা · Key takeaway

ticks_counted সবসময় ঠিক reload_value-এর সমান হয়, কারণ লুপটি প্রতি tick-এ ঠিক ১ করে বাড়ে এবং count == reload_value হওয়া মাত্র থেমে যায় — অর্থাৎ tick-by-tick সিমুলেশন আর বন্ধ-আকৃতির সূত্র (closed-form formula) দুটোই একই ফলাফল দেয়, কারণ তারা আসলে একই হার্ডওয়্যার আচরণের দুটো ভিন্ন বর্ণনা মাত্র। এই "সূত্র বনাম সিমুলেশন" যাচাই-পদ্ধতিটাই L19-এর PWM এবং L20-এর ADC পাঠে আবার ব্যবহৃত হবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ প্রিস্কেলার ছাড়া সরাসরি মূল ক্লক দিয়ে কাউন্টার চালালে কী সমস্যা হতে পারে?

দুটো সমস্যা হতে পারে। প্রথমত, কাউন্টার এত দ্রুত (মেগাহার্টজ রেঞ্জে) ওভারফ্লো করবে যে বাস্তব-জীবনের ধীর টাইমিং কাজে (যেমন প্রতি সেকেন্ডে একবার) প্রয়োজনের চেয়ে বহুগুণ বেশি ইন্টারাপ্ট ফায়ার হবে, যা CPU-কে অকারণে ব্যস্ত রাখবে। দ্বিতীয়ত, দীর্ঘ পিরিয়ড পেতে হলে reload রেজিস্টারে অত্যন্ত বড় মান লাগবে, যা রেজিস্টারের বিট-প্রস্থ (যেমন ১৬-বিট হলে সর্বোচ্চ ৬৫৫৩৫) ছাড়িয়ে যেতে পারে — প্রিস্কেলার এই দুটো সমস্যাই সমাধান করে, একই বিট-প্রস্থের রেজিস্টার দিয়ে অনেক বড় রেঞ্জের পিরিয়ড পাওয়া সম্ভব করে।

প্র ০২ reload ভ্যালু একই রেখে ক্লক ফ্রিকোয়েন্সি দ্বিগুণ করা হলে ইন্টারাপ্ট পিরিয়ড কীভাবে বদলাবে?

সূত্র $T_{period} = (N_{prescaler} \times N_{reload}) / f_{clock}$ অনুযায়ী $f_{clock}$ ভাজক (denominator)-এ আছে — তাই $f_{clock}$ দ্বিগুণ হলে $T_{period}$ অর্ধেক হয়ে যাবে (পিরিয়ড ক্লক ফ্রিকোয়েন্সির সাথে ব্যস্তানুপাতিক)। অর্থাৎ দ্রুত ক্লক মানেই একই reload ভ্যালুতে আগে ইন্টারাপ্ট আসবে — এই সম্পর্কটাই ব্যাখ্যা করে কেন M2-এর PLL/ক্লক-মাল্টিপ্লায়ার কনফিগারেশন পরিবর্তন করলে বিদ্যমান টাইমার কোড পুনরায় ক্যালিব্রেট করতে হতে পারে।

প্র ০৩ উপরের সিমুলেশন কোডে ticks_counted সবসময় ঠিক reload_value-এর সমান হয় কেন — এটা কি কাকতালীয়?

না, কাকতালীয় নয় — এটা লুপের গঠন থেকেই নিশ্চিত। count ০ থেকে শুরু হয়ে প্রতি ইটারেশনে ঠিক ১ করে বাড়ে, এবং লুপ শর্ত count < reload_value মিথ্যা হওয়া মাত্র (অর্থাৎ count == reload_value হওয়া মাত্র) থেমে যায়। প্রতি ইটারেশনে ticks_counted-ও ঠিক ততবারই বাড়ে যতবার লুপ চলে — তাই দুটো সবসময় সমান হবে। এটাই একটা সত্যিকারের হার্ডওয়্যার কাউন্টারের আচরণের বিশ্বস্ত মডেল।

অনুশীলন

  1. চিন্তা করুন: clock_hz=16_000_000, prescaler=8, reload_value=20_000 হলে ইন্টারাপ্ট পিরিয়ড কত হবে বলে আপনার ধারণা? কোড সেলে বসিয়ে যাচাই করুন।

    সূত্র অনুযায়ী $T = (8 \times 20{,}000) / 16{,}000{,}000 = 160{,}000 / 16{,}000{,}000 = 0.01$ সেকেন্ড, অর্থাৎ $10$ মিলিসেকেন্ড। কোড সেলের Timer ক্লাসে এই মান বসিয়ে formula_period_s() কল করলে ঠিক এই ফলাফলই আসবে।

  2. পরীক্ষা করুন: দ্বিতীয় কোড সেলে SimulatedTimer-এর কনফিগারেশন বদলে কনফিগ C (clock_hz=8_000_000, prescaler=1024, reload_value=7_813) দিয়ে Run চাপুন — ticks_counted ও দুই পিরিয়ড কী মান দেখায়?

    ticks_counted হবে ঠিক $7{,}813$ (reload_value-এর সমান)। সিমুলেশন পিরিয়ড হবে $(7{,}813 \times 1024) / 8{,}000{,}000 \approx 1.0001$ সেকেন্ড, আর সূত্র থেকেও একই মান আসবে — দুটো মেলার ফলাফল True দেখাবে, নিশ্চিত করবে কনফিগ C প্রায় ১ সেকেন্ড পরপর একবার ইন্টারাপ্ট দেয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Computer Architecture & Digital Logic কোর্স সহোদর কোর্স রেজিস্টার ও ক্লক সিস্টেমের সাধারণ ভিত্তি সেই কোর্সেই তৈরি হয়েছে — এই কোর্স পেরিফেরাল-স্পেসিফিক টাইমিং হার্ডওয়্যারে গভীরে যায়।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Design and Analysis of Algorithms ও আরও অনেক কোর্স — সব এক জায়গায়।
আগের পাঠ
ইন্টারাপ্ট-ড্রিভেন GPIO ও ISR ডিজাইন