آموزش میکروکنترلر STM32 – خروجی کردن GPIO

قبلاً در این خصوص که GPIO  چیست صحبت کردیم و گفتیم در صورتیکه بخواهیم یک پایه را به شکل دلخواه صفر یا یک کنیم باید آن را در حالت GPIO_Output ، و به طور برعکس اگر بخواهیم صفر یا یک اعمال شده به آن را بخوانیم باید آن را به صورت GPIO_Input ، پیکربندی کنیم. در ادامه مبحث آموزش میکروکنترلر STM32 قصد داریم نحوه‌ی خروجی کردن GPIO را به همراه یک مثال عملی شرح دهیم.

مثال عملی که قرار است در ادامه انجامش دهیم، چشمک‌زن کردن یک ردیف LED از یک دات‌ماتریکس است.

دات ماتریکس (Dot Matrix) چیست؟

وسیله‌ای که معمولاً به ما یک بلوک منظم و 8*8 از پیکسل‌های نوری می‌دهد. دات‌ماتریکس‌‌ها انواع مختلفی دارند. در ساده‌ترین نوع آن‌ها هر پیکسل شامل یک LED و در انواع دیگر شامل دو یا سه رنگ می‌باشد. در مدل 3 رنگ  هر پیکسل شامل 3 LED به رنگ‌های قرمز، سبز و آبی می‌باشد. اگر ابتدای حروف انگلیسی این سه رنگ را ترکیب کنید به کلمه RGB می‌رسید، اصطلاح دیگری که به این نوع دات‌ماتریکس می‌گویند. برای سادگی کار یک دات ماتریکس تک رنگ را در نظر بگیرید. از آنجا که در این نوع ما 64 LED در اختیار داریم، پس باید 64*2 پایه از دات‌ماتریکس خارج شده باشد. اما اگر تعداد پایه‌های یک دات ماتریکس تک رنگ را بشمارید تنها 16 پایه را مشاهده خواهید کرد! اما چگونه می‌توان با 16 پایه بر 64 LED کنترل داشت؟

به شکل زیر که شماتیکی از مدار داخلی دات ماتریکس تک رنگ است، دقت کنید.

 

همانطور که مشاهده می‌کنید، کاتدها در هر ردیف و آندها در هر ستون، به یکدیگر متصل‌اند. در نتیجه برای روشن کردن هر LED کافیست ردیف آن را به GND و ستونش را به ولتاژ مثبت Vf وصل کنیم. مثلاً اگر بخواهیم LED[2,3]  را روشن کنیم. باید ردیف 2 را به GND و ستون 3 را به Vf متصل کنیم.

آرایش پین‌ها :

متاسفانه پین‌های دات‌ماتریکس به این شکل نیستند که یک ردیف مربوط به ستون‌ها و دیگری مربوط به سطرها باشند و این کار ما را کمی مشکل می‌کند. برای پیدا کردن آرایش پین‌ها یا اصلاحاً Pin out دات ماتریکس، راه اصولی این است که به دیتاشیت مراجعه کنیم. البته ممکن است پیدا کردن دیتاشیتی که دقیقاً مربوط به دات‌ماتریکس ما باشد به راحتی نباشد، اما جای نگرانی نیست. اغلب دات ماتریکس‌ها از یک آرایش استفاده می‌کنند و شما کافیست در گوگل عبارت “Dot matrix Pin out” را جستجو کنید و در تصاویر به دنبال شماتیک مناسب کار خود بگردید. اگر از دات‌ماتریکس تک رنگ استفاده می‌کنید، احتمالاً شماتیک بالا همان چیزی است که به آن نیاز دارید. حال باید به کمک مولتی متر، پایه‌ها را طبق شماتیک مشخص کنیم. برای اینکار مولتی‌متر را روی حالت دیود قرار داده و شروع به امتحان کردن و تشخیص شماره پین‌ها کنید. برای ادامه‌ی کار بهتر است به کمک یک برد بورد، سطرها و ستون‌ها را به طور منظم خارج کنید.

