دوستان سلام!
در قسمت قبلی آموزش میکروکنترلر 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 (درصورتیکه قبلاً بهصورت ورودی تعیین شده باشد) یک بود، عملیات دلخواه انجام شود:
1 | if((PEin(n)) |
تا اینجا، کار با پورتهای میکروکنترولر را چه با فرمانهای کتابخانه HAL و چه از طریق کار با خود رجیسترها و ماکرو نویسی فرا گرفتیم. فرض کنیم میخواهیم برنامه یک چشمکزن ساده را بنویسیم؛ غیر از کار با پورتها مهمترین فرمانی که موردنیاز ماست، فرمان تأخیر است. در کتابخانه HAL برای این امر یک فرمان ساده در نظر گرفته شده است:
1 | HAL_Delay(__IO uint32_t Delay) |
همانطور که از شکل فرمان مشخص است، آرگومانی داریم که میتوانیم از آن برای وارد کردن زمان موردنظر بر مبنای میلیثانیه استفاده کنیم. مثلاً ما با فرمان “;(HAL_Delay(1000” یک تأخیر 1000 میلیثانیه (یک ثانیه) ایجاد میکنیم. ولی ما به دلایل مختلف قصد نداریم از این فرمان در برنامه خود استفاده کنیم. برای فهمیدن این دلایل ابتدا باید به محتوای این تابع پی ببریم. پس نخست به عکس زیر که محتوای این تابع را در برگه stm32f1xx_hal.c نشان میدهد، توجه کنید:
قبل از توضیح این تابع باید متوجه باشیم که ما برای درست کردن زمانهای موردنظرمان همیشه یک تایمر در حال شمارش داریم. مطابق عکس زیر این تایمر بهصورت پیشفرض در داخل CubeMX همان systick است:
و هر یک میلیثانیه یکبار، وارد یک تابع وقفه در برگه stm32f1xx_it.c میشود و هر دفعه از طریق تابع، یکی به مقدار متغیری به نام uwTick اضافه میکند. برای فهم بیشتر به عکسهای زیر توجه کنید:
حال طبق شکل زیر، جمله “;()HAL_IncTick” را انتخاب میکنیم و سپس روی آن کلیک راست میکنیم تا منوی زیر ظاهر شود:
حال با کلیک کردن روی گزینه مشخصشده، به محل متن تابع موردنظر میرویم (توجه داشته باشید این روش برای همه توابع و ماکروها ثابت است و از این طریق ما میتوانیم متن توابع و ماکروهای خود را پیدا کنیم که در ادامه آموزشها از این روش زیاد استفاده خواهیم کرد. البته توجه داشته باشید قبل از این عملیات حتماً باید یکبار برنامه را کامپایل کنیم).
همانطور که میبینید در این تابع متغیر uwTick هر یک میلیثانیه یکی به مقدارش اضافه میشود. پس در نتیجه متوجه شدیم که همیشه ما در برنامه تابعی داریم که هر یک میلیثانیه به آن مراجعه میشود و برای محاسبه زمانهای تأخیر مورداستفاده قرار میگیرد. اما این روش برای تولید تأخیر چند عیب اساسی دارد: اول اینکه ما همیشه در برنامه تابع وقفهای داریم که هر یک میلیثانیه روند برنامه را قطع میکند و این خود باعث مشغولیت بیمورد CPU میکروکنترلر است و دوم اینکه ما در داخل توابع وقفه نمیتوانیم از تابع “HAL_Delay” استفاده کنیم؛ به این دلیل که باید از تابع وقفه بیرون بیاییم تا برنامه بتواند به تابع وقفه “SysTick_Handler” برود تا یکییکی به مقدار متغیر uwTick اضافه کند تا به مدتزمان مشخص برسیم. در نتیجه وقتی ما در تابع وقفه خود از اینگونه تأخیر استفاده کنیم برنامه قفل میکند.
حال باید این مشکل را در چند مرحله حل کنیم. اول در ابتدای برنامه با فرمان “;()HAL_SuspendTick” وقفه SysTick_Handler را غیرفعال میکنیم و در مرحله بعد خودمان بهصورت دستی فرمان تأخیر را مینویسیم. متن تابع تأخیر میتواند به شکل زیر نوشته شود:
این تابع مربوط به کرتکس ام تری و فرکانس 72 مگاهرتز است. توجه داشته باشید دقت این تأخیر در نهایت بهصورت دستی و با استفاده از دیجیتال آنالایزر به دست آمده و با فرمول، دقت مناسب به دست نخواهد آمد. برای تأخیر میکروثانیه هم میتوانید از تابعی مشابه این تابع و با تغییر اعداد استفاده کنید. نکته اینکه SystemCoreClock در این تابع همان فرکانس میکروکنترولر یعنی 72000000 است:
در این قسمت از آموزش به تأخیر در برنامه پرداختیم و در قسمت بعدی آموزش STM32 قصد داریم تا وقفههای خارجی را آموزش دهیم. پس با سیسوگ همراه باشید.
سلام وقت بخیر صفحه ی مربوط به قسمت دوم آموزش نمیاد همش میره به صفحه آموزش STM32 با توابع LL قسمت سوم: STM32CubeMX Keil
سلام دوست عزیز
مشکل برطرف شد.
عالی
ممنون از زحماتتون
متشکریم دوست عزیز
ممنون از آموزش عالی و خوبتون
سلام من چجوری میتونم یک تاخیر دقت میکرو ثانیه ای درست کنم؟
بهترین و مطمئن ترین راه استفاده از تایمر است.
عالی عالی عالی
باسلام
لطفا آموزش برنامه نویسی برد stm32f103 رو بدون استفاده از توابع HAL و با نرم افزار MikroCarm را تهیه کنید
یا حداقل یک مرجع خوب در این زمینه معرفی کنید (آموزش با پروژه های عملی)
پیشاپیش بسیار متشکر
سلام دوست عزیز
مدتی است که اموزش میکروکنترلر stm32 رو به شکل کاملا رجیستری در حال انتشار داریم برای دیدنش به لینک زیر مراجعه کنید
https://sisoog.com/tag/%d8%a2%d9%85%d9%88%d8%b2%d8%b4-stm32-%d8%af%d8%b1-keil/
در مورد نرم افزار MikroCarm هم فکر نمیکنم نیاز با آموزش خاصی باشه چون اینطور که از اسمش بر میآد یه کامپایلر باشه 🙂 ولی توصیه میکنم اگر میخواید به شکل حرفه ای کار کنید سراغ این مدل نرم افزارها نروید.
متشکرم
با سلام و خسته نباشید
خروجی نرم افزار ایجاد پروژه من با آموزشها یکسان نیست
امکانش هست پوشه پروژه سایت رو برای دانلود قرار بدین تا طبق سایت آموزشها رو دنبال کرد
سلام دوست عزیز
منظورتون از این که خروجی ها یکسان نیست چیه ؟
در واقع فکر میکنم توی ورژن های جدید keil یه مقداری این فلدر بندی ها فرق میکنه
از چه ورژنی استفاده می کنید.
سلام.
دستتون درد نکنه.واقعا عالی بود.
سوالی که پیش میاد اینه که چون تو وقفه نمی تونیم از تاخیر استفاده کنیم .اگه بخوایم در وقفه,از تاخیر استفاده کنیم و تایمر systik رو هم غیرفعال نکنیم. چه کار می تونیم بکنیم؟
منظور من راه حلی به غیر از حلقه for هست چون معمولا با این روش رابطه معینی با زمان تاخیر دردست نداریم.یعنی با نصف کردن متغیر حلقه زمان تاخیر نصف نمی شود.(تست کردم که میگم.)
خیلی ممنون.
سلام دوست عزیز
خوب اول باید بگم اصلا منطقی نیست که توی روال اینتراپت بخواید تاخیر ایجاد کنید ؛ یعنی غیر اصولی است و حتی ممکنه عملکرد برنامه رو مختل کنه ! در واقع بهتره وقفه به روشی نوشته بشه که روال اون در سریعتری زمان ممکن تمام بشه – اگر لازمه پردازشی روی اطلاعات ورودی انجام بشه بهتره اونو توی بدنه برنامه اصلی انجام بدید !
با این حال صرفا برای این که جواب سوالتون رو داده باشم عرض میکنم ؛ توی میکروکنترلر های ARM وقفه ها دارای الویت هستند برای ایجاد تاخیر دقیق میتونید یک تایمر رو با الویت بالاتر از وقفه ای که درش هستید ران کنید و روال وقفه رو پیاده سازی کنید.
سلام.با تشکر از آموزش های بسیار خوب و همچنین سایت جامع و عالی که دارین.
یک سوال درباره بحث GPIO داشتم.AFIO که در کنار GPIO در رفرنس منوال میاد چی هست؟چی فرقی با GPIO داره؟چه کاربردی داره؟
درباره اش تحقیق کردم ولی به نتیجه جالبی نرسیدم.از رفرنس منوال هم یک چیزایی خوندم ولی چیزی دست گیرم نشد.گفتم از شما بپرسم شاید بتونید کمک کنید؟ببخشید اگر سوال خارج از بحثه.ممنون.
سلام دوست عزیز ؛ لطف دارید
خوب درواقع AFIO مخفف عبارت Alternate function I/O است. اگر دقت کرده باشید هر یک از پایه های میکروکنترلر علاوه بر این که میتوانند کار یه ورودی و خروجی ساده (جنرال IO) را انجام دهند قادرند عملکرد دیگری داشته باشند به عنوان نمونه برخی پایه ها می توانند به عنوان ورودی آنالوگ مورد استفاده قرار بگیرند و برخی به عنوان ورودی یا خروجی تایمر و …. ؛ این کاربرد دیگر جز ورودی و خروجی ساده را AFIO می گویند.
سلام دوستان اگر کسی اطلاع داره برای تاخیر میکروثانیه ای اعداد تابع delayنوشته شده رو چه طور تغییر بدم .
arashmg1382@gmail.com
لطفا اگر کسی میدونه کمک کنه چونکه واقعا گیر کردم نیاز به تاخیر میکرو ثانیه ای دارم .ممنون
قایدتا باید هزاربرابر کمتر از میلی ثانیه ای باشه دیگه !
بهترین راه استفاده از سیستم تایمر هست.
من تایمرام درگیرن نمیتونم ازشون ب عنوان تاخیر استفاده کنم ….تقسیم کلاک میگرو هم دقیق درنمیاد…
معمولا از تایمر systick برای ایجاد تاخیر استفاده میشه
و منطقی ترین راه هم همینه ؛ چون این تایمر داخل هسته پردازنده هست و هیچ خروجی یا ورودی رو کنترل نمیکنه و اصلا با همین هدف طراحی شده.
الان فرکانس کاری من8مگه …لطف میکنی اعدادی که توی تابعی که خودتون نوشتید رو به ازای این فرکانس برای یک میکرو ثانیه بدین بهم ..من خودم ک وارد کردم شاید اشتباه باشه یا هر چیزی خطا دارم
توی میکروکنترلرهایی که از پایپ لاین استفاده میکنند ایجاد چنین حلقه هایی برای ایجاد تاخیر دقیق خیلی سخته !
سلام مهندس تشکر از شما
همه چی داشت خوب پیش میرفت که رسیدیم به عکس یکی مونده به اخری…همون جا که دستور “;()HAL_SuspendTick معرفی شد
این عکس دقیقا کجاست؟؟
منظورم اینه که تابعی که توشه و از خط 416 شروع میشه در کدوم سر برگ میشه پیدا کرد؟؟
بسیار ممنون
سلام دوست عزیز ؛ این خط ها به صورتی دستی توسط نویسنده مقاله اضافه شده اند ، توصیه میکنم از توابع تاخیر cmsis استفاده کنید اگر قرار نیست از تاخیر در تایمر استفاده کنید.
ممنونم
میشه یه توضیح کلی ازتوابع cmsis و hal بدین؟؟
ببینید دوست عزیز ، توابع Cmsis توابعی است که توسط شرکت ARM برای ساده کردن استفاده از این پردازنده ارائه شده ؛ خوب حالا این یعنی چی ؟ ؛ این به این معنیه که وقتی شما از نوابع cmsis استفاده می کنید ، مهم نیست که از چه مدل پردازنده ای دارید استفاده می کنید ، محصول شرکت nxp هست یا st یا حتی ti مهم اینه که arm باشه ، تمام توابع cmsis به درستی کار خواهند کرد. این باعث میشه که به سادگی بتونید کدتون رو از یک پردازنده به پردازنده دیگه منتقل کنید که واقعا ایده جالبی است
اما hal ، یه سری کتابخانه است که شرکت st ارائه کرده برای سهولت کار با میکروکنترلر های خودش ؛ صرفا همین
سلام خسته نباشید .
یک سوال داشتم چرا اینقدر تاکید میشه که بعضی جا ها کتابخانه ها را تغییر دهیم یا از توابع خود HAL استفاده نکنیم یا مستقیم با رجیستر کار کنیم ؟؟
تشکر
سلام دوست عزیز
من فکر میکنم دلیل نویسنده مقاله این بوده که احتمالا میخواسته از تیک تایمر در جای دیگری استفاده کنه ، برای همین دست به چنین اقدامی زده
یا این که قصد داشته از تاخیر در وقفه هم استفاده کنه ، هر چند که این کار چندان اصولی نیست
نمیدونم چطوری باید از مطالب مفیدتون تشکر کنم که بدون چشم داشت مالی ….
یه سوال : بعضی از توابع اولشون __ دارن معنیش چیه ؟
خواهش میکنم دوست عزیز واقعا باعث خوشحالی و دل گرمی ما میشه این چنین کامنت هایی
__ تنها یک جور نام گذاریه که لایه های پایین استفاده میشه
با سلام و تشکر بابت آموزش های خوبتون .بنده دنبال یه کتابآموزشی کامل یاخوب برای میکرو های stm32 هستم .شما چه کتاب هایی رو توصیه می کنید ؟.با تشکر
حقیقت اینه که به شخصه این میکروکنترلر و هیچ میکروکنترلر دیگری رو با استفاده از کتاب یاد نگرفتم و نمی دونم کتاب خوبی در این زمینه هست یا نه !
وقتی که بخوام کتاب خوبی رو معرفی کنم حداقل باید یک بار اون رو خوانده باشم که بتونم توصیه اش کنم اگر نه کتاب فکر میکنم الان دیگه بسیار باشه
روشی که من استفاده کردم ، اینترنت + منوال های ارائه شده توسط خود شرکت بوده است
واقعا فوق العاده بود
ممنون بابت اشتراک گزاری اطلاعات ارزشمندتان
فقط بنده یک سوال داشتم اینکه من تازه شروع به یادگیری میکرو های ARM کردم ولی به AVR و PIC مسلطم برامن سواله که نوع متغیری مثلا unit32_t در این میکرو وجود دارد یا در تمام میکروکنترلر های 32 بیتی و اینکه یک مقدار در رابطه با انوع متغیر با تغیر میکرو میخواستم بدونم
خواهش میکنم دوست عزیز ، خوشحالیم که این مطالب مورد توجه قرار گرفته است
ببینید در واقع استفاده از uint32_t یا انواع مشابه در واقع یک جور نام گذاری استاندارد برای جلوگیری از سردرگمی برنامه نویس است ، بذارید با مثالی مساله رو روشن کنم ، وقتی شما از متغییر int روی میکروی 8 بیتی مثل AVR استفاده می کنید ، در واقع متغیر شما یک متغییر دو بایتی خواهد بود که تا مقدار 65535 رو بیشتر نمی تونه ذخیره کنه ولی وقتی از همین متغیر یعنی int روی یک میکروی 32 بیتی استفاده می کنید کامپایلر اونو یک متغییر 4 بایتی در نظر میگیره که قادره تا 4294967295 رو ذخیره کنه ! پس میبیند که چقدر این مساله مهمه مخصوصا وقتی یه کتابخونه استاندارد داشته باشید که بخواید هم روی AVR ارش استفاده کنید هم روی ARM !
برای رفع این مشکل اومدن هدر stdint.h رو اضافه کردن که وقتی جایی متغیر دو بایتی خواستید از uint16_t استفاده کنید بعد دیگه فرقی نکنه نوع کامپایلر شما چی هست و میکرو چند بیتی ، این نوع متغیر 2 بایت حافظه یا 16 بیت رو در اختیار شما قرار خواهد داد.
با سلام و خسته نباشید خدمت جناب زئوس گرامی
خیلی وقته دارم دنبال مفهوم uint32_t و … می گردم . خیلی جاها مطلب خوندم و از خیلیا پرسیدم ولی هیچ وقت درست نفهمیدم .
یه دنیا ممنون توضیحتون فوق العاده بود .
امیدوارم همواره موفق باشید.
سلام و درود خدمت شما دوست عزیز
خواهش میکنم کاری نکردم 🙂