آموزش برنامه نویسی c, برنامه نویسی, توصیه شده

شروع برنامه‌نویسی – قسمت ششم آموزش امبدد C

آموزش برنامه نویسی C قسمت ششم

در قسمت قبلی آموزش برنامه نویسی C به بررسی محیط های توسعه یکپارچه یا IDE برای برنامه نویسی C و معرفی محیط توسعه یکپارچه System Workbench for STM32 پرداختیم، در این قسمت به بررسی نحوه کانفیگ IDE و شروع برنامه نویسی امبدد C می پردازیم.

آماده‌سازی IDE برای برنامه‌نویسی امبدد

قبل از شروع برنامه‌نویسی، هر پنجره ویرایشی (فایل‌های باز اضافی) که در System Workbench for STM32 باز است را ببندید. پنجره ویرایش، نام فایل را مشخص می‌کند، نه نام پروژه که در ابتدای کار کمی گیج‌کننده است. همه پروژه‌های ما یک فایل main.c خواهند داشت و با باز ماندن چندین پنجره ویرایش main.c، کارکردن بسیار گیج‌کننده می‌شود.

سپس، با انتخاب File ▶ New ▶ C Project، یک پروژه امبدد ایجاد کنید. پنجره C Project باید ظاهر شود (شکل زیر را ببینید).

پنجره C Project

پنجره C Project

برای نام پروژه، 03.blink را وارد کنید. سپس برای نوع پروژه، Ac6 STM32 MCU Project را انتخاب کنید. در اولین راه‌اندازی، IDE ابزارGCC ARM را در دایرکتوری که IDE را در آنجا نصب کرده‌اید دانلود می‌کند، همچنین، کل کتابخانه firmware STM32 را نیز دانلود می‌کند که بخشی از آن در پروژه شما کپی می‌شود. اگر می‌خواهید کد و نمونه‌های این کتابخانه را بیش‌تر بررسی کنید، دایرکتوری کش (cache) آن ~/.ac6 در Linux و macOS و C:\Users<username>\AppData\Roaming\Ac6 در ویندوز است. بااین‌حال، توجه کنید که این نمونه‌ها برای نمایش تراشه‌های STM طراحی شده‌اند و به‌راحتی توسط برنامه‌نویسان مبتدی قابل‌درک نیستند. ولی در ادامه کار بررسی آنها کمک‌کننده خواهد بود.

به ادامه کار می‌پردازیم، روی Next کلیک کنید. پنجره Select Configurations که در شکل زیر نشان داده شده است، باید ظاهر شود.

 پنجره Select Configurations

پنجره Select Configurations

 

گزینه Debug را انتخاب کرده و Release را بردارید. برای سادگی، فقط یک ساخت پروژه ابتدایی انجام می‌دهیم. روی Next کلیک کنید.

بعدی پنجره تنظیمات پیکربندی است (شکل زیر). برای series ،STM32F0 را انتخاب کنید و برای برد، NUCLEO-F030R8 را انتخاب کنید. روی Next کلیک کنید.

پنجره کانفیگ Target

پنجره کانفیگ Target

پنجره بعدی، تنظیمات سخت‌افزار پروژه (شکل زیر) است. 

پنجره تنظیمات سخت‌افزار پروژه

پنجره تنظیمات سخت‌افزار پروژه

گزینه‌های پیکربندی سخت‌افزار پروژه به ما امکان استفاده از کد استاندارد رایگان موجود در STMicroelectronics و سایر تأمین‌کنندگان را می‌دهد. ازآنجایی‌که شخص دیگری بیشتر قسمت‌های سخت کار را انجام داده ما هم ترجیحاً از کار آنها استفاده کنیم. Hardware Abstraction Layer (Cube HAL) را انتخاب کنید و سپس روی دکمه‌ای که با عنوان Download Target Firmware ظاهر می‌شود، کلیک کنید. توافقنامه مجوز را بپذیرید و IDE کتابخانه سخت‌افزار را دانلود خواهد کرد.

پس از اتمام دانلود، سیستم گزینه‌های دیگری را نمایش می‌دهد. آن‌ها را روی مقادیر پیش‌فرض رها کنید و روی Finish کلیک کنید.

در قسمت سمت چپ در Project Expolrer، پروژه 03.blink به لیست پروژه‌ها اضافه شده است. روی مثلث کنار blink کلیک کنید تا لیستی از دایرکتوری‌هایی که پروژه را تشکیل می‌دهند ببینید سپس روی مثلث کنار src کلیک کنید. روی main.c دو بار کلیک کنید تا در پنجره ویرایش ظاهر شود، همان‌طور که در شکل زیر نشان‌داده‌شده است.

