اگر هنگام اجرای یک وقفه، درخواست دوم برسد چه می‌شود؟

فرض کنید پردازنده سرگرم رسیدگی به یک رویداد است و رویداد دیگری درخواست رسیدگی می‌دهد. آیا کار اول قطع می‌شود یا درخواست دوم منتظر می‌ماند؟ در این درس با 2 وقفه و 2 خروجی، تفاوت این رفتارها را پیش‌بینی می‌کنیم و روش مشاهدهٔ آن‌ها را یاد می‌گیریم.

NVIC یا Nested Vectored Interrupt Controller کنترلر وقفهٔ هسته است. Nested یعنی وقفهٔ مناسب با فوریت بالاتر می‌تواند اجرای هندلر جاری را موقتاً متوقف کند. Vectored یعنی آدرس تابع پاسخ‌دهنده از جدول بردار انتخاب می‌شود. تابع پاسخ‌دهنده را هندلر یا ISR می‌نامیم؛ رسیدگی به درخواست به فعال‌سازی، ماسک‌ها و اولویت‌ها وابسته است و همیشه فوری نیست.

پیش‌نیاز، پیکربندی کلاک در درس 9 و آشنایی قبلی با GPIO است. سخت‌افزار STM32F303CBT6 روی GB100ST V2.0، پروگرامر SWD سازگار و برای مشاهدهٔ 2 خروجی اسیلوسکوپ با پروب مناسب لازم است. کلاک پایه همان HSE برابر 8 MHz و PLL با ضریب 9، HCLK برابر 72 MHz، APB1 برابر 36 و APB2 برابر 72 MHz با Flash برابر 2 WS است.

وضعیت: هر 2 پروژه کامپایل شده‌اند؛ اجرا روی برد و ثبت شکل موج هنوز انجام نشده است.

از درخواست تا تابع پاسخ‌دهنده

استثنای هسته با IRQ جانبی چه فرقی دارد؟

استثنا یا Exception سازوکار ویژهٔ هسته برای ورود به مسیرهایی مانند Reset، خطا و وقفه است؛ هر فراخوانی تابع یا تغییر مسیر عادی برنامه استثنا نیست. Reset استثنای 1 با اولویت ثابت -3، NMI استثنای 2 با اولویت ثابت -2 و HardFault استثنای 3 با اولویت ثابت -1 است. SysTick استثنای 15 و دارای اولویت قابل تنظیم است؛ PendSV نیز اولویت قابل تنظیم دارد. شناسهٔ منفی IRQn در CMSIS برای استثناهای هسته، به معنی ثابت‌بودن اولویت آن‌ها نیست. این اولویت‌های ثابت را با شناسهٔ IRQn یکی نگیرید.

IRQهای جانبی از واحدهایی مانند تایمر و ارتباط سریال می‌آیند. شمارهٔ آن‌ها در هدر دستگاه تعریف شده و شمارهٔ استثنا از رابطهٔ زیر به دست می‌آید. در این قطعه EXTI0_IRQn برابر 6 و EXTI1_IRQn برابر 7 است:

Exception number = 16 + IRQn
EXTI0: 16 + 6 = 22
EXTI1: 16 + 7 = 23
شمارهٔ استثنا و تفاوت وقفه‌های هسته و خارجی
نمودار طبقه‌بندی تألیفی استثناهای Cortex-M4 و رابطهٔ شمارهٔ استثنای خارجی با IRQn: EXTI0 با IRQ6 به شمارهٔ 22 و EXTI1 با IRQ7 به 23 می‌رسند. IRQn شناسه است، نه مقدار اولویت. منفی‌بودن شناسهٔ CMSIS به معنی اولویت ثابت نیست: اولویت SysTick و PendSV قابل تنظیم و اولویت Reset و NMI و HardFault ثابت است. PRIMASK و BASEPRI، NMI و HardFault را ماسک نمی‌کنند. برای دیدن جزئیات، تصویر را باز کنید.

