در قسمت قبلی آموزش برنامه نویسی C به آموزش مقدمات برنامه نویسی پرداختیم، در این قسمت به بررسی نحوه کانفیگ IDE و شروع برنامه نویسی امبدد C می پردازیم.
پروگرام و دیباگ برد NUCLEO-F030R8
در طی فرایند کامپایل System Workbench for STM32 تعداد زیادی فایل برای پروژه ما ایجاد و دانلود کرده است. بیایید به فایلهای کلیدی نگاهی بیندازیم. با کلیک بر روی مثلث کنار نام دایرکتوری، میتوانیم محتویات دایرکتوری src را مشاهده کنیم. این دایرکتوری شامل فایلهایی است که در جدول 3-2 فهرست شدهاند.
جدول 3-2: فایل های دایرکتوری src
فایل | توضیح |
main.c | برنامه اصلی، جایی که تمام کد ما در آن قرار میگیرد. |
stm32f0xx_it.c | روتینهای سرویس وقفه (The interrupt service routines). شما در فصل 10 با وقفهها آشنا خواهید شد. برای این برنامه ساده، تنها وقفهای که اهمیت دارد ساعت سیستم است، اما نگران جزئیات فنی این وقفه نباشید؛ زیرا تابع HAL_Delay به طور خودکار از آن استفاده میکند. |
syscal.c | توابع غیرفعال (Dummy) که استفاده نمیشوند. |
stm32f0xx.c | کدی که از ساعت سیستم پشتیبانی میکند (در فصلهای بعدی توضیح داده میشود). |
دایرکتوری startup شامل یک فایل به نام startup_stm32f030x8.S است. این فایل زبان اسمبلی است که تنها تنظیمات اولیه لازم برای اجرای کد C توسط پردازنده را انجام میدهد به عبارتی، این فایل پل ارتباطی بین دنیای اسمبلی (زبان ماشین) و زبان C (زبان سطح بالا) است. زمانی که شما دکمه ریست را فشار میدهید، میکروکنترلر به طور خودکار به این فایل مراجعه میکند و دستورات آن را اجرا میکند.
پس از انجام تنظیمات اولیه، میکروکنترلر کنترل را به کد اصلی برنامه شما که به زبان C نوشته شده است، منتقل میکند.
دایرکتوری inc حاوی یک فایل به نام stm32f0xx_it.h است که اطلاعات مربوط به روتینهای سرویس وقفه موجود در فایل stm32f0xx_it.c را به سایر برنامهها ارائه میدهد. این فایل بسیار کوچک و ساده است.
حال به دایرکتوری HAL_Driver میرسیم. این دایرکتوری حاوی حدود 130 فایل است که کتابخانه HAL را برای استفاده در برنامه ارائه میدهد. HAL تفاوتهای بین پردازندههای مختلف ARM را پنهان میکند. برای مثال، تابع HAL_Init تمام سختافزار را راهاندازی اولیه میکند. اگر پردازنده شما Cortex-M0 باشد، نسخه Cortex-M0 تمام سختافزار Cortex-M0 را راهاندازی اولیه میکند. اگر پردازنده شما Cortex-M4 باشد، تمام سختافزار Cortex-M4 تنظیم خواهد شد. تعداد زیاد فایلها در این پوشه به دلیل وجود سختافزار فراوان در برد مورد استفاده ما است. (و این نسخه سادهای از سیستم است.)
دایرکتوری CMSIS شامل کد سطح پایین است که برای پشتیبانی از لایهHAL طراحی شده است. (در متن اصلی به این دایرکتوری اشاره نشده است.)
در نهایت، دایرکتوری Debug حاوی تمام فایلهای مرتبط با ساختار دیباگ پروژهها است. همچنین، این دایرکتوری شامل یک فایل ورودی برای ساخت به نام Makefile و برخی فایلهای تولید شده (جدول 3-3) است.
جدول 3-3: فایلهای تولید شده در دایرکتوری Debug
فایل | توضیح |
blink.elf | برنامه ما در فرمت ELF (یک فرمت فایل برای فایلهای اجرایی) |
blink.bin | برنامه ما به عنوان یک تصویر حافظه (کد خام) |
output.map | نقشه حافظه برای برنامه |
❗توجه
برخلاف makefile ای که در فصل 1 خودمان نوشتیم، این makefile بهصورت خودکار توسط ماشین ساخته شده است و از ویژگیهای بسیار پیشرفته make استفاده میکند. اگر واقعاً میخواهید چیزهایی را که در این فایل اتفاق میافتد را درک کنید، در اینترنت به دنبال دفترچه راهنمای GNU Make بگردید و چند ساعت را صرف مطالعه آن کنید.
آخرین فایل لیست ما، در سطح بالا قرار دارد: LinkerScript.ld. این اسکریپت به لینکر میگوید که چیدمان حافظه چیپ ما چگونه است و قطعات مختلف برنامه را کجا بارگذاری کند. جزئیات بیشتر در فصل 11 ارائه خواهد شد.
آمادهسازی برد
حال که توانیستیم خروجی برای برنامه خود را تولید کنیم لازم است آن را به میکرو خود انتقال دهیم به عبارتی میکرو خود را پروگرام کنیم . بردی که ما در این کتاب از آن استفاده میکنیم برد توسعه NUCLEO-F030R8 است.
برد توسعه NUCLEO-F030R8
بردهای توسعه (development board) مدارهایی هستند که به عنوان بستر اصلی برای ساخت پروژههای الکترونیکی مورد استفاده قرار میگیرند. این بردها شامل یک پردازنده مرکزی و قطعات جانبی مختلفی هستند که برای توسعه و آزمایش نرمافزار و سختافزار مورد نیاز است.
با وجود اتصالات متعدد و برخی قطعات اولیه مانند دکمه، چراغ LED و پورت سریال، این بردها امکان پیادهسازی سریع ایدهها و نمونهسازی پروژههای الکترونیکی را فراهم میکنند. بردهای پیشرفتهتر نیز معمولاً دارای قابلیتها و قطعات اضافی هستند.
بردهای توسعه به شما امکان میدهند تا به سرعت یک نمونه اولیه برای نرمافزار خود بسازید و آن را روی سختافزاری مانند بردبورد (breadboard) آزمایش کنید. این بردها که توسط تولیدکنندگان تراشه عرضه میشوند، شامل تمام قطعات ضروری برای شروع یک پروژه هستند. به عبارت دیگر، برد توسعه یک محیط آزمایشگاهی کامل را در اختیار شما قرار میدهد تا بتوانید ایدههای خود را به سرعت پیادهسازی کرده و آنها را ارزیابی کنید.
با استفاده از این بردها، دیگر نیازی به طراحی مدار از ابتدا نیست و میتوانید به سرعت مرحله برنامهنویسی و آزمایش نرمافزار را شروع کنید.
برد STM32 NUCLEO-F030R8 شامل تراشه اصلی STM32F030R8، منبع تغذیه، مدار ساعت و چندین قطعه جانبی کاربردی مثل LED، دکمه و پورت سریال میباشد. شکل 3-1 اجزای اصلی این برد را نشان میدهد.
منبع تغذیه و ساعت سیستم، پردازنده را فعال و کنترل میکنند. دکمه ریست، پردازنده را دوباره راهاندازی میکند. LED و دکمه برای تعامل با کاربر استفاده میشود. پورت سریال و کانکتورها برای برنامهریزی و دیباگ به کار میروند.
برنامهریزی و دیباگ برد
برد توسعه شامل سه ابزار اصلی برای برنامهریزی و عیبیابی تراشه است: یک flash programmer، یک رابط JTAG و یک پورت سریال I/O. این سه ابزار که از طریق یک کابل USB به کامپیوتر شما متصل میشوند، نقش ارتباطی بین شما و تراشه را ایفا میکنند.
برای برنامهریزی تراشه، از یک flash programmer استفاده میکنیم. این دستگاه به کامپیوتر ما اجازه میدهد تا برنامه مورد نظرمان را مستقیماً روی حافظه داخلی تراشه بنویسد. به عبارت دیگر، flash programmer مانند یک پل ارتباطی عمل میکند و کدهایی که ما در کامپیوتر نوشتهایم را به زبان قابل فهم برای تراشه ترجمه کرده و در حافظه آن ذخیره میکند. از این پس، هنگامی که تراشه روشن میشود، این برنامه اجرا خواهد شد.
برای دیباگ و رفع مشکلات در برنامههای نوشته شده، از یک رابط استاندارد به نام JTAG استفاده میشود.(شما میتوانید از ابزار stlink هم استفاده کنید که در بازار با قیمت بسیار مناسبتری عرضه میشود) شما JTAG مخفف عبارت “Joint Test Action Group” است و به عنوان یک پل ارتباطی بین کامپیوتر و تراشه عمل میکند. این رابط به برنامهنویسان اجازه میدهد مستقیما به داخل تراشه نفوذ کرده و عملکرد آن را در حین کار بررسی کنند.
قبل از معرفی JTAG، هر شرکت تولیدکننده تراشه از رابط دیباگ مخصوص به خود استفاده میکرد یا اصلاً رابطی ارائه نمیداد. این مسئله، فرایند دیباگ را بسیار پیچیده و زمانبر کرده بود. اما با استاندارد شدن JTAG، تمامی تراشهها از یک رابط واحد پشتیبانی میکنند و این امر باعث شده است عیبیابی به فرآیندی ساده و یکپارچه تبدیل شود.
برای استفاده از JTAG، یک کابل مخصوص به نام debug pod به پورت JTAG برد توسعه و پورت USB کامپیوتر متصل میشود. این کابل به عنوان واسط بین نرمافزار دیباگ روی کامپیوتر و تراشه عمل کرده و امکان کنترل و مشاهده دقیق عملکرد داخلی تراشه را فراهم میکند. با استفاده از این رابط، برنامهنویسان میتوانند برنامه را در حین اجرا متوقف کرده، محتویات حافظه را بررسی و حتی مقادیر متغیرها را مشاهده کنند.( اگر به دانستن جزییات بیشتری علاقمندیم میتوانید قسمت « هر آن چه نیاز است در مورد JTAG و SWD بدانید». را در سیسوگ مشاهده کنید.
یکی دیگر از ابزارهای بسیار مفید برای عیبیابی و نگهداری سیستم های امبدد، چاپ پیامهای diagnostic است. مشکل در برنامههای امبدد محل چاپکردن پیام ها است. شما صفحه نمایش ندارید، بنابراین چاپ روی صفحه نمایش امکانپذیر نیست. چاپ پیامها در یک فایل لاگ مشکل است زیرا سیستم فایل ندارید. اکثر طراحان اغلب از یک پورت سریال استفاده میکنند. پورت سریال یک رابط ارتباطی ساده و کمهزینه است که از تعداد کمی سیم برای انتقال دادهها استفاده میکند. در قسمتهای بعد به جزئیات این دستگاه میپردازد.
بخش پایین برد Nucleo حاوی تراشه و مدارهای پشتیبانی است، با تعداد زیادی پین که به کانکتورهای لبههای برد (برای اتصال قطعاتجانبی) متصل شدهاند. بالای آن، برد پشتیبانی قرار دارد که شامل یک پروگرامر، یک دیباگر، یک دستگاه سریال به USB و یک دستگاه ذخیرهسازی USB است. شکل 3-2 نحوه قرارگیری اجزای برد را نشان میدهد.
روی برد چندین جامپر و LED نیز تعبیه شده است. جامپرها قطعات پلاستیکی کوچکی هستند که برای اتصال کوتاه دو نقطه روی برد به کار میروند. به عبارت سادهتر، جامپرها مانند کلیدهای فیزیکی عمل میکنند و با قرار دادن آنها در یک موقعیت خاص، میتوان برخی از ویژگیهای سختافزاری برد را فعال یا غیرفعال کرد.
جامپرها در اینجا برای فعال کردن دیباگر داخلی (ST-LINK) استفاده میشوند و باید طبق شکل 3-3 نصب شوند. برای انجام این کار مراحل زیر را دنبال کنید:
ST-LINK -1 را با جامپرهای CN2 نصب کنید. با انجام این کار، دستگاه دیباگر موجود روی برد (ST-LINK) برای دیباگ کردن میکروکنترلر روی برد پیکربندی میشود.
2- اگر این دو جامپر را بردارید، میتوانید از ST-LINK برای دیباگ کردن بردهای دیگر استفاده کنید.
3- جامپر منبع تغذیه (JP1) را نصب نکنید. این پیکربندی به برد Nucleo اجازه میدهد تا تا 300 میلیآمپر برق از طریق پورت USB بگیرد و دستگاه را از طریق پورت USB تغذیه کنید. اگر تعداد زیادی از لوازم جانبی که برق زیادی مصرف میکنند به برد متصل کردهاید، میتوانید از JP1 برای فعال کردن منبع تغذیه خارجی استفاده کنید. این کتاب از هیچ سختافزار خارجی استفاده نمیکند، بنابراین JP1 را نصب نکنید.
4- RX-TX را نصب نکنید، این گزینه دیباگ ورودی و خروجی پورت سریال را به هم وصل میکند. ما بعداً از پورت سریال به عنوان یک پورت سریال واقعی استفاده خواهیم کرد، بنابراین این جامپر را نصب نکنید.
5- جامپر JP5 را در موقعیت سمت راست (U5V) نصب کنید. با انجام این کار، برد از طریق پورت USB تغذیه میشود نه از طریق یک منبع تغذیه خارجی.
6- جامپر اندازهگیری (JP6) را نصب کنید. این یک دستگاه کم مصرف است. دو پینی که توسط JP6 کوتاه شدهاند، برق را به تراشه میرسانند. جامپر را بردارید و یک آمپرمتر را وصل کنید تا مصرف برق را اندازهگیری کنید.
7- CN11 و CN12 مکانهایی برای نگهداری جامپرها وقتی که استفاده نمیشوند هستند. نصب جامپرها در این مکانها بر مدار تأثیری نخواهد داشت.
حالا دستگاه را با استفاده از کابل mini USB به کامپیوتر خود وصل کنید. LED شماره 1 باید قرمز شود که نشان میدهد پروگرامر برق دارد. LED شماره 2 باید چشمک بزند، زیرا برد با یک برنامه از پیش نصب شده ارائه میشود. (این در صورتی صحت دارد که برد را بهصورت نو خریداری کرده باشید. اگربرد قبلا استفاده شده باشد حاوی آخرین پروژهی ران شده میباشد.) LED شماره 3 نیز باید قرمز شود که نشان میدهد تراشه برق دارد.
✅نکته
در لینک https://docs.rs-online.com/a623/0900766b814400bf.pdf میتوانید راهنمای استفاده از بورد توسعه STM32 NUCLEO-F030R8 را مشاهده کنید . برای استفاده از این راهنماها نیازی نیست تمام قسمت های آن را مطالعه کنید برای مثال باسرچ کلمهDebug نحوه اتصال پروگرامر را در آن پیدا کنید و یا با سرچ شماره جامپری که در مورد نحوه اتصال آن تردید دارید نحوه اتصال را بررسی کنید.
پرواگرام و دیباگ کردن برنامه
برنامه چشمکزن ما بسیار ساده است و به درستی کار میکند، اما وقتی برنامههای پیچیدهتری بنویسیم، احتمال وجود باگ در آنها بیشتر میشود. از آنجایی که برد برنامهنویسی ما دارای یک ابزار دیباگ قوی است، بهتر است از همین ابتدا نحوه استفاده از آن را یاد بگیریم. برای شروع فرآیند دیباگ، دیباگر را با انتخاب Run ▶ Debug، همانطور که در شکل 3-10 نشان داده شده است، راهاندازی کنید.
سپس IDE نوع دیباگری که باید اجرا شود از شما میپرسد، همانطور که در شکل 3-11 نشان داده شده است.
Ac6 STM32 C/C++ Application را انتخاب کنید.
سیستم میپرسد که آیا میخواهید به «Debug Perspective» بروید. Yes را انتخاب کنید. سپس سیستم به طور خودکار تعدادی از مراحل را طی میکند:
- نرمافزار را میسازد.
- IDE برنامه را با استفاده از فلش programmer در تراشه منتقل میکند.
- یک دیباگر از طریق رابط JTAG به دستگاه متصل میشود.
- دیباگر یک نقطه توقف (breakpoint) در اولین خط main تنظیم میکند.
- نقطه توقف به تراشه میگوید که درست قبل از اجرای اولین خط main متوقف شود.
- ریزپردازنده (microprocessor) ریست (reset) میشود و برنامه تا main اجرا میشود.
- دیباگر زمانی که برنامه به نقطه توقف در main میرسد، کنترل را دوباره به دست میگیرد.
پس از رسیدن دیباگر به نقطه توقف، برای دیباگکردن برنامه آمادهاید، همانطور که شکل 3-12 قابل مشاهده است. در این مرحله، برنامه تا اولین دستور تابع main اجرا شده و قبل از فراخوانی HAL_Init متوقف شده است.
❗توجه
چراغ در گوشه برد Nucleo که به رنگ قرمز و سبز چشمک میزند نشان میدهد که برد تحت کنترل دیباگر خارجی است.
حالا که کنترل را به دست گرفتیم، بیایید از آن استفاده کنیم. دستور Run ▶ Step Over برای شروع اجرای خط به خط برنامه استفاده میشود. ما این کار را چندین بار انجام خواهیم داد، بنابراین کلید میانبر F6 را بهخاطر بسپارید. با استفاده از F6، روی خطوط برنامه گام بردارید تا وارد حلقه for شوید.
توجه داشته باشید که هر بار تابع HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_PIN) را اجرا میکنید، LED روشن یا خاموش میشود. ازآنجاییکه در حلقه for هستید، دائماً بین روشن و خاموشکردن LED رفتوبرگشت انجام میدهید. اگر بادقت نگاه کنید، متوجه میشوید که اجرای فراخوانی HAL_Delay کمی بیش از 400 میلیثانیه (دو پنجم ثانیه) طول میکشد. میتوانید این مقدار را بزرگتر کنید تا تأخیر را بهتر مشاهده کنید.
گام برداشتن در برنامه
حالا به برخی از جزئیات این برنامه میپردازیم. بیشتر مفاهیم در فصول بعدی به طور عمیقتر پوشش داده میشوند، اما در حال حاضر به شما یک نیمنگاه از آنها میدهم. اول، دیباگکردن فعلی را با Run ▶ Terminate لغو کنید. حال بیایید دوباره با Run ▶ Debug شروع کنیم. شما باید به خطی که HAL_Init را فراخوانی میکند برگردید.این بار برای گام برداشتن در برنامه، از دستور دیگری به نام Run ▶ Step Into (یا کلید میانبر F5) استفاده کنید.
با فشردن F5 فایل stm32f0xx_hal.c در پنجره ویرایش ما ظاهر میشود (شکل 3-13 را ببینید). این فایل از کجا آمد؟
خب، ما تابعی به نام HAL_Init را فراخوانی کردیم. این تابع در فایل stm32f0xx_hal.c تعریف شده است؛ بنابراین، وقتی که در حین دیباگ کردن وارد این فراخوانی شدیم، دیباگر به طور خودکار این فایل را باز میکند تا بتوانیم کد درون تابع را خط به خط بررسی کنیم.
اما اگر به جای این کار، از کامند Step Over (در این مورد،HAL_INIT();) استفاده میکردیم، دیباگر کل این فراخوانی را به عنوان یک واحد منفرد در نظر میگرفت و بدون نشان دادن جزئیات داخلی تابع، به خط بعدی برنامه میرفت. به عبارت دیگر، با استفاده از Step Over میتوانیم از جزئیات پیادهسازی تابع صرفنظر کنیم و فقط نتیجه کلی فراخوانی آن را ببینیم.
دستور “Step Into” تشخیص میدهد که ما در حال فراخوانی یک تابع هستیم پس به درون کد آن میرود تا بتوانیم خط به خط اجرای تابع را دنبال کنیم. این به ما اجازه میدهد که ببینیم دقیقاً چه اتفاقی داخل تابع رخ میدهد. نکته جالب اینجاست که برای اجرای یک برنامه ساده، مقدار زیادی کد اضافی لازم است.
برخلاف برنامهنویسی روی کامپیوترهای شخصی که کدهای سیستمی از دید برنامهنویس پنهان هستند و دسترسی به سورس کد آنها بسیار دشوار است، STM32 Workbench همه این کدهای اضافی را در دایرکتوری HAL_Driver/Src قرار میدهد تا بتوانیم آنها را بررسی کنیم.
علاوه بر نمایش کد داخل توابع، دیباگر میتواند وضعیت همه متغیرهای برنامه را به ما نشان دهد. برای دیدن این عملکرد، Run ▶ Step Over (یا F6 را فشار دهید) را حدود شش بار انتخاب کنید تا به main.c در خطی که پین مورداستفاده را انتخاب میکند، برگردید. در گوشه بالا سمت راست صفحه، پنلی با عنوان Variables خواهید دید (شکل 3-14 را ببینید).
در برنامه، متغیری به نام GPIO_InitStruct تعریف کردهایم. در پنل Variables، علامت + قبل از نام نشان میدهد که GPIO_InitStruct یک متغیر گسترده است، به این معنی که حاوی بیش از یک integer ساده، بولیین (Boolean) یا مقدار واحد دیگری است. برای دیدن تمام اجزاء، با کلیک بر روی نماد + آن را باز کنید (شکل 3-15 را ببینید).
شما در فصول بعدی اجزای GPIO_InitStruct و نحوه ایجاد متغیرها توسط خودتان را خواهید آموخت. متغیر GPIO_InitStruct توسط برنامهنویسی ایجاد شده است که دفترچه راهنمای 700 صفحهای تراشه ما را خوانده و متغیری را برای نگهداری این اطلاعات طراحی کرده است.
باور کنید یا نه، این متغیر به طور قابل توجهی آنچه را که در دفترچه راهنما ارائه شده است ساده میکند: حدود 30 صفحه اطلاعات فنی فشرده فقط در مورد زیرسیستم (subsystem) GPIO.
حالا بیایید از چند دستور بعدی عبور کنیم تا مقادیر اجزای این متغیر را ببینیم.
خلاصه
سعی کردهام این برنامه را تاحدامکان ساده کنم، اما همانطور که میبینید، با تراشههای پیچیده امروزی، حتی سادهترین عملیات نیز کمی کار میبرد. برای اجرای یک برنامه به پشتیبانی زیادی نیاز است.
در فصل اول، برنامه “hello world” ما تقریباً به همین تعداد فایل ذکر شده در اینجا نیاز داشت، اما آنها در پشتصحنه وجود داشتند. بهعنوانمثال، فایل راهاندازی بهعنوان بخشی از بسته GCC نصب شد. اما در پروژه چشمکزن، فایل startup_stm32f030x8.S باید مستقیما به پروژه اضافه شود.
این فصل انبوهی از مفاهیم جدید را به روی شما گشود. نگران نباشید اگر هنوز همه آنها را درک نمیکنید. ما در فصلهای آینده عمیقتر به آنها خواهیم پرداخت.
چند مساله برنامهنویسی
با تغییر در دستور Hal_Delay() آزمایش کنید تا فرکانس چشمکزدن طولانیتر و کوتاهتر شود.
LinkerScript.ld را برای یافتن پاسخهای سؤالات زیر بررسی کنید:
- چه مقدار حافظه فلش (فقط خواندنی) دارید؟
- چه مقدار رم (حافظه خواندنی/نوشتنی) دارید؟
- فایل output.map را بررسی کنید و آدرس واقعی Reset_Handler را تعیین کنید.
برای خوانندگان سطح متوسط: برنامه را طوری تغییر دهید که LED برای مدت کوتاهی روشن شود، سپس برای مدت طولانیتری خاموش بماند.
سؤالات
- چه فایلهایی توسط IDE تولید میشوند و حاوی چه مواردی هستند؟
- IDE کامپایلر را در کدام قسمت سیستم شما پنهان کرده است؟
- یک دیباگر JTAG تجاری چهشکلی است؟ چقدر قیمت دارد؟ برای اتصال آن به یک برد توسعه معمولی چه چیزی لازم است؟ (خوشحال باشید که یک سیستم یکپارچه به دست آوردهاید!)