آموزش, آموزش STM32 با توابع HAL, توصیه شده

ریدایرکت کردن توابع stdio در STM32 | قسمت دهم آموزش STM32 با توابع HAL

ریدایرکت کردن توابع stdio

برای سنجش کارایی یک برنامه و پروژه و دیباگ آن، راه‌های مختلفی وجود دارد. در قسمت‌های پیشین پروژه‌های ساده‌ای را توسعه دادیم که در این راستا نیز می‌توانند کاربرد داشته باشند. اجرای صحیح برنامه چشمک‌زن همواره یکی از ساده‌ترین آزمایش‌هایی است که در یک بورد یا پروژه می‌توان انجام داد. اما یک راه بسیار کارآمد و ساده برای منظور دیباگ و خطایابی اجرا، نمایش دادن پیام‌های خاص روی ترمینال سریال است. پس اگر بتوانیم به روشی ساده با ترمینال سریال ارتباط داشته باشیم، فرایند دیباگ پروژه‌ها، آسان و کارآمدتر خواهد شد. در قسمت قبل از سری آموزش STM32 با توابع HAL، در مورد نحوه دریافت اطلاعات به‌وسیله USART صحبت شد. در این قسمت، با طریقه ریدایرکت کردن توابع stdio آشنا می‌شویم، درواقع  می‌خواهیم دستوراتی مثل printf و scanf را به کمک واحد USART ریتارگت یا ریدایرکت کنیم. با سیسوگ همراه باشید.

 

ریدایرکت کردن توابع stdio

 

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

 

دستورات Printf و Scanf در کتابخانه استاندارد

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

سینتکس این تابع به‌صورت زیر است:

بخش اول ورودی یعنی format string، شامل نوع متغیرهای موردنظر برای چاپ می‌شود که می‌تواند ترکیبی از فرمت‌های integer، کاراکتر، رشته، float و … باشد. argument list نیز مشخص می‌کند که در جایگاه فرمت‌های تعریف‌شده، چه متغیر یا مقدارهایی باید چاپ شود.

تابع scanf نیز برای دریافت ورودی استفاده می‌شود و سینتکس آن به‌صورت زیر است:

برنامه زیر یک کاربرد نمونه و ساده این دو تابع را نشان می‌دهد:

نتیجه اجرای این برنامه به این صورت خواهد بود:

 

ریدایرکت کردن یا ریتارگت کردن توابع stdio به چه معناست؟

در محیط Embedded، اگر هدرفایل stdio.h را اضافه کنیم و سپس مثلاً از دستور printf استفاده کنیم، هیچ نتیجه‌ای مشاهده نخواهیم کرد. زیرا کاربرد این تابع در این محیط تعریف‌نشده است. برای اینکه بتوانیم از توابع خروجی این کتابخانه به‌منظور دیباگ و چاپ خروجی استفاده کنیم، باید توابع پایه این کتابخانه را به شکل دیگری تعریف کنیم. یکی از راه‌های استفاده از این توابع، تعریف آن‌ها به‌وسیله رابط USART است. به این عمل ریتارگت یا ریدایرکت کردن نیز گفته می‌شود.

در ادامه روش این کار در محیط STM32CubeIDE توضیح داده می‌شود. در مرحله اول چگونگی ریدایرکت کردن این توابع با USART و در مرحله (اختیاری) دوم چگونگی ایجاد امکان استفاده از اعداد floating point را یاد می‌گیریم.

 

ایجاد پروژه

در این پروژه به یک واحد USART نیاز داریم. پس مثل پروژه قبل واحد USART1 را فعال می‌کنیم و تنظیمات آن را در همان حالت پیش‌فرض Receive and Transmit می‌گذاریم.

اکنون می‌توانیم وارد فایل کد شویم.

 

نوشتن کد و انجام تنظیمات ریدایرکت

در اولین قدم، کتابخانه stdio را به پروژه اضافه می‌کنیم؛

اکنون می‌خواهیم یک کتابخانه برای عمل retarget کردن بسازیم تا بتوانیم از کد این پروژه به‌راحتی در پروژه‌های بعدی نیز استفاده کنیم. بدین منظور، یک پوشه جدید بانام stdio_usart می‌سازیم و دو فایل stdio_usart.c و stdio_usart.h را در آن ایجاد می‌کنیم. اکنون این پوشه را از طریق Drag & Drop به مسیر Core در پروژه اضافه می‌کنیم و بعد از باز شدن پنجره زیر همان گزینه پیش‌فرض Copy files and folders را انتخاب و OK می‌کنیم؛

 

بعد از این مرحله، مسیر پروژه به صورت زیر در می‌آید:

 

مسیر پروژه

 

اکنون باید مسیر این پوشه جدید را به مسیرهای Include اضافه کنیم. برای این کار باید از منوی Project، گزینه Properties را انتخاب کنیم. در منوی بازشده، از قسمت C/++C Build وارد بخش Settings می‌شویم. سپس از مجموعه MCU GCC Compiler، ‌ Include paths کلید Add () را می‌زنیم و مسیر پوشه موردنظر را اضافه می‌کنیم؛

 

حالا می‌توانیم به سراغ نوشتن کد در فایل‌های stdio_usart.c و stdio_usart.h برویم. کار را از stdio_usart.h شروع می‌کنیم و کد زیر را درون این فایل می‌نویسیم؛