اکنون  نوبت به وصل کردن دات ماتریکس به میکروکنترلر می‌رسد. برای مثال این پست ما تنها می‌خواهیم یکی از سطرها را به صورت چشمک زن روشن کنیم. کافیست پین مشترک سطر را بسته به اینکه کاتد یا آند است، به زمین یا ولتاژ هدایت دیود متصل کنیم و 8 پین دیگر آن سطر را به 8 پین میکروکنترلر وصل کنیم. هر پینی که قابلیت کانفیگ به صورت GPIO_Output را دارد، می‌توان انتخاب کرد، اما بهتر است برای هماهنگی شما هم پین‌های PA0 تا PA7 را به ترتیب به LEDهای سطر 1 وصل کنید.  در وضعیتی که پایه میکروکنترلر Set می‌باشد، ولتاژی که بر روی خروجی قرار می‌گیرد تقریباً برابر با VCC میکروکنترلر1است. از طرفی اگر به کمک مولتی متر Vf را اندازه بگیریم، مقداری کمتر از 3.3 ولت را مشاهده می‌کنیم. برای مثال مقداری که من به دست آوردم حدود 1.8 است. یعنی حدود 1.5 ولت اختلاف. میزان جریانی که باعث می‌شود LED نور مناسبی بدهد  حدود 10 میلی‌آمپر است. پس برای به دست آوردن این مقدار جریان بایست مقاومتی در حدود 150 اهم را بین LED و پایه میکروکنترلر قرار داد.

تا اینجای کار تا حد خوبی سخت افزار ما آماده است و حال نوبت به قسمت نرم‌افزار می‌رسد. برای نوشتن نرم‌افزار نیاز به دو برنامه CubeMX و Keil داریم. فکر نمی‌کنم نیاز به توضیح خاصی در خصوص نصب این دو نرم‌افزار  وجود داشته باشد. به همین دلیل با این فرض که تا الان شما این دو نرم‌افزار را بر روی سیستم خود نصب کرده‌اید ادامه می‌دهیم. فایل نصب این نرم‌افزارها به راحتی با کمی گوگل کردن به دست می‌آید. لازم به ذکر است که CubeMX کاملاً رایگان است و شرکت ST آن را توسعه داده. اما نرم‌افزار Keil این‌گونه نیست و نسخه‌ای که شرکت ARM توسعه دهنده این نرم‌افزار به ما ارائه داده دارای محدودیت در حجم کامپایل می‌باشد. 2 این محدودیت برای ما مشکلی ایجاد نمی‌کند، چراکه حافظه‌ی فلش میکروی STM32F030F4P6 تنها 16 کیلوبایت است و نسخه رایگان Keil هم پاسخگوی نیاز ما است.

در ابتدا نرم‌افزار CubeMX را باز می‌کنیم و گزینه New Project را می‌زنیم. به وسیله فیلترهای مختلف می‌توان میکروکنترلر مد نظر خود را انتخاب کرد. اما از آنجا که ما قبلاً میکروی خودمان را تعیین کرده‌ایم، تنها نام آن را در قسمت جستجو (کادر قرمز 1) وارد می‌کنیم و سپس بر روی نام میکروکنترلر در لیست نشان داده شده (کادر قرمز 2) دابل کلیک می‌کنیم.

گفتیم پایه‌های PA0 تا PA7 را برای کنترل وضعیت LEDها خروجی می‌کنیم. برای اینکار روی تک تک این پایه‌ها کلیک کرده و گزینه GPIO_Output  را انتخاب می‌کنیم. بعد از این کار مشاهده می‌کنید که در کادر سمت چپ بعضی از گزینه‌ها به رنگ قرمز یا زرد در آمده‌اند. رنگ قرمز یعنی آن واحد را کلاً نمی‌توان استفاده کرد چراکه پایه‌های آن برای کار دیگری تنظیم شده‌اند. برای مثال در اینجا پایه‌های واحد SPI1 شامل PA7 تا PA1 می‌باشند و چون ما آن‌ها را از قبل به صورت GPIO_Output تعریف کردیم، دیگر نمی‌توانند به عنوان پایه‌های SPI1  به کار روند. بعضی از گزینه‌ها هم زردرنگ هستند که نشان می‌دهد بعضی از حالات آن واحد غیرقابل استفاده است. با کلیک بر روی این واحدهای زردرنگ، حالات قرمز و غیرقابل استفاده را مشاهده خواهید کرد.

