Chapter 6: Embedded C | পর্ব ৬ঃএমবেডেড সি
এই পর্বে আমরা মাইক্রোকন্ট্রোলার প্রোগ্রামিং এ ব্যবহৃত সি ল্যাংগুয়েজের কিছু প্রয়োগ নিয়ে আলোচনা করব। সি ল্যাঙ্গুয়েজে মাইক্রোকন্ট্রোলার প্রোগ্রাম করতে হলে আমাদের মাইক্রোকন্ট্রোলারের রেজিস্টারগুলো সম্পর্কে জানতে হবে। সবচেয়ে বেশি যে কাজগুলো করতে হয় সেগুলো হল কোনো রেজিস্টারের একেকটি বিটকে সেট(১) এবং ক্লিয়ার (০) করা। এবং কোনো বিট ১ নাকি ০ সেটা চেক করা। এই টিউটোরিয়ালে এইসব বিষয়ে বিস্তারিত আলোচনা করা হবে।
টিউটোরিয়ালটি পড়ার আগে আপনাকে বাইনারি নাম্বার সিস্টেম সম্পর্কে জানতে হবে। বাইনারি নাম্বার সিস্টেম সম্প্রর্কে জানতে এই লিঙ্কটিতে ভিজিট করুন।
রেজিস্টারঃ রেজিস্টার হল কয়েকটি বিটের সমষ্টি( ৮-বিটের মাইক্রোকন্ট্রোলারের ক্ষেত্রে আটটি) ।প্রতিটি বিটেরই আলাদা কোনো উদ্দেশ্য থাকে অথবা একটি রেজিস্টরের সবগুলো বিট মিলেই কোনো একটি ভ্যালু ধরে রাখে। সিপিইউ এবং রেরিফেরালগুলোর মধ্যে সংযোগ হিসেবে রেজিস্টরগুলো কাজ করে। রেজিস্টরে যেকোনো পরিবর্তনের মাধ্যমেই সিপিইউ আসলে পেরিফেরালগুলোকে কোনো নির্দিষ্ট কাজ করতে অথবা কনফিগার করতে নির্দেশ দেয়। রেজিস্টরের ভ্যালু রিড করে সিপিইউ পেরিফেরাল ডিভাইসগুলোর অবস্থা সম্পর্কে জানতে এবং তার সাথে সংশ্লিষ্ট ডেটা রিড করতে পারে।
সি প্রোগ্রামিংয়ে বাইনারি নাম্বারঃ সি প্রোগ্রামিংয়ে x=100 লেখার মানে হল ভ্যারিয়েবল x এর মানে ‘এক শত’ তে সেট করা। এমবেডেড প্রোগ্রামিংয়ের ক্ষেত্রে অনেকসময়ই আমাদের ভ্যারিয়েবলের ভ্যালু দরকার হয় না। বরং প্রতিটি বিটের ভ্যালু জানা প্রয়োজন হয়। যেমনঃ ধরা যাক PORTD রেজিস্টরের বিট প্যাটার্ন আমরা লিখতে চাই 10101111(binary)।
এই ক্ষেত্রে PORTD=10101111 লিখলে হবে না। কারন, কম্পাইলার এটাকে বাইনারি সংখ্যা মনে করবে। সি প্রোগ্রামে বাইনারি সংখ্যা বোঝাতে সংখ্যাটির আগে 0b বসাতে হয়। যদি আমরা লিখি PORTD=0b10101111, 10101111 বিট প্যাটার্নটি PORTD রেজিস্টরের বিটগুলোতে লিখিত হবে।
সি প্রোগ্রামিংয়ে হেক্স নাম্বারঃ একইভাবেই যদি কোনো সংখ্যার আগে 0x লিখলে কম্পাইলার সেটাকে হেক্স নাম্বার হিসেবে বুঝতে পারে। কাজেই,
PORTD=0x11; হেক্স 11=ডেসিমেল 17.
PORTD=0xFF; সবগুলো বিটকে 11111111 বা ডেসিমেল 255 তে সেট করে ।
রেজিস্টারের একটি বিট সেট করাঃ এখন আমরা কোনো একটি রেজিস্টারের একটি বিট সেট করব। ধরা যাক রেজিস্টারটির নাম MCUCR, এবং আমরা এর ৫ নম্বর বিটটিকে সেট করব। সেক্ষেত্রে সিনট্যাক্সটি হবে নিম্নরুপঃ
MCUCR=MCUCR|0b00100000;
উপরের কোডটি লিখলে ৫ নং বিটের ভ্যালু 1 হবে ,বাকি বিটগুলো অপরিবর্তিত থাকবে। কোডটি MCUCR রেজিস্টারের প্রতিটি বিটকে 01000000 এর প্রতিটি বিটের সাথে OR করে। এবং OR অপারেশনের ফলাফল আবার MCUCR রেজিস্টরে জমা রাখে। উপরের কোডটি সংক্ষেপে এভাবেও লেখা যায়ঃ
MCUCR|=0b00100000;
এবার কিছু ব্যবহারিক প্রয়োগ দেখা যাক। বাস্তবে সব রেজিস্টরেরই একটি নির্দিষ্ট কাজ থাকে এবং সেই অনুযায়ী তার একটি নাম থাকে। ধরা যাক আমাদের আলোচ্য ৫ নং বিটের নাম Enable। নাম থেকেই বিটটির কাজ কী তা বোঝা যায়। যখন এর ভ্যালু আমরা 1 করি তখন পেরিফেরালটি এনাবল অর্থ্যাৎ সক্রিয় হবে এবং যখন এর ভ্যালু 0 করি তখন এর পেরিফেরালটি ডিজেবল অর্থ্যাৎ নিস্ক্রিয় হবে। বিটটি সেট করার সঠিক নিয়ম হবেঃ
MCUCR|=(1<<ENABLE);
<< চিহ্নটিকে বলা হয় লেফট শিফট অপারেটর। এটি বামদিকের ভ্যারিয়েবলকে ডানদিকের ভ্যারিয়েবল পরিমান বামে শিফট করে। অর্থ্যাত উপরের কোডটি দ্বারা , বাইনারি 1(00000001) ENABLE পরিমাণে বাম দিকে শিফট হবে। যদি ৫ নং বিটের নাম ENABLE হয় তাহলে,
MCUCR|=(1<<ENABLE);
=> MCUCR|=(1<<5);
=>MCUCR|=0b00100000;
এবার দেখা যাক শিফট অপারেশনের কিছু সুবিধা।
১)পড়া সহজঃ MCUCR|=(1<<ENABLE); লিখলে অন্তঃত বোঝা যায় যে এখানে একটি পেরিফেরাল এনাবল করা হচ্ছে। MCUCR|=0b0010000 লিখলেও একই কাজই হবে কিন্তু একনজরে বোঝা যাবে না আমরা কী করছি। আমাদের ডেটাশিট দেখে বের করতে হবে কোন বিটটি পেরিফেরাল এনাবল করার কাজে ব্যবহৃত হয় যেখনে কম্পাইলের নির্মাতারা ENABLE=5 হেডার ফাইলে অলরেডি ডিফাইন করে দিয়েছেন।
২)সহজে বহন/ প্রতিস্থাপনযোগ্যঃ ধরা যাক, আপনি আপনার প্রোগ্রামে অনেকবার এই কোডডি ব্যবহার করেছেন। আপনার কোড বেশ বড় এবং বেশকিছু রেজিস্টার ব্যবহার করা হয়েছে। এখন, আপনি এই কোডটিই অন্য কোনো মডেলের মাইক্রোকন্ট্রোলারে ব্যবহার করতে চান। নতুন মাইক্রোকন্ট্রোলারটি একই গোত্রেরই কিন্তু তার রেজিস্টারগুলোর বিটবিন্যাস একটু আলাদা। নতুন মাইক্রোকন্ট্রোলারের ENABLE বিট ৫ নয়,২ । এখন , আপনি যদি MCUCR|=0b00100000 যত জায়গায় লিখেছেন সবগুলো খুঁজে বের করে MCUCR|=0b00000010 লিখতে হবে। কিন্তু আপনি যদি MCUCR|=(1<<ENABLE) লিখতেন তাহলে শুধুমাত্র কম্পাইলারের সেটিংসে গিয়ে একবার নতুন মাইক্রোকন্ট্রোলার সিলেক্ট করলেই কম্পাইলার সেই অনুযায়ী বুঝে নিত তার ENABLE বিট কোনটা। এতে কাজ অনেক সহজ হত।
একটি রেজিস্টারের কোনো বিটকে ক্লিয়ার করাঃ
কোনো বিটকে ক্লিয়ার করতে হলে লজিক্যাল AND অপারেশন প্রয়োগ করা হয়। এক্ষেত্রে সিনট্যাক্স হবে নিম্নরুপঃ
MCUCR&=~(1<<ENABLE);
উপরের কোডটি MCUCR নামক রেজিস্টারের ENABLE নামক বিটকে ক্লিয়ার অর্থ্যাত তার ভ্যালু ০ করবে। এই অপারশন ENABLE ছাড়া রেজিস্টারের অন্যান্য বিটের উপর কোনো প্রভাব ফেলবে না। এবার দেখা যাক AND অপারেটরটি কিভাবে কাজ করে।
আমরা জেনে গেছি কিভাবে কোনো রেজিস্টারের একটি বিটকে ক্লিয়ার করতে হয়। যদি একাধিক বিট ক্লিয়ার করার প্রয়োজন হয় তাহলে এভাবে লিখতে হবে।
MCUCR &= ~((1<< ENABLE )|(1<<FAST_MODE)|(1<<BUSY));
এখানে ENABLE, FAST_MODE, BUSY তিনটি ভিন্ন ভিন্ন বিটের নাম।
একইভাবে, একাধিক বিটকে একই সাথে সেট করতে চাইলে লিখতে হবে-
MCUCR |= (1<< ENABLE )|(1<<FAST_MODE)|(1<<BUSY);
এখানে ENABLE, FAST_MODE, BUSY তিনটি ভিন্ন ভিন্ন বিটের নাম। উভয়ক্ষেত্রেই আলোচ্য রেজিস্টারের অন্যান্য বিটগুলো অপরিবর্তিত থাকবে।
কোনো একটি বিট সেট নাকি ক্লিয়ার তা পরীক্ষা করবেন কিভাবে?
এতক্ষন আমরা দেখলাম কিভাবে একটি বিটকে সেট বা ক্লিয়ার করতে হয়। কিন্তু কোনো বিট সেট হয়ে আছে নাকি ক্লিয়ার, তা জানব কী করে? এবার সেই ব্যপারের আলোচনা করা হবে। কোনো বিট 0 নাকি 1 তা জানতে আমরা বিটটিকে একটি AND MASK দিয়ে AND করব।
ধরা যাক, আমরা MCUCR রেজিস্টারের ৫ নং বিটটি চেক করব। এক্ষেত্রে আমাদের AND MASK হবে 0b00100000। একে MCUCR এর বর্তমান ভ্যালুর সাথে AND করলে রেজাল্ট তখনই নন-জিরো হবে যখন ৫ নং বিটে 1 থাকবে। তাছাড়া, সব অবস্থাতেই AND অপারেশনটির ফলাফল 0 হবে। AND অপারেশনের সিন্ট্যাক্স হবে নিম্নরূপ।
if(MCUCR & (1<< ENABLE);
{
//ENABLE is ‘1’ in MCUCR;
}
else
{
//ENABLE is ‘0’ in MCUCR;
}
এখন আমরা বিট অপারেশন সম্পর্কে বেসিক ধারনা পেলাম। বিট অপারেশন মাইক্রোকন্ট্রোলার প্রোগ্রামিং এর ক্ষেত্রে ব্যপকভাবে ব্যবহৃত হয়। এই বিষয়টি বুঝতে পারলে এই টিউটোরিয়ালের অন্যান্য প্রোগ্রাম বুঝতেও সুবিধা হবে।