جدول بردار و نماد weak

در فایل واقعی startup_stm32f303xc.s، جدول بردار با کلمات 32 بیتی قرار دارد. کلمهٔ نخست مقدار اولیهٔ MSP، اشاره‌گر پشته، و کلمهٔ دوم آدرس Reset_Handler است. برای EXTI0، آفست از ابتدای جدول برابر 22 ضرب در 4، یعنی 88 بایت یا 0x58 است؛ برای EXTI1 مقدار 0x5C است.

جدول در ELF این پروژه‌ها در 0x08000000 لینک شده است. در همین سورس، SystemInit تنظیم جابه‌جایی جدول را فعال نکرده و VTOR را بازنویسی نمی‌کند؛ در بوت معمول از Flash، نگاشت ابتدای حافظه اجازه می‌دهد VTOR اولیهٔ صفر به جدول Flash دسترسی بدهد. این توضیح را به بوت از SRAM، بوت‌لودر یا مقدار خوانده‌شده از برد تعمیم ندهید؛ VTOR روی برد خوانده نشده است.

بیت 0 اشاره‌گر هندلر در جدول برابر 1 است و وضعیت اجرای Thumb را نشان می‌دهد. نمادهای weak راه‌انداز در نبود تعریف قوی‌تر به Default_Handler، حلقهٔ بی‌پایان، اشاره می‌کنند. یک تابع C با نام و امضای درست، در مرحلهٔ لینک جای weak متناظر را می‌گیرد. در پروژهٔ کامل این درس، هندلرهای قوی EXTI0 و EXTI1 در بلوک USER CODE 4 فایل main.c هستند و در فایل وقفهٔ تولیدشده تعریف تکراری ندارند.

جدول بردار لینک‌شده و جایگزینی هندلر weak
نمودار تألیفی چند ورودی واقعی بخش بردار لینک‌شده در 0x08000000: MSP اولیه و Reset و ورودی‌های 22 و 23 در آفست‌های 0x58 و 0x5C. تعریف قوی برنامه alias ضعیف startup را در لینک جایگزین می‌کند. این شکل خواندن زندهٔ VTOR نیست؛ بیت 0 اشاره‌گر حالت اجرای Thumb را مشخص می‌کند. برای دیدن جزئیات، تصویر را باز کنید.

فعال‌بودن، انتظار و در حال رسیدگی بودن

Enabled یعنی اجازهٔ رسیدگی به خط داده شده است؛ این ویژگی را با Active اشتباه نگیرید. یک خط فعال‌شده ممکن است هیچ درخواستی نداشته باشد. وضعیت درخواست و رسیدگی را جدا دنبال می‌کنیم:

وضعیت معنا
Inactive نه درخواست Pending دارد و نه در رسیدگی است
Pending درخواست ثبت شده و منتظر پذیرش است؛ حتی ممکن است خط Disabled یا ماسک شده باشد
Active هسته درخواست را پذیرفته؛ وقفهٔ متوقف‌شده توسط وقفهٔ فوری‌تر نیز Active می‌ماند
Active and Pending هنگام رسیدگی به همان IRQ، درخواست دیگری برای آن معلق شده است

Pending یک شمارندهٔ همهٔ رویدادها نیست؛ چند درخواست لزوماً به همان تعداد فراخوانی ISR تبدیل نمی‌شوند. در وقفهٔ متصل به پریفرال، وضعیت خود واحد جانبی هم مهم است.

ثبات NVIC اثر یا مشاهده رابط CMSIS مرتبط
ISER / ICER نوشتن 1: فعال‌سازی / غیرفعال‌سازی؛ صفر بی‌اثر NVIC_EnableIRQ / NVIC_DisableIRQ
ISPR / ICPR نوشتن 1: ایجاد Pending / پاک‌کردن Pending NVIC_SetPendingIRQ / NVIC_ClearPendingIRQ
IABR خواندن وضعیت Active NVIC_GetActive
IPR اولویت رمزگذاری‌شدهٔ هر IRQ NVIC_SetPriority یا HAL_NVIC_SetPriority

