ARM, STM32, آموزش, توصیه شده, دوره آموزشی STM32, مقاله های سیسوگ, میکروکنترلر

آموزش میکروکنترلر 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 قصد داریم تا وقفه‌های خارجی را آموزش دهیم. پس با سیسوگ همراه باشید.

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

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

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

  1. Avatar for hasan66647 hasan66647 گفت:

    سلام وقت بخیر صفحه ی مربوط به قسمت دوم آموزش نمیاد همش میره به صفحه آموزش STM32 با توابع LL قسمت سوم: STM32CubeMX Keil

    1. Avatar for Shadow Shadow گفت:

      سلام دوست عزیز
      مشکل برطرف شد.

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

    عالی
    ممنون از زحماتتون

    1. Avatar for Zeus ‌ Zeus ‌ گفت:

      متشکریم دوست عزیز

  3. Avatar for www.amiraliadibmehr www.amiraliadibmehr گفت:

    ممنون از آموزش عالی و خوبتون

  4. Avatar for امیرحسین باقری امیرحسین باقری گفت:

    سلام من چجوری میتونم یک تاخیر دقت میکرو ثانیه ای درست کنم؟

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

      بهترین و مطمئن ترین راه استفاده از تایمر است.

  5. Avatar for مجید مجید گفت:

    عالی عالی عالی

  6. Avatar for عالی عالی گفت:

    باسلام
    لطفا آموزش برنامه نویسی برد stm32f103 رو بدون استفاده از توابع HAL و با نرم افزار MikroCarm را تهیه کنید
    یا حداقل یک مرجع خوب در این زمینه معرفی کنید (آموزش با پروژه های عملی)
    پیشاپیش بسیار متشکر

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

      سلام دوست عزیز
      مدتی است که اموزش میکروکنترلر stm32 رو به شکل کاملا رجیستری در حال انتشار داریم برای دیدنش به لینک زیر مراجعه کنید
      https://sisoog.com/tag/%d8%a2%d9%85%d9%88%d8%b2%d8%b4-stm32-%d8%af%d8%b1-keil/
      در مورد نرم افزار MikroCarm هم فکر نمیکنم نیاز با آموزش خاصی باشه چون اینطور که از اسمش بر میآد یه کامپایلر باشه 🙂 ولی توصیه میکنم اگر میخواید به شکل حرفه ای کار کنید سراغ این مدل نرم افزارها نروید.

  7. Avatar for دهقانی دهقانی گفت:

    با سلام و خسته نباشید
    خروجی نرم افزار ایجاد پروژه من با آموزشها یکسان نیست
    امکانش هست پوشه پروژه سایت رو برای دانلود قرار بدین تا طبق سایت آموزشها رو‌ دنبال کرد

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

      سلام دوست عزیز
      منظورتون از این که خروجی ها یکسان نیست چیه ؟
      در واقع فکر میکنم توی ورژن های جدید keil یه مقداری این فلدر بندی ها فرق میکنه
      از چه ورژنی استفاده می کنید.

  8. Avatar for mohammad mohammad گفت:

    سلام.
    دستتون درد نکنه.واقعا عالی بود.
    سوالی که پیش میاد اینه که چون تو وقفه نمی تونیم از تاخیر استفاده کنیم .اگه بخوایم در وقفه,از تاخیر استفاده کنیم و تایمر systik رو هم غیرفعال نکنیم. چه کار می تونیم بکنیم؟
    منظور من راه حلی به غیر از حلقه for هست چون معمولا با این روش رابطه معینی با زمان تاخیر دردست نداریم.یعنی با نصف کردن متغیر حلقه زمان تاخیر نصف نمی شود.(تست کردم که میگم.)
    خیلی ممنون.

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

      سلام دوست عزیز
      خوب اول باید بگم اصلا منطقی نیست که توی روال اینتراپت بخواید تاخیر ایجاد کنید ؛ یعنی غیر اصولی است و حتی ممکنه عملکرد برنامه رو مختل کنه ! در واقع بهتره وقفه به روشی نوشته بشه که روال اون در سریعتری زمان ممکن تمام بشه – اگر لازمه پردازشی روی اطلاعات ورودی انجام بشه بهتره اونو توی بدنه برنامه اصلی انجام بدید !
      با این حال صرفا برای این که جواب سوالتون رو داده باشم عرض میکنم ؛ توی میکروکنترلر های ARM وقفه ها دارای الویت هستند برای ایجاد تاخیر دقیق میتونید یک تایمر رو با الویت بالاتر از وقفه ای که درش هستید ران کنید و روال وقفه رو پیاده سازی کنید.

  9. Avatar for morteza morteza گفت:

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

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

      سلام دوست عزیز ؛ لطف دارید
      خوب درواقع AFIO مخفف عبارت Alternate function I/O است. اگر دقت کرده باشید هر یک از پایه های میکروکنترلر علاوه بر این که میتوانند کار یه ورودی و خروجی ساده (جنرال IO) را انجام دهند قادرند عملکرد دیگری داشته باشند به عنوان نمونه برخی پایه ها می توانند به عنوان ورودی آنالوگ مورد استفاده قرار بگیرند و برخی به عنوان ورودی یا خروجی تایمر و …. ؛ این کاربرد دیگر جز ورودی و خروجی ساده را AFIO می گویند.

  10. Avatar for علیرضا علیرضا گفت:

    سلام دوستان اگر کسی اطلاع داره برای تاخیر میکروثانیه ای اعداد تابع delayنوشته شده رو چه طور تغییر بدم .
    arashmg1382@gmail.com
    لطفا اگر کسی میدونه کمک کنه چونکه واقعا گیر کردم نیاز به تاخیر میکرو ثانیه ای دارم .ممنون

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

      قایدتا باید هزاربرابر کمتر از میلی ثانیه ای باشه دیگه !
      بهترین راه استفاده از سیستم تایمر هست.

      1. Avatar for علیرضا علیرضا گفت:

        من تایمرام درگیرن نمیتونم ازشون ب عنوان تاخیر استفاده کنم ….تقسیم کلاک میگرو هم دقیق درنمیاد…

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

          معمولا از تایمر systick برای ایجاد تاخیر استفاده میشه
          و منطقی ترین راه هم همینه ؛ چون این تایمر داخل هسته پردازنده هست و هیچ خروجی یا ورودی رو کنترل نمیکنه و اصلا با همین هدف طراحی شده.

      2. Avatar for علیرضا علیرضا گفت:

        الان فرکانس کاری من8مگه …لطف میکنی اعدادی که توی تابعی که خودتون نوشتید رو به ازای این فرکانس برای یک میکرو ثانیه بدین بهم ..من خودم ک وارد کردم شاید اشتباه باشه یا هر چیزی خطا دارم

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

          توی میکروکنترلرهایی که از پایپ لاین استفاده میکنند ایجاد چنین حلقه هایی برای ایجاد تاخیر دقیق خیلی سخته !

  11. Avatar for نوید نوید گفت:

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

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

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

      1. Avatar for نوید نوید گفت:

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

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

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

  12. Avatar for محمد محمد گفت:

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

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

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

  13. Avatar for reza reza گفت:

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

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

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

  14. Avatar for Ali Ali گفت:

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

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

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

  15. Avatar for Arian Arian گفت:

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

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

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

      1. Avatar for سروش سروش گفت:

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

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

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

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

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