پنجره ویرایش، با نمایش main.c

پنجره ویرایش، با نمایش main.c

اولین برنامه امبدد C شما

محیط توسعه (IDE) به طور پیش‌فرض یک فایل اصلی (main) را در اختیار شما قرار می‌دهد که ساختار پایه برنامه شما را شامل می‌شود. این فایل شامل موارد زیر است:

  • یک کامنت: این کامنت اطلاعاتی راجع به برنامه شما مانند نویسنده، تاریخ، نسخه و شرح مختصری از وظایف آن ارائه می‌دهد.
  • کتابخانه‌های کد: این کتابخانه‌ها شامل توابع و دستورات لازم برای کار با برد هسته هستند.
  • یک تابع اصلی (main): این تابع نقطه شروع برنامه شما است. جایی که کد شما اجرا می‌شود.

علامت مثبت (+) در کنار خط 3 نشان می‌دهد که برخی از خطوط کد مخفی شده‌اند. برای مشاهده این خطوط، روی علامت مثبت کلیک کنید. با کلیک بر روی این علامت، یک کامنت طولانی که شرح وظایف فایل را ارائه می‌دهد، نمایش داده می‌شود.

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

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

برای درک چگونگی اجرای مداوم برنامه‌های امبدد، به

for(;;)

در خط 19 توجه کنید. این خط کد C یک حلقه بی‌نهایت ایجاد می‌کند که به طور مداوم اجرا می‌شود و هیچ کاری (به جز اجرای خود حلقه) انجام نمی‌دهد.

بدون سیستم‌عامل، برنامه چگونه اجرا می‌شود؟ برنامه ما زمانی شروع به کار می‌کند که پردازنده روشن شود یا ریست شود (دلیل وجود دکمه ریست بزرگ روی بردهای امبدد همین است).

در حال حاضر، برنامه ما کاری انجام نمی‌دهد و برای همیشه به همین صورت ادامه خواهد داد. هدف این است که با اضافه‌کردن کد، به برنامه قابلیت انجام کارهای خاصی را بدهیم.

راه‌اندازی سخت‌افزار در برنامه‌نویسی امبدد

اولین قدم در این برنامه، راه‌اندازی سخت‌افزار است. برای انجام این کار، از کتابخانه HAL استفاده خواهیم کرد. لایه نرم‌افزاری HAL برای پنهان‌کردن تمام جزئیات پیچیده مربوط به راه‌اندازی تراشه (میکروکنترلر) طراحی شده است. برای مثال، قبل از اینکه از کلاک داخلی میکروکنترلر برای زمان‌بندی چشمک‌زدن یک LED استفاده کنیم، باید آن را راه‌اندازی کنیم. انجام این کار به‌صورت دستی نیازمند برنامه‌ریزی رجیسترهای خاص I/O است که به طور مستقیم نحوه عملکرد یک دستگاه I/O را کنترل می‌کنند. این رجیسترها بخشی از سخت‌افزار هستند.

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

به‌جای انجام دستی این کار، می‌توانیم از نرم‌افزار HAL و تابع HAL_Init استفاده کنیم تا همه این کارها را برای ما انجام دهد. تابع HAL_Init ساعت سیستم را برنامه‌ریزی می‌کند تا بعداً بتوانیم از آن برای کنترل زمان روشن و خاموش‌شدن LED استفاده کنیم.

یک فراخوانی به تابع HAL_Init را درست بعد از اولین آکولاد باز در تابع main قرار دهید، به‌صورت زیر:

به‌طورکلی، برای هر مجموعه آکولاد که استفاده می‌کنید، بهتر است به‌اندازه چهار فضای خالی برای تورفتگی کد (intend) استفاده کنید. اگرچه زبان C این کار را اجباری نمی‌کند، اما باعث می‌شود درک برنامه آسان‌تر شود. (در مورد تعداد فضاهای خالی برای تورفتگی اجباری وجود ندارد. برخی برنامه‌ها از دو فضا، برخی از هشت فضا و برخی افراد عجیب از سه فضا استفاده می‌کنند!)

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

برنامه‌نویسی پین GPIO

میکروکنترلر دارای تعدادی پین ورودی/خروجی چندمنظوره است که به‌عنوان پین‌های GPIO شناخته می‌شوند که می‌توانیم آنها را به‌عنوان ورودی یا ارسال خروجی برای موارد مختلف برنامه‌نویسی کنیم. برای مثال، می‌توانیم پینی را برای خروجی برنامه‌ریزی کنیم و آن را به یک LED وصل کنیم (دقیقاً همان کاری که در این برنامه انجام خواهیم داد). همچنین، می‌توانیم پینی را برای ورودی برنامه‌ریزی کنیم و آن را به یک کلید وصل کنیم (این کار را در فصل بعدی انجام خواهیم داد).

