ARM, STM32, آموزش, توصیه شده, مقاله

آموزش میکروکنترلر STM32 و چالش تأخیر (قسمت سوم)

دوستان سلام!

در قسمت قبلی آموزش‌ میکروکنترلر STM32  در سیسوگ، با رجیسترهای GPIO آشنا شدیم. اکنون زمان آن رسیده که خودمان به‌صورت عملی با رجیستر‌های میکروکنترلر STM32 وارد کار شویم.

در این قسمت از آموزش STM32 با استفاده از یک مثالِ عملی، نحوه‌ی کار با GPIOها را فرا خواهیم گرفت.

میکروکنترلر STM32 و مثال GPIO:

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

 

تعریف پورت های میکروکنترلر STM32

 

همان‌طور که در عکس هم مشخص است، ما در ماکرو اول برای روشن کردن خروجی از رجیستر GPIOx->BSRR و در ماکرو دوم برای خاموش کردن همان خروجی از رجیستر GPIOx->BRR استفاده می‌کنیم. در ماکرو سوم برای بالعکس کردن خروجی موردنظر، از عملیات XOR بیتی استفاده کرده‌ایم. همان‌طور که می‌دانید، تنها زمانی خروجی گیت XOR یک می‌شود که فقط یکی از ورودی‌ها برابر یک باشد. برای درک بیشتر عملکرد گیت XOR به جدول زیر توجه کنید:

 

نحوه عملکرد گیت XOR

 

اگر اینجا عملکرد گیت XOR را فراگرفته باشید، در قسمت‌های دیگر برنامه‌نویسی برای شما کاربرد خواهد داشت. در ماکرو چهارم هم با خواندن از بیت متناظر با پین مشخص از رجیستر GPIOx->ODR می‌توانیم متوجه وضعیت خروجی موردنظرمان شویم. اگر پایه‌ای را به‌صورت ورودی تعریف کرده بودیم، برای خواندن وضعیت آن پین از پورت مشخص باید از رجیستر GPIOx->IDR استفاده می‌کردیم.

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

 

در کادر قرمز ماکروهای کمکی برای انجام عملیات روی پورت‌های میکروکنترلر STM32 مشخص شده است.

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

 

همان‌طور که از کدها مشخص است، شکل استفاده از ماکروها بسیار ساده است. مثلاً با فرمان ” ;PAout(0)=1″ می‌توانید وضعیت پین صفرم از پورت A (درصورتی‌که قبلاً به‌عنوان خروجی تعیین شده باشد) را یک منطقی کنید و یا با فرمان زیر چک کنید که اگر پین پنجم از پورت E (درصورتی‌که قبلاً به‌صورت ورودی تعیین شده باشد) یک بود، عملیات دلخواه انجام شود:

 

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

 

