آموزش STM32 با توابع LL قسمت دوم: روش‌های پیکره‌بندی و انواع توابع

آموزش ARM

در قسمت اول از آموزش STM32 با توابع LL، در رابطه با پردازنده و میکروکنترلر صحبت کردیم و به تفاوت‌های میکروکنترلر و پردازنده پرداختیم. همچنین گفته بودیم که خود شرکت ARM فقط طراح پردازنده است، نه میکروکنترلر. و سه دسته‌ی مهم از پردازنده‌های نوین این شرکت را با ذکر جزئیات و کاربردها بررسی کردیم.

در نهایت در قسمت اول، میکروکنترلرهای شرکت ST که یکی از استفاده‌کنندگان پردازنده‌های ARM است را با ذکر انواع میکروکنترلرهای مبتنی بر این نوع پردازنده‌ها معرفی کردیم.

اگر به یاد داشته باشید گفتیم که شرکت ST دو دسته میکروکنترلر 8 بیتی و 32 بیتی دارد که میکروکنترلرهای 32 بیتی آن مدنظر ما بودند. در ادامه قصد داریم کمی به همین 8 بیتی یا 32 بیتی بودن میکروکنترلرها و اینکه چگونه با STM32 شروع به کار کنیم بپردازیم.

8 بیتی یا 32 بیتی؟

اگر این آموزش را دنبال می‌کنید به احتمال زیاد با مفهوم بیت آشنا هستید و از بابت مفهوم بیت مسئله‌ای نیست. وقتی می‌گوییم پردازنده‌ای n بیتی است یعنی اینکه رجیسترهای آن پردازنده n بیتی هستند. حال شاید سوال کنید که رجیستر چیست؟ رجیستر از کنار هم قرار دادن چندین بیت تشکیل می‌شود. پس وقتی 32 بیت را برای پردازنده‌های ARM به کار می‌بریم یعنی اینکه درون این پردازنده‌ها رجیسترهای 32 بیتی وجود دارند.

خب تا بحث در مورد بیت و رجیستر است اجازه بدهید همین ابتدای کار به یک مفهوم مرتبط با این موضوع نیز اشاره کنیم.

مفهوم دیگری به نام Flag یا پرچم نیز وجود دارد که می‌تواند دربرگیرنده‌ی یک یا چند بیت باشد. در واقع به بیت‌هایی Flag اطلاق می‌شود که نشان‌دهنده‌ی وضعیت خاصی باشند، پس مرسوم نیست که به هر بیتی Flag بگویند، اگرچه خود فلگ چیزی به جز بیت نیست.

به عنوان مثال اگر بیت چهارم و پنجم رجیستر A مقدار 1 منطقی را داشتند یعنی اینکه فلان واحد جانبی مشغول به پردازش دیتای قبلی است و نمی‌توان دیتای جدیدی را به این واحد فرستاد. یا اگر بیت هفتم رجیستر B مقدار 0 منطقی را داشت یعنی اینکه در شمارنده‌ی شما سرریز رخ داده است.

مزایای 8 بیتی یا 32 بیتی

مقایسه پردازنده‌های 8 و 32 بیتی
مقایسه پردازنده‌های 8 و 32 بیتی

در نگاه اول به نظر می‌رسد که هرچقدر تعداد بیت‌های رجیسترها بیشتر باشد پردازنده‌ی قوی‌تری داریم، گرچه در بهتر بودن پردازنده‌ها بررسی تنها رجیستر معیار کافی‌ای نمی‌باشد، اما خب به صورت کلی می‌توان گفت که همان نگاه اولیه تقریبا درست است و هرچقدر تعداد رجیسترها بیشتر باشد پردازنده می‌تواند در ابعادی قوی‌تر باشد.

به عنوان مثال یکی از مزایا این است که هرچه تعداد بیت‌های رجیستر بیشتر باشد می‌تواند فضای بیشتری را آدرس‌دهی کند.

در حقیقت زیاد بودن تعداد رجیستر مملو از مزایاست، اما خب شاید بعضی‌ها زمان‌بر بودن پیکره‌بندی این همه رجیستر را اگر بخواهند در سطح رجیستر کار کنند، جز معایب بدانند.

 

نحوه‌ی پیکره‌بندی میکروکنترلر

نحوه‌ی پیکره‌بندی میکروکنترلر
نحوه‌ی پیکره‌بندی میکروکنترلر