برخی از پین‌های میکروکنترلر را می‌توان به‌عنوان ورودی یا خروجی آنالوگ استفاده کرد. اکثر پین‌های GPIO می‌توانند روشن یا خاموش باشند. پین‌های آنالوگ می‌توانند ولتاژهای بین روشن و خاموش را کنترل کنند. همچنین می‌توان آن‌ها را  به یک USART (کنترل‌کننده سریال I/O) یا یک باس I2C (باس I/O ساده) برای برقراری ارتباط با تراشه‌های جانبی و برقراری ارتباط سریال متصل کرد.

خبر خوب این است که این پایه‌ها قابلیت‌های بسیار زیادی دارند. اما خبر بد این است که باید تراشه را طوری برنامه‌ریزی کنیم که کارهای پیچیده‌تری انجام ندهد و فقط زمانی روشن و خاموش شود که ما می‌خواهیم. ما پین GPIO که به LED کاربر (LED2) متصل است را برنامه‌ریزی می‌کنیم. ما باید به تراشه بگوییم که از این پین برای خروجی استفاده می‌کنیم.

سپس باید در مورد نحوه استفاده از آن اطلاعات زیادی به تراشه بدهیم. این شامل فعال‌سازی تنظیم کلاک واحد GPIO است که سرعت واکنش آن را کنترل می‌کند. اکثر این کارها را می‌توان توسط سخت‌افزار HAL انجام داد، اما باید به HAL دستور دهیم که چه کاری انجام دهد. برای این کار کافی است استراکچری را که شامل تمام این تنظیمات می‌شود را  مقداردهی کنیم و آن را به‌عنوان ورودی به تابع  HAL_GPIO_Init پاس دهیم. با مفهوم استراکچر (structure) در زبان برنامه‌نویسی C در قسمت‌های آینده به طور کامل شرح داده می‌شود.

توجه داشته باشید که طراحان سخت‌افزار STM این LED را روی برد “LD2” نام‌گذاری کردند، اما طراحان نرم‌افزار آن را “LED2” می‌نامند؛ بنابراین، اگر بیت کنترل LED2_PIN را تنظیم کنید، LD2  روشن می‌شود. 

ما پین LED2_PIN را به‌عنوان پین موردنظر خود انتخاب می‌کنیم که همان پینی است که به LED کاربر در NUCLEO-F030R8 متصل است. در مرحله بعد، مشخص می‌کنیم که از پین برای خروجی استفاده می‌شود، زیرا قرار است مقدار ولتاژ این پین را تغییر دهیم و LED را خاموش روشن کنیم و قرار نیس وضعیت آن را به‌عنوان ورودی بخوانیم و Mode را روی push/pull تنظیم می‌کنیم.

این گزینه را باید متناسب با آنچه به پین خود متصل کرده‌ایم انتخاب کنیم. در این مورد، مدار ما به push/pull نیاز دارد. این گزینه سخت‌افزار داخلی مورداستفاده برای هدایت پین GPIO را کنترل می‌کند. مرجع تراشه STM به شما نشان می‌دهد که این مدار چگونه سازماندهی شده است (یا به عبارت بهتر، این اطلاعات به مهندس سخت‌افزار نشان می‌دهد که تراشه چگونه ساخته شده است و او می‌تواند به شما بگوید که کدام حالت کاری برای شما مناسب‌تر است.).

پرچم‌های GPIO_PULLUP پین GPIO را طوری پیکربندی می‌کنند که در حالت ورودی، یک مقاومت به‌صورت pullup بخشی از مدار باشد. این برای پین‌های خروجی خیلی معنادار نیست؛ اما همچنان باید تنظیم شود. ما آن را روی GPIO_PULLUP تنظیم می‌کنیم؛ اما این تنظیم در شرایط فعلی هیچ تأثیری ندارد. در نهایت، سرعت را با GPIO_SPEED_FREQ_HIGH روی high تنظیم می‌کنیم.

✅نکته

LED2_PIN نام جایگزینی است  برای شماره پینی که به LED متصل است. همچنین، LED2_GPIO_PORT  نام جایگزینی است برای پورتی است که به LED متصل است. اگر شما با سخت‌افزار دیگری کار می‌کنید که در آن LED به پین دیگری متصل است این برنامه برای شما کار نمی‌کند و می‌توانید LED2_PIN, LED2_GPIO_PORT را متناسب با سخت‌افزاری که در دسترس دارید تغییر دهید.