این ثبات‌های set/clear نیاز به تغییر خواندن-نوشتن کل کلمه را برای آن کار کم می‌کنند؛ ایمنی همهٔ متغیرهای برنامه را تضمین نمی‌کنند. آدرس و رفتارشان در PM0214 و رابط‌های آن‌ها در core_cm4.h همراه پروژه آمده است.

حالت‌های Pending و Active و گیت‌های مستقل
نمودار تألیفی حالت‌ها و چند گذار منتخب: Inactive به معنی disabled نیست و IRQ ماسک‌شده یا غیرفعال می‌تواند pending شود. Active تا بازگشت handler می‌ماند و شامل handler تعلیق‌شده بر اثر preemption هم می‌شود. ذخیره و بازگرداندن PRIMASK با فعال‌سازی بی‌قیدوشرط فرق دارد. برای دیدن جزئیات، تصویر را باز کنید.

عدد کوچک‌تر یعنی فوریت بیشتر

در هدر STM32F303 مقدار __NVIC_PRIO_BITS = 4 است؛ 4 بیت بالایی بایت اولویت، یعنی بیت‌های 7 تا 4، پیاده‌سازی شده‌اند. اولویت منطقی از 0 تا 15 است و 0 فوری‌ترین مقدار قابل تنظیم است. این تعداد بیت را به همهٔ STM32ها تعمیم ندهید.

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

Preemption Priority یا اولویت تقدم مشخص می‌کند درخواست جدید در صورت مجازبودن پذیرش، می‌تواند هندلر جاری را متوقف کند یا نه. Subpriority یا زیراولویت نوبت انتخاب درخواست‌های Pending هم‌تقدم را تعیین می‌کند؛ بهتر بودن زیراولویت، اجازهٔ قطع یک ISR هم‌تقدم را نمی‌دهد. در برابری کامل اولویت، شمارهٔ استثنای کوچک‌تر معیار انتخاب است.

گروه HAL PRIGROUP خام بیت تقدم / زیراولویت تعداد حالت تقدم / زیراولویت
NVIC_PRIORITYGROUP_4 3 4 / 0 16 / 1
NVIC_PRIORITYGROUP_3 4 3 / 1 8 / 2

در GROUP4، صفر بیت زیراولویت یعنی فقط 1 حالت داریم: زیراولویت 0 است و میان درخواست‌های هم‌تقدم تمایزی ایجاد نمی‌کند. انتخاب گروه با HAL_NVIC_SetPriorityGrouping و مقدارگذاری با HAL_NVIC_SetPriority انجام می‌شود. آرگومان‌های HAL اعداد منطقی‌اند؛ آن‌ها را از قبل 4 بیت جابه‌جا نکنید. HAL در stm32f3xx_hal_cortex.c و CMSIS رمزگذاری را انجام می‌دهند.

GROUP4: IPR byte = preempt × 16
EXTI0 preempt 2 → 0x20; EXTI1 preempt 1 → 0x10
GROUP3: IPR byte = preempt × 32 + sub × 16
EXTI0 (Preempt=1, Sub=1) → 0x30
EXTI1 (Preempt=1, Sub=0) → 0x20
چهار بیت اولویت و گروه‌بندی HAL در STM32F303
نمودار تألیفی بیت‌های پیاده‌سازی‌شدهٔ IPR در STM32F303 و جدول تقسیم 4 بیت بین preemption و subpriority. HAL ورودی منطقی می‌گیرد؛ اولویت 1 در IPR برابر 0x10 کد می‌شود و نباید دوباره shift شود. subpriority وقفهٔ فعال هم‌اولویت را قطع نمی‌کند. برای دیدن جزئیات، تصویر را باز کنید.