ما قصد داریم قبل از اینکه به کدنویسی و ابزارهای آن و همچنین ابزارهای پیکره‌بندی بپردازیم به خود این موضوع که میکروکنترلر چگونه و با چه روش‌هایی برای هدف خاصی تنظیم یا پیکرده‌بندی می‌شود بپردازیم.

اینکه میکروکنترلر برای هدف خاصی تنظیم شود چیزی جز اینکه در رجیسترهای مختلف آن 0 یا 1 منطقی را بنویسیم نیست، به همین سادگی؟

خیر داستان به همین سادگی سادگی هم نیست، اگر تعداد بیت‌ها ده تا یا صد تا بود این حرف می‌توانست درست باشد، اما مسئله اینجاست که تعداد این رجیسترها بسیار زیادتر از تعدادی است که در بالا ذکر شد و هر کدام در آدرس خاصی قرار دارند. به علاوه اینکه کارهای کنترلی فراوان، دستورات خود پردازنده و بسیاری از موارد دیگر وجود دارد که همه و همه مسئله‌ساز است.

پس راه‌حل چیست؟

یک راه‌حل این است که مستندات میکروکنترلر مربوطه را از سایت شرکت سازنده دانلود کنیم و با توجه به مستندات، فایل‌هایی برای کانفیگ اولیه میکروکنترلر بنویسیم و سپس اقدام به راه‌اندازی یک واحد جانبی بکنیم.

فکر کردید که اگر با روش بالا بخواهید فقط یک LED را خاموش و روشن کنید، چقدر زمان لازم است؟

در خوشبینانه‌ترین حالت اگر دانش زبان C خوبی داشته باشید و معماری و سخت‌افزار را نیز به خوبی بشناسید. در وهله‌ی اول این کار می‌تواند چندین ساعت طول بکشد.

برای خاموش و روشن کردن یک LED چندین ساعت!

بله درست متوجه شدید این کار می‌تواند چندین ساعت طول بکشد. اگر مثل من این راه‌حل را غیرمنطقی می‌دانید با ما همراه باشید تا راه‌حل بهتری را به شما معرفی کنیم.

یک راه‌حل مناسب این است که شرکت‌های سازنده معمولا آدرس‌ رجیسترها و مقادیری که برای هر هدف خاص قرار است درون این رجیسترها قرار بگیرد را با ساختارها و اسامی مناسبی طبق استاندارد خاصی دسته‌بندی کرده و در اختیار ما قرار می‌دهند و همینطور خیلی از تنظیمات و کارها را در قالب ماکرو یا تابع به ما ارائه می‌دهند. و زمانی که ما پروژه را می‌سازیم به صورت خودکار تمامی موارد بالا همراه با پروژه ایجاد می‌شوند و ما می‌توانیم از آن‌ها استفاده کنیم.

پس اکنون علاوه بر اینکه مشکل راه‌حل قبلی حل شد، توابع و ماکروهای زیادی نیز در اختیار داریم که می‌تواند کار ما را خیلی سریع‌تر کند.

خود این راه‌حل به چندین روش مختلف قابل استفاده است که در ادامه هر کدام از این روش‌ها را شرح می‌دهیم.

پس به طور خلاصه ما دو راه‌حل داشتیم که یکی از آن‌ها را بنا به دلایل ذکر شده گفتیم که مناسب نیست و آن را کنار گذاشتیم و راه‌حل دیگر خود شامل چندین روش می‌شود که قرار شد این روش‌ها را بررسی کنیم.

 

روش‌های پیکره‌بندی میکروکنترلرهای STM32

در ادامه هر کدام از روش‌ها را با ذکر جزئیات بررسی می‌کنیم و در نهایت با دلایلی منطقی یک روش را برای ادامه‌ی کار برخواهیم گزید.

 

Register

در این نوع پیکره‌بندی هیچ تابعی وجود ندارد و ما فقط از همان ساختارها و اسامی‌ای که شرکت سازنده در اختیارمان قرار داده است استفاده می‌کنیم و در اصل تنها در رجیسترها مقادیر 0 یا 1 منطقی را قرار می‌دهیم.

حال شاید از خود بپرسید که این روش هم مثل همان راه‌حل اولی بود که گفتیم مناسب نیست و از ما کلی وقت می‌گیرد. خیر، در این روش در واقع فایل‌های اولیه و ماکروهای از پیش تعریف‌شده‌ای وجود دارد که ما از آن‌ها استفاده می‌کنیم.

