آموزش UART Interrupt و بافر حلقوی در STM32 | قسمت 25 امبدد C

embedded C 25
3 بازدید
۱۴۰۴-۰۶-۰۴
8 دقیقه
  • نویسنده: Alireza Abbasi
  • درباره نویسنده: ---

جزئیات برنامه

کد زیر شباهت زیادی به برنامه ورودی/خروجی سریال دارد، چون تنظیمات مربوط به ورودی/خروجی همان است البته با جزئیات اضافی بیشتر. اما در این مورد، موارد جدیدی را هم اضافه کرده‌ایم:

تابع NVIC_EnableIRQ سخت‌افزار کنترل‌کننده وقفه تودرتو (NVIC) را پیکربندی می‌کند. این بخش از سخت‌افزار وظیفه دارد هنگام وقوع یک وقفه (Interrupt) تصمیم بگیرد پردازنده چه کاری انجام دهد. در اینجا، این تابع وقفه‌ی USART2 را فعال می‌کند.

وقتی پردازنده ریست می‌شود، همه وقفه‌ها به‌صورت پیش‌فرض غیرفعال هستند، بنابراین باید به طور صریح اعلام کنیم که می‌خواهیم وقفه‌ی USART2 فعال و پردازنده در زمان وقوع آن، واکنش نشان دهد.

حالا بیایید به تابع myPuts نگاه کنیم که یک رشته (به‌جای یک کاراکتر واحد، مانند myPutchar ) را به دستگاه سریال ارسال می‌کند:

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

این که کدام وقفه‌ها در میکروکنترلر فعال هستند را با نمادهایی (symbols) نشان داده و کنترل می‌شوند. برای مثال بیت USART_CR1_TXNEIE به UART می‌گوید که وقتی بافر داده فرستنده خالی شد، وقفه ایجاد کند. در اینجا برخی از نمادهایی که باید به آنها توجه کنید، آورده شده است:

  • USART_CR1_IDLEIE: فعال‌سازی وقفه IDLE
  • USART_CR1_RXNEIE: فعال‌سازی وقفه دریافت
  • USART_CR1_TCIE: فعال‌سازی وقفه تکمیل انتقال (ایجاد وقفه در زمان خارج‌شدن کاراکتر از بافر، نه زمانی که برای اولین‌بار یک کاراکتر را در رجیستر انتقال بارگذاری می‌کنیم)
  • USART_CR1_PEIE: فعال‌سازی وقفه خطای پریتی (Parity)

وقتی می‌خواهیم اولین کاراکتر را بفرستیم، رجیستر TDR پر می‌شود. با انتقال داده از TDR به TSR، رجیستر TDR خالی شده و وقفه مربوطه فعال می‌شود. از این مرحله به بعد، روال وقفه وظیفه ارسال ادامه رشته را بر عهده خواهد داشت.

روال وقفه واقعی به شرح زیر است:

اعلان تابع (function declaration) از یک «نام خاص» یا همان magic name استفاده می‌کند که به کامپایلر یا سیستم می‌فهماند این تابع همان روال وقفه (Interrupt Service Routine) شماره ۱ در کد بالا است.

وقتی این تابع اجرا شود، می‌فهمیم که یک وقفه از USART2 دریافت شده است.

بااین‌حال، این فقط به ما می‌گوید که وقفه‌ای از سمت USART2 آمده، ولی نوع دقیق آن را مشخص نمی‌کند، زیرا USART می‌تواند چندین نوع وقفه مختلف ایجاد کند، مثل:

  • USART_ISR_TXE → بافر ارسال (TDR) خالی شده و آماده ارسال داده جدید است.
  • USART_ISR_CTSIF → وقفه تغییر وضعیت CTS (Clear To Send).
  • USART_ISR_TC → ارسال کامل داده (Transmission Complete).
  • USART_ISR_RXNE → رجیستر دریافت خالی نیست، یعنی داده‌ی جدید آماده خواندن است.
  • USART_ISR_ORE → خطای Overrun، یعنی داده‌ی جدید قبل از خوانده‌شدن داده قبلی رسیده.
  • USART_ISR_IDLE → خط idle یا بیکار شناسایی شده (یعنی مدتی داده‌ای دریافت نشده).
  • USART_ISR_FE → خطای Frame، یعنی ساختار فریم داده نادرست بوده.

همه این وقفه‌ها منجر به فراخوانی USART2_IRQHandler می‌شوند.

به زبان ساده‌تر: تابع وقفه فقط شروع ماجراست؛ ما باید آن را بررسی کنیم تا مشخص  شود چه رویدادی در USART2 اتفاق افتاده تا واکنش مناسب آن را انجام دهیم.

