STM32

آموزش استفاده از تابع printf در stm32

ریدایرکت کردن تابع printf در STM32

5/5 - (22 امتیاز)

تابع printf در stm32 موضوع این مقاله می باشد . یکی از مواردی که برای اشکال زدایی کدها بسیار مفید می باشد استفاده از تابع printf برای چاپ یک رشته یا متغیر جهت نمایش خروجی توابع و بخش های مختلف برنامه می باشد .

آشنایی با تابع printf در کتابخانه stdio.h

همانطور که می دانید printf یک تابع از کتابخانه stdio.h می باشد . بهتر است قبل از هر چیز ابتدا کمی با کتابخانه stdio.h آشنا شویم .

کتابخانه stdio.h یک هدر فایل (header file) در زبان برنامه‌نویسی C است که شامل تعاریف و توابعی برای ورودی و خروجی استاندارد می‌باشد. این کتابخانه توابعی مانند printf برای چاپ خروجی و scanf برای خواندن از ورودی را فراهم می‌کند. به عبارت دیگر، stdio.h مخفف “Standard Input/Output” است و به عنوان یک رابط بین برنامه و محیط Input/Output عمل می‌کند.

توابع مهم در کتابخانه stdio.h

printf : این تابع برای چاپ مقادیر مختلف (مانند اعداد، متن، متغیرها) به خروجی استاندارد (معمولاً صفحه نمایش) استفاده می‌شود.
scanf : این تابع برای خواندن ورودی از ورودی استاندارد (معمولاً صفحه کلید) استفاده می‌شود.
getchar : این تابع برای خواندن یک کاراکتر از ورودی استاندارد استفاده می‌شود.
putchar : این تابع برای نوشتن یک کاراکتر به خروجی استاندارد استفاده می‌شود.

به طور خلاصه، کتابخانه stdio.h یک ابزار ضروری برای هر برنامه C است که نیاز به تعامل با کاربر دارد.

در مثال زیر نحوه استفاده از تابع printf برای نمایش انواع متغییرها را مشاهده می کنید . (منبع)

نتیجه و خروجی توابع printf بالا به شکل زیر خواهد بود .

نمایش انواع متغیرها در printf
نمایش انواع متغیرها در printf

 

 

 

 

در برخی از میکروکنترلرها مثل AVR ، کامپایلر کتابخانه stdio.h را به صورت پیش فرض بر روی پورت سریال UART پیکربندی می کند و زمانی که داخل برنامه از تابع printf استفاده می کنیم داده های ما به پایه TX از پورت UART هدایت می شوند .

اما در برخی از میکروکنترلرهای پیشرفته تر مانند STM32 که انواع پورتهای Input/Output و واحدهای مختلف اشکال زدایی و دیباگینگ دارند ، کامپایلر روش‌های مختلفی برای پیاده‌سازی printf ارایه می دهد . از جمله ارسال رشته ها و کاراکترها توسط printf به واحد UART  یا ITM  .

احتمالا برای شما هم این اتفاق افتاده است که در محیطی مثل STM32CubeIDE از تابع printf درون برنامه خود استفاده کرده اید و برنامه شما با موفقیت کامپایل می‌شود ، اما هیچ رشته‌ای به UART ارسال نمی‌شود .

البته ما می توانیم رشته های خود را توسط توابع HAL (مثلا HAL_UART_Transmit) یا رجیستری به پورت سریال UART هدایت کنیم . اما گاهی اوقات استفاده از توابع استاندارد کتابخانه C مانند printf و scanf و غیره استاندارد و آسان‌تر است.

برای انجام این کار، باید برخی توابع اساسی که توسط کامپایلر تولید شده را دوباره بازنویسی کنیم و برای کامپایلر مشخص کنیم رشته های ما را به بلوک UART یا ITM هدایت کند که در اصطلاح به این عمل ریدایرکت (Redirect) کردن می گوییم .

وقتی پروژه ای توسط کامپایلر ایجاد می کنیم توابع خواندن/نوشتن از قبل در فایل syscalls.c (معمولاً در مسیر Core/Src) ارائه شده است .

در فایل syscalls.c ، کامپایلر توابع زیر را برای انجام ورودی/خروجی سطح پایین فراخوانی می‌کند.

توابع read_ و write_ در فایل syscalls.c با پیوند ((weak)) یا ضعیف تعریف شده‌اند . خاصیت __weak به شما امکان می‌دهد اگر هر یک از توابع فوق را در کد اصلی در فایل main.c قرار دهید ، تابع تعریف شده در فایل syscalls.c لغو شده و اولویت اجرا با توابعی باشد که در فایل main.c بازتعریف یا rewrite می شوند .