به عنوان یک مثال ساده، در این روش اگر قرار باشد در یک رجیستر 32 بیتی مقداری را بنویسیم از یک ماکرو استفاده می‌کنیم و فقط یک عبارت مانند عبارت “LL_GPIO_PIN_1” را در رجیستر قرار می‌دهیم. اما اگر قرار بود از راه‌حل اول استفاده بکنیم باید یک عبارت مانند عبارت “11110000111100001111000011110000” یا معادل هگز آن که برابر با “0xF0F0F0F0” است را در رجیستری که آدرسش را هم باید خودمان تنظیم کنیم، بنویسیم.

مثال بالا کوچک‌ترین مزیتی است که این روش نسبت به راه‌حل اول دارد، و با بررسی جزئیات به مزیت‌های بیشتری پی خواهیم برد.

 

توابع (Low Layer)LL

در این روش دیگر به صورت مستقیم با رجیسترها کار نخواهیم کرد و فقط از توابعی به نام توابع LL استفاده خواهیم کرد که خود این توابع با توجه به آرگومان‌های ورودی‌شان، رجیسترها را مقداردهی می‌کنند. با فراخوانی هر کدام از این توابع عملا بخشی از یک واحد جانبی راه‌اندازی می‌شود. برای اینکه یک واحد جانبی به طور کامل راه‌اندازی شود باید با یک توالی خاص از چندین توابع LL استفاده کنیم.

 

توابع (Hardware Abstraction Layer)HAL

در این نوع توابع که در بالاترین سطح ممکن است، با سطح رجیستر خیلی فاصله داریم و عملا به ندرت از رجیسترها استفاده می‌کنیم، اگرچه دسترسی به رجییسترها در این سطح بدون هیچ مشکلی ممکن است و با استفاده از ماکروهایی می‌توانیم به رجیسترها دسترسی داشته باشیم.

وقتی که از یک تابع HAL استفاده می‌کنیم علاوه بر اینکه خودش رجیسترهای یک بخش را تنظیم می‌کند، در اکثر موارد بسیاری از کارهایی که در توابع LL باید خود کاربر انجام می‌داد را نیز انجام می‌دهد.

مثلا اگر قرار بود در توابع LL، از 5 تابع استفاده کنیم و کمی هم توابع زبان C را به کار می‌بردیم تا یک واحد جانبی راه‌اندازی شود، ممکن است تمامی این کارها با یک تابع HAL انجام شود.

 

توابع (Standard peripheral libraries)SPL

توابع SPL در سطح میانی قرار دارند و می‌توان گفت سطحی بین LL و HAL را دارا هستند. البته این توابع دیگر به‌روزرسانی نمی‌شوند و بهتر است که شما هم از این توابع استفاده نکنید.

منطقی است که وقتی خودتان از هر کدام از توابع مختلفی که در بالا به تشریح آن‌ها پرداختیم استفاده‌ای نداشتید، به خوبی مفهوم توضیحات بالا را درک نکنید. پس اجازه بدهید که با یک مثال، موضوع را به خوبی شرح بدهیم.

مقایسه توابع LL با توابع HAL
مقایسه توابع LL با توابع HAL

فرض کنید می‌خواهیم رشته‌ی “Kamin” را بر روی پورت سریال بفرستیم.

در توابع LL پس از اینکه تنظیمات اولیه پورت سریال را انجام دادیم و همچنین آن را فعال کردیم ابتدا باید کارکتر ‘K’ از این رشته را بر روی پورت سریال قرار بدهیم و منتظر بمانیم که آیا ارسال به اتمام رسیده است یا خیر، و اگر ارسال انجام شد کارکتر ‘a’ و سپس تکرار همین روند تا آخرین کارکتر.

اما وقتی با توابع HAL کار می‌کنیم پس از اینکه تنظیمات اولیه را انجام دادیم و پورت را فعال کردیم، تمام رشته‌ی “Kamin” را در ورودی تابع مربوطه قرار می‌دهیم و خود تابع تمامی کارهایی که در توابع LL لازم بود خودمان انجام بدهیم را با زمان‌بندی مناسب برای ما انجام می‌دهد.

توابع HAL به قدری همه چیز را آماده کرده‌اند که اگر به خوبی آن‌ها را بررسی کنید، می‌بینید که در بعضی موارد اگر شما سهوا یا از روی ندانستن اشتباهی انجام داده باشید، آن اشتباه را اگر ممکن باشد و تشخیص بدهد، برایتان تصحیح می‌کند.

اکنون که هر کدام از انواع راه‌حل‌ها، روش‌ها و توابع را با ذکر جزئیات برسی کردیم، وقت آن است که طبق قولی که داده بودیم با ذکر دلایل منطقی یک روش را برای ادامه‌ی راه انتخاب کنیم.

