۱-۶ آشنایی مقدماتی با سیگنالهای آنالوگ و مفهوم ADC
۲۶ بهمن ۱۳۹۷۳-۶ راهاندازی سنسور فاصله سنج IR با واحد ADC
۲۶ بهمن ۱۳۹۷در میکرو کنترلرها و بهطور خاص در واحد ADC در لحظات مختلف از این سیگنال نمونه برمیداریم. درواقع بهجای در نظر گرفتن تمام نقاط سیگنال تنها نقاط خاصی را به عنوان نمایندهی سیگنال اصلی در نظر میگیریم.
حال اگر بهشکل ۲ (در این صفحه) نگاهکنید یک سیگنال دیجیتال شده از یک سیگنال آنالوگ سینوسی را مشاهده خواهیدکرد. حال واضح است هرچه تعداد نمونههای ما بیشتر باشند سیگنال ما به سیگنال واقعی نزدیکتر است.
به تعداد نمونههای برداشته شده در واحد زمان نرخ نمونه برداری میگویند.
حال اگر به Data sheet میکروکنترلرهای AVR کمی نگاه بیندازید، پایههایی را میبینید که با نام ADC در روبروی خود مشخص شده اند.
مثلا در میکروکنترلر ATmega32 یا ATmega16 با شکلی شبیه زیر مواجه خواهید شد:
همانطور که میبینید پایههای پورت A دارای این قابلیت هستند. در واقع شما میتوانید از پایههای پورت A هم به عنوان ورودی/خروجی استفاده کنید و یا از آن به عنوان واحد ADC استفاده کنید. برای راهاندازی واحد ADC ابتدا باید پایهی AVCC را به ولتاژ 5+ ولت متصل کنید.
همچنین پایههای شمارهی ۳۱ که همان GND میباشد، باید به Ground و پایهی AREF را به ولتاژ مرجع مورد نظر باید وصل کرد. در ادامه با AREF بیشتر آشنا خواهید شد.
همانطور که ملاحظه فرمودید در یک میکروکنترلر مثل ATmega16 تعداد ۸ کانال مربوط به ADC وجود دارد و هریک از این کانالها میتوانند به تنهایی عمل کنند و یک ولتاژ آنالوگ را دریافت و عملیات مربوطه را بر روی آن انجام دهند. در میکروکنترلر ATmega16 براساس یک تبدیل خاص تمام ولتاژهای بین صفر ولت تا AREF به یک عدد بین ۰ تا ۱۰۲۳ تبدیل میشوند. در واقع در میکروکنترلر ATmega16 یک حافظهی ۱۰ بیتی برای ذخیره سازی مقدار تبدیل یافته ولتاژ آنالوگ روی هر پایه استفاده شدهاست که میتواند اعدادی بین ۰ تا ۱۰۲۳ را در خود جایدهد.
در واقع این عدد از رابطهی زیر به دست میآید:
ADC = (Vin×1024)/Vref
در فرمول بالا کلمات به شرح زیرمیباشند:
ADC: همان عدد به دست آمده پس از تبدیل
Vin: ولتاژ روی پایهی مخصوص مثلا پین A.0
Vref: همان ولتاژ مرجع متصل شده به پایهی AREF
حال با استفاده از فرمول بالا میتوان درصورت داشتن عدد ADC مقدار ولتاژ روی هر پایه راحساب کرد.
در واقع داریم:
Vin = (ADC×Vref)/1024
با استفاده از این فرمول میتوانیم بسیاری از سنسورهایی را که خروجی ولتاژ را به عنوان جواب میدهند، راه اندازی کنیم، در ادامه دراین باره بیشتر صحبت خواهیم کرد. اگر بخواهیم خلاصهای از مطالب قبل را ذکر کنیم بهتر است به شکل زیر نگاه بیندازید:
در واقع در شکل ۵ نحوهی انجام تبدیلات و مفهوم آن مدل شده است. حال کمی به دنیای شبیه سازی وارد میشویم.
اگر نرمافزار CodeVision را راهاندازی کنیم و پس از اجرای Code Wizard وارد بخش ADC شویم و پس از انتخاب گزینهی ADC enabled با شکلی شبیه زیر مواجه خواهیم شد:
در صفحهی قبل همانطور که مشاهده میکنید چند گزینه برای انتخاب وجود دارد که خلاصهای از کاربرد هریک به شرح زیر است:
گزینهی use 8 bits: برای تبدیل دقت ۱۰ بیت به ۸ بیت برای ذخیره سازی مقدار ADC
گزینهی Volt. Ref: برای انتخاب ولتاژ مرجع میکرو که اگر درحالت AREF pin باشد پایهی AREF را بهعنوان ولتاژ مرجع و یا Vref در فرمول انتخاب خواهدکرد.
گزینهی Clock: در واقع مشخص کنندهی نرخ نمونه برداری یا فرکانس نمونه برداری است.
هرچه فرکانس نمونه برداری بیشتر باشد احتمال بروز خطا بیشتر و دقت کمتر است ولی در عوض تعداد نمونهها در واحد زمان بیشتر است.
اینکه شما دقت بیشتر را ترجیح میدهید یا سرعت بالاتر را، به کاری که در حال انجام آن هستید بستگی دارد.
حال با ساختن یک پروژه کار خود را ادامه میدهیم.
در این پروژه قصد داریم میزان ولتاژ روی پایهی PORTA.0 که همان معرف ADC0 است را بر روی LCD نشان دهیم.
ابتدا مدار زیر را در Proteus میبندیم:
همانطور که در شکل میبینید پایههای AREF و AVCC هردو به 5+ ولت متصل شده اند. پایهی GND در شبیه سازی Proteus به طور پیشفرض به زمین متصل شده است. به پایهی PA0 یا ADC0 ولتاژ 1+ ولت را متصل کردهایم. برای این کار یک منبع متغیر ولتاژ DC را به پایهی A.0 متصل کردیم.
حال به فرمول Vin = (ADC×Vref)/1024 باردیگر نگاه بیندازید.
در این فرمول طبق این پروژه مقدار Vref برابر 5+ ولت است. همچنین مقدار ADC را میتوان از طریق تابع (read_adc (port No بهدست آورد. که در اینجا مقصود از port No همان شمارهی پایه در واحد ADC است. مثلا برای خواندن مقدار ADC برای PORTA.0 یا همان ADC0 باید تابع را به نحو زیر فراخوانی کنیم:
;(int ADC_amount=read_adc(0
در واقع با چنین فراخوانیی مقدار ADC محاسبه شده برای PORTA.0 بر روی متغیر ADC_amount که از نوع integer است قرار خواهد گرفت.
حال به کد زیر نگاه کنید:
[php] // Declare your global variables here char str[20]; void main(void) { // Declare your local variables here while (1) { sprintf(str,"ADC_amount:%d",read_adc(0)); lcd_puts(str); delay_ms(5000); lc_clear(); } } [/php]
در این کد سعی برآن است که یک مقدار خوانده شدهADC از PORTA.0 بر روی LCD نمایش داده شود و پس از آن LCD پاک شده و دوباره مقدار ADC بر روی LCD چاپ گردد.
حال اگر این کد را بر روی شبیه سازی Proteus بریزیم با نتیجهای مشابه شکل ۸ مواجه خواهیم شد.
حال اگر نگاهی به مقدار عدد داده شده بیندازید میبینید که این عدد در واقع حدود یک پنجم عدد ۱۰۲۴ است پس در واقع یک پنجم Vref نیز خواهد بود که میشود همان ۱ ولت که همان ولتاژ مورد نظر ما است.
حال به بررسی ادعای مطرح شده در بالا میپردازیم.
در این بخش سعی میکنیم علاوه بر محاسبهی مقدار ADC مقدار ولتاژ روی پایهی ADC0 یا PORTA.0 را اندازهگیری کنیم.
به قطعه کد زیر نگاه بیندازید:
[php] // Declare your global variables here char str[20]; int Voltage; void main(void) { // Declare your local variables here while (1) { Voltage=(read_adc(0)*5/1024); sprintf(str,"ADC=%d,Volt=%d",read_adc(0),Voltage); lcd_puts(str); delay_ms(5000); lc_clear(); } } [/php]
در کد بالا ابتدا از طریق فرمول معرفی شده مقدار ولتاژ محاسبه شده است و به همراه آن مقدار ADC بر روی LCD چاپ شده است.
در شکل ۹ نتایج شبیهسازی با این قطعه کد را مشاهده خواهیدکرد.
همانطور که میبینید مقدار ولتاژ در شبیه سازی برابر ۱ ولت به دست آمده است که همان چیزی است که انتظارش را داشتیم.
بهیاد داشته باشید ما بر روی PORTA.0 تنها میتوانیم مقادیر ولتاژهای بین صفر ولت تا ۵ ولت را اندازهگیری کنیم چراکه ولتاژ مرجع خود را به 5+ ولت متصل کرده ایم.
حال با دوبار کلیک کردن بر روی منبع ولتاژ متصل به پایه ی A.0 مقدار ولتاژ آن را به ۲.۳۶۸ تغییر میدهیم.
اینکار با تغییر مقدار عدد که در شکل داخل کادر قرمز آمده است امکانپذیر است.
با تغییر مقدار عدد ۱ داخل کادر قرمز به عدد ۲.۳۶۸ کار خود را آغاز میکنیم.
ابتدا به قطعه کد زیر نگاه کنید:
[php] // Declare your global variables here char str[20]; float Voltage; void main(void) { // Declare your local variables here while (1) { Voltage=(read_adc(0)*5.0/1024); float(Voltage,3,str); lcd_putsf("Voltage is:") lcd_puts(str); delay_ms(5000); lc_clear(); } } [/php]
در قطعه کد بالا مقدار Voltage که به صورت float نیز میباشد توسط فرمول محاسبه شدهاست.
در محاسبهی ولتاژ چون تمامی مقادیر از قبیل مقدار بازگشتی تابع ()read_adc ، مقدار ۵ ولت ولتاژ مرجع و عدد ۱۰۲۴ که همگی از نوع integer هستند، درنهایت مقدار نهایی محاسبه شده نیز طبق قواعد زبان C مقداری integer خواهد داشت.
برای اینکه بتوانیم از فرمول بالا مقداری float بهدست بیاوریم باید در صورت یا مخرج کسر حداقل یک مقدار float داشته باشیم. بنابراین جلوی عدد ۵ یک ممیز شناور به همراه مقدار اعشار ۰ گذاشتیم تا این عدد به حالت float رفته و نتیجهی نهایی نیز یک عدد float باشد. حال برای نمایش دادن یک عدد float برروی LCD ابتدا باید مقدار float را به یک رشته تبدیل کنیم تا بتوانیم آن را روی LCD نمایش دهیم.
در قبل با تابع ()itoa آشنا شدیم، اما آن تابع مقادیر integer را به رشته تبدیل میکرد، اما اکنون برای تبدیل یک مقدار float به رشته باید از تابع ()ftoe استفاده کنیم.
این تابع در کتابخانهی stdlib.h موجود است. بنابراین آن را در ابتدای برنامهی خود در قالب <include <stdlib.h# به برنامهی خود اضافه کنید.
نحوهی استفاده از تابع بهشکل زیر است:
(void ftoe (float n, unsigned char decimals, char *str
همانطور که در تعبیر بالا میبینید این تابع هیچ مقداری را به عنوان مقدار بازگشتی ندارد.
همچنین برای انجام عمل تبدیل شما باید ابتدا عدد float که میخواهید عمل تبدیل بر روی آن انجام گیرد را به عنوان متغیر اول به این تابع پاس دهید.
ورودی دوم این تابع یک عدد از نوع integer است که تعداد رقمهایی بعد از اعشاری است که قرار است در این تبدیل از عدد float اعمال شوند را مشخص میکند.
مثلا اگر میخواهید این ورودی را برای تبدیل عدد ۲.۳۶۸۷ به یک رشته بدهید باید این عدد را برابر ۴ قرار دهید. ورودی سوم نیز اشارهگر به یک رشته و یا به تعبیر سادهتر نام یک رشته است که در قبل تعریف شده است.
پاسخ اعمال این کد در نرمافزار Proteus شبیه شکل ۱۱ خواهد بود.