اگر جلسات قبلی آموزش میکروکنترلرهای LPC1768 را دنبال کرده باشید میدانید که در جلسه قبلی پورت های ورودی و خروخی LPC1768 را برسی کردیم و یادگرفتیم چطوری پورت ها را بعنوان ورودی/ خروجی تعریف کنیم و مقادیر را روی آنها بنویسیم و یا بخوانیم و همه این کارها را با توابع CMSIS انجام دادیم. در این جلسه به برسی مبدل آنالوگ به دیجیتال lpc17xx_adc.h می پردازیم.
مبدل آنالوگ به دیجیتال یا ADC چیست ؟
فرض کنید یک سنسور دما مانند LM35 داریم که به ازای هر درجه سانتی گراد 10mV خروجی دارد.مثلا اگر دما 1درجه سانتیگراد باشد ولتاژ خروجی سنسور 10mV میشه. برای اینکه مقدار دمای این سنسور را بصورت دیجیتالی(روی سون سگمنت ، نمایشگر ال_سی_دی و..) نمایش دهیم باید این مقدار آنالوگ را به دیجیتال تبدیل کنیم.
آیسی های مبدل آنالوگ به دیحیتال یا ADC این کار را برای ما انجام میدهند. روی میکروکنترلرهای ARM و خیلی دیگر از میکروکنترلرها یک ADC نسبتا خوب پیش بینی شده که در موارد زیادی میتواند کارراه انداز باشد.
مشخصات ADC میکروکنترلر LPC1768
- دقت 12 بیتی (هر مقدار آنالوگ در 12 بیت ذخیره میشود.)
- بازه یا محدوده اندازه گیری از VREFN تا VREFP میباشد.
- دارای حالت کاهش توان میباشد.
- ورودی این ADC مالتی پلکس شده روی 8 پین از پورت های ورودی/خروجی میکروکنترلر
- سرعت نمونه برداری 200KSPS یا 200000 نمونه در ثانیه
- هر کانال ورودی دارای یک رجیستر مخصوص جهت نگهداری آخرین وضعیت
مراحل تنظیم و راه اندازی ADC
- فعال سازی بیت توان از قسمت ادوات جانبی از رجیستر PCONP
- فعال سازی بیت PDN در رجیستر AD0CR
- تنظیم کلاک ADC با بیت های 24 و 25 در رجیستر PCLKSEL0
- انتخاب حالت ADC برای پین های مورد نظر از رجیستر های PINSEL
- تنظیم وقفه های ADC
- نکته : حداکثر کلاک قابل اتصال به واحد مبدل آنالوگ به دیجیتال 13مگاهرتز میباشد.
- نکته : دقت یا رزولیشن مبدل برابر است با 0.8mV
پایه های ADC روی میکروکنترلر
دو نوع پایه روی میکروکنتلر برای ADC وجود دارد یکی پایه های تغذیه و رفرنس (VDDA,VSSA.VREFN,VREFP) دیگری پایه های کانال ها از ADC0[0-7]
پایه های رفرنس و تغذیه ADC روی میکروکنترلر باید به ولتاژ مناسب وصل شود.معمولا VREFP را به VDD و VREFN را به GND وصل میکنن و همچنین پایه های تغذیه VDDA را به 3.3 ولت و VSSA را هم به زمین وصل میکنن.
پین های کانال های ADC
کانال ADC |
پورت و پین |
عملکرد های دیگر پین |
AD0 | P0.23 | 0-GPIO, 1-AD0[0], 2-I2SRX_CLK, 3-CAP3[0] |
AD1 | P0.24 | 0-GPIO, 1-AD0[1], 2-I2SRX_WS, 3-CAP3[1] |
AD2 | P0.25 | 0-GPIO, 1-AD0[2], 2-I2SRX_SDA, 3-TXD3 |
AD3 | P0.26 | 0-GPIO, 1-AD0[3], 2-AOUT, 3-RXD3 |
AD4 | P1.30 | 0-GPIO, 1-VBUS, 2- , 3-AD0[4] |
AD5 | P1.31 | 0-GPIO, 1-SCK1, 2- , 3-AD0[5] |
AD6 | P0.3 | 0-GPIO, 1-RXD0, 2-AD0[6], 3- |
AD7 | P0.2 | 0-GPIO, 1-TXD0, 2-AD0[7], 3- |
توابع کار با ADC در داخل lpc17xx_adc.h
تابع ADC_Init
این تابع برای پیکربندی اولیه ADC ، اتصال کلاک و تنظیم فرکانس ADC
void ADC_Init(LPC_ADC_TypeDef *ADCx, uint32_t rate);
وروی های تابع :
- اولی باید LPC_ADC باشه (اشاره گر از نوع LPC_ADC_TypeDef )
- دومی فرکانس کاری ADC میباشد که حداکثر میتونه 200000 باشه
تابع ADC_IntConfig
تنظیم وقفه ADC
void ADC_IntConfig (LPC_ADC_TypeDef *ADCx, ADC_TYPE_INT_OPT IntType, FunctionalState NewState);
- ورود اول که باید LPC_ADC باشه
- ورودی دوم شماره کانالی است که وقفه مربوط به آن را میخواهید فعال کنید.
- ورودی سوم برای فعال سازی یا غیر فعال سازی وقفه مربوطه هست که با SET فعال و با RESET غیر فعال میشه.
تابع ADC_ChannelCmd
void ADC_ChannelCmd (LPC_ADC_TypeDef *ADCx, uint8_t Channel, FunctionalState NewState);
این تابع کانال های ADC را فعال یا غیر فعال میکنه.
- ورود اول که باید LPC_ADC باشه 🙂
- ورودی دوم شماره کانال
- ورودی سوم فعال و غیر فعال سازی با ENABLE فعال و با DISABLE غیر فعال میشه
تابع ADC_StartCmd
void ADC_StartCmd(LPC_ADC_TypeDef *ADCx, uint8_t start_mode);
ورود اول که باید LPC_ADC باشه
ورودی دوم حالت نمونه برداری میباشد که میتونه پیوسته نمونه برداری کنه و یا فقط یک نمونه برداره و موارد دیگر که در زیر همه موارد هستن و عملکرد هر کدام را میتونید تحقیق کنید و ما فعلا با ADC_START_NOW کار کنیم و بعدا میریم سراغ بقیه.
* - ADC_START_CONTINUOUS * - ADC_START_NOW * - ADC_START_ON_EINT0 * - ADC_START_ON_CAP01 * - ADC_START_ON_MAT01 * - ADC_START_ON_MAT03 * - ADC_START_ON_MAT10 * - ADC_START_ON_MAT11
تابع ADC_ChannelGetStatus
FlagStatus ADC_ChannelGetStatus(LPC_ADC_TypeDef *ADCx, uint8_t channel, uint32_t StatusType);
این تابع وضعیت پرچم تبدیل های انجام شده را میخونه
- ورودی اول تابع که LPC_ADC هست
- ورودی دوم تابع شماره کانال
- ورودی سوم تابع ، اگر یک باشه پرچم DONE را میخونه و اگر 0 باشه پرچم BURST را میخونه.
تابع ADC_ChannelGetData
uint16_t ADC_ChannelGetData(LPC_ADC_TypeDef *ADCx, uint8_t channel);
این تابع مقدار ADC را میخونه
- ورودی اول LPC_ADC است
- ورودی دوم شماره کانال مورد نظر میباشد
تابع ADC_DeInit
void ADC_DeInit(LPC_ADC_TypeDef *ADCx)
این تابع هم ADC را غیر فعال میکنه.
مثال راه اندازی ADC میکروکنترلر LPC1768
چندتا نکته قبل از مثال گفتی است که اولا روش های مختلفی برای خواندن مقدار ADC وجود دارد که ما از روش سرکشی یا POLLING استفاده کردیم که بعدا در موارد پیشرفته بحث میکنیم و برای سادگی بحث زیاد وارد این جور چیزا نمیشیم.
نکته دوم اینکه همانطور که میدانید شاید یک پین میکروکنترلر چندین عملکرد داشته باشد که در جلسه قبلی بحث کردیم.برای انتخاب حالت ADC عملکرد پین مورد نظر از کتابخونه lpc17xx_pinsel.h استفاده میکنیم که در زیر نحوه پیکر بندی آن آمده است.
پین استفاده شده AD0[5] میباشد. که روی پایه P1[31] قرار دارد.
//begin pinsel confing PINSEL_CFG_Type PinCfg; PinCfg.Funcnum = 3; PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL; PinCfg.Pinmode = PINSEL_PINMODE_TRISTATE; PinCfg.Pinnum = 31; PinCfg.Portnum = 1; PINSEL_ConfigPin(&PinCfg); //end pinsel confing
مثال
#include "lpc17xx.h" #include "lpc17xx_adc.h" #include "lpc17xx_pinsel.h" #include "debug_frmwrk.h" void delay(void){ uint32_t i=0; for(i=0;i<9999999;i++); } uint16_t read_adc(adc_channel_number) { //this function writed by Melec.ir uint16_t adc_value; //adc init ADC_Init(LPC_ADC, 100000); //adc interrupt DISABLE ADC_IntConfig(LPC_ADC,adc_channel_number,DISABLE); //adc channel on ADC_ChannelCmd(LPC_ADC,adc_channel_number,ENABLE); // Start conversion ADC_StartCmd(LPC_ADC,ADC_START_NOW); //Wait conversion complete while (!(ADC_ChannelGetStatus(LPC_ADC,adc_channel_number,ADC_DATA_DONE))); //read adc channel adc_value = ADC_ChannelGetData(LPC_ADC,adc_channel_number); return adc_value; } int main (void){ uint16_t V; //begin pinsel confing PINSEL_CFG_Type PinCfg; PinCfg.Funcnum = 3; PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL; PinCfg.Pinmode = PINSEL_PINMODE_TRISTATE; PinCfg.Pinnum = 31; PinCfg.Portnum = 1; PINSEL_ConfigPin(&PinCfg); //end pinsel confing SystemInit(); debug_frmwrk_init(); while(1){ V=read_adc(5); _DBG("ADC= "); _DBD32(V); _DBG("\n"); delay(); } return 1; }
برای راحتی کار یک تابع به اسم uint16_t read_adc(adc_channel_number) نوشتن ورودی آن شماره کانال و خروجی آن مقدار ADC میباشد. بطور مثال در کد بالا با فراخوانی V=read_adc(5) مقدار ولتاژ روی کانال 5 را میریزه داخل متغیر V و با توابع _DBG مقدار تابع را میفرستیم روی پورت UART0
خروجی روی پورت سریال
پایان جلسه ششم 🙂 دوستان عزیز برای تهیه هر جلسه از این آموزش ها کلی نفر ساعت کار میکنیم با رعایت کپی رایت و معرفی وبسایت ما به دوستانتان با ما در این راه شریک باشید.
اگر این نوشته برایتان مفید بود لطفا کامنت بنویسید.
مطمئن هستین کد درست هست.
مثلا تو قسمت uint16_t read_adc(adc_channel_number) نباید type آرگمان ورودی مشخص بشه؟
کامپایلر من keil هست و خطا میگیره از برنامه شما .
سلام ، لازم نیست و البته شما میتونید این تابع را در تابع دلخواه خودتان نیز بنویسید به خطای keil دقت کنید …
سلام
می خواستم ازتون به خاطر این مطالب و وقتی که میزارین تشکر کنم.
حتما شما رو به دوستام معرفی می کنم.
سلام،
خیلی ممنون آقا پوریا ، امیدوارم مطالب آموزشی براتون مفید واقع شده باشن. آرزوی موفقیت و سلامتی
سلام خسته نباشید.
مثال بخش آموزش آنالوگ به دیجیتال lpc17xx_adc.h موبه مو اجام دادم ولی با ارور زیر مواجه میشم لطفا راهنمایی بفرمایید.
adc.axf: Error: L6218E: Undefined symbol check_failed (referred from lpc17xx_adc.o).
سلام آقا من گاها دیدم adc میکر lpc 1768 اعداد نا معقولی نشون می ده تحقیق کردم اثر نویز بیرونی هم نیس از خود میکرو است حالا چطور میشه این مشکل رو حل کرد . واقعا خیلی بده
سلام، ولتاژ مرجع صاف صاف و فیلترهای مناسب برای ADC و برای تغذیه میکروکنترلر را درست و در جای مناسب تعبیه کنید. با اسیلوسکوپ نویز تغذیه و ADC را چک کنید. اگر بازم مشکلتون حل نشد که برای منم فک کنم چنین اتفاقی چند سال پیش امد از فیلترهای دیجیتال که با برنامه نویسی پیاده میشن استفاده کنید. مثلا در ساده ترین حالت میانگیر گیری.
متاسفانه از فیلتر های دیجیتال استفاده کردن باعث افزایش حجم محاسبات و اینکه من قصد دارم با تبدیل فوریه تا چند هارمونیک رو محاسبه کنم ، و این حجم محاسبات میکرو رو کاملا درگیر کرده و توان محاسبات برای این میکرو دیگه کافی نیست ، باید سعی کنم میزان محاسبات هرچه بیشتر کاهش پیدا کنه و بنظرم باید دنبال ماژول جدا برای اندازه گیری باشم
سلام ببخشید سوالم ابتدایی هست ولی من این فایل های هدر رو پیدا نمیکنم توی نت
#include “lpc17xx_adc.h”
#include “lpc17xx_pinsel.h”
#include “debug_frmwrk.h”
چندتا پک هم اضافه کردم به keil ولی بازم ارور میده
سلام، بنظرم جلسه اول همین آموزش را ببینی اکی میشه.
سلام ببخشید من کتابخونه ها رو هم اضافه کردم آژور ها رفته فقط تو تابع تنظیم وقفه ایراد می خوره و ارور میده یعنی خطی که میگه (ADC_IntConfig(LPC_ADC,adc_channel_number,DISABLE
که فکر میکنم مشکل از پارامتر وسطی هست و هر نوعی هم بهش میدم ارور رفع نمیشه توی فایل هدر از نوع ADC_TYPE_INT_OPT هست که خودش قبلا define شده و این نوع هم باز قبول نمی کنه … کلا پیچیده شده … میخواستم بدونم الآن شما تست کردین بدون ارور اوکی بوده ؟ چون من دیگه مغزم جواب نمیده برای این ارور …
سلام علی جان اگر KEIL عوض شده باشه یا لایبری ها تغییر کرده باشن و غیره را من نمیدونم چون چندسال پیش فک کنم 96 این متن را نوشتم…ولی کد بالا با روشی که در جلسه اول نوشتم جواب میداد. آرگومان دوم هم عدد هست فک کنم 8 تا کانال داره که میشه بجاش مثلا 1 بذاری…