بهترین روش برای پیکره‌بندی میکروکنترلر از بین توابع HAL ،LL و SPL
بهترین روش برای پیکره‌بندی میکروکنترلر از بین توابع HAL ،LL و SPL

منطقا هر کدام از این روش‌ها برای هدف خاصی مناسب هستند، و اینگونه نیست که یک روش کاملا ناکارآمد و به دردنخور باشد.

به عنوان مثال اگر هدف سرعت برنامه باشد، توابع HAL اصلا توصیه نمی‌شوند و رجیستری بهترین انتخاب است. در نقطه‌ی مقابل اگر هدف سرعت توسعه‌ی پروژه باشد و سرعت برنامه مدنظر نباشد، بهترین انتخاب توابع HAL هستند و رجیستری اصلا توصیه نمی‌شود.

اما هدف ما چیست؟

چون هدف آموزش است، بهتر است که ابتدا تا جای ممکن در سطوح پایین کار کنید، مفاهیم را به خوبی بشناسید و بر آن‌ها مسلط شوید، بعد که کمی پیشرفت کردید و مهارت‌تان بیشتر شد، می‌توانید از سطوح بالا نیز استفاده کنید تا سرعت توسعه بالاتر برود.

توابع HAL به این دلیل که تنها با فراخوانی یک تابع کنترل کار را به دست می‌گیرند و غالب کار را خودشان انجام می‌دهند و برنامه‌نویس را درگیر با سخت‌افزار و جزئیات برنامه‌نویسی نمی‌کنند برای هدف ما مناسب نیستند.

از سمت دیگر رجیستری نوشتن برنامه هم به دلیل اینکه شما را بیش از اندازه درگیر تنظیم رجیسترهای زیاد موجود در میکروکنترلر می‌کند و ممکن است تمرکزتان از مفهوم و  اصل کاری که قرار است انجام بدهید دور شود نیز انتخاب مناسبی برای کار ما نمی‌باشد.

انتخاب ما توابع LL خواهد بود که در ادامه‌ی این مجموعه آموزشی به صورت مفصل با این نوع توابع و نحوه‌ی به کارگیری آن‌ها آشنا خوهیم شد.

توجه داشته باشید اکنون که این مقاله در حال نگارش است، هیچ آموزش فارسی زبانی در ایران برای راه‌اندازی میکروکنترلرهای STM32 با استفاده از توابع LL ارائه نشده است. بر همین اساس تصمیم گرفته شد تا آموزشی کاربردی و مطابق با نیاز، تدوین و به صورت رایگان در اختیار علاقه‌مندان قرار داده شود.

شاید هنوز در انتخاب توابع LL تردید داشته باشید، به همین خاطر قبل از اینکه این قسمت را به پایان برسانیم، می‌خواهیم نتیجه‌ی یک تست عملی بین توابع LL و توابع HAL را ارائه بدهیم. اما چون هنوز با ابزارهای برنامه‌نویسی و پیکره‌بندی آشنا نشدیم فقط نتیجه‌ی تست را ارائه می‌دهیم و توضیحات تکمیلی در قسمت مربوطه ارائه خواهد شد.

به نمودار زیر که سرعت پین میکروکنترلر با استفاده از توابع LL و HAL را نشان می‌دهد دقت کنید:

مقایسه سرعت بین توابع LL و HAL
مقایسه سرعت بین توابع LL و HAL

حتما شما هم از این نتیجه شگفت‌زده شده‌اید! پشت‌پرده‌ی این اختلاف سرعت فاحش دلایل زیادی وجود دارد که ما همه‌ی این دلایل را در قسمت‌های آتی بررسی خواهیم کرد، پس با ما همراه باشید.

در قسمت سوم ساخت پروژه با استفاده از نرم‌افزار STM32CubeMX را آموزش می‌دهیم و یک کد ساده را با استفاده از نرم‌افزار Keil خواهیم نوشت و به صورت عملی با استفاده از پروگرامر، برنامه را بر روی برد پروگرام خواهیم کرد.

کانال تلگرام آرملینکس

برای دسترسی به کانال تلگرام و دانلود فایل‌های پروژه و ویدئو، بر روی دکمه زیر کلیک کنید:

2 دیدگاه دربارهٔ «آموزش STM32 با توابع LL قسمت دوم: روش‌های پیکره‌بندی و انواع توابع»

    1. کامین جلیلی

      سلام سحر. فعلا که برنامه‌ای براش نداریم، ولی ممکنه که در آینده این آموزش را هم قرار بدیم.

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

اسکرول به بالا