آموزش میکروکنترلر STM32; سیسوگ در قسمت هشتم از آموزش میکروکنترلر STM32 رابط سریال UART را به صورت یک مثال عملی آموزش داد. در این قسمت از آموزش میکروکنترلر STM32 قصد دارد طریقه کار با وقفه رابط سریال را با استفاده و بدون استفاده از توابع کتابخانه hal آموزش دهد. در مجموعه آموزشی ” آموزش میکروکنترلر STM32 ” با سیسوگ همراه باشید.
وقفه رابط سریال با استفاده از توابع کتابخانه hal
برای شروع کار دوباره به محیط CubeMX میرویم و اینبار در قسمت Configuration بعد از زدن دکمه UART1 و رفتن به قسمت تنظیمات این رابط سریال برگه NVIC Setting را انتخاب میکنیم و تیک مورد نظر را مطابق عکس زیر فعال مینمائیم.
بعد از انجام این تنظیمات دوباره از CubeMX خروجی میگیریم و به محیط برنامهنویسی نرمافزار keil وارد میشویم. حال اگر توجه کنید در برگه stm32f1xx_it.c یک روتین وقفه برای رابط USART1 اضافه شده است.
خوب همانطور که پیش از این گفته شد اینبار سعی داریم توسط توابع کتابخانه hal وقفه رابط سریال را تست کنیم، برای همین به سراغ توابع عمومی رابط سریال UART که مربوط به وقفه این رابط میگردد میرویم. اولین تابعی که باید مورد بررسی قرار دهیم، تابع مربوط به پیکره بندی وقفه دریافت رابط سریال است .
1 | HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) |
در آرگومان *huart تابع نام uart مورد استفاده ما قرار میگیرد و در آرگومان *pData رشتهای که قرار است اطلاعات دریافتی از رابط سریال در داخل آن قرار بگیرد. در آرگومان Size سایز یا تعداد بایتی که قرار است دریافت شود را تعیین میکند. برای فهم بهتر مسئله و قابلیتهای این تابع بهتر است به متن تابع در برگه stm32f1xx_hal_uart.c نگاه کنیم.
همانطور که میبینید در این تابع آرایهای که قرار است اطلاعات دریافتی داخل آن ریخته شود مشخص میشود و در ادامه سایز و تعداد بایتی که قرار است دریافت شود نیز مشخص میگردد. همچنین در سه فرمان وقفههای خطای پریتی و خطای عمومی دریافت و همچنین وقفه دریافت فعال میگردد. خوب تا اینجا متوجه شدیم که ما باید در ابتدای برنامه از این فرمان برای پیکرهبندی و فعال کردن وقفه دریافت رابط سریال UART استفاده کنیم. سیستم کار هم بدین صورت است که ما بعد از دریافت تعداد کاراکتر که مشخص کردیم به صورت خودکار به تابع HAL_UART_RxCpltCallback که باید آن را در main برنامه جاسازی کنیم برود و این عمل به صورت نرمافزاری صورت میپذیرد و نه سختافزاری. برای درک بیشتر مسئله بهتر است کد نویسیهای داخل توابع را بررسی کنیم تا با نحوه دریافت بیشتر آشنا شویم. اصولا با دریافت هر کاراکتر ما به روتین وقفه USART1_IRQHandler در برگه stm32f1xx_it.c خواهیم رفت حال اگر توجه کنید در آن تابع ، تابع دیگری با نام HAL_UART_IRQHandler فراخوانی شده است، اکنون به محل تعریف شدن آن تابع میرویم تا متن آن را مورد بررسی قرار دهیم.
همانطور که در عکس بالا میبینید، در صورتی که پرچمهای خطا فعال نشده باشند و در عین حال پرچم وقفه دریافت فعال شده باشد به تابع UART_Receive_IT خواهیم رفت. برای بررسی این تابع به محل تعریف این تابع میرویم تا متن این تابع را هم بررسی کنیم. برای فهم بیشتر به عکس زیر که متن تابع را به همراه توضیحات لازم نمایش میدهد توجه کنید.
در نیمه بالایی تابع ما با دریافت هر بایت، بافر دریافت اطلاعات را با هر بار فراخوانی این تابع یک بایت پر میکنیم و در نیمه پایینی تابع هر دفعه یکی از متغیر huart->RxXferCount کم میکنیم تا زمانی که به صفر برسد و در این حالت است که ما همه تعداد بایتهایی را که میخواستیم دریافت کردهایم. بعد از این مرحله سه وقفهای که قبلا فعال کردهایم غیر فعال میشود و در مرحله بعد به تابع HAL_UART_RxCpltCallback هدایت میگردیم. این تابع را میتوانیم در main.c برنامه خود جایگذاری کنیم. به عکس زیر که نمایی از کدهای تابع مورد نظر در main.c برنامه است توجه کنید، همانطور که در عکس زیر میبینید ما باید در ابتدای برنامه یکبار تابع HAL_UART_Receive_IT را بنویسیم تا وقفه دریافت فعال گردد و بافر دریافت و تعداد کاراکتری که میخواهیم دریافت کنیم مشخص شود.
حالا ما به دو روش میتوانیم اتمام دریافت اطلاعات از وقفه رابط سریال را متوجه شویم. یکی اینکه در حلقه اصلی main یکسره متغیر huart1.RxXferCount را چک کنیم که اگر صفر شد یعنی هر 10 بایتی که ما میخواستیم دریافت شده، بعد از این مرحله بایتهایی که دریافت شده را توسط همان رابط سریال نمایش میدهیم و دوباره همان فرمان پیکرهبندی وقفه دریافت را مینویسیم تا برای بار دیگر 10 بایت از درگاه سریال دریافت کنیم.
توجه داشته باشید در اینجا کاراکتر یازدهم که در بحث نمایش اطلاعات دریافتی نمایش میدهیم، منظور همان کاراکتر 13 است که برای همان نمایش اینتر از آن استفاده میکنیم. روش دیگر هم نمایش و پیکره بندی دوباره در همان تابع HAL_UART_RxCpltCallback است که در عکس زیر مشاهده میکنید.
برای وقفه ارسال هم در کتابخانه hal از تابع HAL_UART_Transmit_IT استفاده میشود، که بدلیل مشابه بودن مراحل و جلوگیری از ازدیاد مطلب بر عهده خود دانش پژوهان گذاشته میشود.
وقفه رابط سریال بدون استفاده از توابع کتابخانه hal
حال سعی داریم طریقه کار با وقفه رابط سریال را بدون استفاده از توابع کتابخانه hal و کار مستقیم با رجیسترها آموزش دهیم. برای شروع کدهای قبلی را که به برنامه قبل اضافه کرده بودیم پاک میکنیم تا برنامه را به صورت دیگری بنویسیم. شکل برنامه هم به صورتی است که ما داخل وقفه دریافت در یک بافر تا زمانی که کاراکتر 13 یا همان کاراکتر اینتر بیاید اطلاعات را ذخیره میکنیم و بعد از رسیدن به این کاراکتر کل رشته دریافتی را با فرمان printf در main برنامه نمایش میدهیم. بدین منظور به تابع وقفه رابط سریال UART1 با نام USART1_IRQHandler در برگه stm32f1xx_it.C میرویم و کدها را به این صورت اصلاح میکنیم.
بر طبق عکس بالا ما در وقفه دریافت رابط سریال تابع HAL_UART_IRQHandler را حذف میکنیم و تنها با چک کردن پرچم دریافت رابط سریال و خواندن از رجیستر USART1->DR بافر خود را پر میکنیم. در عکس زیر میتوانید متن برنامه را حلقه اصلی برنامه در برگه main.c ببینید.
سیسوگ در قسمت دهم آموزش میکروکنترلر STM32 کار با DMA رابط سریال UART را آموزش خواهد داد. با سیسوگ همراه باشید.
سلام. متغیرهای تعریفی به چه صورت باید باشد
درود بسیار عالی بود
بعد از فرمان فعال سازی وقفه
HAL_UART_ENABLE_IT( &huart2 , UART_IT_RXNE )
الویت وقفه را با چگونه فرمان دهیم ( بدون استفاده از حیط MxCube )
سپاس
سلام کار مدیریت وقفه ها و اولویت بندی آنها به عهده NVIC است
اگر در مورد وقفه ها اطلاعات بیشتری نیاز دارید مطلب زیر را مطالعه کنید.
آموزش STM32 با توابع LL قسمت هفتم: Interrupt
درود. خیلی عالی بود
الویت وقفه را چگونه تنظیم کنیم؟
سپاس
سلام دوست عزیز
اولویت ها رو با توابع NVICمیشه تنظیم کرد.
دمت گرم خیلی خوب بود
در ضمن من از آموزشهایی که توی این سایت قرار دادین بهره زیادی بردم و خیلی از جوابهایی که دنبالش بودم رو اینجا پیدا کردم
خواستم از این طریق تشکر ویژه ای از شما داشته باشم
خبر خوشحال کننده ایه برای ما
ممنون از شما
سلام
در کدویژن با استفاده از دو دستور asm cli و asm sei میشه وقفه عمومی رو فعال یا غیر فعال کرد
میخواستم بپرسم در IAR یا KEIL چطوری میشه این وقفه ها رو فعال یا غیر فعال کرد چون تا اونجا که من میدونم فقط asm nop تو این محیط کار میکنه
ممنون میشم از پاسخگویی شما
سلام دوست عزیز برای این کار میتونید از ماکروهای
__disable_irq()
__enable_irq()
استفاده کنید که در نهایت با کد اسمبلی معادلش جایگزین میشه
ممنونم از پاسخگویی شما
🙂
سلام . خیلی ممنون بابت آموزشها .
من یک مشکلی دارم با وقفه دریافت یوزارت .مشکل اینه که وقتی 2 یا 3 ارتباط یوزارت استفاده میکنم پس از یه مدتی وقفه دریافت غیر فعال میشه و دیگه اطلاعت دریافقت نمیشه .
ممنون میشم راهنمایی کنید
سلام رسول. در تابع روتین وقفه ابتدا با شرط چک کنید که وقفه از کدوم UART رسیده، سپس عملیات رو انجام بدید.
با سلام
علت رویکرد کتابخانههای به نوشتن وقفه به صورت سه تابع چیست؟ چرا تو همون سرویس روتین خواسته های وقفه اجرای نمیشه و ما باید بریم تو تابع وقفه هال و بعد هم کالبک؟
سلام ، ممنون بابت مطالب خوبتون .
این قسمت آخر که بدون توابع Hal نوشتین رو میشه به همین شکل برای دریافت در spi با وقفه پیاده سازی کرد ؟
عالییییییییی بود. ممنونم
خواهش میکنم ممنون از شما