مقصد این تنظیم‌ها میدان PRIGROUP در SCB_AIRCR و بایت‌های NVIC_IPR است. پیکربندی اولیهٔ HAL و سپس کد کاربر را با هم ببینید؛ معیار رفتار، تنظیم نهایی است. جدول تنظیمات یا انتخاب یک گزینهٔ CubeMX، بدون بررسی فایل تولیدشده، شاهد اجرای آن نیست.

هسته هنگام ورود و خروج چه می‌کند؟

Stacking: نگه‌داشتن وضعیت کار قبلی

برای بازگشت به کار قبلی، هسته یک قاب پایه شامل R0 تا R3، R12، LR، PC و xPSR روی پشته ذخیره می‌کند؛ 8 کلمهٔ 32 بیتی، یعنی 32 بایت. این مقدار، مصرف کل پشتهٔ ISR نیست: هم‌ترازی، متغیرهای تابع و در شرایط مربوط به زمینهٔ ممیز شناور، قاب FPU و lazy stacking هم باید در نظر گرفته شوند. صرف روشن‌بودن FPU به معنی اضافه‌شدن همیشگی همهٔ ثبات‌های آن نیست.

Tail-chaining و Late arrival

در Tail-chaining، اگر هنگام خروج، استثنای Pending واجد شرایط پذیرش باشد، هسته می‌تواند بازیابی و ذخیرهٔ دوبارهٔ قاب را حذف و به هندلر بعدی منتقل شود. وجود هر Pending دلخواه کافی نیست؛ ماسک و قواعد اولویت همچنان برقرارند.

در Late arrival، اگر درخواست فوری‌تر هنگام ورود به استثنا و پیش از اجرای دستور نخست هندلر اولیه برسد، هسته می‌تواند همان پشته‌گذاری را ادامه دهد و هندلر فوری‌تر را انتخاب کند. درخواست اولیه طبق وضعیت و قواعد پذیرش بعداً رسیدگی می‌شود.

مدل فازهای Tail-chaining و Late arrival
نمودار تألیفی و کیفی فازهای بازگشت و ورود جداگانه، استفادهٔ دوباره از frame در tail-chaining و ورود درخواست فوری‌تر هنگام ذخیرهٔ frame. طول بلوک‌ها مقیاس زمان واقعی ندارد و شمارش ثابت 12 یا 6 سیکل یا تأخیر برد ادعا نشده است. frame پایه 8 word است و هم‌ترازی و FPU می‌تواند به آن اضافه کند. برای دیدن جزئیات، تصویر را باز کنید.

اعداد معماری مانند 12 چرخه برای ورود و 6 چرخه برای زنجیره‌شدن، با فرض‌های مناسب دربارهٔ حافظه و سامانه مطرح می‌شوند؛ زمان تضمین‌شدهٔ این برد نیستند. انتظار Flash، دسترسی باس، ماسک‌ها و وضعیت زمینه اثر دارند. نمودار ترتیب پالس‌های آزمایش بعدی، اندازه‌گیری دقیق این چرخه‌ها یا اثبات مستقل Late arrival نیست.

از دادهٔ مشترک چگونه محافظت کنیم؟

اگر حلقهٔ اصلی داده‌ای را تغییر دهد و ISR همان داده را میان عملیات بخواند یا تغییر دهد، ممکن است نتیجه ناسازگار شود. بخش کوتاهی که برای حفظ سازگاری از این تداخل محافظت می‌کنیم، بخش بحرانی است.

__disable_irq() با PRIMASK پذیرش استثناهای دارای اولویت قابل تنظیم را ماسک می‌کند؛ NMI و HardFault ماسک نمی‌شوند. وضعیت قبلی را ذخیره و همان را بازیابی کنید: روشن‌کردن بی‌قید IRQ در پایان می‌تواند ماسکِ ایجادشده در کد بالاتر را اشتباه بردارد. بخش بحرانی را کوتاه نگه دارید و در آن منتظر وقفه یا HAL_Delay نمانید.