پس از انجام این کارها لازم است که تنظیمات مربوط به پروگرام کردن میکرو را انجام دهیم. در واقع، این کار ثابتی است که از این به بعد در همه‌ی پروژه‌ها انجام خواهیم داد. روش‌های متفاوتی برای پروگرام کردن وجود دارند که در آینده در خصوصشان صحبت خواهیم کرد. در اینجا من قصد دارم از روش SWD  استفاده کنم. برای این کار از قسمت configuration (سمت چپ تصویر، کادر 1) ، واحد SYS، تیک گزینه Debug Serial Wire را می‌زنیم. با انجام این کار مشاهده می‌کنید که دو پین PA13 و PA14 به صورت SWDIO(دیتا) و SWCLK(کلاک) کانفیگ شدند.

حال پروژه برای ایجاد شدن آماده است. از بالا گزینه generate (کادر 2 تصویر) را انتخاب می‌کنیم تا کدها و پروژه ایجاد شوند. اما قبل از ایجاد شدن مشاهده می‌کنید که تنظیمات پروژه را از ما می‌خواهد. اغلب گزینه‌ها را می‌توان به صورت پیش فرض رها کرد. اما مهم است که نام، محل ذخیره پروژه و همچنین ابزاری که قرار است با آن به مشاهده و ویرایش کدها بپردازیم را تعیین کنیم. مشخص کردن این 3 مورد در سربرگ project، (به ترتیب) در قسمت‌های Project Name و Project Location و Toolchain/IDE انجام می‌گیرد. از آنجا که قصد دارم از Keil 5 برای انجام پروژه استفاده کنم. از لیست IDE های نشان داده شده MDK-ARM V5 را انتخاب می‌کنم. پس از تنظیم این موارد (و در صورت لزوم سایر موارد) بر روی OK کلیک می‌کنیم تا پروژه ما ایجاد شود. در صورتی که پس از ایجاد پروژه قصد داشتید تنظیمات را تغییر دهید. این کار از طریق منو Project -> Configuration امکان پذیر است.

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

در آینده ممکن است دوباره بخواهیم از طریق CubeMX پریفرالی را فعال کنیم یا هر تغییر دیگری را در پروژه خودمان اعمال کنیم. برای اینکار کافی است که به پوشه پروژه ایجاد شده برویم و بر روی فایل با فرمت ioc. دابل کلیک کنیم. در این صورت مشاهده می‌کنیم که پروژه ما در محیط CubeMX باز شده و امکان ویرایش آن برای ما وجود دارد. بعد از هر بار ویرایش در صورتی که می‌خواهیم تغییرات بر روی پروژه اعمال شوند بایست دوباره پروژه را generate کرد. اما در اینجا اگر قبلاً کدهایمان را در جای مناسب ننوشته باشیم از بین خواهند رفت و در پروژه جدیدی اثری از آن‌ها نخواهد بود. برای جلوگیری از این موضوع بایست حتماً کدها را در بین محل‌های از پیش تعیین شده توسط CubeMX یا به عبارتی بین دو خطی که با عبارت مشابه:

/* USER CODE BEGIN*/ 

/* USER CODE END*/

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

خب حالا بعد از دانستن این نکته به سراغ کدنویسی می‌رویم.

ساختار متداول، در کدزنی برای میکروکنترلرها به این شکل است که در ابتدای برنامه در تابع main ما به سراغ راه‌اندازی اولیه پریفرال‌ها و یا در صورت لزوم مقداردهی بعضی از متغیرها می‌رویم. بعد از آن وارد حلقه اصلی برنامه که CPU دائماً در آن می‌چرخد می‌شویم.

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

