آموزش STM32 راه اندازی وقفه خارجی EXTI

آموزش STM32 راه اندازی وقفه خارجی EXTI
در این آموزش یاد می گیریم چطور از وقفه های خارجی EXTI در میکروکنترلر STM32 استفاده کنیم. در بخش قبلی با راه اندازی واحد سریال UART آشنا شدیم. برای دیدن مقاله قبلی اینجا کلیک کنید.
اگر با محیط STM32cubeMX آشنایی ندارید، اینجا کلیک کنید.
اگر اولین بار است که از میکرو STM32 استفاده می کنید، اینجا کلیک کنید که یک شروع سریع داشته باشید.
پروژه عملی
در این پروژه از برد mini STM32 استفاده شده است که بر مبنای میکروکنترلر STM32f103c8t می باشد. پایه ی PB5 را به صورت وقفه خارجی پیکربندی می کنیم. با یک مقاومت پایین کش Pull down سطح پیشفرض این پایه را صفر منطقی در نظر می گیریم. با آمدن لبه ی بالا رونده بر روی این پایه وقفه عمل خواهد کرد.
زمانی که وقفه تشخیط داده شد، یک فلگ را تنظیم می کنیم. سپس در روتین برنامه در صورتی که وقفه رخ داده بود، LED متصل به پایه ی PC13 یک بار چشمک می زند. به صورت Built-in یک LED بر روی برد mini
STM32 متصل به این پایه وجود دارد.
تنظیمات در محیط cubeMX
برای آماده سازی پروژه از محیط cubeMX استفاده می کنیم.
- مطابق تصویر زیر پایه ی PB5 را به صورت وقفه خارجی EXTI تنظیم می کنیم.
- پایه ی PC13 را به صورت خروجی GPIO OUTPUT تنظیم می کنیم.
- همینطور به منظور پروگرام و دیباگ برد، بخش Debug را به صورت Serial Wire تنظیم می کنیم.
در بخش GPIO تنظیمات بیشتر برای وقفه خارجی را مطابق تصویر زیر انجام می دهیم.
- پایه ی ورودی را حساس به لبه ی پایین رونده در نظر می گیریم.
- حالت پیش فرض پایه ی وقفه خارجی با بک مقاومت خارجی Pull down صفر در نظر می گیریم. اگر نمی خواهید از مقاومت خارجی استفاده کنید، در این بخش بجای No pull-up and no pull-down می توانید از گزینه ی pull-down نیز استفاده کنید. در این صورت مقاومت داخلی میکرو به صورت پایین کش برای این پایه فعال خواهد شد. برای پروژه های واقعی حتما از مقاومت ها خارجی استفاده کنید!
مطابق تصویر زیر وقفه EXTI line[9-5] را تیک می زنیم که فعال شود. برای این منظور به تپ NVIC مطابق تصویر رجوع می کنیم.
حال که کار تنظیمات و پیکربندی اولیه ی پروژه تمام شد، از محیط cubeMX خروجی می گیریم. در این آموزش از محیط Keil استفاده می کنیم. اما شما برای هر IDE دیگری می توانید خروجی بگیرید، روال راه اندازی تفاوتی ندارد.
درک روال EXTI در HAL
از آنجایی که از سبک HAL استفاده می کنیم، نیاز است که ساختار آن را بشناسیم. وقفه ها در قایل stm32f1xx_it.c وجود دارند و روال تمام وقفه های فعال را در این بخش می توانید مشاهده کنید.
بخش مورد نظر ما در این آموزش، روال روتین وقفه خارجی است. روتین زیر که مربوط به روال وقفه خارجی می شود را در فایل یاد شده می توانید پیدا کنید.
void EXTI9_5_IRQHandler(void) { /* USER CODE BEGIN EXTI9_5_IRQn 0 */ /* USER CODE END EXTI9_5_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5); /* USER CODE BEGIN EXTI9_5_IRQn 1 */ /* USER CODE END EXTI9_5_IRQn 1 */ }
همانطور که مشاهده می شود در این روال یک تابع به نام HAL_GPIO_EXTI_IRQHandler فراخوانی می شود. اگر این تابع را با زدن دکمه F12 رهگیری کنیم به پیاده سازی این تابع در فایل stm32f1xx_hal_gpio.c می رسیم که مربوط به درایورهای HAL است. توجه کنید، که فایل های درایور را نباید بی دلیل دستکاری کنید، تنها دلیل برای دستکاری فایل های درایور وجود باگ است!
به این ترتیب روتین زیر را مشاهده می کنید که یک تابع را به صورت Call back فراخوانی می کند.
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); HAL_GPIO_EXTI_Callback(GPIO_Pin); } }
در این روتین تابع HAL_GPIO_EXTI_Callback را می بینیم که به صورت weak تعریف شده است. روتین آن در همین فایل پیاده سازی شده است و به صورت زیر است.
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { /* Prevent unused argument(s) compilation warning */ UNUSED(GPIO_Pin); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_GPIO_EXTI_Callback could be implemented in the user file */ }
هیچ چیز پیچیده ای وجود نداره!
خبر خوب این هست توابعی که به صورت weak تعریف شده اند، توابعی هستند که کاربر که ما باشیم می توانیم روتین آن ها را مجدد با همین نام پیاده سازی کنیم. حالا که متوجه شدید ساختار به چه صورت است بیاید، روتین جایگزین این تابع را در main.c تعریف کنیم.
شروع برنامه نویسی
بسیار خوب بلاخره به مرحله جذاب ماجرا می رسیم، آستین ها را بالا بزنیم و کار را شروع کنیم. به فایل main.c بر می گردیم. یادتون هست پروژه چی بود؟ اگر نه یک بار مرور کنید و برگردید.
بسیار عالی، یک فلگ تعریف می کنیم با نام btn_event_f که مقدار اولیه ی false و یا 0 دارد. اگر وقفه رخ دهد این متغییر را در روتین وقفه true و یا 1 تنظیم می کنیم.
توجه
- این فلگ باید به صورت Global تعریف شود.
- در صورتی که از مود های بهینه سازی کامپایلر بخواهید استفاده کنید، برای اینکه برنامه درست کار کند باید این متغییر را به صورت volatile تعریف کنید.
در ادامه روتین روال وقفه را در تابع call back تعریف می کنیم، پس کدهای زیر را به main.c اضافه می کنیم.
volatile uint8_t btn_event_f = false; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_5) { btn_event_f = true; } }
در حلقه ی بینهایت super loop درون تابع main ، کد زیر را قرار می دهیم.
if(btn_event_f == true) { btn_event_f = false; HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); }
همانطور که مشخص است قرار بود در صورتی که فلگ set شده باشد، ادامه برنامه را main برنامه پیگیری کنیم. روش استفاده از فلگ یک روش موثر برای استفاده از وقفه هاست چرا که مهم است تا جای ممکن روتین وقفه ها کوتاه نگه داشته شود.
- به این ترتیب در صورتی که فلگ set شده باشد، فلگ را false می کنیم که برای بار بعدی، وقفه را بتوانیم تشخیص دهیم.
- در ادامه LED یک بار با فاصله زمانی 500 میلی ثانیه چشمک می زند.
منابع پیشنهادی جهت یادگیری بیشتر STM32
می توانید در دوره آموزش STM32 در لینک زیر شرکت کنید.
لینک های مفید
شروع کار با میکروکنترلر stm32 و stm32cubeMX
برنامه نویسی به روش RTOS امروز یک ضرورت است؟
اگر سوال، پیشنهاد، نظر و یا … دارید، از قسمت نظرات در پایین همین صفحه، می توانید با ما و بقیه ی خوانندگان درمیان بگذارید.
جهت مطلع شدن از آموزش های جدید به خبرنامه ی EasyMCU بپیوندید.
درباره مرتضی زندی
خلق کردن شماره 1 علاقه مندی های من هست. برنامه نویسی بهترین ابزاری بوده که تا الان برای این کار پیدا کردم. بیس الکترونیک دارم و دستی بر آتش در دنیای رشته های مجاور. تجربه سال ها فعالیت در تیم های R&D رو دارم، در این بین در حوزه ی امبدد سیستم ها فعالیتم پررنگ تر هست. عاشق یادگیری و یاد دادن هستم و تلاش می کنم تجربیاتم رو به زبانی ساده از طریق EasyMCU.org به نحوی کاربردی و موثر با علاقه مندان این حوزه به اشتراک بذارم.
نوشته های بیشتر از مرتضی زندیمطالب زیر را حتما مطالعه کنید
آموزش نصب و راه اندازی سیستم عامل Free RTOS برای آردوینو Arduino
آموزش نصب و راه اندازی سیستم عامل Free RTOS روی STM32
آموزش FreeRTOS نحوه استفاده از میوتکس Mutex
آموزش Free RTOS نحوه استفاده از سمافور Semaphore
معرفی کتاب ARM
راه اندازی واحد سریال UART در STM32
4 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
دیدگاهتان را بنویسید لغو پاسخ
این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش میشوند.
ممنون مهندس بابت اطلاعات مفیدتون
درود بر شما.
خواهش میکنم. خوشحالم مفید بوده. مرسی از وقتی که گذاشتی و نظرت رو نوشتی.
با آرزوی بهترین ها.
سلام ممنون از اموزش های خوبتون عالی
میخواستم بدونم چطور میشه وقفه اینتراپت را برای پین مورد نظر خاموش کرد در زمانی که وقفه رخ می دهد و پایان تابع وقفه دوباره فعالش کرد
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_12)
{
__NVIC_DisableIRQ(EXTI15_10_IRQn);
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
__NVIC_EnableIRQ(EXTI15_10_IRQn);
}
}
درود بر شما.
مرسی از کامنتت. خوشحالم مفید بوده.
در STM32 هر پایه به صورت مستقل اولویت وقفه نداره و به ازاء تعدادی پایه، یک اولویت وقفه وجود داره. بنابراین با استفاده از تابع NVIC_DisableIRQ میتونید به خواسته اتون برسید.
با آرزوی بهترین ها.