BASEPRI آستانهٔ رمزگذاری‌شدهٔ اولویت است. در GROUP4 مقدار 0x40 اولویت‌های منطقی 4 تا 15 را از پذیرش بازمی‌دارد؛ 0 تا 3 با این آستانه مسدود نمی‌شوند، ولی برای اجرا هنوز باید سایر شرط‌ها را داشته باشند. BASEPRI برابر صفر این آستانه را غیرفعال می‌کند. پروژه‌های همراه آزمایش BASEPRI ندارند؛ این بخش توضیح معماری بر پایهٔ PM0214 است.

volatile قفل نیست

volatile باعث می‌شود دسترسی‌های مربوط به شیء طبق قواعد زبان حفظ شوند؛ برای متغیرهایی که ISR تغییر می‌دهد مفید است. اما عملیات مرکب مانند counter++ را اتمیک نمی‌کند و همگام‌سازی عمومی نمی‌سازد. تعداد و شکل دستورهای ماشین به کد و کامپایلر وابسته است؛ عبارت C را «همیشه 3 دستور اسمبلی» ننامید. اگر چند زمینه دادهٔ مشترک را تغییر می‌دهند، باید روش حفاظت متناسب طراحی شود.

آزمایش: درخواست دوم قطع می‌کند یا منتظر می‌ماند؟

در این آزمایش فقط ورودی‌های NVIC با نام EXTI0 و EXTI1 را نرم‌افزاری Pending می‌کنیم. خط EXTI، لبهٔ GPIO، کلید یا پرچم EXTI_PR پیکربندی نشده است؛ تابع NVIC_SetPendingIRQ مستقیماً درخواست NVIC می‌سازد. سرویس وقفهٔ فیزیکی پایه به درس بعد مربوط است.

در GB100ST V2.0، PB6 به LED فعال با High از طریق NPN وصل است و PB7 نشانگر هندلر دوم می‌شود. شماره‌های بستهٔ LQFP48 برای آن‌ها 42 و 43 است؛ این‌ها شمارهٔ هدر نیستند. محل اتصال را از برچسب و شماتیک پیدا کنید. خروجی‌ها Push-Pull، بدون Pull و ابتدا Low هستند.