در این کد، ابتدا کتابخانه stm32f1xx_hal برای دسترسی به توابع مربوط به uart در میکروکنترلر، و در خط دوم کتابخانه stdio برای دسترسی به توابع مربوط به ورودی و خروجی، اضافه‌شده‌اند. در سه خط بعدی نیز توابع موردنظر برای راه‌اندازی، نوشتن و خواندن، اعلان‌شده است.

حالا به سراغ فایل stdio_usart.c می‌رویم تا این توابع را تعریف کنیم. در این فایل، ابتدا stdio_usart.h را اضافه می‌کنیم و همچنین یک اشاره‌گر از نوع UART_HandleTypeDef تعریف می‌کنیم؛

اکنون به تعریف توابع می‌پردازیم. اولین تابع یعنی RetargetInit را به‌صورت زیر تعریف می‌شود؛

در خط اول این تابع، uart handler مقداردهی شده است و در خط بعدی، به‌وسیله تابع setvbuf، قابلیت I/O buffering برای افزایش سرعت اجرای دستور چاپ اطلاعات، غیرفعال شده است.

تابع بعدی یعنی write_ به‌صورت زیر تعریف می‌شود:

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

در آخر، تعریف تابع read_ نیز به‌صورت زیر است:

در این مرحله، نوشتن هر دو فایل stdio_uart.h و stdio_uart.c، به اتمام رسیده است و می‌توانیم آن‌ها را ذخیره کنیم و به سراغ فایل اصلی برنامه، یعنی main.c، برویم. در این فایل نیز ابتدا stdio_uart.h را اضافه می‌کنیم؛

سپس درون بدنه تابع int main یک بافر برای ذخیره اطلاعات دریافتی، تعریف می‌کنیم:

اکنون می‌توانیم به صورت زیر، از دستورات Printf و Scanf استفاده کنیم:

حالا از پروژه build می‌گیریم و کد را روی میکرو دانلود می‌کنیم. در صورت درست انجام دادن مراحل گفته‌شده، و اتصال صحیح میکرو به پورت سریال، می‌توانیم نتیجه اجرای برنامه را در ترمینال سریال مشاهده کنیم؛

 

 

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

 

ریدایرکت کردن توابع stdio

 

فعال سازی پشتیبانی از Floating Point

اگر در این مرحله از توسعه پروژه، بخواهیم از فرمت float در دستورات ورودی خروجی استفاده کنیم، به مشکل بر خواهیم خورد. برای مشخص شدن این مشکل، قطعه کد زیر که از صفحه مرجع مربوط به تابع printf برداشته‌شده است را در برنامه به‌کار می‌بریم (کد نوشته در بدنه while(1) در قسمت قبل را کامنت می‌کنیم و کد زیر را قبل از while(1) می‌نویسیم؛

پس از دانلود برنامه، نتیجه اجرا را در ترمینال مشاهده می‌کنیم؛

 

 

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

 

 

این پیغام، هشدار می‌دهد که پشتیبانی از فرمت float فعال نیست و باید از مسیر مربوطه فعال شود. اکنون برای فعال‌سازی به همان قسمت تنظیمات Properties از منوی Project می‌رویم. بخش Settings را از قسمت C/++C Build انتخاب می‌کنیم. سپس از تب Tool Settings وارد MCU Settings می‌شویم. در این پنجره، مانند شکل گزینه “Use float with printf from newlib-nano” را فعال می‌کنیم و درنهایت Apply and Close را می‌زنیم:

 

 

اکنون مجدداً پروژه را build و روی میکرو دانلود می‌کنیم و نتیجه اجرای کد را در ترمینال سریال می‌بینیم:

 

 

همان‌طور که در تصویر دیده می‌شود، این بار دستور چاپ مقادیر float به عمل کرده است.

توجه به این نکته حائز اهمیت است که فعال‌سازی پشتیبانی از فرمت float در دستور printf، باعث مصرف بخش قابل‌توجه ای از حافظه خواهد شد. این عمل به‌طور تقریبی حدود 0.35KB از حافظه RAM و 10.30KB از حافظه Flash را اشغال خواهد کرد که این مقدار حافظه، علاوه بر مقداری است که خود برنامه و تابع printf (بدون پشتیبانی float) مصرف می‌کند. درنتیجه توصیه می‌شود در برنامه‌هایی که استفاده از این امکان ضرورت ندارد، آن را فعال نکرده و حافظه را اشغال نکنیم. به‌خصوص در سخت‌افزارهایی که حافظه محدودی دارند، توجه به این نکته بیش‌ازپیش اهمیت پیدا می‌کند.

 

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

لینک این پروژه در گیت‌هاب

منبع 1

   منبع 2

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

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

3 دیدگاه در “ریدایرکت کردن توابع stdio در STM32 | قسمت دهم آموزش STM32 با توابع HAL

  1. Avatar for کامران کامران گفت:

    برنامه‌م رو تبدیل کردم به cpp دیگه printf کار نکرد

  2. Avatar for محمدرضا محمدرضا گفت:

    سلام
    میشه دارک مود سیسوگ رو رونمایی کنید لطفاً؟
    طولانی مدت توی سایتیم، همه جا هم دارکه، سوئیچ میکنیم تو سیسوگ باید عینک آفتابی بزنیم 😁 مرسی.

  3. Avatar for ss ss ss ss گفت:

    در برنامه اصلی RetargetInit فراخوانی نشده، لذا خروجی نخواهیم داشت.

    تشکر از کار زیبای شما

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

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