داخل توابع read_ و write_ از دو تابع __io_getchar و  __io_putchar استفاده شده است . این دو تابع از توابع مهم کتابخانه stdio.h هستند و وظیفه ارسال و دریافت فقط یک کاراکتر را دارند . این توابع در ابتدای فایل syscalls.c به صورت extern با پیوند ((weak)) تعریف شده اند . خاصیت extern به ما این امکان را می دهد توابع یا متغییرهایی که در فایل یا کتابخانه ای دیگر تعریف شده اند را در کتابخانه ای دیگر فراخوانی و از آنها استفاده کنیم .

حالا برای هدایت یا ریدایرکت کردن رشته ها به پورت UART توسط printf باید دو تابع _write و __io_putchar را داخل فایل main.c بازنویسی یا rewrite کنیم . سپس داخل تابع __io_putchar دستورات مربوط به ارسال یک کاراکتر به پورت uart را قرار دهیم .

 

به عنوان مثال زمانی که از دستور printf(“Hello World!!”) استفاده می کنیم . ابتدا تابع  write_ فراخوانی می شود و آدرس رشته Hello World!! درون آرگومان تابع (*ptr) که از نوع اشاره گر است قرار می گیرد . سپس توسط حلقه for کاراکترها یکی یکی توسط تابع putchar ارسال می شوند .

 

اکنون که تا حدی با مفهوم ریدایرکت کردن آشنا شدیم قصد داریم در ادامه با ذکر مثال عملی از تابع printf در برنامه خود استفاده کنیم و آن را به واحدهای UART و ITM ریدایرکت کنیم .

ریدایرکت کردن printf به uart در STM32CubeIDE

برای استفاده از printf با UART در STM32CubeIDE برای اشکال‌زدایی یا خروجی داده ، تابع استاندارد printf باید به دستگاه جانبی UART هدایت شود. این شامل redirected تابع _write (یا __io_putchar) در فایل syscalls.c برای ارسال کاراکترها از طریق UART می باشد .

ابتدا با استفاده از ابزار پیکربندی CubeMX یک پروژه ایجاد کنید. در اینجا، فقط تنظیمات UART را توضیح خواهیم داد.

زبانه Pinout & Configuration → Connectivity → USART2 → Mode را انتخاب کنید و حالت را از Disable به Asynchronous تغییر دهید. سایر تنظیمات را مطابق تصویر زیر تنظیم کنید.

تنظیم UART در STM32CubeMX
تنظیم UART در STM32CubeMX

نرخ Baud Rate سرعت ارتباط است، بنابراین اگر سرعت بیشتری می‌خواهید، آن را روی مقدار بزرگتری تنظیم کنید.

پس از اتمام تنظیمات، دکمه Generate را برنید تا کدها داخل فایل main.c ایجاد شود .

فایل main,c را باز کنید و کتابخانه stdio.h را به فایل اصلی C خود اضافه کنید.

اکنون تابع _write و __io_putchar باید بازنویسی شوند .کافیست کد زیر را در فایل main.c اضافه کنید.

می توانیم هر دو تا تابع را در یک تابع ادغام کنیم .

اگر از توابع hal استفاده می کنید ساده ترین راه هم استفاده به شکل زیر است

printf به خودی خود کار نمی‌کند، باید مقداردهی اولیه شود. قبل از فراخوانی printf، یک بار تابع setbuf ( stdout , NULL ) را در ابتدای تابع main فراخوانی کنید . این تابع باعث غیر فعال شدن بافرینگ printf  می شوند .

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

کد زیر را در حلقه while قرار دهید ، این کد باعث می شود عبارت Hello World!! هر یک ثانیه روی پورت UART2 هدایت شود .

اکنون برای نمایش عبارت Hello World!! بر روی مانیتور ، ما نیاز به یک مبدل usb to serial داریم تا رشته ها را از روی UART2 بگیرد و به COM کامپیوتر ارسال کند .

ارسال printf به UART
ارسال printf به UART

 

بر روی کامپیوتر هم نیاز به یک کنسول یا ترمینال سریال داریم تا رشته های دریافت شده بر روی COM را نمایش دهد . می توانید از کنسول سریال خود STM32CubeIDE برای نمایش استفاده کنید . تنظیمات را مطابق تصویر زیر انجام دهید .

1. دکمه New Consol View را بفشارید.
2. عبارت Command Sell Console را انتخاب کنید .
3. در پنجره باز شده منوی Connection Type را بر روی Serial Port قرار دهید .
4. دکمه New را بفشارید و در پنجره باز شده تنظیمات پورت سریال را انجام دهید .