همان‌طور که از شکل فرمان مشخص است، آرگومانی داریم که می‌توانیم از آن برای وارد کردن زمان موردنظر بر مبنای میلی‌ثانیه استفاده کنیم. مثلاً ما با فرمان “;(HAL_Delay(1000” یک تأخیر 1000 میلی‌ثانیه (یک ثانیه) ایجاد می‌کنیم. ولی ما به دلایل مختلف قصد نداریم از این فرمان در برنامه خود استفاده کنیم. برای فهمیدن این دلایل ابتدا باید به محتوای این تابع پی ببریم. پس نخست به عکس زیر که محتوای این تابع را در برگه stm32f1xx_hal.c نشان می‌دهد، توجه کنید:

 

محتوای تابع Delay در کتابخانه HAL میکروکنترلر STM32

کادر سبز: مقداردهی اولیه. کادر قرمز: یکی به مقدار زمان اضافه می‌کند تا حداقل زمان وارد شده باشد. کادر بنفش: تا رسیدن به زمان موردنظر در این حلقه باقی می‌ماند.

 

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

 

تنظیم تایم سورس در نرم افزار CubeMX مرویط به میکروکنترلر STM32

 

و هر یک میلی‌ثانیه یک‌بار، وارد یک تابع وقفه در برگه stm32f1xx_it.c می‌شود و هر دفعه از طریق تابع، یکی به مقدار متغیری به نام uwTick  اضافه می‌کند. برای فهم بیشتر به عکس‌های زیر توجه کنید:

 

آپدیت نرخ زمان

کادر قرمز: تابعی که هر یک میلی‌ثانیه به آن وارد می‌شود. کادر بنفش: محل افزایش متغیر موردنظر

 

حال طبق شکل زیر، جمله  “;()HAL_IncTick” را انتخاب می‌کنیم و سپس روی آن کلیک راست می‌کنیم تا منوی زیر ظاهر شود:

 

پرش به محل تعریف یک تایع در نرم افزار Keil

 

 

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

 

 

همان‌طور که می‌بینید در این تابع متغیر uwTick هر یک میلی‌ثانیه یکی به مقدارش اضافه می‌شود. پس در نتیجه متوجه شدیم که همیشه ما در برنامه تابعی داریم که هر یک میلی‌ثانیه به آن مراجعه می‌شود و برای محاسبه زمان‌های تأخیر مورداستفاده قرار می‌گیرد. اما این روش برای تولید تأخیر چند عیب اساسی دارد: اول اینکه ما همیشه در برنامه تابع وقفه‌ای داریم که هر یک میلی‌ثانیه روند برنامه را قطع می‌کند و این خود باعث مشغولیت بی‌مورد CPU میکروکنترلر است و دوم اینکه ما در داخل توابع وقفه نمی‌توانیم از تابع “HAL_Delay” استفاده کنیم؛ به این دلیل که باید از تابع وقفه بیرون بیاییم تا برنامه بتواند به تابع وقفه “SysTick_Handler” برود تا یکی‌یکی به مقدار متغیر uwTick اضافه کند تا به مدت‌زمان مشخص برسیم. در نتیجه وقتی ما در تابع وقفه خود از این‌گونه تأخیر استفاده کنیم برنامه قفل می‌کند.

حال باید این مشکل را در چند مرحله حل کنیم. اول در ابتدای برنامه با فرمان “;()HAL_SuspendTick” وقفه SysTick_Handler را غیرفعال می‌کنیم و در مرحله بعد خودمان به‌صورت دستی فرمان تأخیر را می‌نویسیم. متن تابع تأخیر می‌تواند به شکل زیر نوشته شود:

 

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

 

این تابع مربوط به کرتکس ام تری و فرکانس 72 مگاهرتز است. توجه داشته باشید دقت این تأخیر در نهایت به‌صورت دستی و با استفاده از دیجیتال آنالایزر به دست آمده و با فرمول، دقت مناسب به دست نخواهد آمد. برای تأخیر میکروثانیه هم می‌توانید از تابعی مشابه این تابع و با تغییر اعداد استفاده کنید. نکته اینکه SystemCoreClock در این تابع همان فرکانس میکروکنترولر یعنی 72000000 است:

 

 

 

نکته ، به دلیل ماسک پذیر بودن وقفه‌ها در میکروکنترلر STM32، می‌توان وقفه تایمر systik در اولویت بالاتری نسبت به دیگر وقفه‌ها قرار داد و این یکی از راه‌های استفاده از تابع تأخیر در وقفه است.

در این قسمت از آموزش به تأخیر در برنامه پرداختیم و در قسمت بعدی آموزش STM32 قصد داریم تا وقفه‌های خارجی را آموزش دهیم. پس با سیسوگ همراه باشید.

نوشته های مشابه

14 دیدگاه در “آموزش میکروکنترلر STM32 و چالش تأخیر (قسمت سوم)

  1. نوید گفت:

    سلام مهندس تشکر از شما
    همه چی داشت خوب پیش میرفت که رسیدیم به عکس یکی مونده به اخری…همون جا که دستور “;()HAL_SuspendTick معرفی شد
    این عکس دقیقا کجاست؟؟
    منظورم اینه که تابعی که توشه و از خط 416 شروع میشه در کدوم سر برگ میشه پیدا کرد؟؟
    بسیار ممنون

    1. زئوس Zeus زئوس Zeus گفت:

      سلام دوست عزیز ؛ این خط ها به صورتی دستی توسط نویسنده مقاله اضافه شده اند ، توصیه میکنم از توابع تاخیر cmsis استفاده کنید اگر قرار نیست از تاخیر در تایمر استفاده کنید.

      1. نوید گفت:

        ممنونم
        میشه یه توضیح کلی ازتوابع cmsis و hal بدین؟؟

        1. زئوس Zeus زئوس Zeus گفت:

          ببینید دوست عزیز ، توابع Cmsis توابعی است که توسط شرکت ARM برای ساده کردن استفاده از این پردازنده ارائه شده ؛ خوب حالا این یعنی چی ؟ ؛ این به این معنیه که وقتی شما از نوابع cmsis استفاده می کنید ، مهم نیست که از چه مدل پردازنده ای دارید استفاده می کنید ، محصول شرکت nxp هست یا st یا حتی ti مهم اینه که arm باشه ، تمام توابع cmsis به درستی کار خواهند کرد. این باعث میشه که به سادگی بتونید کدتون رو از یک پردازنده به پردازنده دیگه منتقل کنید که واقعا ایده جالبی است
          اما hal ، یه سری کتابخانه است که شرکت st ارائه کرده برای سهولت کار با میکروکنترلر های خودش ؛ صرفا همین

  2. محمد گفت:

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

    1. زئوس Zeus زئوس Zeus گفت:

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

  3. reza گفت:

    نمیدونم چطوری باید از مطالب مفیدتون تشکر کنم که بدون چشم داشت مالی ….
    یه سوال : بعضی از توابع اولشون __ دارن معنیش چیه ؟

    1. زئوس Zeus زئوس Zeus گفت:

      خواهش میکنم دوست عزیز واقعا باعث خوشحالی و دل گرمی ما میشه این چنین کامنت هایی
      __ تنها یک جور نام گذاریه که لایه های پایین استفاده میشه

  4. Ali گفت:

    با سلام و تشکر بابت آموزش های خوبتون .بنده دنبال یه کتابآموزشی کامل یاخوب برای میکرو های stm32 هستم .شما چه کتاب هایی رو توصیه می کنید ؟.با تشکر

    1. زئوس Zeus زئوس Zeus گفت:

      حقیقت اینه که به شخصه این میکروکنترلر و هیچ میکروکنترلر دیگری رو با استفاده از کتاب یاد نگرفتم و نمی دونم کتاب خوبی در این زمینه هست یا نه !
      وقتی که بخوام کتاب خوبی رو معرفی کنم حداقل باید یک بار اون رو خوانده باشم که بتونم توصیه اش کنم اگر نه کتاب فکر میکنم الان دیگه بسیار باشه
      روشی که من استفاده کردم ، اینترنت + منوال های ارائه شده توسط خود شرکت بوده است

  5. Arian گفت:

    واقعا فوق العاده بود
    ممنون بابت اشتراک گزاری اطلاعات ارزشمندتان
    فقط بنده یک سوال داشتم اینکه من تازه شروع به یادگیری میکرو های ARM کردم ولی به AVR و PIC مسلطم برامن سواله که نوع متغیری مثلا unit32_t در این میکرو وجود دارد یا در تمام میکروکنترلر های 32 بیتی و اینکه یک مقدار در رابطه با انوع متغیر با تغیر میکرو میخواستم بدونم

    1. زئوس Zeus زئوس Zeus گفت:

      خواهش میکنم دوست عزیز ، خوشحالیم که این مطالب مورد توجه قرار گرفته است
      ببینید در واقع استفاده از uint32_t یا انواع مشابه در واقع یک جور نام گذاری استاندارد برای جلوگیری از سردرگمی برنامه نویس است ، بذارید با مثالی مساله رو روشن کنم ، وقتی شما از متغییر int روی میکروی 8 بیتی مثل AVR استفاده می کنید ، در واقع متغیر شما یک متغییر دو بایتی خواهد بود که تا مقدار 65535 رو بیشتر نمی تونه ذخیره کنه ولی وقتی از همین متغیر یعنی int روی یک میکروی 32 بیتی استفاده می کنید کامپایلر اونو یک متغییر 4 بایتی در نظر میگیره که قادره تا 4294967295 رو ذخیره کنه ! پس میبیند که چقدر این مساله مهمه مخصوصا وقتی یه کتابخونه استاندارد داشته باشید که بخواید هم روی AVR ارش استفاده کنید هم روی ARM !
      برای رفع این مشکل اومدن هدر stdint.h رو اضافه کردن که وقتی جایی متغیر دو بایتی خواستید از uint16_t استفاده کنید بعد دیگه فرقی نکنه نوع کامپایلر شما چی هست و میکرو چند بیتی ، این نوع متغیر 2 بایت حافظه یا 16 بیت رو در اختیار شما قرار خواهد داد.

      1. سروش گفت:

        با سلام و خسته نباشید خدمت جناب زئوس گرامی
        خیلی وقته دارم دنبال مفهوم uint32_t و … می گردم . خیلی جاها مطلب خوندم و از خیلیا پرسیدم ولی هیچ وقت درست نفهمیدم .
        یه دنیا ممنون توضیحتون فوق العاده بود .
        امیدوارم همواره موفق باشید.

        1. زئوس Zeus زئوس Zeus گفت:

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

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

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