ابتدا باید بررسی شود که آیا وقفه «بافر ارسال خالی» (Transmit Buffer Empty Interrupt) رخ داده است یا خیر.

بررسی و مدیریت وقفه «بافر ارسال خالی» (TXE Interrupt)

1. بررسی وقوع وقفه

ابتدا باید مشخص شود که آیا وقفه Transmit Buffer Empty (TXE) رخ داده است یا خیر.

این مرحله نقطه شروعِ روال وقفه است.

2. لزوم بررسی مقدار usart2String

به صورت منطقی، تابع وقفه نباید زمانی اجرا شود که متغیر usart2String مقدار NULL داشته باشد.

اما در عمل، تکیه بر فرضیات می‌تواند خطرناک باشد، بنابراین:

  • یک لایه محافظتی اضافه می‌کنیم تا در صورت وقوع شرایط غیرمنتظره، برنامه دچار کرش یا رفتار غیرقابل پیش‌بینی نشود.
  • اگر usart2String برابر NULL باشد:
    • وقفه کنونی غیرفعال شود.
    • از انجام هر عملیات دیگری خودداری گردد.

3. علت اهمیت این بررسی

در صورت انجام ندادن این بررسی، ممکن است یک Dereference روی اشاره‌گر NULL انجام شود.

این یعنی تلاش برای دسترسی به داده‌ای که آدرس معتبر ندارد.

  • در STM32، چنین عملی تشخیص داده شده و باعث Memory Fault Interrupt می‌شود.
  • در این لحظه:
    1. روال وقفه فعلی متوقف می‌گردد.
    2. کنترل برنامه به وقفه Memory Fault منتقل می‌شود.
  • اگر این روال توسط ما تعریف نشده باشد، پردازنده به روال پیش‌فرضش منتقل خواهد شد که باعث توقف کامل سیستم (Lock-up) تا زمان ریست می‌شود.
راه‌اندازی واحد ADC توسط DMA در STM32 | آموزش STM32 با توابع LL

4. بررسی پایان رشته

اگر مقدار usart2String معتبر باشد، مرحله بعد بررسی پایان رشته است:

  • رشته تمام شده باشد:
    • مقدار usart2String به NULL تغییر داده می‌شود.
    • این کار به سطح بالاتر برنامه اطلاع می‌دهد که عملیات ارسال کامل شده و وقفه ارسال باید غیرفعال شود.
  • رشته تمام نشده باشد:
    • چون بافر ارسال (TDR) خالی است، یک کاراکتر جدید در آن قرار می‌گیرد.
    • سپس اشاره‌گر یک خانه جلو می‌رود تا در وقفه بعدی، کاراکتر بعدی ارسال شود.

5. بازگشت به اجرای عادی

بعد از قرار دادن کاراکتر در TDR:

  • واحد UART مشغول ارسال است.
  • روال وقفه کار اضافی ندارد، پس اجرای برنامه ادامه پیدا می‌کند.

زمانی که کاراکتر فعلی ارسال شد و TDR دوباره خالی شد، وقفه جدید ایجاد می‌شود.

این چرخه تا ارسال آخرین کاراکتر و غیرفعال شدن وقفه ادامه دارد.

✅ نکته

یکی از ویژگی‌های مثبت پردازنده‌های مبتنی بر ARM این است که یک روال معمولی (Ordinary Procedure) می‌تواند به‌عنوان یک روال وقفه مورداستفاده قرار گیرد.

سخت‌افزار این پردازنده وظیفه ذخیره و بازیابی تمام وضعیت‌های لازم (Register State) را برای اجرای ایمن روال وقفه بر عهده دارد.

بااین‌حال، همه پردازنده‌ها چنین قابلیتی ندارند.

برای مثال، در خانواده پردازنده‌های PIC لازم است پیش از هر روال وقفه، از یک کلمه کلیدی اختصاصی با نام interrupt (به‌عنوان یک افزونه خاص PIC) استفاده شود:                                                              

مهندسان سخت‌افزار در STMicroelectronics با لطف فراوان این فرایند را با یک نمودار توضیح داده‌اند (شکل زیر را ببینید).

نگاشت وقفه USART

نگاشت وقفه USART