تنظیمات consol serial نرم افزار STM32CubeIDE
تنظیمات consol serial نرم افزار STM32CubeIDE

 

در پایان نتیجه کار را باید بر روی کنسول مشاهده کنید .

نمایش printf روی consol serial
نمایش printf روی consol serial

ریدایرکت کردن printf به uart در Keil

در نرم افزار keil نیازی به بازنویسی تابع write_ و putchar نمی باشد . به منوی Manage Run-Time Environment  بروید و گزینه Compiler->I/O را انتخاب کنید و تیک گزینه های STDIN , SDTOUT را به حالت انتخاب درآورید و از زبانه کشویی گزینه USER را انتخاب کنید . این کار باعث می شود خروجی تابع printf به واحد USART هدایت یا Redirect شود .

فعال کردن printf در keil
فعال کردن printf در keil

ارسال printf بر روی SWO

در روش قبل ، ریدایرکت printf به uart  یکی از پورتهای UART میکروکنترلر اشغال می شد و همچنین نیاز به سخت افزار مجزا مثل مبدل usb to serial و استفاده از یک کابل USB و سیم کشی دیگر فقط برای استفاده از UART داشتیم .

یک راه ساده‌تر برای debuging یا اشکال‌زدایی میکروکنترلر STM32 با تابع printf با استفاده از خود پروگرامر ST-Link وجود دارد و نیازی به اضافه کردن پورت UART و سخت افزار اضافی دیگری نیست.

اگر یک پروگرامر ST-Link (اصلی یا کپی) دارید، اشکال‌زدایی میکروکنترلرهای STM32 خود با تابع printf  آسان‌تر خواهد بود. دلیل این امر این است که ST یک ساختار اشکال‌زدایی و ردیابی انعطاف‌پذیر را در نرم‌افزار و بیشتر MCUهای STM32 خود با نام ITM گنجانده است. تنها چیزی که نیاز دارید پین‌های اصلی Serial Wire Debug (SWD) و یک پین خروجی سیم سریال اضافی (SWO) Serial Wire Output است.

ارسال printf روی SWO
ارسال printf روی SWO

ARM امکان استفاده از تابع printf مانند خروجی سریال را با استفاده از رابط ITM را فراهم می‌کند. این مقاله نحوه‌ی استفاده از تابع printf را با استفاده از پروگرامر ST LINK و ITM Stimulus Ports شرح می‌دهد.

ITM چیست؟

(Instrumentation Trace Macrocell) ITM  یک بلوک اختیاری و یک منبع ردیابی(Trace) اختیاری مبتنی بر برنامه است که در پیاده‌سازی‌های خاص Cortex-M یافت می‌شود. معمولاً میکروکنترلرهای با هسته  Cortex-M3، M4 و M7 شامل ITM هستند، در حالی که Cortex-M0 و M0+ این ویژگی را ندارند. ITM به عنوان یک منبع ردیابی(TRACE) مبتنی بر برنامه عمل می‌کند و از اشکال‌زدایی به سبک printf برای ردیابی رویدادهای سیستم عامل و برنامه پشتیبانی می‌کند.

STM32 Cortex M3 Debug ITM
STM32 Cortex M3 Debug ITM

SWV چیست؟

وقتی از هر دو پین SWD و SWO استفاده می‌کنید، آماده ورود به حالت اشکال‌زدایی Serial Wire Viewer (SWV) هستید. SWV ردیابی و تحلیل پیشرفته سیستم را به صورت real-time ارائه می‌دهد که همتای حالت اشکال‌زدایی SWD  آن قادر به انجام آن نیست. علاوه بر این، برخلاف SWD، با SWV بدون نیاز به توقف در طول وضعیت اشکال‌زدایی، داده‌های real-time را دریافت می‌کنید.

پایه های SWV
پایه های SWV

ITM (Instrumentation Trace Macrocell) به برنامه‌ها اجازه می‌دهد تا از طریق پورت‌ها، داده‌ها را روی خط پین SWO بنویسند. با این کار، می‌توانید تابع printf را برای نوشتن در یک کنسول در حالت SWV با استفاده از ویژگی ITM هدایت کنید.

چه نوع سخت‌افزار ST-Link برای Trace مورد نیاز است؟

  • Nucleo
    سخت‌افزار بورد توسعه Nucleo آماده راه‌اندازی SWV است زیرا معمولاً ST-Link بصورت یکپارچه روی بورد آن وجود دارد . برای فعال کردن این ویژگی، ممکن است لازم باشد برخی از پدهای روی برد را پل بزنید.
  • ST-Link clone
    متاسفانه، کلون‌های ST-Link V2 (برخلاف بردهای اصلی NUCLEO)، برای رفتن به حالت ردیابی اشکال‌زدایی SWV آماده نیستند. این کلون‌ها فقط پین‌های SWD را خروجی می‌دهند و پین SWO ندارند.

 

