مدیریت یک سیستم سخت افزاری در حقیقت مدیریت رخدادهای غیر همزمانش هست که اکثرشون مربوط به لوازم جانبی میکروکنترلر میشن. مثلا یک تایمر به مقدار تعیین شده خودش میرسه و یا درگاه سریال خبر از رسیدن اطلاعات رو میده. بقیه وفقه ها از دنیای بیرون از میکروکنترلر میان مثل دکمه ای که به یک پایه GPIO متصل شده.
تمام میکروکنترلرها ویژگی دارن به نام وقفه یا interrupts . وقفه یک رخداد غیر همزمان هست که باعث میشه اجرای کد فعلی براساس اولویت تعیین شده (هر چی وقفه مهم تر باشه اولویتش بالاتره و باعث میشه وقف با اولویت پایین تر در حالت تعلیق قرار بگیره. جلوتر بصورت نمودار توضیح میدم) متوقف بشه و کد مربوط به وفقه به اجرا دربیاد. به کدی که در حالت وقفه اجرا میشه میگن ISR که مخفف کلمات Interrupt Service Routine هست.
وفقه ها منشا برنامه نویسی چند وظیفه ای هستن. این قابلیت باعث میشه سخت افزار تمام شرایط سیستم رو قبل از اجرای ISR حفظ کنه و جریان اجرای برنامه اصلی از دست نره. سیستم عامل ها بلادرنگ یا Real Time Operating System که به طور مخفف RTOS نامیده میشن هم از همین قابلیت سخت افزاری استفاده می کنن. در سیستم عاملها تمام کارها توسط رویه هایی به نام Task یا وظیفه انجام میشن.
وقفه ها هم میتونن منبع سخت افزاری داشته باشن و نرم افزاری. در ARM این دو توسط وقفه های سخت افزاری و استثنائات نرم افزاری ( software exceptions ) متمایز میشن. در ARM وقفه در حقیقت یک نوع استثناء یا exception به حساب میاد.
در میکروکنترلرهای سری Cortex-M واحدی وجود داره که وظیفه اش مدیریت همین استثنائات هست که بهش میگن NVIC. این عبارت مخفف کلمات Nested Vectored Interrupt Controller که اگر بخوام لغوی معنییش کنم میشه کنترل کننده وقفه برداری تو در تو (چی شد!!! … سخت نگیرید مهم اینه طرز کارش رو بفهمیم)
کنترلر NVIC
NVIC یک واحد سخت افزاری در میکروکنترلرهای Cortex-M هست که وظیفه اش رسیدگی به استثنائات هست. شکل زیر ارتباط بین واحد NVIC ، پردازنده هسته ( Processor Core ) و لوازم جانبی رو نشون میده. اگراز منظر هسته Cortex-M به شکل نگاه کنیم دو نوع وقفه خواهیم داشت. وفقه های داخلی که چیزهای مثل تایمر و درگاه سریال و غیره میان و وقفه هایی که خارجی هستن و منشا اونها پایه های I/O یا همون GPIO هست. همون طور که در شکل هم می بینید یک کنترولر به نام EXTI وجود داره که وظیفه اش اتصال بین سیگنال پایه های GPIO به کنترلر NVIC هست.

