در بخش دهم، با واحد ADC آشنا شدیم. در این بخش، میخواهیم واحد ADC بورد Blue Pill را راهاندازی کنیم و به کمک واحد DMA از این واحد اطلاعات را دریافت و در حافظه ذخیره کنیم. واحد DMA یا Direct Memory Access همانطور که از نام آن مشخص است برای کنترل ارتباط با حافظه به کار میرود. مزیت استفاده از DMA این است که این واحد مستقل از پردازنده اصلی میتواند ورود و خروج اطلاعات بین وسایل جانبی و حافظه را کنترل کند، در نتیجه توان پردازشی پردازنده میتواند صرف انجام کارهای دیگر شود.
با ماهمراه باشید.
مرحله اول) ایجاد پروژه در محیط STM32CubeMX
مثل قبل تنظیمات کلاک و دیباگ و انتخاب پینهای ورودی و خروجی مورد نیاز انجام میشود. در صورت نیاز به ارسال اطلاعات دریافتی از ADC، تنظیم USART نیز باید به همان شکل قبل انجام شود. برای تنظیم ADC در منوی Analog، واحد ADC1 را انتخاب میکنیم. در بخش Mode کانالهای مورد نیاز انتخاب میشوند (در اینجا کانال 0 و کانال 1 انتخاب شدهاند). در بخش Configuration و از تب پارامترها Continuous Conversion Mode را در حالت Enable قرار میدهیم(زیرا میخواهیم به طور پیوسته مقدار ورودی در کانالهای ADC را بخوانیم). در صورتی که مانند این مثال بیش از یک کانال انتخاب شده باشند باید Scan Conversion Mode نیز فعال کنیم. بدین منظور ابتدا Number Of Conversion را روی تعداد مورد نظر تنظیم میکنیم (در اینجا 2). اولویت تبدیل کانالها و همچنین مدت زمان نمونه برداری را نیز طبق شکل زیر تنظیم میشوند:
نکته: تب بعدی که برای کار با واحد ADC، در صورت نیاز تنظیم میشود، NVIC یا تنظیم وقفه است. که در اینجا چون ما از DMA استفاده میکنیم، نیازی به تنظیم این بخش نیست.
سپس باید از تب DMA و کلید Add یک کانال از DMA را به ADC1 اختصاص دهیم. همچنین از بخش Mode در این تب، حالت انتقال اطلاعات را انتخاب میکنیم (در اینجا Circular انتخاب شده زیرا میخواهیم انتقال اطلاعات از ADC به طور پیوسته انجام شود).
بخشهای Clock و Project Manager نیز مانند گذشته تنظیم میشوند.
مرحله دوم) نوشتن کد پروژه
ابتدا باید کدهای مربوط به تنظیم و راهاندازی واحد ADC و DMA را بنویسیم. برای واحد ADC، به کدهای فعالسازی، کالیبره کردن، و استارت نرمافزاری تبدیل، (و در صورت نیاز به فعال سازی وقفه) نیاز داریم؛
LL_ADC_Enable(ADC1); LL_ADC_StartCalibration(ADC1); while(LL_ADC_IsCalibrationOnGoing(ADC1)) __NOP(); LL_ADC_REG_StartConversionSWStart(ADC1);
ثابت مربوط به اندازه حافظه مورد نیاز و همچنین متغیرهای مورد استفاده را به کد اضافه میکنیم:
#define ARRAYSIZE (uint32_t) 2 uint16_t adc_read[ARRAYSIZE]; int j = 0; float Voltage;
و سپس کدهای مربوط به تنظیم و شروع به کار DMA را مینویسیم:
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), (uint32_t)adc_read, LL_DMA_DIRECTION_PERIPH_TO_MEMORY); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ARRAYSIZE); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
با نوشتن این کد، آدرس اولین خانه حافظه مورد نظر، و آدرس رجیستر مربوط به داده تبدیل شده توسط ADC1 تنظیم شدهاند. همچنین مقدار اطلاعاتی که میخواهیم منتقل شوند(در اینجا 2) مشخص شده است.
اکنون میتوانیم در بخش Debug و با تنظیم متغیر adc_read در watch1 مشاهده کنیم که مقادیر دو کانال 0 و 1 ADC پس از تبدیل در دو خانه حافظه که تعریف کردهایم نوشته میشوند :
همچنین میتوان این اطلاعات ذخیره شده در حافظه را از طریق واحد USART ارسال کرد، بدین منظور میتوانیم کد زیر را در حلقه while(1) قرار دهیم:
Voltage = (float)(adc_read[j] * 3.3) / (4095); if(j == 0) printf("Voltage of Channel0 is: %0.4f v\r\n", Voltage); if(j == 1) printf("Voltage of Channel1 is: %0.4f v\r\n", Voltage); LL_mDelay(750); j++; if (j>=ARRAYSIZE) j = 0;
خروجی در ترمینال :
خطاهای احتمالی
- دستورات مربوط به تنظیم فعالسازی ADC و DMA، باید حتما در تابع int main و بعد از فراخوانی SystemClock_Config نوشته شود. در غیر این صورت، برنامه دچار خطا خواهد شد.
- دقت شود که واحد ADC، حتما در مود Circular تنظیم شود. در غیر این صورت پس از یک بار تبدیل و یک بار پر شدن حافظه، اجرا متوقف خواهد شد و اطلاعات دیگری دریافت و در حافظه ثبت نمیشود.
- دو تابع تنظیم آدرس و تنظیم مقدار دادههایی که باید منتقل شوند برای واحد DMA باید به درستی تنظیم شوند(مشابه کد ارائه شده)، زیرا در غیر این صورت واحد DMA راهاندازی نمیشود.
در بخش بعد با نحوه ارسال اطلاعات به وسیله USART و از طریق DMA آشنا خواهیم شد.
منبع : سیسوگ