در بخش دهم، با واحد ADC و در بخش قبلی هم با ریدایرکت کردن Printf و Scanf آشنا شدیم. در این بخش، میخواهیم نحوهی راهاندازی واحد ADC توسط DMA (بورد Blue Pill) را توضیح دهیم و البته به کمک واحد DMA از این واحد اطلاعات را دریافت و در حافظه ذخیره کنیم. واحد DMA یا Direct Memory Access همانطور که از نام آن مشخص است برای کنترل ارتباط با حافظه بهکار میرود. مزیت استفاده از DMA این است که این واحد مستقل از پردازنده اصلی میتواند ورود و خروج اطلاعات بین وسایل جانبی و حافظه را کنترل کند، درنتیجه توان پردازشی پردازنده میتواند صرف انجام کارهای دیگر شود.
مرحله اول ایجاد پروژه در محیط STM32CubeMX
برای راهاندازی واحد ADC توسط DMA ، مثل قبل تنظیمات کلاک و دیباگ و انتخاب پینهای ورودی و خروجی موردنیاز انجام میشود. در صورت نیاز به ارسال اطلاعات دریافتی از 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، به کدهای فعالسازی، کالیبره کردن، و استارت نرمافزاری تبدیل (و در صورت نیاز به فعالسازی وقفه) نیاز داریم:
1 2 3 4 5 6 7 | LL_ADC_Enable(ADC1); LL_ADC_StartCalibration(ADC1); while(LL_ADC_IsCalibrationOnGoing(ADC1)) __NOP(); LL_ADC_REG_StartConversionSWStart(ADC1); |
ثابت مربوط به اندازه حافظه مورد نیاز و همچنین متغیرهای مورد استفاده را به کد اضافه میکنیم:
1 2 3 4 5 | #define ARRAYSIZE (uint32_t) 2 uint16_t adc_read[ARRAYSIZE]; int j = 0; float Voltage; |
و سپس کدهای مربوط به تنظیم و شروع به کار DMA را مینویسیم:
1 2 3 4 5 6 7 8 9 10 11 12 | 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) قرار دهیم:
1 2 3 4 5 6 7 8 9 10 11 | 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 راهاندازی نمیشود.
- دستورات مربوط به تنظیم فعالسازی ADC و DMA، باید حتما در تابع int main و بعد از فراخوانی SystemClock_Config نوشته شود. در غیر این صورت، برنامه دچار خطا خواهد شد.
- دقت شود که واحد ADC، حتما در مود Circular تنظیم شود. در غیر این صورت پس از یک بار تبدیل و یک بار پر شدن حافظه، اجرا متوقف خواهد شد و اطلاعات دیگری دریافت و در حافظه ثبت نمیشود.
- دو تابع تنظیم آدرس و تنظیم مقدار دادههایی که باید منتقل شوند برای واحد DMA باید بهدرستی تنظیم شوند(مشابه کد ارائهشده)، زیرا در غیر این صورت واحد DMA راهاندازی نمیشود.
در بخش بعد با نحوه ارسال اطلاعات به وسیله USART و از طریق DMA آشنا خواهیم شد.
با سلام ارور
Error[Li005]: no definition for “LL_ADC_StartCalibration” [referenced from C:\Users\Farshid\Documents\Stm32F407ZGT6\STM32F407ZGT6\EWARM\STM32F407ZGT6\Obj\Application\User\Core\main.o]
و
Error[Li005]: no definition for “LL_ADC_IsCalibrationOnGoing” [referenced from C:\Users\Farshid\Documents\Stm32F407ZGT6\STM32F407ZGT6\EWARM\STM32F407ZGT6\Obj\Application\User\Core\main.o]
و
Error[Li005]: no definition for “LL_ADC_IsCalibrationOnGoing” [referenced from C:\Users\Farshid\Documents\Stm32F407ZGT6\STM32F407ZGT6\EWARM\STM32F407ZGT6\Obj\Application\User\Core\main.o]
برای چیه
یه سری فانکشن رو لینکر پیدا نمیکنه که میتونه به دلیل اضافه نشدن کتابخانه باشه
سلام. ممنون از اموزش خوبتون و وقتی که میذارید
واحد timer+ADC+DMA رو هم میتونید اموزشش رو بذارید که زمان نمونه برداری دقیقا همون مقداری 1MHz؟ به نظر وقتی از تایمر استفاده نشه نرم افزاری زمان نمونه برداری 1Mhz تنظیم میشه و احتمال داره دقیقا هر 1 میکوثانیه داده برداری نکنه اگه اشتباه نکنم درسته؟
ممنون از لطفتون
سلام دوست عزیز
ببینید اگر نرخ نمونه برداری 1mhz مد نظر باشه این کار رو بدون دخالت تایمر هم میشه انجام داد، با تنظیم صحیح مقدار کلاک واحد ADC و کلاک سیستم میشه این کار رو انجام داد و نیازی به تایمر نیست
و چون کلاک سیستم پایدار هست دقیقا نمونه برداری همون ۱ میکروثانیه یک بار اتفاق خواهد افتاد
سلام. از اموزش خوبتون ممنونم . اگه امکانش هست رابط i2c رو با توابع ll hal هم اموزش دهید ممنون
سلام دوست عزیز. مرسی از همراهی شما. رابط I2C در قسمت 25ام این آموزش گفته میشه که در آینده منتشرش میکنیم.
سلام . ممنون از وقتی که گذاشتید . سوالم اینه که آیا توابع وقفه ای که در فایل وقفه وجود دارند رو میشه در فایل main باز نویسی کرد که هر وقت وقفه ای رخ می دهد cpu تو همون فایل main به اون رسیدگی کنه و وارد فایل وقفه نشه . منظورم از فایل وقفه فایل
Stm32f2xx_it.c هست
ممنونم
سلام مجدد. خواهش میکنم. بله امکان این کار وجود داره، به شرط اینکه تابع مورد نظرتون رو توی فایل stm32f2xx_it.c حذف یا کامنت کنین و توی فایل main.c تعریفشون کنین.