Vector Table
همونطور که از اسم پست هم مشخص هست ما قرار وقفه های مربوط به GPIO ها بررسی کنیم. اما در ابتدا این پست هم گفتم وفقه ها منابع مختلفی دارن که با توجه به وظیفه شون دارای اولویتهای متفاوتی هستند که پرداختن بهشون تو این پست بی معنی هست اما برای اینکه نسبت بهشون دید مناسبی پیدا کنید User Manual مربوط به میکرو کنترلر خودتون رو دانلود کنید و عبارت Vector Table رو در اون جستجو کنید. همونطور که مشاهده می کنید با منابع متعددی از وفقه ها مواجه می شید که ترتیب اولویت در این جدول قرار گرفتن. شاید تنها وفقه ای که تا الان بدون کد نویسی ازش استفاده کردید وقفه Reset باشه. اگر به Priority یا اولویتش در جدول نگاه کنید می بینید اولویتش -۳ هست یعنی بالاترین اولویت. مهم نیست میکروکنترلر داره چی کار میکنه دکمه ریست رو فشار بدید یا به هر نحوی پایه ریست رو فعال کنید میکروکنترلر اجرای برنامه رو در هرجایی که هست متوقف میکنه و اجرای برنامه رو از اول انجام میده. وقفه RESET جزو وقفه های غیر قابل چشم پوشی یا اصطلاحا None Maskable هست. برای اینکه پست خواب آور و غیر قابل تحمل نشه من وقفه های مربوط به هر بخش رو در پست مربوط به خودش بررسی میکنم.
فعال سازی وقفه ها
وقتی که میکروکنترلر بوت میشه فقط وقفه یا استثنائات NMI ، Reset و Hard Fault بطور پیشفرض فعال هستند و مابقی استثنائات و وقفه های لوازم جانبی غیر فعال هستن و باید بنا به نیاز فعال بشن. برای فعال کردن یک IRQ از تابع HAL زیر استفاده می کنیم:
1 |
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn); |
در این تابع IRQn_Type یک نوع شمارشی هست که شامل تمام وفقه ها و استثنائات تعریف شده برای میکروکنترلر مورد استفاده هست. این نوع شمارشی در داخل یک فایل هدر در کتابخونه HAL مربوط به میکروکنترلر قرار گرفته.
برای غیر فعال کردن یک IRQ از تابع زیر استفاده می کنیم:
1 |
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn); |
لازم به توضیح هست که بگم توابع گفته شده فعال/غیرفعال سازی یک وقفه رو در سطح کنترلر NVIC انجام میدن. اگر به تصویر بالا با دقت نگاه کنید می بینید که هر کدوم از لوازم جانبی با یک مسیر یا خط مستقل به کنترلر NVIC متصل میشن. این یعنی اگر شما می خواهید از وقفه یکی از لوازم جانبی یا اصطلاحا Peripheral استفاده کنید هم باید تنظیمات مربوط به وقفه مربوط اون رو مستقلا انجام بدید و هم وفقه رو در سطح NVIC فعال کنید تا به درستی کار کنه. با استفاده از توابع HAL خیلی راحت میتونیم وفقه مربوط به یکی از لوازم جانبی رو فعال کنیم. یک مثال میزنم (فقط اشاره میکنم) با استفاده از تابع HAL_USART_Transmit_IT() که مربوط به ارسال داده درگاه سریال هست به طور ضمنی داریم وفقه مربوط به زمان ارسال اطلاعاتش رو هم تنظیم می کنیم. واضح هست این به تنهایی جواب نمیده و اگر میخوایم وفقه ارسال داده در درگاه سریال کار بکنه باید توسط تابع HAL_NVIC_EnableIRQ() اون رو در سطح NVIC هم فعال کنیم.
خطوط خارجی و NVIC
همونطور که در تصویر بالا دیدید میکروهای ST از طریق بخش EXTI Controller میتونن تعداد متغیری منابع وقفه خروجی رو مدیریت کنن. این که میگم متغیر دقیقا بستگی به میکرویی داره که دارید استفاده می کنید. به تصویر زیر نگاه کنید:

تصویر فوق نحوه اتصال پایه های GPIO رو به خطوط EXTI نشون میده. با توجه به این تصویر شما میتونید وفقه رو روی تمام پایه های میکرو فعال کنید فقط حواستون به دو نکته زیر باید باشه:
- در هر خط EXTI فقط یک پایه GPIO میتونه فعال باشه بطور مثال اگر شما وقفه رو برای PA0 فعال کردید دیگه نمی تونید برای پایه PB0 هم وقفه رو فعال کنید.
- بعضی از میکروها مثل سری STM32F4 بعضی از GPIO ها از خط IRQ مشترک در داخل NVIC استفاده می کنن برای اینکه در هنگام وقفه اونها رو هم از هم تفکیک کنید باید براشون کد بنویسید. به عنوان مثال می تونید به خطوط EXTI مربوط به سری STM32F4 در تصویر زیر نگاه کنید. در این سری میکرو ها خطوط EXTI10 الی EXTI15 مشترک هستن.

اگر می خواهید از طریق فایلهای کتابخونه بدونید در میکرو stm32f103c8 که در برد Bluepill استفاده شده اوضاع چطوریه میتونید به محتویات فایل stm32f103xb.h یک نگاهی بندازید. چون حجم کد خیلی زیاد هست کد رو اینجا قرار نمی دم و فقط اشاره میکنم. در انتهای پست وقتی پروژه رو ایجاد کردید فایل مورد نظر رو پیدا کنید، بازش کنید و به مواردی که من اینجا اشاره میکنم دقت کنین. این که جای دقیق فایل رو نمیگم علتش اینه که بسته به نوع IDE که استفاده می کنید ممکنه مکان فایل متفاوت باشه که راحت با ابزار سیستم عاملی که در اختیار دارید میتونید پیداش کنید. خطوط ۸۹ الی ۹۳ دارن نشون میدن که برای GPIO های ۰ تا ۴ خطوط EXTI مستقل هستن. خط ۱۰۶ داره میگه GPIO های ۵ تا ۹ از خط EXTI مشترک استفاده می کنن. این مورد در خط ۱۲۳ برای GPIO های ۱۰ تا ۱۵ هم صدق میکنه.
اولویت بندی وقفه ها
در میکروکنترلرهای با هسته Cortex-M3/4/7 اولویت هر وقفه از طریق رجیستری به نام IPR مشخص میشه. این رجیستر ۸ بیتی هست و انتظار داریم حداقل ۲۵۵ سطح در اولویت بندی در اختیار ما قرار بده اما در عمل اینجوری نیست. علتش هم اینه که برای تعیین اولویت بندی فقط از چهار بیت وزن بالا استفاده میشه.

شکل بالا نشون میده محتویات این رجیستر چطوری تفسیر میشه. می تونیم این نتیجه گیری رو داشته باشیم که حداکثر تعداد اولویت بندی که می تونیم داشته باشیم ۱۶ تا هست: ۰xF0 ، ۰xE0 ، ۰xD0 ، ۰xC0 ، ۰xB0 ، ۰xA0 ، ۰x90 ، ۰x80 ، ۰x70 ، ۰x60 ، ۰x50 ، ۰x40 ، ۰x30 ، ۰x20 ، ۰x10 ، ۰x00 . پایین تر عدد بالاترین اولویت رو داره. مثلا وقفه ای که اولویت ۰x10 رو داشته باشه اولویتش از وفقه ای با مقدار ۰xA0 بالاتر هست. اگر دو وقفه رو در یک زمان شلیک کنن اونی که اولویت بالاتری (مجددا میگم … عدد کوچیکه) اجرا میشه. اگر پردازنده در حال اجرای یک وقفه باشه و یک وقفه با اولویت بالاتر شلیک بشه ، وقفه فعلی به حالت تعلیق در میاد و وقفه با اولویت بالاتر اجرا میشه. بعد از تمام وقفه با اولویت بالا، دنباله وقفه با اولویت پایین تر اجرا میشه.
البته موضوع به اون گل و بلبلی که فکر می کنید نیست چون بیت های رجیستر IPR به دو بخش تقسیم میشه. تعدادی از بیتها مربوط به حق تقدم یا preemption هستن و تعداد از اونها مربوط به اولویتهای فرعی یا sub-priority. اولویتی که بین اجرای کدهای ISR حکمرانی میکنه اولویت از نوع preemption هست. اگر یک ISR اولویتی بالاتر از یک ISR دیگه داشته باشه زودتر اجرا میشه یا در حقیقت داره حق اجرا شدن در مقابل ISR اولویت پایین تر رو preempt میکنه یا به انحصار در میاره. سخت نگیرید بریم جلوتر بهتر متوجه میشید.

تصویر بالا رو نگاه کنید. تو این تصویر فقط داریم در مورد اولویت preemption یا اولویت انحصاری صحبت میکنیم. مقدار عددی اولویت A از همه بیشتره پس حق تقدمش از همه پایین تره. در اولویت بالاتر B و بعد از اون C قرار داره. وقفه A در لحظه t0 شروع به اجرا میکنه. یک دفعه در لحظه t1 وقفه B شلیک میشه. منطقی هست که A به حالت تعلیق درمیاد و B شروع به اجرا میکنه. B در حال اجرا هست که باز وقفه C میگه میخوام اجرا بشم. چی میشه؟ هیچی دیگه B هم میگه روم سیاه میره کنار A وامیسته تا جناب C اجرا بشه. C که کارش تموم شد B از کنار A بلند میشه میره کارش رو ادامه. B که کارش تموم شد A بدبخت کاری رو که شروع کرده بود ادامه میده. اما زیر اولویت ها چه تاثیری در اجرا وقفه دارن؟ برای اینکه این موضوع متوجه بشید تصویر زیر رو نگاه کنید:

تو این تصویر از نظر اولویت انحصاری یا اصلی همه وقفه ها مثل هم هستن. اما از نظر زیر اولویت، B از حق تقدم پایین تری نسبت به دوتای دیگه برخورداره. این رو هم باید دقت کنیم که وقفه A و C چه از نظر اولویت انحصاری و چه از نظر زیر اولویت عینا مثل هم هستن و هیچ فرقی با هم ندارن.
در لحظه t0 اولویت A شلیک میشه و شروع به اجرا میکنه. همین طوری که داره اجرا میشه B میگه میخوام اجرا بشم. میکرو بهش میگه بشین سر جات درسته اولویت اصلیت با A یکی هست اما از نظر زیر اولویت پایین تری داداش پس باید صبر کنی میگه باشه. A پوزخند میزنه و ادامه میده. یک دفعه در t2 وقفه C میگه من می خوام اجرا بشم. همه چیم هم اوکی هست. هم انحصاریم مثل A هست و هم زیر اولویتم. برگه رضایت والدین رو هم آوردم. پردازنده میگه کاملا درسته. تو که C باشی و A به یک اندازه برای برای من عزیز هستین. اما چون A زودتر شروع کرده بذار ادامه بده کارش که تموم شد تو ادامه بده. B میگه ای آقا من تو صف بودم که؟!!! میکرو میگه داداش زیر اولویتت پایینه بفهم !!! B میگه باشه فهمیدم بازم صبر میکنم. A که کارش تموم میشه طبق قرار C شروع میکنه. C که کارش تموم B تازه کارش شروع میکنه. (نمی دونم چرا دلم برای B سوخت)
خب حالا سوال پیش میاد که چطوری باید تنظیمات مربوط به اولویتهای انحصاری ( Preempt Priority ) و زیر اولویتها ( Subpriority ) زو انجام بدیم. قبل از اینکه دستورش رو بگم شکل زیر رو نگاه کنید:

شکل بالا تمام پنج حالتی که رجیستر IPR میتونه داشته باشه رو نشون میده همچنین در جدول زیر می تونید ببینید که در هر حالت چند اولویت انحصاری و چند زیر اولویت می تونید داشته باشید. هر کدوم از این حالات در حقیقت یک گروه حساب میشن که در جدول نام متناظر هر گروه هم درج شده.
اولویت گروه NVIC | تعداد اولویت انحصاری | تعداد زیر اولویت ها |
NVIC_PRIORITYGROUP_0 | 0 | 16 |
NVIC_PRIORITYGROUP_1 | 2 | 8 |
NVIC_PRIORITYGROUP_2 | 4 | 4 |
NVIC_PRIORITYGROUP_3 | 8 | 2 |
NVIC_PRIORITYGROUP_4 | 16 | 0 |
برای انجام تنظیمات مربوط به EXTI ، اولویت انحصاری و زیر اولویت HAL تابع زیر رو در اختیار ما قرار میده:
1 |
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority); |
درتابع فوق IRQn نام مربوط به IRQ ی هست که قرار استفاده بشه. مثلا شما اگر پایه بخواهید وقفه مربوط به پایه PA3 میکرو stm32f103c8 که در برد BluePill مورد استفاده قرار گرفته فعال کنید با توجه به دیتاشیت این پایه از خط IRQ به نام EXTI3_IRQn استفاده میکنه پس برای فعال کردنش باید نام این IRQ رو به عنوان آرگومان اول تابع پاس داده بشه. دو پارامتر بعدی مربوط به اولویتهای Preempt Priority و Subpriority که با توجه به جدول بالا در هر گروه میتونه مورد استفاده قرار بگیره. این مقدایر بطور خودکار به چهار بیت پر وزن مربوط به رجیستر IPR منتقل میشن.
خب به اندازه کافی تئوری گفتم بیائید با یک مثال این پست رو تموم کنیم. به طبق معمول یک پروژه جدید ایجاد کنید. سپس تنظیمات وقفه رو به ترتیب تصوایر زیر انجام بدید و در نهایت دکمه Generate رو بزنید:




خب تنظیمات تا اینجا کامل هست. دکمه Generate Code رو کلیک کنید و پروژه رو باز کنید. قبل از اینکه کد رو در فایل main.c وارد کنیم بیاین نگاهی به فایل stm32f1xx_it.c که در همون مسیر فایل main.c قرار داره یک نگاهی بندازیم. فایل رو باز کنید و کدها رو به سمت پایین مرور کنید. اگر تنظیمات Cube رو درست انجام داده باشید به کد مثل کنید زیر برخورد می کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * @brief This function handles EXTI line3 interrupt. */ void EXTI3_IRQHandler(void) { /* USER CODE BEGIN EXTI3_IRQn 0 */ /* USER CODE END EXTI3_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); /* USER CODE BEGIN EXTI3_IRQn 1 */ /* USER CODE END EXTI3_IRQn 1 */ } |
این دقیقا تابعی هست که موقع فعال شدن وقفه خط EXTI_IRQ3 پایه PA3 از طریق اون به کنترلر NVIC وصل میشه. در تابع Callback ی که ما می نویسیم وضعیت پین GPIO رو چک می کنیم. پین GPIO از اینجا به تابع Callback پاس داده میشه.
حالا فایل main.c رو باز کنید و بعد از عبارت /* USER CODE BEGIN 0 */ کد زیر رو تایپ یا کپی کنید:
1 2 3 4 5 6 7 8 |
/* USER CODE BEGIN 0 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_3) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); } } |
Callback فوق کار خاصی انجام نمیده فقط در زمان فعال شده وقفه وضعیت LED متصل به برد رو معکوش میکنه.حالا متن کامل فایل main.c باید مثل زیر باشه:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * <h2><center>© Copyright (c) 2020 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_3) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); /*Configure GPIO pin : PC13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /*Configure GPIO pin : PA3 */ GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI3_IRQn); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
از اونجائیکه از مدار پست مربوط به آموزش GPIO داریم استفاده میکنیم ۹۰ درصد کل مشابه پست قبلی هست و من فقط تفاوتها رو میگم
در تنظیمات GPIO مربوط به پین PA3 خاصیت Mode برابر با GPIO_MODE_IT_RISING قرار گرفته. همونطور که قبل تر هم تو مراحل انجام تنظیمات پروژه در Cube هم گفتم ما قصد داریم وقفه با لبه بالا رونده پالس ورودی فعال بشه. این خاصیت این کار رو برای ما انجام میده.
در خط ۱۷۹ تنظیمات مربوط به اولویت انحصاری و زیر اولیت انجام شده که هردو برابر ۰ هستن. البته با این دستور در اوسط آموزش و نحوه استفاده اش آشنا شدیم.
در خط ۱۸۰ فعال سازی وقفه سراسری انجام شده.
مدار مورد استفاده همون مداری هست که در پست آموزش GPIO ازش استفاده کردیم:

اگر در محیط Keil هستید با فشار دادن دکمه F7 برنامه رو کامپایل و با زدن F8 کد رو آپلود کنید. سپس دکمه ریست بر بروی برد BluePill رو فشار بدبد. حالا با فشار دادن دکمه متصل به PA3 ال ئی دی روی برد یک بار روشن و با فشار مجدد دکمه خاموش میشه که این کار از طرق وقفه انجام میشه. این مثال خیلی ساده بود اما بعد از آموزش مربوط به درگاه سریال نحوه راه اندازی کی پد ۴×۴ رو با استفاده از وقفه ها آموزش میدم.
عالی بود
عالی بود واقعا، دم شما گرم..
ممنون و خدا قوت
آموزش و نحوه بیان خیلی خوب هست…