فرمت نام‌گذاری GPIOها در میکروهای stm به این صورت است که هر میکرو دسته‌هایی به نام پورت دارد و هر پورت شامل تعدادی پین است وقتی می‌گوییم PA12 منظورمان پین 12 در پورت A است؛ بنابراین با بررسی نقشه راهنمای سخت‌افزاری که در دسترس دارید شماره پین را مشخص کنید؛ مثلاً اگر LED شما به PB4 متصل است: GPIO_PIN_4 را جایگزین LED2_PIN و GPIOB را جایگزین LED2_GPIO_PORT کنید.

تغییر حالت LED

حالا سمی‌کالن (;) آخر بعد از عبارت (;;)for را حذف کنید. به یاد داشته باشید که این سمی‌کالن اساساً به معنای «هیچ کاری انجام نده» است. برای معرفی کدی که حلقه for باید اجرا کند، این خطوط جدید را اضافه می‌کنیم:

تابع HAL_GPIO_TogglePin حالت پین GPIO LED2 را از خاموش به روشن یا از روشن به خاموش تغییر می‌دهد. در تراشه‌ها، پین‌های GPIO در گروه‌های ۳۲ بیتی سازماندهی می‌شوند.  هر پورت شامل چندین رجیستر 32 بیتی، وظیفه کنترل GPIO را بر عهده دارند.  LED2_GPIO_PORT تعیین می‌کند از رجیسترهای کدام پورت استفاده شود و   LED2_PIN تعیین می‌کند کدام پین رجیستر مربوط به LED ما است و با تغییر آن می‌توانیم وضعیت LED را تغییر دهیم.

برای اینکه چشم ما تغییر وضعیت را در LED تشخیص بدهد لازم است با فاصله زمانی بیشتری آن را toggle کنیم. برای این کار، از تابع HAL_Delay برای ایجاد تأخیر ۴۰۰ میلی‌ثانیه‌ای استفاده می‌کنیم.

ساخت برنامه کامل

برنامه کامل ما به این صورت است:

حالا با انتخاب Project ▶ Build Project پروژه را بسازید. اگر همه چیز به‌درستی پیش رفت، نباید مشکلی در پنجره Problems ببینید. در صورت وجود مشکل، آن‌ها را برطرف کرده و دوباره امتحان کنید.

در پنجره Console، خواهید‌ دید که IDE فراخوانی make را انجام داده که سپس کامپایلر GCC با نام arm-none-eabi-gcc فراخوانی کرده است. این کامپایلر برای تراشه امبدد‌‌ ما است.

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

در نتیجه، شما باید چشمک‌زدن آهسته LED سبز را ببینید.

بررسی فرایند ساخت (Build)

پنجره Console که در شکل زیر نشان داده شده است، خروجی فرآیند build را در‌بر می‌گیرد. (اگر این پنجره خالی است، می‌توانید محتوا را با Project ▶ Clean و سپس Project ▶ Build Project  دوباره ایجاد کنید.)

اگر گزینه منوی Build Project فعال نیست، در Project Explorer مسیر سطح بالا پروژه (top-level) را انتخاب کنید و دوباره امتحان کنید.

نمای کنسول

بیایید به بالا اسکرول کنیم و به یک خط در فرایند build نگاه کنیم، فراخوانی معمولی کامپایلر GCC:

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

این یک کامپایلر GCC است، اما برخلاف GCC معمولی که برای کامپیوتر شما کامپایل می‌کند، یک کامپایلر متقابل است که کد را برای پردازنده ARM تولید می‌کند. هیچ سیستم‌عامل زیربنایی (underlying) وجود ندارد (بنابراین گزینه none) و سیستم برای یک رابط باینری برنامه کاربردی امبدد (eabi) طراحی شده است که نحوه برقراری ارتباط قطعات برنامه با یکدیگر و با دنیای خارج را تعریف می‌کند.

این باعث تولید کد برای نسخه cortex-m0 پردازنده می‌شود. ARM انواع مختلفی از پردازنده‌ها دارد و این پرچم (flag) به GCC می‌گوید از کدام نسخه استفاده کند.