افزودن پشتیبانی Trace به پروگرامر ST-Link v2

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

Trace یک ویژگی بسیار جالب است. یکی از کاربردهای رایج، هدایت جریان stdout توسط تابع printf در stm32 به پورت صفر محرک(stimulus port 0) برای چاپ اطلاعات اشکال‌زدایی است.

همه نسخه‌های اخیر ST-Link v2 از ویژگی Trace پشتیبانی می‌کنند ، SWO Trace یک ویژگی بسیار مفید است که با افزودن این ویژگی به پروگرامر کلون ST-Link v2 خود از این به بعد یک دانگل بسیار مفیدتر در اختیار دارید .

اما برای حل مشکل نبود پایه swo در پروگرامر کلون ST-Link v2 ، باید داخل پروگرامر ST-Link پایه ای که به عنوان SWO عمل می‌کند را پیدا کنید و آن را بیرون بیاورید. معمولاً این کلون‌های ST-Link فقط STM32F103 هستند. عملکرد SWO همانطور که در زیر مشاهده می‌کنید در PA10 قرار دارد.

 schematic st-link v2
schematic st-link v2

من نتوانستم شماتیک یک پروگرامر مستقل کلون ST-Link v2 پیدا کنم، بنابراین فرض کردم که دقیقاً مشابه پروگرامر موجود روی برد دیسکاوری خواهد بود. PA10 برای SWO استفاده می‌شود، بنابراین مرحله بعدی بسیار سرراست خواهد بود.

st link v2 clone swo
st link v2 clone swo

من مسیر پین ۵ ولتی را درست بعد از via بریدم و مقداری سیم رابط به PA10 لحیم کردم. همچنین یک مقاومت بین ۲۲ تا 100 اهمی را برای محافظت از جریان اتصال کوتاه اضافه کردم تا در صورت بروز مشکل جدی پروگرامر یا میکروکنترلر stm32 آسیب نبیند.

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

توجه : اگر از نرم افزار STM32CUBEIDE استفاده می کنید ، ممکن است پروگرامر ST-Link v2 clone و هدر بورد شما مانند Bluepill در محیط نرم افزار شناسایی نشود ، طبق تجربیات خودم به دلیل فیک یا کوپی بودن چیپ روی پروگرامر یا هدربورد شما می باشد .

در این صورت نرم افزار STM32CUBEIDE اجازه هر گونه عملیات پروگرم کردن و اشکال زدایی و Trace را به شما نمی دهد . اما از پروگرامر و هدربورد خود می توانید در نرم افزار هایی دیگری مانند ST-Link Utility و Keil و STM32CubeProgramer استفاده کنید .

تصویر زیر یک نمونه پروگرامر ST-Link v2 clone با چیپ فیک می باشد که در محیط نرم افزار STM32CUBEIDE شناسایی نمی شود .

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

ST-Link Fake Chip
ST-Link Fake Chip

ریدایرکت printf به ITM در STM32CubeIDE

یک پروژه و نمونه کد آماده کنید ، کد زیر را کپی کرده و در فایل main.c خود قرار دهید. در صورت ابهام، این کد از فایل syscalls.c آمده است.

تابع write_ که توسط تابع printf در stm32 فراخوانی خواهد شد را باید تغییر دهیم. تابع io_putchar__ را با تابع ()ITM_SendChar جایگزین کنید. تابع ()ITM_SendChar کاراکترهای ()printf را به  ITM Port 0 ارسال می‌کند.

 

همچنین فایل stdio.h را نیز اضافه کنید، زیرا این فایل برای عملکرد تابع pintf در stm32 و عدم تولید هشدارهای اشکال‌زدایی غیرضروری مورد نیاز است.

اکنون می‌توانید تابع printf را در فایل اصلی خود وارد کنید.

قبل از اینکه بتوانید با استفاده از SWV اشکال‌زدایی کنید، باید Trace Asynch Sw را در Debug on SYS در System Core فعال کنید. این کار تمام پین‌های مورد نیاز برای SWV را تنظیم می‌کند.

stm32 debug trace
stm32 debug trace

سپس، در مسیر Run -> Debug Configurations، به تب Debugger بروید. Serial Wire Viewer (SWV) را فعال کنید و سپس فرکانس کلاک سیستم خود را با Core Clock مورد استفاده توسط SWV مطابقت دهید.