همانطور که گفتیم، می‌خواهیم برنامه‌ای بنویسیم که یک ردیف از دات ماتریکس را به صورت چشمک‌زن خاموش و روشن کند. در نتیجه باید پین‌های متصل به دات‌ماتریکس را با تاخیری محسوس (مثلاً نیم ثانیه) صفر و یک کنیم. در اینجا ما به کمک کتابخانه HAL قصد داریم این کار را انجام دهیم. راه‌های دیگری هم برای اینکار وجود دارد. اما این مهم است که بدانیم این‌ها همه یک، روش و ابزار هستند و هرکدام در جای خود می‌توانند مفید واقع شوند. تعصب داشتن نسبت به یک روش اصلاً کار حرفه‌ای نیست. کتابخانه HAL یا هر کتابخانه دیگر یک ابزار است. مهم این است که بتوانیم بر حسب نیاز خود متوجه شویم که کدام ابزار برای هدف ما مناسب‌تر است. برای مثال گاهی سادگی پیاده‌سازی مهم‌تر است و بهینه سازی در اجرای سریع کد اهمیت بالایی ندارد. مثل همین پروژه‌ای که قصد پیاده‌سازی‌اش را داریم. خب در اینجا استفاده از کتابخانه‌ای چون  HAL  توجیه‌پذیر می‌تواند باشد. اما گاهی سرعت اجرای یک خط کد بسیار اهمیت پیدا می‌کند. تا حدی که شاید لازم باشد برای صفر و یک کردن پین (یا هر عملیات دیگری) سراغ استفاده از تعاریف و توابع CMSIS یا حتی کدزنی به زبان اسمبلی برویم.

اما از کجا بدانیم که توابعی که کتابخانه HAL در اختیار ما قرار می‌دهد چه هستند؟

راه اول این است که به سراغ فایل راهنمای کتابخانه HAL برای سری STM32F0 برویم. اما راه ساده‌تر و در دسترس‌تر مراجعه مستقیم به خود فایل کتابخانه است. اگر پروژه را به کمک Keil 5 باز کنیم. احتمالاً در سمت چپ برنامه (مشابه تصویر زیر) پنجره Project را مشاهده می‌کنید.

اگر چنین پنجره‌ای ندارید با رفتن به View -> Project Window می‌توانید آن را فعال کنید. این پنجره محلی است که اغلب شامل فایل‌های c. برنامه است. برای انسجام بهتر این فایل‌ها، دسته‌ دسته شده‌اند و هر دسته تحت یک زیرمجموعه قرار گرفته‌اند. یکی از این دسته‌ها، به نام Driver/STM32F0xx_HAL_Driver نامگذاری شده که حاوی فایل‌های کتابخانه HAL می‌باشد. در بین اعضا این دسته اگر بر روی فایل stm32f0xx_hal_gpio.c دابل کلیک کنید کدهای مربوط به GPIO این کتابخانه برای شما باز خواهد شد. گاهی این فایل بسیار طولانی است و پیدا کردن نام توابع در آن سخت است. در این صورت شاید بهتر باشد نگاهی به فایل stm32f0xx_hal_gpio.h بیندازیم. اما برای پیدا کردن این فایل لازم است یک بار برنامه را کامپایل کنیم. برای کامپایل کردن هم کافی است بر روی آیکون مربوطه در نوار ابزار کلیک کنیم یا دکمه F7 کیبورد را فشار دهیم.

بعد از انجام کامپایل مشاهده می‌کنید که در کنار فایل‌های c. پنجره Project، علامتی مثبت ظاهر شده که با کلیک کردن بر روی آن لیستی از فایل‌های .h  را مشاهده خواهید کرد که به این فایل مرتبط‌‌ اند. در بین لیستی که برای فایل stm32f0xx_hal_gpio.c ایجاد شده به دنبال stm32f0xx_hal_gpio.h باشید. با دوبار کلیک کردن بر روی آن می‌توانید محتویات آن را ببینید. در بین این محتویات، عنوان و نام توابعی که به دنبال آن هستیم، وجود دارد. با خواندن نام این توابع احتمالاً بتوانید کارکرد هر کدام را حدس بزنید.

نام‌گذاری مناسب، از ویژگی‌های یک کد خوبه. قبلاً در این خصوص صحبت کردیم.

در بین توابع معرفی شده تابعی به اسم HAL_GPIO_WritePin  وجود دارد که به ما کمک می‌کند با دادن نام پورت، پین و مقدار مدنظرمان که Set یا Reset است، بتوانیم 8 LED مدنظرمان را روشن یا خاموش کنیم. برای اینکه بدانیم دقیقاً قرار است با چه تابعی مواجه شویم به سراغ نحوه پیاده‌سازی‌اش می‌رویم. می‌توانید به راحتی با راست کلیک کردن بر روی عنوان این تابع در stm32f0xx_hal_gpio.h و انتخاب گزینه Go To Definition  به بدنه این تابع در فایل stm32f0xx_hal_gpio.c  هدایت شوید.

البته این تنها راه نیست و شما به جای انجام این کار می‌توانید مستقیماً به دنبال تعریف این تابع در فایل stm32f0xx_hal_gpio.c بگردید. در هر صورت از هر راهی که بروید هدف این است که به کد تصویر زیر برسید.

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

بعد از خواندن توضیحات بالا، باید کارکرد تابع زیر را بتوانید توضیح دهید.

;HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET)

این تابع پین PA0 را یک می‌کند و احتمالاً فهمیده‌اید که اگر آرگومان سوم را به GPIO_PIN_RESET تغییر دهیم آن را صفر می‌کند.

نکته‌ای که در استفاده از این تابع وجود دارد این است که می‌توان بر روی پین‌های یک پورت به طور هم‌زمان اعمال تغییر کرد. برای مثال در اینجا اگر قرار باشد پین‌های PA0 تا PA7 را یک کنیم لازم نیست برای تک تک این پین‌ها این تابع را فراخوانی کنیم. بلکه کافی است همانند زیر نام تعریف شده‌ی این پین‌‌ها را با یکدیگر OR کنیم و به عنوان آرگومان دوم این تابع به آن بدهیم.

   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                           |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);

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

 تابع Hal_Delay() برای ایجاد تاخیر در برنامه‌ها به کار می‌رود. عددی که به عنوان ورودی به این تابع می‌دهیم، مشخص کننده‌ی میزان تاخیر در واحد میلی‌ثانیه است. یعنی Hal_Delay(500) قرار است به ما 500 میلی ثانیه یا نیم ثانیه تاخیر بدهد.

نهایتاً با توضیحات بالا برنامه ما به شکل زیر در می‌آید.

بعد از نوشتن برنامه نوبت به این می‌رسد تا نتیجه را در عمل هم مشاهده کنیم. برای این منظور ابتدا برنامه را با زدن F7 کامپایل می‌کنیم. اگر مشکلی وجود نداشته باشد و به درستی برنامه را نوشته باشید، نرم‌افزار کیل، مشابه تصویر بالا گزارشی مبنی بر نداشتن خطا در کامپایل می‌دهد. پس از وصل کردن پایه‌های SWIO، SWCLK، GND و VCC از برد به پروگرامر و وصل کردن تغذیه برد، می‌توانید میکروکنترلر را پروگرام کنید و از نتیجه اولین برنامه خود لذت ببرید.

تمرین:

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

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

پی نوشت:

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

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

پانوشت:

  1. در اینجا 3.3 ولت.
  2. حجم سایزی که به طور رایگان کامپایل می‌کند، محدود به 32 کیلوبایت است.

3
Leave a Reply

avatar
2 Comment threads
1 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
3 Comment authors
adminmori77علی Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
علی
Guest
علی

عالییییییی بود عزیز خیلی کمکم کرد دستت درد نکنه ادامه بده

mori77
Guest
mori77

کارتون خیلی درسته. ادامه بدین لطف.اگه منابع اموزشی خوبی هم میشناسین معرفی کنین مثلا خودتون چه جوری st رو شروع کردین یا امبد کارکردین؟. واقعن جای این بحثا تو وب فارسی خالیه!(متاسفانه!)