دوستان سلام! در قسمت قبلی آموزش میکروکنترلر STM32، با رجیسترهای GPIO آشنا شدیم. اکنون زمان آن رسیده که خودمان بهصورت عملی با رجیسترهای میکروکنترلر STM32 وارد کار شویم. در این قسمت از آموزش STM32 بااستفادهاز یک مثالِ عملی، نحوهیکار با GPIOها را فراخواهیمگرفت.
میکروکنترلر STM32 و مثال GPIO
فرضکنید در یک برنامه، خروجیای روی پین نهم پورت A لازم داریم تا بتوانیم ازطریق نوشتن ماکرو عملیاتهای مختلف ازجمله خاموشو روشنکردن و بالعکس و همچنین خواندن وضعیت خروجی را انجام بدهیم و همچنین بتوانیم از وضعیت این خروجی اطلاع حاصلکنیم. باید ماکروها را مطابق عکسزیر بنویسیم:
همانطورکه در عکس هم مشخص است، ما در ماکرو اول برای روشنکردن خروجی از رجیستر GPIOx->BSRR و در ماکرو دوم برای خاموشکردن همان خروجی از رجیستر GPIOx->BRR استفاده میکنیم. در ماکرو سوم برای بالعکسکردن خروجی موردنظر، از عملیات XOR بیتی استفاده کردهایم. همانطورکه میدانید، تنها زمانی خروجی گیت XOR یک میشود که فقط یکیاز ورودیها برابر یک باشد. برای درک بیشتر عملکرد گیت XOR به جدولزیر توجهکنید:
اگر اینجا عملکرد گیت XOR را فراگرفته باشید، در قسمتهایدیگر برنامهنویسی برای شما کاربرد خواهد داشت. در ماکرو چهارم هم با خواندن از بیت متناظر با پین مشخص از رجیستر GPIOx->ODR میتوانیم متوجه وضعیت خروجی موردنظرمان شویم. اگر پایهای را بهصورت ورودی تعریف کردهبودیم، برای خواندن وضعیت آن پین از پورت مشخص باید از رجیستر GPIOx->IDR استفاده میکردیم. زمانیکه هنوز کتابخانه HAL مرسوم نبود، یک سری ماکرو بخصوص در بردهای آموزشی alientek برای خواندن و نوشتن مرسوم بود که البته تنها برای خانواده کرتکس ام تری بهبالا کاربرد داشت و برای سری کرتکس ام صفر کار نمیکرد. این ماکروها را میتوانید در عکس زیر ببینید:
*در کادر قرمز، ماکروهای کمکی برای انجام عملیات روی پورتهای میکرو مشخص شده است*
همانطورکه از کدها مشخص است، شکل استفادهاز ماکروها بسیارساده است. مثلاً با فرمان ” ;PAout(0)=1″ میتوانید وضعیت پین صفرم از پورت A (درصورتیکه قبلاً بهعنوان خروجی تعیین شده باشد) را یک منطقی کنید و یا با فرمانزیر چککنید که اگر پین پنجم از پورت E (درصورتیکه قبلاً بهصورت ورودی تعیین شده باشد) یک بود، عملیات دلخواه انجام شود:
if((PEin(n))
تا اینجا، کار با پورتهای میکروکنترولر را چه با فرمانهای کتابخانه HAL و چه ازطریق کار با خود رجیسترها و ماکرونویسی فراگرفتیم. فرضکنیم میخواهیم برنامه یک چشمکزن ساده را بنویسیم؛ غیراز کار با پورتها مهمترین فرمانی که موردنیاز ماست، فرمان تأخیر است. در کتابخانه HAL برای این امر یک فرمان ساده درنظر گرفتهشدهاست:
HAL_Delay(__IO uint32_t Delay)
همانطورکه از شکل فرمان مشخص است، آرگومانی داریم که میتوانیم از آن برای واردکردن زمان موردنظر برمبنای میلیثانیه استفادهکنیم. مثلاً ما با فرمان “;(HAL_Delay(1000” یک تأخیر ۱۰۰۰میلیثانیه (یک ثانیه) ایجاد میکنیم. ولی ما بهدلایل مختلف قصدنداریم از این فرمان در برنامه خود استفاده کنیم. برای فهمیدن این دلایل ابتدا باید به محتوای این تابع پیببریم. پس نخست به عکسزیر که محتوای این تابع را در برگه stm32f1xx_hal.c نشان میدهد، توجهکنید:
*کادر سبز: مقداردهی اولیه*
*کادر قرمز: یکی به مقدار زمان اضافه میکند تا حداقل زمان وارد شده باشد*
*کادر بنفش: تا رسیدن به زمان موردنظر در این حلقه باقی میماند*
قبلاز توضیح این تابع باید متوجهباشیم که ما برای درستکردن زمانهای موردنظرمان همیشه یک تایمر درحال شمارش داریم. مطابق عکسزیر این تایمر بهصورت پیشفرض در داخل CubeMX همان systick است:
و هر یک میلیثانیه یکبار، وارد یک تابع وقفه در برگه stm32f1xx_it.c میشود و هردفعه ازطریق تابع، یکی به مقدار متغیری بهنام uwTick اضافه میکند. برای فهم بیشتر به عکسهایزیر توجهکنید:
*کادر قرمز: تابعی که هر یک میلیثانیه به آن وارد میشود. کادر بنفش: محل افزایش متغیر موردنظر*
حال طبق شکلزیر، جمله “;()HAL_IncTick” را انتخاب میکنیم و سپس روی آن کلیک راست میکنیم تا منویزیر ظاهر شود:
حال با کلیککردن روی گزینه مشخصشده، به محل متن تابع موردنظر میرویم (توجهداشتهباشید این روش برای همه توابع و ماکروها ثابت است و از اینطریق ما میتوانیم متن توابع و ماکروهای خود را پیداکنیم که در ادامه آموزشها از این روش زیاد استفاده خواهیمکرد. البته توجهداشتهباشید قبلاز این عملیات حتماً باید یکبار برنامه را کامپایل کنیم).
همانطورکه میبینید در این تابع متغیر uwTick هر یک میلیثانیه یکی به مقدارش اضافه میشود. پس درنتیجه متوجهشدیم که همیشه ما در برنامه تابعی داریم که هر یک میلیثانیه به آن مراجعه میشود و برای محاسبه زمانهای تأخیر مورداستفاده قرار میگیرد. اما این روش برای تولید تأخیر چند عیب اساسی دارد: اول اینکه ما همیشه در برنامه تابع وقفهای داریم که هر یک میلیثانیه روند برنامه را قطع میکند و این خود باعث مشغولیت بیمورد CPU میکروکنترلر است و دوم اینکه ما در داخل توابع وقفه نمیتوانیم از تابع “HAL_Delay” استفاده کنیم؛ بهایندلیل که باید از تابع وقفه بیرون بیاییم تا برنامه بتواند به تابع وقفه “SysTick_Handler” برود تا یکییکی به مقدار متغیر uwTick اضافهکند تا به مدتزمان مشخص برسیم. درنتیجه وقتی ما در تابع وقفه خود از اینگونه تأخیر استفادهکنیم برنامه قفل میکند. حال باید این مشکل را در چند مرحله حل کنیم. اول در ابتدای برنامه با فرمان “;()HAL_SuspendTick” وقفه SysTick_Handler را غیرفعال میکنیم و در مرحلهبعد خودمان بهصورت دستی فرمان تأخیر را مینویسیم. متن تابع تأخیر میتواند به شکلزیر نوشتهشود:
این تابع مربوط به کرتکس ام تری و فرکانس ۷۲مگاهرتز است. توجهداشتهباشید دقت این تأخیر درنهایت بهصورت دستی و بااستفادهاز دیجیتال آنالایزر بهدستآمده و با فرمول، دقت مناسب بهدست نخواهد آمد. برای تأخیر میکروثانیه هم میتوانید از تابعی مشابه این تابع و با تغییر اعداد استفاده کنید. نکته اینکه SystemCoreClock در این تابع همان فرکانس میکروکنترولر یعنی ۷۲۰۰۰۰۰۰ است:
نکته: بهدلیل ماسکپذیربودن وقفهها در میکروکنترلر STM32، میتوان وقفه تایمر systik در اولویت بالاتری نسبتبه دیگر وقفهها قرار داد و این یکیاز راههای استفاده از تابع تأخیر در وقفه است.
در این قسمت از آموزش به تأخیر در برنامه پرداختیم و در قسمت بعدی آموزش STM32 قصد داریم تا وقفههای خارجی را آموزش دهیم.
با ما همراه باشید.
منبع: سیسوگ