در جلسه گذشته، به راهاندازی واحد آنالوگ به دیجیتال (ADC) در میکروکنترلر STM32f103 پرداختیم و یک کد ساده برای راه اندازی آن به وسیلهی کتابخانه HAL نوشتیم. در این جلسه قصد داریم تا به این واحد به صورت عملی تر و کاربردی تر بپردازیم.
DMA چیست؟
واحد DMA به معنای واحد دسترسی مستقیم به حافظه است (Direct memory access) که واحدی است برای انتقال داده از حافظه RAM به اجزاء سیستم بدون پردازش آن توسط CPU میباشد.
بدون استفاده از DMA، زمانی که پردازنده در حال استفاده از پورت های خروجی و ورودی است، در تمام طول خواندن یا نوشتن مشغول و درگیر می ماند و این موضوع سبب می گردد که از انجام کارهای دیگر بازبماند. با استفاده ازDMA ، CUP عملیات انتقال را شروع می کند و در زمانی که عملیات انتقال در حال انجام شدن است عملیات دیگری را انجام می دهد، و زمانی که عملیات انجام شد یک وقفه از کنترلر DMA دریافت می کند. این قابلیت زمانی مهم است که CPU نمی تواند پابه پای نرخ انتقال داده ای کار کند ، یا زمانی که انتقال داده نسبتا کند است و پردازنده لازم است که کار مهمی را انجام دهد.
ساخت پروژه در نرمافزار CubeMX
در گام اول با استفاده از نرم افزار CubeMX پروژه را ساخته و تنظیمات آنرا تا بخش کلاک به مانند جلسه ۲ انجام میدهیم. در ادامه در بخش تنظیم پایه های میکروکنترلر مانند تصاویر تنظیم کرده و ورودی PA0 و PA1 را به عنوان ورودی های آنالوگ ADC1 (دوتا ورودی انالوگ برای نمایش بهتر موضوع انتخاب شده اند.) و پایه PB15 را تحت نام LED1 به عنوان خروجی تعریف میکنیم.
و تنظیمات آن را در بخش ADC و به مانند شکل زیر تنظیم می کنیم.
در قسمت اول نوع ADC را انتخاب می کنیم که می تواند در چندین حالت کار کند. ما برای این پروژه از همان حالت پیش فرض استفاده می کند که به صورت عموم استفاده می گردد. ( تفاوت این دو نوع (regular , injected) در الویت کاری آنها ست یعنی درصورتی که پردازنده بر روی یکی از ورودی ها که در حالت regular قرار داشت، در حال پردازش باشد، و در همان حال نوبت ورودی injected فرا رسد، پردازنده ابتدا به این ورودی(injected) الویت می دهد و دیتای آن را پردازش می کند و سپس به کار قبلی خود باز میگردد.
در اولین قسمت مشخص شده(continuous conversion mode) گزینه فعال را می زنیم. این قسمت برای به طور پیوسته کار کردن واحد آنالوگ به دیجیتال می باشد.
در ادامه در بخش regular تعداد conversion را برابر ۲ قرار می دهیم( چون از دو پایه برای خواندن ADC استفاده می کنیم) و باقی تنظیماتش را به مانند جلسه قبل انجام می دهیم.(به تفاوت شماره Rankها توجه کنید). قسمت External trigger conversion Source که با مربع قرمز رنگ مشخص شده نیز برای کاربرد هایی است که سورس تریگر واحد ADC را می خواهیم بر اساس واحدی دیگر(نظیر تایمر) قرار دهیم تا با استفاده از این سیگنال تریگر این واحد فعالیتش را انجام دهد. برای این قسمت آموزشی تهیه شده است که پس از بیان واحد تایمر به این قسمت پرداخته خواهد شد.
در ادامه برای فعال کرده DMA مطابق شکل زیر به بخش مربوطه رفته و فعالیت های زیر را براساس شماره قرارداده شده انجام می دهیم.
با وارد شدن به این بخش ابتدا DMA را اضافه میکنیم(شماره ۲) و سپس با انتخاب کانال ADC مدنظر(شماره۳) حالت DMA فعال شده(شماره ۴) را انتخاب می کنیم. بخش شماره۴ دو حالت normal , circular دارد که حالت اول زمانی استفاده می شود که می خواهیم این واحد(DMA) پس از یکبار انجام وظیفه، فعالیتی نداشته باشد و حالت دوم زمانی است که می خواهیم به طور پیوسته این فعالیت صورت گیرد.
بخش ۵ نیز مربوط به نوع دیتا و تعداد بیت های آن می باشد و از آنجا که ما در این پروژه از adc 12 بیتی استفاده کردیم از Half Word که به اندازه نصف Word تعداد بیت های آن می باشد(۱۶ بیت) استفاده می کنیم.
با تنظیمات فوق، واحد ADC را با استفاده از DMA می توانیم استفاده کنیم.
ضمنا توجه شود که در بعضی سریهای میکرو کنترلر نظیر f407 واحد dma نیز به مانند تصویر زیر فعال سازی شود. منتها در میکروی مورد استفاده ما (f103) با اضافه کردن dma به مانند تصویر فوق دیگر نیازی به فعال کردن آن نمی باشد ( اجرای تصویر زیر برای میکرو F103 نیاز نیست).
برای فعال کردن قسمت مشخص شده لازم است ابتدا به بخش DMA setting رفته و پس از اضافه کردن DMA برای ADC مورد نظر، این بخش را فعال شود.
ادامه پروژه در نرمافزار KEIL
فایل پروژه برای نرمافزار KEIL را از مسیر انتخاب شده و واقع در پوشه MDK-ARM پروژه انتخاب کردن و باز میکنیم.
در بخش فانکشن در نوار سمت چپ نرمافزار زیر گروه adc را باز کرده و توابع مربوط به این کتابخانه را در آن مشاهده می کنید و توضیحات هر تابع در کد هر قسمت موجود است.
دو قطعه کد ای که در این قسمت از پروژه از انها استفاده می شود به شرح زیر است:
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)HAL_ADC_GetValue(ADC_HandleTypeDef* hadc)
کد فوق جهت فعال کردن واحد ADC با استفاده از DMA می باشد و با استفاده از کد زیر می توان تابع ADC را متوقف کرد.
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc)
با استفاده از این دو تابع و دادن متغیر های مناسب به آن می توان این واحد را به همین سادگی پیاده سازی کرد. منتها تابع دیگری نیز وجود دارد که پس از هر بار جاروکردن کانال مربوطه توسط DMA و ارسال اینتراپتی به پردازنده جهت اعمال پردازش بر روی داده ها، به این تابع رجوع شود و فرآیند مربوطه انجام گردد که این تابع در ذیل مشاهده می گردد.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
حال با نوشتن کد زیر برنامه را شروع میکنیم
و متغیر adcval نیز در بالا به صورت گفته شده(uint32_t) و آرایه ای دوتایی تعریف شده است.
در ادامه با آوردن تابع callback که پس از هر دورکامل خواندن ADC توسط DMA فعال می گردد، فعال مدنظر را در آن تابع نوشته و برنامه را خاتمه می دهیم.
و همانطور که گفته شد با استفاده از کد موجود در خط ۳۹۳ واحد انالوگ به دیجیتال(ADC) را به کمک DMAفعال کرده،
و در خط ۲۲۴ با آوردن تابع اینتراپت مربوطه، عملیاتی را در درون آن انجام می دهیم.
(دقت شود در این پروژه، خطوط کد پروژه بنده با خطوط کدی که در پروژه شما وجود دارد یکسان نمی باشد)
در جلسه بعدی به آموزش بخش پر استفاده و حیاتی تایمر(Timer) در میکروکنترلر STM32f103 خواهیم پرداخت. با ما همراه باشید.
نویسنده: اشکان راکی