این شکل نشان می‌دهد که اگر بیت فعال‌سازی وقفه ارسال کاراکتر (TCIE) روشن  باشد (1) و یک کاراکتر ارسال شده باشد (2) (TC)، خروجی گیت AND درست (true) است (3). این نتیجه با خروجی سه وقفه دیگر ترکیب می‌شود و اگر هر کدام از آن‌ها درست باشد، نتیجه نهایی گیت OR (4) درست (true) است. سپس این نتیجه با خروجی یک گیت OR دیگر (5) که برای تمام سیگنال‌های دیگر است ترکیب می‌شود و خروجی آخرین گیت OR سیگنال وقفه USART است. توجه داشته باشید که این شکل برای ساده‌سازی فرایند است. اگر می‌خواهید معنی مخفف‌ کلمه‌های ورودی را بدانید، باید دفترچه راهنمای 800 صفحه‌ای این پردازنده را مطالعه کنید.

جهنم وقفه (Interrupt Hell)

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

اولین مشکل، وقفه‌ها می‌توانند در هر زمانی جریان عادی برنامه را قطع کنند. برای مثال، کد زیر را در نظر بگیرید:

بعد از اجرای این کد، مقدار i چه عددی است؟ پاسخ واضح 5 است، مگر اینکه درست قبل از اجرای این کد، یک روال وقفه اجرا شده و مقدار i را تغییر داده باشد:

روال‌های وقفه (Interrupt Routines) به‌صورت غیرهم‌زمان (Asynchronous) اجرا می‌شوند، به این معنا که ممکن است در هر لحظه‌ای اجرا شوند.

به همین دلیل، خطاهایی که در اثر پیاده‌سازی نامناسب این روال‌ها ایجاد می‌شوند، می‌توانند بسیار دشوار برای بازتولید (Reproduce) باشند.

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

کار با ماژول EC200 کویکتل (4G LTE)

کشف این مشکل نیازمند حجم زیادی از آزمون و بررسی بود.

به دلیل مشکلات ذاتی روال‌های وقفه، بهتر است با آن‌ها محترمانه برخورد کنیم. مهم‌ترین قانون طراحی هنگام کار با روال‌های وقفه کوچک و ساده نگه‌داشتن آنها است. هر چه روال کار کمتری انجام دهد، احتمال خطای کمتری وجود دارد. بهتر است کارهای پیچیده را به کد سطح بالاتر واگذار کنید، جایی که دیباگ به‌راحتی انجام می‌پذیرد و بازتولید خطا مشکل نیست.

همچنین روال‌های وقفه باید به‌سرعت اجرا شوند، زیرا در حین اجرای یک روال وقفه، سایر وقفه‌ها تا پایان‌یافتن آن به تعویق می‌افتند. اگر در یک روال وقفه که در حال خواندن یک کاراکتر از UART1 است، زمان زیادی صرف کنید، دستگاه دیگری مانند UART2 ممکن است داده‌هایی را از دست بدهد؛ زیرا وقفه آن نمی‌تواند به‌موقع اجرا شود.

استفاده از بافر برای افزایش سرعت

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

به‌عنوان‌مثال، اگر بخواهیم چندین پیام کوتاه را پشت‌سرهم ارسال کنیم، هر پیام باید منتظر اتمام ارسال پیام قبلی باشد:

در این حالت، هر فراخوانی تابع myPuts تا زمانی که ارسال پیام قبلی کامل نشده باشد، متوقف می‌ماند (Blocking).

یکی از راه‌حل‌های این مشکل، ایجاد یک بافر برای ذخیره داده‌های کاراکتری است تا روال وقفه بتواند آن را به‌مرور پردازش و ارسال کند.

این روش باعث افزایش پیچیدگی در طراحی برنامه می‌شود، اما سرعت ارسال داده توسط لایه اصلی برنامه (Top-Level) را به شکل قابل‌توجهی بهبود می‌بخشد.

استفاده از ورودی/خروجی سریال (Serial I/O) همواره نیازمند درنظرگرفتن تعادل میان سرعت و سادگی پیاده‌سازی است.

  • روش Polling بسیار ساده اما کند بود.
  • روش مبتنی بر وقفه برای ارسال یک رشته واحد (Single String Interrupt) سرعت بیشتری داشت، اما پیچیدگی آن نیز بالاتر بود.
  • روش فعلی مبتنی بر بافر سرعت بسیار بیشتری را فراهم می‌کند، ولی در مقابل، پیچیدگی پیاده‌سازی به شکل قابل‌توجهی افزایش‌یافته است.

این رابطه میان افزایش سرعت و افزایش پیچیدگی در اکثر برنامه‌ها و سامانه‌های نرم‌افزاری صادق است.

برای حل این مشکل، بیایید دوباره به بافر برگردیم. ما از یک بافر حلقه‌ای (circular buffer) با ساختار پایه‌ مشابه زیر استفاده خواهیم کرد:

به این ساختار بافر حلقوی (Circular Buffer) گفته می‌شود؛ زیرا اندیس‌ها (Indices) در هنگام رسیدن به انتهای بافر، مجدداً به ابتدای آن بازمی‌گردند.

به بیان دیگر، پس از قراردادن یک کاراکتر در آخرین خانه آرایه داده، مقدار putIndex از ۷ (معادل BUFFER_SIZE – 1) به 0 بازمی‌گردد.

به‌صورت گرافیکی، این شکل شبیه به شکل زیر است.

بافر حلقوی در حال اجرا

بافر حلقوی در حال اجرا

 

پیاده‌سازی ما به‌صورت عمدی شامل یک خطای جدی خواهد بود تا بتوانیم روش‌ها و فرایندهای شناسایی چنین خطاهایی را در یک برنامه کوچک و کنترل‌شده بررسی کنیم.

در ادامه، علائم این خطا، تکنیک‌های عیب‌یابی مورداستفاده برای شناسایی محل آن، و راهکار اصلاحی را در حین مرور برنامه نشان خواهم داد.

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

به‌صورت شبه‌کد، وظیفه فرستنده به شکل زیر است:

  • بررسی پر بودن بافر: اگر بافر پر باشد، فرستنده منتظر می‌ماند تا فضای کافی برای ارسال داده ایجاد شود.
  • قراردادن کاراکتر در بافر: فرستنده کاراکتر موردنظر را در عنصر بافر با اندیس putIndex قرار می‌دهد.
  • به‌روزرسانی اندیس putIndex: اندیس putIndex یک واحد افزایش می‌یابد و در صورت نیاز به ابتدای بافر برمی‌گردد (wrap می‌شود).
  • به‌روزرسانی تعداد کاراکترها: تعداد کاراکترهای موجود در بافر افزایش می‌یابد.

وظایف گیرنده:

  • دریافت کاراکتر از بافر: گیرنده کاراکتر را از بافر با اندیس getIndex دریافت می‌کند.
  • به‌روزرسانی اندیس getIndex: اندیس getIndex یک واحد افزایش می‌یابد و در صورت نیاز به ابتدای بافر برمی‌گردد (wrap می‌شود).
  • به‌روزرسانی تعداد کاراکترها: تعداد کاراکترهای موجود در بافر کاهش می‌یابد.
اطلاعات
3
0
0
لینک و اشتراک
profile

نویسنده: Alireza Abbasi

متخصص الکترونیک

ویراستار: MasoudHD
مقالات بیشتر
slide

پالت | بازار خرید و فروش قطعات الکترونیک

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

آیسی | موتور جستجوی قطعات الکترونیک

سامانه آی سی سیسوگ (Isee) قابلیتی جدید و کاربردی از سیسوگ است. در این سامانه سعی شده است که جستجو، انتخاب و خرید مناسب تر قطعات برای کاربران تسهیل شود. جستجو در آیسی
family

سیسوگ‌شاپ | فروشگاه محصولات Quectel

فروشگاه سیسوگ مجموعه ای متمرکز بر تکنولوژی های مبتنی بر IOT و ماژول های M2M نظیر GSM، GPS، LTE، NB-IOT، WiFi، BT و ... جایی که با تعامل فنی و سازنده، بهترین راهکارها انتخاب می شوند. برو به فروشگاه سیسوگ
family

سیسوگ فروم | محلی برای پاسخ پرسش‌های شما

دغدغه همیشگی فعالان تخصصی هر حوزه وجود بستری برای گفتگو و پرسش و پاسخ است. سیسوگ فروم یک انجمن آنلاین است که بصورت تخصصی امکان بحث، گفتگو و پرسش و پاسخ در حوزه الکترونیک را فراهم می‌کند. پرسش در سیسوگ فرم
family

سیکار | اولین مرجع متن باز ECU در ایران

بررسی و ارائه اطلاعات مربوط به ECU (واحد کنترل الکترونیکی) و نرم‌افزارهای متن باز مرتبط با آن برو به سیکار
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
خانواده سیسوگ
سیسوگ‌شاپ

فروشگاه محصولات Quectel

پالت
سیسوگ فروم

محلی برای پاسخ پرسش‌های شما

سیسوگ جابز
سیسوگ
سیسوگ فروم
سی‌کار

اولین مرجع متن باز ECU در ایران

سیسوگ مگ
آی‌سی

موتور جستجوی قطعات الکترونیکی

سیسوگ آکادمی
پالت

بازار خرید و فروش قطعات الکترونیک

دیدگاه ها

become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله