وضعیت اعتبارسنجی: 2 پروژهٔ واقعی با CubeMX 6.14.0، DB 6.0.140 و CubeF3 1.11.6 تولید و با GCC 13.2.1 در ساخت پاک Debug با -O0 -g3 -Wall -Werror بدون خطا و هشدار ساخته شده‌اند. اتصال هندلرها در ELF و محاسبات اولویت بررسی شده است. پروگرام، اجرای روی برد، محیط گرافیکی CubeIDE و اندازه‌گیری اسیلوسکوپ انجام نشده‌اند. هر 6 شکل تألیفی یا مدل آموزشی است.

پیش‌نیاز: درخت کلاک STM32 و پیکربندی 72 MHz. اگر یک وقفه هنگام اجرای وقفهٔ دیگر برسد، کدام اجرا می‌شود؟ این درس مسیر درخواست تا هندلر، جدول بردار و تفاوت اولویت تقدم با زیر‌اولویت را با 2 پروژهٔ قابل مقایسه توضیح می‌دهد.

هدف دقیق، STM32F303CBT6 در LQFP48 روی GB100ST V2.0 است. هر دو پروژه HSE برابر 8 MHz، PREDIV برابر 1، PLL برابر 9، SYSCLK و HCLK برابر 72 MHz، PCLK1 برابر 36 MHz، PCLK2 برابر 72 MHz و FLASH_LATENCY_2 دارند؛ SWD حفظ شده است. هر ساخت Flash برابر 8956 بایت و ناحیهٔ RAM لینک برابر 1632 بایت دارد؛ RAM شامل رزرو پشته و heap است و مصرف لحظه‌ای اجرا نیست. منبع CubeF3 با commit 663d2baae3604244f90ed80d0d757df17e36ada9 و HAL با commit 953955afe65f89e60e556bbcdba752597f5da65d ثبت شده‌اند؛ ELF/MAP، فایل .ioc و گزارش‌ها همراه بسته‌اند.

مدل استثناها در معماری Cortex-M4 و ساختار NVIC

تفکیک استثناهای هسته (Reset, NMI, HardFault, SysTick) از وقفه‌های محیطی (IRQ)

استثنا مسیر اجرای عادی را به هندلر منتقل می‌کند. شمارهٔ استثنا و IRQn یک چیز نیستند: برای وقفهٔ جانبی، شمارهٔ استثنا برابر 16 + IRQn است؛ CMSIS برای استثناهای پیاده‌سازی‌شدهٔ هسته مانند NMI و SysTick مقادیر IRQn منفی به کار می‌برد. Reset یک ورودی ویژهٔ جدول بردار است. همهٔ جایگاه‌های 1 تا 15 هندلر کاربردی ندارند و بعضی رزرو هستند.

Reset، NMI و HardFault اولویت ثابت دارند؛ Reset از همه مقدم است، سپس NMI و HardFault. در مقیاس معماری، اولویت آن‌ها به‌ترتیب منفی 3، منفی 2 و منفی 1 است؛ این عددها با IRQn منفی CMSIS اشتباه نشوند. PRIMASK و BASEPRI، NMI و HardFault را نمی‌پوشانند. استثناهای قابل تنظیم مانند SysTick و PendSV از SHPR و وقفه‌های جانبی از IPR اولویت می‌گیرند. رفتار FAULTMASK بحث جداگانه‌ای است.

چرخه حیات وقفه در ثباتهای NVIC: حالتهای Inactive، Pending، Active و Active+Pending

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

  • غیرفعال (Inactive): هندلر فعال نیست و درخواست معوق ندارد؛ این حالت مستقل از Enable بودن خط است.
  • در انتظار (Pending): سیگنال تریگر رخداد توسط سخت‌افزار ثبت شده یا توسط نرم‌افزار در رجیستر ISPR فعال گردیده است، اما پردازنده هنوز اجرای کدهای تابع را آغاز نکرده است.
  • فعال (Active): هندلر وارد شده و هنوز بازنگشته است؛ بیت Active در IABR برقرار می‌ماند، حتی اگر اجرای آن زیر وقفهٔ مهم‌تر موقتاً تعلیق شده باشد.
  • فعال و در انتظار (Active and Pending): در حین اجرای وقفه جاری، رخداد جدیدی روی همان شیار ایجاد شده است؛ پس از پایان روتین، درخواست برای داوری باقی می‌ماند؛ ماسک‌ها و اولویت درخواست‌های دیگر تعیین می‌کنند چه زمانی دوباره اجرا شود.

در تصویر شماتیک زیر، چرخه انتقال میان این چهار وضعیت در سخت‌افزار NVIC تشریح شده است.

تفاوت حالت Pending و Active
نمودار تألیفی حالت‌های یک استثنا؛ Active با Enable یکی نیست و درخواست جدید می‌تواند هنگام فعال‌بودن معوق شود. برای دیدن جزئیات، تصویر را باز کنید.

جدول بردار وقفه در حافظه فلش و پیاده‌سازی استارتاپ

نگاشت بردارها در آدرس 0x08000000، پشته اولیه MSP و بردارهای سخت‌افزاری

در چیدمان پیوند (Linker Script) میکروکنترلر STM32F303CBT6، جدول بردار وقفه در ابتدای حافظه فلش با آدرس پایه 0x08000000 قرار می‌گیرد. رجیستر جابه‌جایی بردار VTOR (Vector Table Offset Register) در واحد SCB، آدرس ابتدای این جدول را در زمان اجرا معین می‌سازد. نخستین کلمه 32 بیتی در آدرس 0x08000000، آدرس پشته اولیه (Initial Main Stack Pointer / MSP) است و به یک تابع اشاره نمی‌کند. دومین کلمه در آدرس 0x08000004، اشاره‌گر به تابع Reset_Handler با بیت صفر یک‌شده (برای مشخص‌کردن حالت اجرایی Thumb-2) است.

پس از بردارهای سیستم، بردار وقفه پریفرال‌ها چیده می‌شوند. برای نمونه، شیار EXTI0_IRQn (شماره IRQ معادل 6) در بردار 22 با آفست بایت 0x58 (آدرس 0x08000058) و شیار EXTI1_IRQn (شماره IRQ معادل 7) در بردار 23 با آفست بایت 0x5C (آدرس 0x0800005C) قرار دارد.

در پروژهٔ تحویلی، USER_VECT_TAB_ADDRESS در system_stm32f3xx.c تعریف نشده است؛ SystemInit جدول را جابه‌جا نمی‌کند. در بوت عادی از Flash با BOOT0 روی GND، نگاشت آغاز حافظه امکان استفاده از جدول با VTOR اولیهٔ 0 را می‌دهد، در حالی که آدرس لینک جدول در ELF برابر 0x08000000 است. مقدار VTOR برابر 0x08000000 در شکل صرفاً نمونهٔ آدرس‌دهی مستقیم Flash است؛ مقدار خوانده‌شده از برد نیست. برای کد تحویلی، تغییر VTOR لازم نیست.

سازوکار نمادهای ضعیف .weak در startup_stm32f303xc.s و رفتار Default_Handler

در فایل اسمبلی راه‌انداز startup_stm32f303xc.s، توابع خدمت‌رسان با صفت .weak تعریف شده‌اند. هندلرهای ضعیف وقفه‌های مورد بحث به برچسب Default_Handler پیوند داده می‌شوند که شامل یک حلقهٔ بی‌نهایت است. هرگاه برنامه‌نویس تابعی با همان امضا (مثلاً EXTI0_IRQHandler) در کدهای C بازتعریف کند، پیوند‌دهنده (Linker) به طور خودکار تابع جدید را جایگزین برچسب پیش‌فرض ضعیف می‌کند. اگر نام تابع در فایل C حتی یک نویسه اشتباه تایپ شود، بردار کماکان به Default_Handler ارجاع خواهد داد و رخ‌دادن وقفه باعث قفل پردازنده در حلقه بسته می‌شود.

نمودار زیر سازوکار ارجاع آدرس‌های جدول فلش به توابع C و رفتار پیوند‌دهنده با نمادهای ضعیف را نمایش می‌دهد.

جدول بردار و اتصال هندلرهای وقفه
نمودار تألیفی جدول بردار در چیدمان لینک همین پروژه؛ کلمهٔ اول MSP است و نماد قوی کاربر جای هندلر ضعیف را می‌گیرد. برای دیدن جزئیات، تصویر را باز کنید.

سازوکار اولویت‌بندی در میکروکنترلر STM32F303

تفکیک 4 بیت اولویت (__NVIC_PRIO_BITS = 4) در ثباتهای IPR و وزن بیتهای 7:4

معماری ARM Cortex-M4 امکان پیاده‌سازی 3 تا 8 بیت اولویت را برای سازندگان تراشه فراهم می‌کند. شرکت ST در هدر اختصاصی stm32f303xc.h مقدار ثابت __NVIC_PRIO_BITS را برابر با 4 تعریف کرده است. رجیسترهای اولویت وقفه (Interrupt Priority Registers / IPR) برای هر شیار وقفه یک بایت 8 بیتی در نظر می‌گیرند، اما در سخت‌افزار STM32F303 تنها 4 بیت پرارزش (بیت‌های 7 تا 4) پیاده‌سازی شده‌اند و 4 بیت کم‌ارزش (بیت‌های 3 تا 0) پیاده‌سازی نشده‌اند و 0 خوانده می‌شوند. بنابراین برای نوشتن یک سطح منطقی در رجیستر، شیفت به چپ به اندازه 4 بیت (logical_priority << 4) الزامی است.

گروهبندی اولویت در HAL (کلاسهای PRIORITYGROUP_0 تا 4): Preemption در برابر Subpriority

واحد SCB در رجیستر AIRCR فیلدی به نام PRIGROUP دارد که مرز میان اولویت تقدم (Preemption Priority) و زیر‌اولویت (Subpriority) را در این 4 بیت تعیین می‌کند. جدول زیر گروه‌بندی‌های استاندارد در کتابخانه HAL را نشان می‌دهد:

گروه HAL بیت‌های Preemption بیت‌های Subpriority تعداد سطوح تقدم تعداد زیر‌اولویت
NVIC_PRIORITYGROUP_4 4 بیت (بیت‌های 7:4) 0 بیت 16 سطح (0 تا 15) 1 حالت؛ بدون بیت زیر‌اولویت
NVIC_PRIORITYGROUP_3 3 بیت (بیت‌های 7:5) 1 بیت (بیت 4) 8 سطح (0 تا 7) 2 سطح (0 و 1)
NVIC_PRIORITYGROUP_2 2 بیت (بیت‌های 7:6) 2 بیت (بیت‌های 5:4) 4 سطح (0 تا 3) 4 سطح (0 تا 3)
NVIC_PRIORITYGROUP_1 1 بیت (بیت 7) 3 بیت (بیت‌های 6:4) 2 سطح (0 و 1) 8 سطح (0 تا 7)
NVIC_PRIORITYGROUP_0 0 بیت 4 بیت (بیت‌های 7:4) 1 سطح (بدون تقدم) 16 سطح (0 تا 15)

اولویت تقدم به یک وقفه جدید اجازه می‌دهد که اجرای یک وقفه جاری را قطع کند (وقفه تودرتو). در حالی که زیر‌اولویت تنها زمانی به کار می‌آید که دو وقفه به طور همزمان در وضعیت Pending قرار داشته باشند و پردازنده بخواهد یکی را برای اجرا انتخاب کند. زیر‌اولویت هرگز نمی‌تواند یک وقفه در حال اجرا با همان اولویت تقدم را قطع کند.

منطق معکوس فوریت (عدد کمتر = فوریت بالاتر) و داوری وقفه‌های همسطح

در کنترلر NVIC، مقیاس عددی اولویت‌ها معکوس است؛ مقدار 0 بالاترین فوریت و مقدار 15 کمترین فوریت را دارد. اگر دو وقفه با اولویت تقدم و زیر‌اولویت کاملاً یکسان به صورت همزمان ثبت شوند، داوری سخت‌افزاری بر اساس شماره بردار وقفه انجام می‌شود و وقفه‌ای که شماره شیار (IRQ Number) کمتری دارد زودتر پردازش خواهد شد.

در دیاگرام زیر، نگاشت 4 بیت سخت‌افزاری در بایت اولویت رجیسترهای IPR نمایش داده شده است.

بیت‌های اولویت و گروه‌بندی NVIC
نمودار تألیفی 4 بیت پیاده‌سازی‌شده در IPR؛ بیت‌های PRE تقدم و SUB داوری درخواست‌های معوق را مشخص می‌کنند. برای دیدن جزئیات، تصویر را باز کنید.

دینامیک پاسخ به وقفه: تاخیر ورود، Tail-Chaining و Late Arrival

فرآیند خودکار Stacking روی پشته و مفهوم ایدهآل 12 سیکل ورود

هنگامی که NVIC تصمیم به پاسخ به یک وقفه می‌گیرد، سخت‌افزار هسته به صورت خودکار 8 رجیستر اساسی شامل R0, R1, R2, R3, R12, LR, PC, xPSR را روی پشته جاری ذخیره می‌کند (Stacking). عدد شناخته‌شدهٔ 12 سیکل برای ورود ایده‌آل با حافظهٔ بدون Wait State و شرایط معماری مناسب است؛ هزینهٔ دسترسی حافظه، دستور جاری، رقابت باس و زمینهٔ FPU می‌توانند زمان واقعی را تغییر دهند. این عدد اندازه‌گیری این برد نیست. همزمان، مقدار رجیستر LR با کد ویژه بازگشت استثنا (EXC_RETURN مانند 0xFFFFFFF9) بازنویسی می‌شود تا پردازنده در پایان بداند از کدام پشته بازگردد.

بهینهسازی سخت‌افزاری: کاهش سربار بازگشت با Tail-Chaining (6 سیکل) و سبقت Late Arrival

هسته Cortex-M4 برای کاهش تأخیر در سناریوهای بلادرنگ از دو شتاب‌دهنده سخت‌افزاری استفاده می‌کند:

  • زنجیره‌سازی وقفه‌ها (Tail-Chaining): اگر پس از پایان یک روتین، وقفه دیگری در صف انتظار باشد، سخت‌افزار از بازیابی بیهوده رجیسترها از پشته (Unstacking) و ذخیره مجدد آن‌ها صرف‌نظر می‌کند. پردازنده در وضعیت ایده‌آل تنها ظرف 6 پالس ساعت، مستقیماً وارد آدرس بردار بعدی می‌شود.
  • سبقت ورود دیرهنگام (Late Arrival): اگر در حین پشته‌گذاری برای ورود برای یک وقفه کم‌اهمیت، ناگهان وقفه‌ای با اولویت بالاتر رخ دهد، پردازنده فرآیند ذخیره پشته را متوقف نکرده و پس از آماده‌شدن زمینهٔ لازم، ابتدا اجرای وقفه با اولویت بالاتر را آغاز می‌کند.

در شکل زیر دو ترتیب مورد انتظار آزمایش مقایسه شده‌اند: در حالت A وقفهٔ مهم‌تر داخل وقفهٔ کم‌اولویت اجرا می‌شود؛ در B اولویت تقدم برابر است و وقفهٔ معوق پس از بازگشت وقفهٔ اول فرصت اجرا می‌گیرد. محور، ترتیب رخداد است و مقیاس زمانی اندازه‌گیری‌شده ندارد.

ترتیب پالس‌های دو حالت اولویت
مدل ترتیب رخدادها، بدون مقیاس زمانی اندازه‌گیری‌شده؛ در GROUP4 پالس PB7 داخل PB6 است و در حالت تقدم برابر پس از آن می‌آید. برای دیدن جزئیات، تصویر را باز کنید.

بخشهای بحرانی و متغیرهای اشتراکی در محیط وقفه

مقایسه ثباتهای کنترل دسترسی: تفاوت __disable_irq (ثبات PRIMASK) با فیلتر BASEPRI

بخش بحرانی (Critical Section) بخشی از کد است که نباید همزمانی اجرای وقفه یکپارچگی داده‌های آن را مخدوش سازد. برای محافظت از داده، دو رویکرد اصلی در کدهای Bare-metal وجود دارد:

دستور __disable_irq() بیت 0 رجیستر PRIMASK را فعال می‌کند. این کار به جز NMI و HardFault، ورود استثناهای با اولویت قابل تنظیم را ماسک می‌کند؛ رخداد می‌تواند همچنان Pending شود. بخش بحرانی طولانی، تأخیر پاسخ را افزایش می‌دهد.

در مقابل، رجیستر BASEPRI به توسعه‌دهنده اجازه می‌دهد تنها وقفه‌هایی با سطح اولویت مساوی یا کمتر (مقدار عددی بزرگتر یا مساوی) را ماسک کند. برای نمونه، در کلاس NVIC_PRIORITYGROUP_4، تنظیم مقدار 0x40 در رجیستر BASEPRI، وقفه‌های با اولویت منطقی 4 تا 15 را مسدود کرده و سطوح 0 تا 3 را با این آستانه ماسک نمی‌کند؛ Enable، ماسک‌های دیگر و اولویت جاری هنوز مؤثرند. BASEPRI برابر 0 یعنی این ماسک آستانه‌ای غیرفعال است. این ثبات تنها در حالت ممتاز (Privileged Mode) قابل دسترسی است.

کلیدواژه volatile در C و اثبات عدم تضمین اتمیک بودن در عملیات Read-Modify-Write

در این مثال، متغیرهای مشاهده‌شوندهٔ مشترک volatile هستند تا دسترسی لازم حذف نشود. این صفت برای هر روش ارتباط میان ISR و حلقهٔ اصلی الزام عمومی نیست و اتمیک‌بودن یا ترتیب‌دهی همگانی حافظه را تضمین نمی‌کند. افزایش یک شمارنده می‌تواند خواندن، تغییر و نوشتن جدا داشته باشد؛ اگر نویسندهٔ دیگری میان آن‌ها اجرا شود، به‌روزرسانی ممکن است از دست برود. برنامه هنگام پاک‌کردن آرایهٔ ردگیری، PRIMASK قبلی را ذخیره و همان مقدار را بازیابی می‌کند؛ فعال‌کردن بی‌قید وقفه‌ها به‌جای بازیابی وضعیت قبلی درست نیست. آزمایش اجرایی BASEPRI در این پروژه پیاده نشده است.

دیاگرام زیر تفاوت عملکرد مسدودسازی کامل با PRIMASK در مقایسه با فیلتر دقیق رجیستر BASEPRI را نشان می‌دهد.

ماسک PRIMASK و آستانهٔ BASEPRI
نمودار تألیفی در GROUP4؛ BASEPRI برابر 0x40 اولویت‌های منطقی 4 تا 15 را ماسک می‌کند و NMI و HardFault تحت این 2 ماسک نیستند. برای دیدن جزئیات، تصویر را باز کنید.

آزمایش عملی NVIC با تحریک نرم‌افزاری و 2 نشانگر

تحریک نرم‌افزاری با NVIC_SetPendingIRQ روی شیارهای EXTI0 و EXTI1 بدون پیکربندی پریفرال

برای بررسی رفتار سخت‌افزاری NVIC بدون نیاز به نویز تحریک‌های فیزیکی، می‌توان وقفه‌ها را مستقیماً با تحریک نرم‌افزاری فعال کرد. تابع استاندارد NVIC_SetPendingIRQ(IRQn_Type IRQn) بیت متناظر در رجیستر ISPR را فعال می‌کند. ما دو شیار EXTI0_IRQn (اولویت پایین‌تر 2) و EXTI1_IRQn (اولویت بالاتر 1) را در گروه اولویت NVIC_PRIORITYGROUP_4 فعال می‌کنیم.

پیکربندی پینهای نشانگر PB6 و PB7 و تحلیل تقدم (Preemption) روی اسیلوسکوپ

روی برد توسعه GebraBit STM32F303 مجهز به میکروکنترلر STM32F303CBT6 (بسته 48 پایه LQFP)، فرکانس هسته با کریستال خارجی HSE = 8 MHz، ضرب‌کننده PLL = 9، تقسیم‌کننده PREDIV = 1 و زمان تأخیر فلش 2 پالس ساعت، روی SYSCLK = 72 MHz تنظیم شده است. خطوط پایشگر سیگنال به شرح زیر سیم‌بندی می‌شوند:

  • پایه PB6 (پایه شماره 42 میکروکنترلر): نشانگر ورود و خروج وقفه کم‌اولویت EXTI0.
  • پایه PB7 (پایه شماره 43 میکروکنترلر): نشانگر ورود و خروج وقفه پر‌اولویت EXTI1.

سیکل اجرای آزمایش بدین صورت است که در ابتدا وقفه EXTI0 به صورت نرم‌افزاری تحریک می‌شود. با ورود به EXTI0_IRQHandler، پین PB6 سطح یک منطقی می‌گیرد. در میانه این روتین، دستور تحریک نرم‌افزاری EXTI1_IRQn صادر می‌شود. از آنجا که اولویت 1 از 2 بالاتر است، NVIC بلافاصله اجرای EXTI0 را تعلیق کرده و به EXTI1_IRQHandler پرش می‌کند. در این روتین، پین PB7 یک شده، یک حلقهٔ کوتاه و محدود NOP اجرا شده، سپس PB7 صفر می‌شود. با خروج از EXTI1، پردازنده ادامه روتین EXTI0 را از سر گرفته و نهایتاً PB6 را صفر می‌کند. پروب‌های اسیلوسکوپ پالس کانال 2 (PB7) را کاملاً درون پالس کانال 1 (PB6) طبق پیش‌بینی باید نشان دهند؛ این مشاهده هنوز روی برد انجام نشده است.

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

اتصال پروب به PB6 و PB7 و زمین
نقشهٔ اتصال منطقی تألیفی برای آزمایش؛ عکس برد یا چیدمان هدر نیست. PB6 و PB7 به‌ترتیب پایه‌های بستهٔ 42 و 43 هستند. برای دیدن جزئیات، تصویر را باز کنید.

تنظیمات CubeMX و جای دقیق کد

در .ioc، PB6 و PB7 خروجی Push-Pull، No Pull، سرعت Low و مقدار اولیه Low هستند؛ نام‌های USER_LED و HIGH_ISR_SCOPE در main.h تولید شده‌اند. PA13 و PA14 برای Serial Wire باقی می‌مانند. هیچ GPIO_EXTI فعال نکنید. MX_GPIO_Init خروجی‌ها را در MODER و مقدار اولیه را با HAL_GPIO_WritePin تنظیم می‌کند. تنظیم اولویت و Enable عمداً در USER CODE 2 است: HAL_NVIC_SetPriorityGrouping فیلد AIRCR.PRIGROUP، HAL_NVIC_SetPriority بایت IPR از مسیر CMSIS و HAL_NVIC_EnableIRQ بیت ISER را تنظیم می‌کنند. مقدار GROUP4 در HAL برابر فیلد خام PRIGROUP با عدد 3 و GROUP3 برابر 4 است؛ پسوند گروه را مستقیم در AIRCR ننویسید.

کد زیر عین بلوک‌های فایل Core/Src/main.c پروژهٔ gebrabit_lesson10_nvic است. هر بخش را در بلوک هم‌نام قرار دهید؛ توابع تولیدشده را دوباره تعریف نکنید. USER CODE 3 واقعی شامل آکولاد پایان حلقه نیز هست؛ پروژهٔ کامل مرجع چیدمان است و این قطعه تابع main مستقل نیست. هندلرها در USER CODE 4 قرار دارند و در stm32f3xx_it.c دوباره تعریف نشده‌اند. در پروژهٔ gebrabit_lesson10_equal فقط مقدار پیش‌فرض LESSON10_EQUAL_PREEMPT برابر 1 است؛ 2 نسخه را روی هم نچسبانید.

gebrabit_lesson10_nvic: USER CODE

USER CODE PD

/* USER CODE BEGIN PD */
#define LESSON10_EQUAL_PREEMPT 0
/* USER CODE END PD */

USER CODE PV

/* USER CODE BEGIN PV */
volatile uint32_t trace_events[8] = {0U};
volatile uint32_t trace_length = 0U;
volatile uint32_t trace_overflow = 0U;
volatile uint32_t low_active_seen = 0U;
volatile uint32_t high_saw_low_active = 0U;
/* USER CODE END PV */

USER CODE PFP

/* USER CODE BEGIN PFP */
void EXTI0_IRQHandler(void);
void EXTI1_IRQHandler(void);
static void Trace_Event(uint32_t event);
/* USER CODE END PFP */

USER CODE 2

/* USER CODE BEGIN 2 */
  /* Software NVIC experiment: no EXTI line is configured. */
#if LESSON10_EQUAL_PREEMPT
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_3);
  HAL_NVIC_SetPriority(EXTI0_IRQn, 1U, 1U);
  HAL_NVIC_SetPriority(EXTI1_IRQn, 1U, 0U);
#else
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
  HAL_NVIC_SetPriority(EXTI0_IRQn, 2U, 0U);
  HAL_NVIC_SetPriority(EXTI1_IRQn, 1U, 0U);
#endif
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);
  HAL_NVIC_EnableIRQ(EXTI1_IRQn);
/* USER CODE END 2 */

USER CODE 3

/* USER CODE BEGIN 3 */
    uint32_t saved_primask = __get_PRIMASK();
    __disable_irq();
    NVIC_DisableIRQ(EXTI0_IRQn);
    NVIC_DisableIRQ(EXTI1_IRQn);
    NVIC_ClearPendingIRQ(EXTI0_IRQn);
    NVIC_ClearPendingIRQ(EXTI1_IRQn);
    trace_length = 0U;
    trace_overflow = 0U;
    low_active_seen = 0U;
    high_saw_low_active = 0U;
    for (uint32_t i = 0U; i < 8U; i++) { trace_events[i] = 0U; }
    NVIC_EnableIRQ(EXTI0_IRQn);
    NVIC_EnableIRQ(EXTI1_IRQn);
    __set_PRIMASK(saved_primask);
    NVIC_SetPendingIRQ(EXTI0_IRQn);
    __DSB();
    __ISB();
    HAL_Delay(1000U);
  }
/* USER CODE END 3 */

USER CODE 4

/* USER CODE BEGIN 4 */
static void Trace_Event(uint32_t event)
{
  if (trace_length < 8U)
  {
    trace_events[trace_length] = event;
    trace_length++;
  }
  else { trace_overflow++; }
}

/* Strong application handlers override startup weak aliases. */
void EXTI0_IRQHandler(void)
{
  HAL_GPIO_WritePin(USER_LED_GPIO_Port, USER_LED_Pin, GPIO_PIN_SET);
  low_active_seen = NVIC_GetActive(EXTI0_IRQn);
  Trace_Event(1U);
  for (volatile uint32_t i = 0U; i < 256U; i++) { __NOP(); }
  NVIC_SetPendingIRQ(EXTI1_IRQn);
  __DSB();
  __ISB();
  Trace_Event(3U);
  for (volatile uint32_t i = 0U; i < 256U; i++) { __NOP(); }
  Trace_Event(4U);
  HAL_GPIO_WritePin(USER_LED_GPIO_Port, USER_LED_Pin, GPIO_PIN_RESET);
}

void EXTI1_IRQHandler(void)
{
  HAL_GPIO_WritePin(HIGH_ISR_SCOPE_GPIO_Port, HIGH_ISR_SCOPE_Pin, GPIO_PIN_SET);
  high_saw_low_active = NVIC_GetActive(EXTI0_IRQn);
  Trace_Event(2U);
  for (volatile uint32_t i = 0U; i < 64U; i++) { __NOP(); }
  HAL_GPIO_WritePin(HIGH_ISR_SCOPE_GPIO_Port, HIGH_ISR_SCOPE_Pin, GPIO_PIN_RESET);
}
/* USER CODE END 4 */

اول پیش‌بینی، سپس مشاهده روی برد

رخدادهای آرایه: 1 ورود کم‌اولویت، 2 ورود پر‌اولویت، 3 ادامهٔ کم‌اولویت پس از درخواست و 4 نزدیک خروج کم‌اولویت است. برای پروژهٔ GROUP4، ترتیب مورد انتظار [1,2,3,4]، IPR دو خط 0x20 و 0x10 و high_saw_low_active برابر 1 است. برای GROUP3 با Preempt هر دو برابر 1 و Sub به‌ترتیب 1 و 0، ترتیب مورد انتظار [1,3,4,2]، IPR برابر 0x30 و 0x20 و متغیر مذکور برابر 0 است. این‌ها پیش‌بینی مدل هستند، نه ردگیری گرفته‌شده از برد.

verify_model.py اجرا شده و 5 بررسی محاسبهٔ گروه‌بندی، ترتیب دو حالت، بازیابی ماسک و اتصال واقعی بردارها را گذرانده است. بررسی ELF تأیید می‌کند نمادهای قوی کاربر واقعاً در شیارهای 22 و 23 قرار دارند؛ این شاهد لینک است، نه اجرای ISR. main تقریباً هر 1 s یک درخواست ایجاد می‌کند؛ HAL_Delay فقط در main است. حلقه‌های NOP کوتاه و محدود صرفاً برای قابل مشاهده‌شدن پالس‌ها هستند و زمان ثابت تضمین نمی‌کنند. ثبت آرایه در این آزمایش کنترل‌شده ایمن است چون وقفهٔ دوم فقط پس از پایان ثبت رخداد 1 درخواست می‌شود؛ Trace_Event بافر عمومی چندنویسنده نیست.

  1. پیش از پروگرام، ترتیب و هم‌پوشانی دو پالس را برای هر نسخه بنویسید.
  2. نسخهٔ درست را بسازید و با روش درس 6 پروگرام کنید؛ کانال‌ها را به PB6 و PB7 و زمین مشترک وصل کنید. شمارهٔ بسته را با شمارهٔ هدر اشتباه نگیرید.
  3. با تریگر CH1 شکل واقعی را ثبت کنید؛ در GROUP4 انتظار پالس PB7 داخل PB6 و در GROUP3 پس از آن است. شکست‌نقطه زمان‌بندی را تغییر می‌دهد.
  4. آرایه و متغیرهای Active را در دیباگر بررسی و شرایط ابزار، مقیاس‌ها و نتیجه را در hardware-worksheet.csv بنویسید. تفاوت را با پیش‌بینی مقایسه کنید.

خطای عمدی آموزشی: نسخهٔ برابر را در حالی انتخاب کنید که انتظار تقدم دارید. ISR همچنان اجرا می‌شود ولی زیر‌اولویت، وقفهٔ جاری هم‌سطح را قطع نمی‌کند؛ با خواندن گروه و IPR علت را مشخص کنید. برای خطای نام هندلر، مدل بررسی ELF می‌تواند اتصال اشتباه را پیش از فلش تشخیص دهد. هیچ خطای واقعی HAL در این آزمایش تزریق نشده است.

اشکال‌زدایی و خطاهای رایج در پیاده‌سازی NVIC

در جریان پیاده‌سازی و اشکال‌زدایی سیستم‌های مبتنی بر وقفه در STM32، خطاهای مهندسی پرتکراری رخ می‌دهند که باید به صورت ساختاریافته کنترل شوند:

  • عدم تطابق نام روتین در C با فایل استارتاپ: نوشتن اسامی اشتباهی مانند EXTI0_Handler به جای EXTI0_IRQHandler باعث عدم جایگزینی نماد ضعیف شده و برنامه بدون بروز خطای کامپایل، در Default_Handler قفل می‌کند.
  • اشتباه‌گرفتن آزمایش با EXTI واقعی: این پروژه هیچ EXTI سخت‌افزاری ندارد و EXTI_PR را سرویس نمی‌کند. فقط درخواست نرم‌افزاری NVIC هنگام ورود پذیرفته می‌شود. اگر بعداً منبع فیزیکی اضافه شود، باید پرچم همان پریفرال نیز درست سرویس شود؛ این تغییر به درس 11 مربوط است.
  • اعمال جابجایی دوگانه در اولویت: توابع کتابخانه مانند HAL_NVIC_SetPriority خودشان مقادیر را شیفت می‌دهند. اگر برنامه‌نویس مقدار شیفت‌داده‌شده را به ورودی این توابع پاس دهد، بیت‌های اولویت در بایت رجیستر به طور کامل مخدوش می‌شوند.
  • انتظار تقدم در سطوح زیر‌اولویت: در صورت تنظیم گروه اولویت روی NVIC_PRIORITYGROUP_3 و اختصاص سطح تقدم یکسان به دو وقفه، زیر‌اولویت بالاتر هرگز نمی‌تواند وقفه هم‌گروه خود را قطع کند.

پرسش‌های چهارگزینهای ارزیابی مفهومی

پرسش 1: در معماری Cortex-M4، اولین کلمه 32 بیتی واقع در آدرس پایه جدول بردار وقفه (0x08000000) نشان‌دهنده چیست؟
الف) آدرس بردار ریست (Reset_Handler)
ب) مقدار اولیه پشته اصلی (Initial MSP)
ج) آدرس اولین بردار خطای سخت‌افزاری (HardFault)
د) کلمه کلیدی پیکربندی کلاک سیستم
پاسخ صحیح: گزینه ب. مقدار کلمه در آدرس 0x08000000 به رجیستر پشته MSP اختصاص می‌یابد و آدرس تابع ریست در آدرس 0x08000004 قرار دارد.

پرسش 2: در میکروکنترلر STM32F303 با تعریف __NVIC_PRIO_BITS = 4، مقدار اولویت 3 در کدام بیت‌های فیلد 8 بیتی رجیستر IPR ثبت می‌شود؟
الف) بیت‌های 0 تا 3 با مقدار عددی 0x03
ب) بیت‌های 4 تا 7 با مقدار هگزادسیمال 0x30
ج) بیت‌های 1 تا 4 با مقدار عددی 0x06
د) در تمام 8 بیت پخش می‌شود
پاسخ صحیح: گزینه ب. در سخت‌افزار STM32 تنها بیت‌های 7:4 پیاده‌سازی شده‌اند، بنابراین اولویت 3 به صورت 0x3 << 4 = 0x30 در بایت رجیستر قرار می‌گیرد.

پرسش 3: در گروه اولویت NVIC_PRIORITYGROUP_3، تفاوت وقفه A با (Preempt=1, Sub=0) و وقفه B با (Preempt=1, Sub=1) در چیست؟
الف) وقفه A می‌تواند در حین اجرای وقفه B، آن را متوقف کند.
ب) وقفه B می‌تواند در حین اجرای وقفه A، آن را متوقف کند.
ج) هیچ‌یک توانایی قطع کردن دیگری را ندارند، اما در صورت وقوع همزمان، وقفه A زودتر اجرا می‌شود.
د) وقفه A بدون تاخیر Stacking اجرا می‌شود.
پاسخ صحیح: گزینه ج. از آنجا که هر دو وقفه اولویت تقدم یکسان (1) دارند، هیچ‌کدام نمی‌توانند اجرای دیگری را قطع کنند. زیر‌اولویت تنها در مرحله داوری صف انتخابی کاربرد دارد.

پرسش 4: مکانیسم سخت‌افزاری Tail-Chaining چگونه زمان تعویض میان دو وقفه پیاپی را کاهش می‌دهد؟
الف) با متوقف ساختن کلاک پریفرال‌های دیگر
ب) با صرف‌نظر کردن از فرآیند بازیابی و ذخیره‌سازی مجدد رجیسترهای پشته
ج) با غیرفعال کردن بیت‌های رجیستر PRIMASK
د) با کاهش تعداد رجیسترهای ذخیره‌شده به 4 عدد
پاسخ صحیح: گزینه ب. در حالت Tail-Chaining پردازنده با حذف بازیابی و پشته‌گذاری مجدد، با هزینهٔ ایده‌آل 6 سیکل به وقفهٔ واجد شرایط بعدی می‌رود؛ مقدار واقعی این برد باید اندازه‌گیری شود.

پرسش 5: اگر رجیستر BASEPRI در گروه NVIC_PRIORITYGROUP_4 با مقدار 0x40 مقداردهی شود، کدام وقفه‌ها مسدود خواهند شد؟
الف) وقفه‌های با اولویت منطقی 0، 1، 2 و 3
ب) وقفه‌های با اولویت منطقی 4 تا 15
ج) کلیه وقفه‌های میکروکنترلر به جز Reset
د) فقط خطای سخت‌افزاری HardFault
پاسخ صحیح: گزینه ب. رجیستر BASEPRI اولویت‌هایی را مسدود می‌کند که مقدار عددی آن‌ها بزرگتر یا مساوی مقدار مشخص‌شده باشد. این آستانه، اولویت‌های 0 تا 3 را ماسک نمی‌کند؛ شرایط دیگر ورود همچنان برقرارند.

پرسش 6: چرا استفاده از کلمه کلیدی volatile برای تضمین عدم بروز تداخل داده میان وقفه و حلقه اصلی کافی نیست؟
الف) زیرا کامپایلر آن را در حالت بهینه‌سازی نادیده می‌گیرد.
ب) زیرا تنها از بهینه‌سازی خواندن جلوگیری می‌کند اما عملیات Read-Modify-Write را اتمیک نمی‌سازد.
ج) زیرا فقط برای متغیرهای پشته کارایی دارد.
د) زیرا وقفه دسترسی مستقیم به حافظه فلش را قطع می‌کند.
پاسخ صحیح: گزینه ب. متغیرهای volatile مانع استفاده از کش رجیستر می‌شوند اما کدهایی مثل عملگر افزایش نیازمند چند چرخه اسمبلی هستند که وقفه می‌تواند در میانه آن‌ها رخ دهد.

تمرین‌های عملی و چالشهای تحلیلی

تمرین 1: شبیه‌سازی رفتار زیر‌اولویت و عدم تقدم در گروهبندی 3
گروه‌بندی NVIC را به NVIC_PRIORITYGROUP_3 تغییر دهید. وقفه EXTI0_IRQn را با اولویت تقدم 1 و زیر‌اولویت 1، و وقفه EXTI1_IRQn را با اولویت تقدم 1 و زیر‌اولویت 0 مقداردهی کنید. در روتین EXTI0، مجدداً وقفه EXTI1 را تحریک نرم‌افزاری نمایید. پیش‌بینی کنید خروجی پین‌های PB6 و PB7 روی اسیلوسکوپ چه تغییری خواهد کرد و نتیجه را تحلیل کنید.

تمرین 2: محاسبه ریاضی آدرس بردار و رجیستر IPR برای رابط USART1
با توجه به اینکه شیار وقفه USART1_IRQn در پردازنده شماره 37 است، محاسبه کنید:
الف) آدرس بردار این وقفه در جدول حافظه فلش با شروع از 0x08000000 دقیقاً کجاست؟
ب) این شیار در کدام شماره از رجیسترهای IPR با بسته‌بندی 4 بایت اولویت در هر کلمهٔ 32 بیتی و در کدام بایت (آفست 0 تا 3) قرار گرفته و برای تنظیم اولویت تقدم 5 در گروه 4، چه مقداری باید مستقیماً در این بایت نوشته شود؟

راهنمای تمرین 2: USART1_IRQn برابر 37 است؛ بردار 53 در آفست 0xD4 و آدرس 0x080000D4 قرار می‌گیرد. در نمای کلمه‌ای IPR، کلمهٔ شمارهٔ 9 و بایت شمارهٔ 1 است؛ اولویت منطقی 5 در GROUP4 به بایت 0x50 تبدیل می‌شود.

مراجع و پیوندهای مستندات رسمی

نمونهٔ رسمی نزدیک بررسی‌شده در STM32CubeF3 V1.11.6: مسیر Projects/STM32303C_EVAL/Examples/Cortex/CORTEXM_SysTick/Src/main.c برای مبنای کلاک و وقفهٔ هسته. آزمایش نرم‌افزاری 2 IRQ این درس تألیفی است. فهرست ناشر Mastering STM32، ویرایش دوم، فصل 7 را برای مسیر مفاهیم معرفی می‌کند؛ متن کامل کتاب در اختیار این بازبینی نبوده و ادعای مطالعهٔ آن نمی‌شود. مرجع رفتار، اسناد رسمی و منبع همان نسخه است.

دانلود فایل‌های همراه این آموزش

پروژهٔ واقعی، فایل .ioc، منابع، خروجی ELF/MAP، گزارش ساخت بدون هشدار، اسکریپت بررسی، کاربرگ آزمایش و 6 نمودار آموزشی با منبع ترسیم. فایل‌ها داخل ZIP هستند؛ اجرای روی برد و اندازه‌گیری واقعی انجام نشده‌اند.

دانلود به نسخهٔ مشخص همراه مقاله متصل است. ابتدا ZIP را استخراج و راهنما را مطالعه کنید.

با نظرات خود به تیم جبرا در بهبود کیفیت کمک کنید

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

سبد خرید
پیمایش به بالا