تنظیم واقعی دو نمونه

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

  /* 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);

هندلر نخست، PB6 را High و رویداد 1 را ثبت می‌کند، سپس EXTI1 را Pending می‌کند؛ رویدادهای 3 و 4 ادامهٔ کار نخست‌اند. هندلر دوم، PB7 را High، Active بودن EXTI0 را ثبت و رویداد 2 را اضافه می‌کند. این گزیده عین بلوک USER CODE 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);
}

این قطعه‌ها برنامهٔ مستقل نیستند؛ اعلان‌های volatile و prototypeها در بلوک‌های PV و PFP، پیکربندی GPIO و حلقهٔ اصلی در پروژهٔ کامل موجودند. تابع Trace_Event برای همین ترتیب کنترل‌شده استفاده شده است؛ درخواست دوم پس از پایان ثبت رویداد اول صادر می‌شود. آن را با اتکا به volatile به یک بافر عمومی امن برای چند نویسنده تبدیل نکنید. حلقه‌های NOP فقط فاصلهٔ آموزشی ایجاد می‌کنند و پهنای پالس دقیق یا اندازه‌گیری‌شده نیستند.

حلقهٔ اصلی پیش از هر آزمون، وضعیت را با حفظ PRIMASK پاک می‌کند. دو خط زیر عین آغاز بدنهٔ بلوک موجود USER CODE 3 در حلقهٔ while است؛ فقط همین بخش نمایش داده شده، نه آکولادهای قالب تولیدی:

uint32_t saved_primask = __get_PRIMASK();
    __disable_irq();

در ادامهٔ همین بلوک، کد موجود پس از پاک‌کردن داده‌ها و فعال‌سازی این 2 IRQ، با __set_PRIMASK(saved_primask) ماسک ذخیره‌شده را بازمی‌گرداند؛ سپس با NVIC_SetPendingIRQ(EXTI0_IRQn) آزمون را آغاز می‌کند و DSB/ISB دارد. HAL_Delay(1000U) فقط در حلقهٔ اصلی است و تکرار آزمون را تقریباً هر 1 ثانیه ممکن می‌کند؛ برنامه زمان‌سنج دقیق تکرار نیست. در ISR نه HAL_Delay داریم و نه قالب‌بندی متن.

قبل از اتصال ابزار پیش‌بینی کنید

نمونه رفتار مورد انتظار trace_events مورد انتظار high_saw_low_active
GROUP4، تقدم EXTI1 بهتر EXTI1 در میانهٔ EXTI0؛ PB7 درون بازهٔ High بودن PB6 1, 2, 3, 4 1
GROUP3، تقدم برابر EXTI1 پس از پایان EXTI0؛ PB7 پس از بازهٔ PB6 1, 3, 4, 2 0
ترتیب پیش‌بینی‌شدهٔ دو آزمایش نرم‌افزاری NVIC
مدل تألیفی ترتیب نشانگرهای PB6 و PB7 در دو پروژهٔ واقعی: GROUP4 با تقدم متفاوت trace برابر [1,2,3,4] و high_saw_low_active برابر 1 را پیش‌بینی می‌کند؛ GROUP3 با تقدم برابر و زیر‌اولویت متفاوت trace برابر [1,3,4,2] و مقدار 0 را پیش‌بینی می‌کند. IRQ6 و IRQ7 مستقیماً در NVIC نرم‌افزاری pending می‌شوند؛ پریفرال EXTI یا لبهٔ فیزیکی پیکربندی نشده و شکل ثبت اسیلوسکوپ یا اثبات سیکل tail-chain نیست. برای دیدن جزئیات، تصویر را باز کنید.

در نمونهٔ نخست، EXTI0 هنگام وقفه‌پذیری همچنان Active است؛ در نمونهٔ دوم، بهتر بودن زیراولویت EXTI1 باعث قطع EXTI0 نمی‌شود. این جدول پیش‌بینی بر پایهٔ کد است، نه خروجی ثبت‌شدهٔ اسیلوسکوپ.

  1. یکی از پروژه‌های کامل را جدا بسازید و نام نمونه و ترتیب پیش‌بینی‌شده را در کاربرگ بنویسید. هر دو پروژه را در یک پوشه ادغام نکنید.
  2. با تغذیه و اتصال SWD مناسب، همان نمونه را پروگرام کنید. زمین پروب‌ها را به GND و کانال‌ها را به PB6 و PB7 وصل کنید؛ به خروجی‌ها ولتاژ خارجی ندهید و تنظیم تضعیف و سطح منطقی ابزار را تطبیق دهید.
  3. با Trigger مناسب روی لبهٔ PB6، ترتیب 2 خروجی را ثبت کنید. برای دیدن پهنای واقعی باید مقیاس زمانی را از مشاهده تعیین کنید؛ این مقاله عدد ساختگی برای آن نمی‌دهد.
  4. آرایهٔ trace_events، طول، overflow و متغیر Active را از راه SWD بررسی و با پیش‌بینی مقایسه کنید. توقف دیباگر رفتار زمانی را تغییر می‌دهد؛ یک اجرای متوقف‌شده را شکل موج کار پیوسته معرفی نکنید.
  5. نمونهٔ دوم را مستقل تکرار و نام firmware، ترتیب، شرایط ابزار و هر اختلاف را ثبت کنید. ترتیب پالس‌ها به‌تنهایی اندازه‌گیری 6 چرخهٔ Tail-chaining نیست.

اگر ترتیب مورد انتظار دیده نشد

نشانه مسیر بررسی در همین پروژه
هیچ هندلری اجرا نمی‌شود به رسیدن main به NVIC_SetPendingIRQ، فعال‌بودن IRQها و PRIMASK نگاه کنید. Pending ممکن است پیش‌تر پذیرفته و پاک شده باشد؛ صفر دیدن ISPR به‌تنهایی نبود درخواست را ثابت نمی‌کند
Default_Handler اجرا می‌شود نام دقیق EXTI0_IRQHandler و EXTI1_IRQHandler و حضور تعریف قوی در لینک را با startup تطبیق دهید؛ تعریف تکراری هم درست نیست
PB7 داخل PB6 نیست نام نمونه و macro و تنظیم نهایی گروه/تقدم را بررسی کنید؛ در نمونهٔ تقدم برابر این رفتار طبیعی است
متغیرها با مشاهدهٔ موج ناسازگارند زمان خواندن نسبت به پاک‌شدن دور بعد، توقف دیباگر، trace_length و trace_overflow را بررسی کنید؛ آرایه نمونهٔ همین آزمون است
پالس دیده نمی‌شود ولی trace تغییر می‌کند پایه و زمین، trigger، مقیاس زمان و سطح سیگنال ابزار را بررسی کنید؛ چشم انسان پالس کوتاه LED را لزوماً تفکیک نمی‌کند

در همین نمونه برای EXTI_PR پاک‌کردن لازم نداریم، چون منبع پیرامونی EXTI پیکربندی نشده است. در درس وقفهٔ واقعی GPIO، سرویس منبع و پرچم جانبی هم به مسیر افزوده می‌شود. افزودن بی‌دلیل HAL_GPIO_EXTI_IRQHandler به این هندلرها، توضیح این آزمایش را عوض می‌کند.

خودآزمایی

پرسش 1: آیا IRQ فعال‌شده حتماً در حال اجراست؟
پاسخ: خیر. Enabled اجازهٔ رسیدگی است؛ Pending درخواست ثبت‌شده و Active درخواست پذیرفته‌شده است. Active بودن حتی هنگام توقف توسط ISR فوری‌تر باقی می‌ماند.

پرسش 2: IRQn با اولویت منفی Reset و NMI چه تفاوتی دارد؟
پاسخ: IRQn شناسهٔ بردار در CMSIS است، نه فوریت. اولویت ثابت Reset/NMI/HardFault و اولویت منطقی قابل تنظیم 0 تا 15، موضوع جداگانه‌اند.

پرسش 3: چرا بیت 0 اشاره‌گر هندلر در جدول 1 است؟
پاسخ: برای وضعیت اجرای Thumb است. شمارهٔ شیار از شمارهٔ استثنا و فاصلهٔ هر شیار از 4 بایت به دست می‌آید؛ این بیت شمارهٔ اولویت نیست.

پرسش 4: آیا زیراولویت بهتر ISR هم‌تقدم را قطع می‌کند؟
پاسخ: خیر. تقدم شرط مقایسه برای preemption است؛ زیراولویت نوبت درخواست‌های Pending هم‌تقدم را تعیین می‌کند.

پرسش 5: آیا volatile شمارنده را در برابر تداخل امن می‌کند؟
پاسخ: به‌تنهایی خیر؛ دسترسی‌های volatile را حفظ می‌کند ولی عملیات مرکب و چندمرحله‌ای را اتمیک نمی‌کند. حفاظت باید متناسب با نویسندگان و خوانندگان طراحی شود.

پرسش 6: چرا PRIMASK قبلی را بازیابی می‌کنیم؟
پاسخ: ممکن است کد فراخواننده قبلاً وقفه‌ها را ماسک کرده باشد. بازیابی مقدار ذخیره‌شده آن وضعیت را حفظ می‌کند؛ روشن‌کردن بی‌قید IRQ می‌تواند آن را نقض کند. BASEPRI به‌جای ماسک کلی، آستانهٔ اولویت دارد.

تمرین‌ها

تمرین 1، پایه: برای EXTI1 با IRQn برابر 7، شمارهٔ استثنا و آفست شیار از ابتدای جدول را حساب کنید. پاسخ: شماره 23 و آفست 92 بایت، یعنی 0x5C. کلمهٔ نخست جدول مقدار MSP است و در شماره‌گذاری شیارها صفر محسوب می‌شود.

تمرین 2، میانی: در GROUP3، A تقدم 2 و زیراولویت 1 و B تقدم 2 و زیراولویت 0 دارد. بایت IPR هر کدام چیست و آیا B می‌تواند A فعال را قطع کند؟ پاسخ: A برابر 0x50 و B برابر 0x40 از رابطهٔ تقدم ضرب در 32 به‌علاوهٔ زیراولویت ضرب در 16؛ B تقدم بهتری ندارد و A فعال را قطع نمی‌کند. این رمزگذاری مربوط به 4 بیت پیاده‌شدهٔ همین تراشه است.

تمرین 3، پیشرفته: ابتدا مدل verify_nvic_v2.py را روی رایانه اجرا کنید، سپس هر 2 پروژه را جدا روی برد مشاهده و جدول پیش‌بینی را با کاربرگ مقایسه کنید. معیار پایان: 5 دستهٔ بررسی مدل بگذرند؛ برای هر نمونه نام firmware، ترتیب trace، مقدار Active و شرایط مشاهده یا علت انجام‌نشدن آن ثبت شود. قبولی مدل معادل اجرای برد نیست و نباید برای پهنای پالس عدد حدسی ثبت کرد.

شاهد ساخت و منابع همراه

هر 2 پروژه با CubeMX 6.14.0، پایگاه 6.0.140 و CubeF3 V1.11.6 تولید شده‌اند. ساخت پاک با GCC 13.2.1 و CMake Debug با -O0 -g3 -Wall -Werror در 2026-11-02 بدون خطا و هشدار انجام شد. Flash هر نمونه 8956 بایت و RAM لینک 1632 بایت است: data برابر 12، BSS برابر 80، هم‌ترازی 4 و رزرو heap/stack برابر 512/1024 بایت. این اعداد مصرف اوج پشته را اندازه نمی‌گیرند.

مدل همراه 5 دستهٔ بررسی محاسبات و بردار و ابزار آرشیو 24 بررسی سورس، hash و ELF دارد. تعریف قوی هندلرها و شیارهای واقعی بردار از ELF بررسی شده‌اند؛ این شاهد آفلاین است. سورس/IOC، مجوزهای وابستگی‌ها و ELF/MAP آرشیوی تأییدشده با منشأ مشخص، گزارش ساخت تازه و کاربرگ مشاهده همراه‌اند. ساخت CMake شاهد واردکردن به GUI نیست؛ CubeIDE، پروگرام، اجرای برد، شکل موج و زمان ورود وقفه در این مرحله آزموده نشده‌اند.

منابع فنی: PM0214 Rev 10، مدل استثنا و اولویت‌ها؛ DS9118 Rev 14، قطعه و پایه‌ها؛ RM0316، بخش‌های تراشه؛ شماتیک GB100ST V2.0؛ HAL ثابت 953955a و stm32f3xx_hal_cortex.c. فایل‌های core_cm4.h، cmsis_gcc.h، هدر دستگاه و startup داخل سورس همراه، مراجع پیاده‌سازی CMSIS و بردار همین پروژه‌اند. نمودارها توضیح تألیفی معماری و پیش‌بینی‌اند؛ دادهٔ اندازه‌گیری نیستند.

دانلود فایل‌های همراه درس 10

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

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

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

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

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