شما باید نمایشگر سیم سریال (SWV) را فعال کنید. مرتبط‌ترین تنظیم ، Core Clock است. شما باید فرکانس ساعت صحیح را که در برنامه خود مقداردهی اولیه می‌کنید، پیکربندی کنید.

stm32 trace config
stm32 trace config

 

برای اینکه بتوانید تابع printf خود را در کنسول نمایش دهید، باید به پنجره کنسول داده SWV ITM دسترسی داشته باشید. در حالی که هنوز در حالت اشکال‌زدایی (Debug) هستید (در حالت مکث)، به مسیر Window -> Show View -> SWV ITM Data Console بروید.

Show ITM Console
Show ITM Console

پنجره کنسول SWV ITM را از طریق دکمه پیکربندی زیر پیکربندی کنید.

 ITM Console Config
ITM Console Config

سپس، پورت 0 از پورت‌های ITM Stimulus را تیک بزنید. معمولاً از این پورت برای تغییر مسیر کاراکترهای تولید شده توسط تابع printf در stm32 استفاده می‌شود. برای ادامه، OK را فشار دهید.

ITM Stmulus Ports
ITM Stmulus Ports

برای اجرای SWV، باید به یاد داشته باشید که روی دکمه Start Trace کلیک کنید.

Start Trace
Start Trace

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

stm32 printf
stm32 printf

ریدایرکت printf به ITM در keil

تابع printf یکی از توابع موجود در کتابخانه stdio می باشد . بنابراین ابتدا باید stdio.h  را به ابتدای برنامه اضافه کنید . در نرم افزار keil نیازی به اضافه کردن تابع write_ به برنامه نمی باشد .

سپس به منوی Manage Run-Time Environment  بروید و گزینه Compiler->I/O را انتخاب کنید و تیک گزینه های STDERR , STDIN , SDTOUT را به حالت انتخاب درآورید و از زبانه کشویی گزینه ITM را انتخاب کنید . این کار باعث می شود خروجی تابع printf به واحد ITM هدایت یا Redirect شود .

Keil ITM Config
Keil ITM Config

بعد از redirect کردن تابع printf ، حالا باید ردیابی یا Trace را فعال کنید . منوی Options for Target را انتخاب کنید و به مسیر Debug->Settings بروید ، در مرحله بعد منوی Trace را انتخاب کنید .

تیک گزینه Trace Enable را انتخاب کنید و فرکانسی که برای میکرو تنظیم کرده اید در قسمت Core Clock  وارد کنید . از قسمت ITM Stimulus Ports تیک قسمت Port 0  را به حالت انتخاب در آورید و در پایان گزینه OK را بزنید .

keil trace config
keil trace config

با زدن گزینه Start/Stop Debug session وارد حالت اشکال زدایی یا دیباگ شوید ، اکنون باید رشته ای را که توسط تابع printf  درون برنامه نوشتید در بخش  Debug (printf) Viewer مشاهده نمایید .

keil printf
keil printf

استفاده از printf در STM32 ST-Link Utility

از منوی ST-LINK گزینه Printf via SWO viewer را انتخاب کنید ، پنجره Serial Wire Viewer(swv) باز می شود . گزینه System clock را برابر فرکانس کاری میکروکنترلر قرار دهید و Stimulus port را 0 انتخاب کنید و در پایان گزینه START را بزنید .

stlink utility swv
stlink utility swv

 

آموزش استفاده از اعداد float در printf در STM32CubeIDE

به صورت پیش فرض مقادیر float در printf در پروژه‌های جدید ایجاد شده در stm32cubeide کار نمی کند.

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

فعال کردن float در printf در stm32cubeide
فعال کردن float در printf در stm32cubeide

اگر با علامت زدن تیک گزینه use float with printf همچنان اعداد اعشاری در خروجی printf نمایش داده نمی شود . باید پرچم یا flag زیر را بصورت دستی طبق تصویر زیر به linker اضافه کنید .

 

استفاده از اعداد float در printf در stm32cubeide
استفاده از اعداد float در printf در stm32cubeide

 

توجه داشته باشید که فعال کردن پشتیبانی از قالب‌بندی اعشاری برای printf مقدار قابل توجهی حافظه اضافی مصرف می‌کند. این ممکن است برای میکروکنترلرهای رده پایین مشکل‌ساز باشد.

نویسنده : حسین غیاثوند
تاریخ انتشار : 1404/02/19

 

مشترک شدن
اطلاع رسانی کن
0 دیدگاه
قدیمی ترین
جدیدترین محبوب ترین
بازخورد داخلی
مشاهده همه نظرات