برخی از پردازنده‌های ARM می‌توانند دو مجموعه دستورالعمل مختلف را اجرا کنند. یک مجموعه دستورالعمل کامل 32 بیتی RISC وجود دارد که بسیار سریع اجرا می‌شود اما از حافظه زیادی استفاده می‌کند، و یک مجموعه دستورالعمل thumb وجود دارد که کندتر است اما بسیار فشرده تر می‌باشد. این دستورالعمل به GCC می‌گوید که ما کد thumb می‌خواهیم (اگر از یک تراشه ارزان قیمت با حافظه محدود استفاده می‌کنید، ایده خوبی است).

پردازنده ما، واحد سخت‌افزاری ممیز شناور (floating-point) را ندارد، بنابراین این پرچم (flag) به GCC می‌گوید که آن را با نرم‌افزار شبیه‌سازی کند. (در قسمت‌های بعدی در مورد floating-point) هم صحبت خواهیم کرد)

-O0

سطح بهینه‌سازی 0 (یعنی بدون بهینه‌سازی) را مشخص می‌کند. این ویژگی کامپایلر را غیر فعال می‌کند که در آن کامپایلر کد شما را آنالیز می‌کند و انواع ترفندها را برای سریع تر کردن آن انجام می‌دهد. این ترفندها باعث می‌شود تا کد پایه برای درک و دیباگ سخت‌تر شود.

-g3

دیباگ را روشن می‌کند.

-Wall

مجموعه هشدارهایی به نام all را روشن می‌کند که تقریباً شامل تمام هشدارهای کاربردی است.

-c

با کامپایل هر سورس فایل یک آبجکت فایل (object) تولید می‌کند.

به کامپایلر می‌گوید که فایل شیء خروجی را با نام “stm32f0xxll_tim.o” در پوشه “HALDriver/Src/” ذخیره کند.

نام فایل منبعی که باید کامپایل شود.

گزینه‌های دیگر به کامپایلر می‌گویند که فایل‌های header کتابخانه کجا هستند و چگونه باید این فایل‌ها را پیکربندی کرد. (ما در فصل ۱۲ در مورد دستورالعمل D- بحث خواهیم کرد.) دستورالعمل I- به کامپایلر می‌گوید که علاوه بر دایرکتوری‌های استاندارد فایل‌های header، در دایرکتوری مشخص شده نیز به دنبال فایل‌های header بگردد.

علاوه بر دستورات کامپایل، دستور لینکر (linker) را نیز می‌بینیم:

دستورالعمل کلیدی -T”/home/sdo/bare/workspace/blink/LinkerScript.ld” به لینکر می‌گوید که از فایل LinkerScript.ld برای تعیین محل قرارگیری بخش‌های مختلف برنامه استفاده کند. (این موضوع در بخش‌های آینده به‌صورت مفصل موردبحث قرار خواهد گرفت.)

فرایند build با دو کامند زیر به پایان می‌رسد:

کامند arm-none-eabi-objcopy فایل اجرایی قابل‌حمل و قابل لینک ELF را به یک تصویر باینری خام تبدیل می‌کند. فرمت فایل ELF حاوی اطلاعات ساختاری پیچیده‌ای است که به لودر (loader یا برنامه‌ای که کد را در حافظه بارگذاری می‌کند) می‌گوید چگونه داده‌ها و کد را در حافظه قرار دهد. در مقابل، تصویر باینری خام یک فایل ساده است که حاوی دقیقاً همان بیت‌هایی است که باید به‌صورت مستقیم در حافظه فلش نوشته شود. به‌عبارت‌دیگر، فایل ELF حاوی اطلاعات کنترلی است درحالی‌که تصویر باینری صرفاً داده‌های خام اجرایی هستند.

در نهایت، کامند arm-none-eabi-size اندازه برنامه نهایی را چاپ می‌کند (جدول زیر).

بخششرح
text

اندازه داده‌های فقط خواندنی (وارد فلش می‌شود)

dataاندازه داده‌های خواندنی/نوشتنی که نیاز به مقداردهی اولیه دارند (وارد رم می‌شود)
bss

اندازه داده‌های خواندنی/نوشتنی که به صفر مقداردهی اولیه می‌شوند (وارد رم می‌شود)

dec

اندازه کل به‌صورت اعشاری

hex

اندازه کل به‌صورت هِگزادِسیمال

 اندازه بخش‌های حافظه برنامه

ما انواع مختلف حافظه مانند فلش و رم را در قسمت‌های بعدی بررسی خواهیم کرد. در حال حاضر، هدف از این مرحله این است که به پاسخ این سؤال برسیم: “اگر به برنامه‌نویسی ادامه دهم، چه زمانی حافظه من تمام می‌شود؟”

انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

شما نیز میتوانید یکی از نویسندگان سیسوگ باشید.   همکاری با سیسوگ

دیدگاهتان را بنویسید

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