GebraBit

رابط UART

متن سربرگ خود را وارد کنید

chapter 9

رابط UART

  1. خانه
  2. »
  3. میکروکنترلر
  4. »
  5. رابط UART

بررسی

امروزه تعداد بسیار زیادی از پروتکل های ارتباطی سریال و رابط های سخت افزاری در صنعت الکترونیک موجود است. بیشتر آنها بر روی پهنای باند انتقال بالا متمرکز هستند، مانند استانداردهای اخیر USB 2.0 و 3.0، Firewire (IEEE 1394) و غیره. برخی از این استانداردها  قدیمی هستند، اما هنوز هم استفاده از آنها به عنوان پروتکل ارتباطی ویژه بین ماژول ها در یک برد رایج است. یکی از این رابط ها ، رابط  Universal Synchronous/Asynchronous Receiver/Transmitter  است که برای سادگی با نام USART نیز شناخته می شود.

تقریباً هر میکروکنترلر حداقل یک واحد جانبی UART دارد. همه MCU های STM32 حداقل دو رابط UART/USART ارائه می دهند، اما اکثر آنها بیش از دو رابط (برخی تا هشت رابط)  ، با توجه به تعداد I/O پشتیبانی شده توسط پکیج MCU ارائه می دهند.

در این فصل خواهیم دید که چگونه این ابزار جانبی مفید را با استفاده از CubeHAL راه اندازی کنیم. علاوه بر این، نحوه توسعه برنامه‌های کاربردی با استفاده از UART را هم در حالات polling and interrupt  مطالعه می‌کنیم .

Introduction to UARTs and USARTs

 قبل از شروع به تجزیه و تحلیل توابع ارائه شده توسط HAL برای تنظیمات واحد UART/USART ، بهتر است نگاهی کوتاه به رابط UART/USART و پروتکل ارتباطی آن بیندازیم.

هنگامی که می خواهیم بین دو (یا حتی بیشتر) دستگاه، دو دیتا مبادله کنیم، دو گزینه داریم: می توانیم دیتا را به صورت موازی ارسال کنیم، یعنی استفاده از تعداد معینی از خطوط ارتباطی برابر با اندازه هر Word داده (به عنوان مثال، هشت خط مستقل برای یک داده Word که از 8 بیت تشکیل شده است) ، یا می توانیم هر بیتی که داده Word را تشکیل می دهد، یک به یک انتقال دهیم. UART/USART واحدی است که دنباله‌ای موازی از بیت‌ها (معمولاً در یک بایت گروه‌بندی شده‌اند) را در یک جریان پیوسته از سیگنال‌هایی که روی یک سیم واحد جاری هستند را، ترجمه می‌کند.

هنگامی که اطلاعات ، بین دو دستگاه در داخل یک کانال مشترک جریان می یابد، هر دو دستگاه (در اینجا، برای سادگی،  آنها را به عنوان فرستنده و گیرنده نام میبریم) باید در مورد مدت زمانی که طول می کشد تا هر بیت جداگانه ارسال شود، به توافق برسند. در یک synchonous transmission  ، فرستنده و گیرنده یک کلاک مشترک تولید شده توسط یکی از دو دستگاه (معمولاً دستگاهی که به عنوان Master این اتصال عمل می کند) را به اشتراک می گذارند.

در شکل 1، یک دیاگرام زمان‌بندی معمولی داریم .  در این دیاگرام دستگاه A را نشان می‌دهد که یک بایت (0b01101001) را با استفاده از یک کلاک مرجع مشترک به‌صورت سریال به دستگاه B ارسال می‌کند. کلاک مشترک همچنین برای توافق در مورد زمان شروع نمونه برداری از دنباله بیت ها استفاده می شود: وقتی دستگاه Master شروع به کلاک کردن خط اختصاصی می کند، یعنی قرار است دنباله ای از بیت ها را ارسال کند.

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

شکل 2 دیاگرام زمان بندی یک انتقال ناهمزمان را نشان می دهد. حالت بیکار idle state (یعنی هیچ انتقالی انجام نمی شود) با سیگنال HIGH نشان داده می شود. انتقال با یک بیت START آغاز شده که با سطح سیگنال Low نشان داده می شود. لبه منفی توسط گیرنده تشخیص داده شده  و پس از دوره 1.5 بیتی (در شکل 1s T1:5bit نشان داده شده است)، نمونه برداری از بیت ها آغاز می شود. هشت بیت داده نمونه برداری شده است. معمولاً بیت کم اهمیت (LSB) ابتدا ارسال می شود. سپس یک بیت برابری parity اختیاری (برای بررسی خطای بیت های داده) ارسال می شود. اگر کانال انتقال بدون نویز فرض شود یا اگر خطای بررسی بالاتری در لایه های پروتکل وجود داشته باشد، اغلب این بیت حذف می شود. انتقال با یک بیت STOP که 1.5 بیت طول می کشد به پایان می رسد.

UART Initialization

مانند همه واحدهای جانبی STM32، USARTs نیز در ناحیه واحدهای جانبی حافظه ، که از x4000 0000 شروع می‌شود قرار میگیرد . به عنوان مثال، می توانیم به سادگی از ماکرو USART2 برای اشاره به دومین واحد جانبی USART که توسط میکروکنترلرهای STM32 پکیج LQFP64 ارائه شده است، استفاده کنیم.

با این حال، تمام توابع HAL مربوط به مدیریت UART طوری طراحی شده اند که به عنوان پارامتر اول نمونه ای از ساختار UART_HandleTypeDef را که به شکل زیر تعریف شده است ، بپذیرند:

typedef struct

}

USART_TypeDef *Instance; /* UART registers base address */

UART_InitTypeDef       Init; /* UART communication parameters */

UART_AdvFeatureInitTypeDef AdvancedInit; /* UART Advanced Features              initialization parameters */

uint8_t *pTxBuffPtr;    /* Pointer to UART Tx transfer Buffer */

uint16_t TxXferSize;   /* UART Tx Transfer size */

uint16_t TxXferCount; /* UART Tx Transfer Counter */

uint8_t *pRxBuffPtr;    /* Pointer to UART Rx transfer Buffer */

uint16_t RxXferSize;   /* UART Rx Transfer size */

uint16_t RxXferCount; /* UART Rx Transfer Counter */

DMA_HandleTypeDef *hdmatx; /* UART Tx DMA Handle parameters */

DMA_HandleTypeDef *hdmarx; /* UART Rx DMA Handle parameters */

HAL_LockTypeDef Lock; /* Locking object */

__IO HAL_UART_StateTypeDef State; /* UART communication state */

__IO HAL_UART_ErrorTypeDef ErrorCode; /* UART Error code */

{UART_HandleTypeDef;

  • Instance: این پارامتر اشاره گر به توصیف کننده USART ایست که قرار است از آن استفاده کنیم. برای مثال، USART2 توصیفگر UART مرتبط با رابط ST-LINK در برد Nucleo است.
  • Init: نمونه ای از ساختار UART_InitTypeDef که برای پیکربندی رابط UART استفاده می شود.
  • AdvancedInit: این فیلد برای پیکربندی ویژگی های پیشرفته تر UART مانند تشخیص خودکار BaudRate و تعویض پین TX/RX استفاده می شود. برخی از کتابخانه های HAL این فیلد اضافی را ارائه نمی دهند. زیرا رابط های USART در همه MCU های STM32 برابر نیستند. این مورد موضوع مهمی است که باید هنگام انتخاب میکروکنترلر مناسب برای برنامه خود در نظر داشته باشید.
  • pTxBuffPtr و pRxBuffPtr: این فیلدها به ترتیب به بافر ارسال و دریافت اشاره می کنند. آنها به عنوان منبع انتقال بایت های TxXferSize بر روی UART و برای دریافت RxXferSize زمانی که UART در حالت Full Duplex پیکربندی شده است استفاده می شوند. فیلدهای TxXferCount و RxXferCount به صورت داخلی توسط HAL برای شمارش بایت‌های ارسالی و دریافتی استفاده می‌شوند.
  • LOCK: این فیلد به صورت داخلی توسط HAL برای قفل کردن دسترسی های همزمان به رابط های UART استفاده می شود.

همانطور که در بالا گفته شد، تقریبا  فیلد Lock برای کنترل دسترسی های همزمان در تمام روتین های HAL استفاده می شود. اگر نگاهی به کد HAL بیندازید، می توانید چندین کاربرد ماکرو __HAL_LOCK() را مشاهده کنید که به این صورت گسترش یافته است:

#define __HAL_LOCK(__HANDLE__)

do{

 if((__HANDLE__)->Lock == HAL_LOCKED) {

return HAL_BUSY; }

 else

{ (__HANDLE__)->Lock = HAL_LOCKED; }

}while (0)

\\\ \\\\\\ \

مشخص نیست چرا مهندسان ST تصمیم گرفتند از دسترسی های همزمان به روتین های HAL مراقبت کنند. احتمالاً می خواستند که یک رویکرد امن با موضوع  thread safe داشته باشند، و توسعه‌دهنده برنامه را از مسئولیت مدیریت دسترسی‌های چندگانه به یک رابط سخت‌افزاری ، در صورت اجرای چندین thread در یک برنامه ، آزاد کنند.

 با این حال، این موضوع جنبه ای آزاردهنده برای کاربران HAL دارد: حتی اگر برنامه شما قصد دسترسی‌های همزمان به واحد جانبی مورد نظر را نداشته باشد، کد نوشته شده به دلیل بررسی‌های زیاد در مورد وضعیت فیلد Lock ، بهینه‌سازی ضعیفی خواهد داشت. علاوه بر این، این روش  قفل کردن، ذاتاً ناامن است، زیرا هیچ بخش مهمی برای جلوگیری از شرایط رقابت ISR با اولویت بالاتر برای جلوگیری از کد در حال اجرا ، استفاده نمی‌شود. در نهایت، اگر برنامه شما از RTOS استفاده می کند، بهتر است که از برنامه های اولیه قفل کننده سیستم عامل (مانند سمافورها و mutexes که نه تنها اتمی atomic هستند، بلکه به درستی زمان بندی تسک ها را مدیریت و از busy waiting  جلوگیری می کنند) برای مدیریت دسترسی های همزمان، بدون نیاز به بررسی یک مقدار بازگشتی خاص (HAL_BUSY) از توابع HAL. استفاده کنید.

بسیاری از توسعه دهندگان این روش را برای قفل کردن واحدهای جانبی ، از اولین انتشار HAL ،رد کرده اند. مهندسان ST اخیراً اعلام کرده اند که در حال کار روی راه حل بهتری هستند.

 

تمام پیکربندی UART با استفاده از نمونه ای از ساختار UART_InitTypeDef انجام می شود که به صورت زیر تعریف شده است:

typedef struct {

uint32_t BaudRate;

uint32_t WordLength;

uint32_t StopBits;

uint32_t Parity;

uint32_t Mode;

uint32_t HwFlowCtl;

uint32_t OverSampling;

} UART_InitTypeDef;

    • BaudRate: این پارامتر به سرعت انتقال اشاره دارد که بر حسب بیت در ثانیه بیان می شود. حتی اگر این پارامتر بتواند یک مقدار دلخواه دریافت کند، معمولا BaudRate از لیست مقادیری شناخته شده و استاندارد انتخاب میشود. زیرا تابعی از کلاک جانبی Peripheral مربوط به USART است (که از کلاک اصلی HSI یا HSE توسط زنجیره پیچیده ای از PLL ها و ضرب کننده ها در برخی از میکروکنترلر STM32 نشات گرفته است) و نمی توان به راحتی بدون خطای نمونه برداری و خطاهای ارتباطی به همه BaudRate ها دست یافت. جدول 2 لیست BaudRates های معمول و محاسبه خطای مربوطه را برای STM32F030 نشان می دهد. همیشه با مطالعه reference manual میکروکنترلر ، بهترین فرکانس کلاک جانبی Peripheral که با BaudRate مورد نیاز تطابق دارد را انتخاب کنید.

UART Configuration Using CubeMX

همانطور که قبلاً گفته شد، اولین باری که USART2 را برای Nucleo پیکربندی می کنیم، بهتر است از CubeMX استفاده کنیم. مرحله اول فعال کردن واحد جانبی USART2 در نمای Pinout است، همانطور که در شکل 5 نشان داده شده است، ورودی Asynchronous را در قسمت Mode انتخاب کنید. هر دو پین PA2 و PA3 به طور خودکار با رنگ سبز برجسته می شوند. سپس وارد قسمت Configuration شده و بر روی دکمه USART2 کلیک کنید. مانند شکل 5 در سمت راست کادر پیکربندی ظاهر می شود. در این کادر میتوانیم تنظیمات USART مانند BaudRate، طول داده  و غیره را انجام دهیم.

هنگامی که رابط USART را پیکربندی کردیم، می توانیم کد C را ایجاد کنیم. متوجه خواهید شد که CubeMX تمام کدهای اولیه USART2 را در MX_USART2_UART_Init() (که در فایل main.c موجود است) قرار می دهد. در عوض، تمام کدهای مربوط به پیکربندی GPIO در تابع HAL_UART_MspInit() قرار می گیرد که در داخل فایل stm32xxxx_hal_msp.c وجود دارد.

UART Communication in Polling Mode

میکروکنترلرهای STM32 و از این رو CubeHAL، سه راه برای تبادل داده از طریق ارتباط UART ارائه می‌دهند: polling, interrupt , DMA . این مد ها نه تنها سه روش متفاوت برای مدیریت ارتباط UART هستند بلکه سه رویکرد برنامه نویسی متفاوت برای یک تسک مشخص هستند که مزایای متعددی را هم از نظر طراحی و هم از نظر عملکرد ارائه می دهند.

  • در حالت polling که به آن حالت blocking نیز می گویند، برنامه اصلی یا یکی از thread های آن به طور همزمان منتظر ارسال و دریافت داده می شود. این ساده ترین شکل ارتباط داده با استفاده از یک واحد جانبی است، و زمانی که نرخ ارسال خیلی کم نیست و UART به عنوان ابزار جانبی حیاتی در برنامه ما استفاده نمی شود، می توان از آن استفاده کرد (مثال کلاسیک استفاده از UART به عنوان کنسول خروجی برای دیباگ  است.).
  • در حالت interrupt که حالت non-blocking نیز نامیده می شود، برنامه اصلی از انتظار برای تکمیل ارسال و دریافت داده ها رها می شود. روتین های انتقال داده به محض تکمیل پیکربندی واحد جانبی پایان می یابند. هنگامی که انتقال داده پایان یافت، وقفه بعدی  به کد اصلی سیگنال می دهد. این حالت زمانی که سرعت ارتباط کم است (زیر 38400 Bps) یا زمانی که در مقایسه با سایر فعالیت های انجام شده توسط MCU “به ندرت” اتفاق می افتد و ما نمی خواهیم میکروکنترلر را در انتظار انتقال داده نگه داریم ، مناسب تر است.
  • حالت DMA به لطف دسترسی مستقیم UART به RAM داخلی MCU بهترین نتیجه انتقال داده را ارائه می دهد. این حالت برای ارتباطات پرسرعت و زمانی که می خواهیم کاملاً MCU را از سربار انتقال داده آزاد کنیم، بهترین انتخاب است. بدون حالت DMA، دستیابی به سریعترین نرخ انتقال که واحد جانبی USART قادر به انجام آن است تقریباً غیرممکن است. این حالت ارتباطی USART را در فصل اختصاص داده شده به مدیریت DMA بررسی خواهیم کرد.

برای انتقال رشته ای از بایت ها بر روی USART در حالت polling ، HAL تابع را ارائه می دهد:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

  • huart: اشاره­گر به نمونه ای از ساختار UART_HandleTypeDef است که ابزار جانبی UART را شناسایی و پیکربندی می کند.
  • pData: اشاره­گر یک آرایه با طولی برابر با پارامتر Size است که حاوی رشته بایت هایی برای ارسال است.
  • Timeout: حداکثر زمان بر حسب میلی ثانیه که می خواهیم منتظر اتمام ارسال باشیم. اگر انتقال در مدت زمان تعیین شده تکمیل نشود، تابع لغو می شود و مقدار HAL_TIMEOUT را برمی گرداند. در غیر این صورت در صورت عدم بروز خطا، مقدار HAL_OK را برمی گرداند. علاوه بر این، می‌توانیم یک بازه زمانی برابر با HAL_MAX_DELAY (0xFFFF FFFF) بگذرانیم تا به طور نامحدود برای تکمیل ارسال منتظر بمانیم.

برعکس، برای دریافت رشته ای از بایت ها بر روی USART در حالت polling ، HAL تابع زیر را ارائه می دهد:

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

UART Communication in Interrupt Mode

اجازه دهید دوباره اولین مثال این فصل را بررسی کنیم. از آنجایی که برنامه ما فقط این کار ساده را  انجام میدهد، استفاده از UART در حالت polling اشکالی ندارد. میکروکنترلر اساساً در انتظار ورودی کاربر مسدود می شود (به اندازه مقدار HAL_MAX_DELAY تابع HAL_UART_Receive() مسدود می شود تا زمانی که یک کاراکتر از طریق UART ارسال شود). اما اگر برنامه ما مجبور باشد سایر فعالیت های فشرده cpu را در زمان واقعی به صورت real-time انجام دهد چطور؟

فرض کنید main() در مثال اول را به شکل زیر می نویسیم:

38 while (1) {

39 opt = readUserInput();

40 processUserInput(opt);

41 if(opt == 3)

42  goto printMessage;

43 44 performCriticalTasks();

45 }

در این حالت نمی‌توانیم اجرای تابع ()processUserInput را در انتظار انتخاب کاربر مسدود کنیم، اما باید مقدار زمان‌بندی کوتاه‌تری را برای تابع HAL_UART_Receive() تعیین کنیم، در غیر این صورت () performCriticalTasks هرگز اجرا نمی‌شود. این موضوع می‌تواند باعث از دست رفتن داده‌های مهمی شود که از واحد جانبی UART می‌آیند (به یاد داشته باشید که رابط UART دارای بافر یک بایتی است).

برای رفع این مشکل، HAL حالت interrupt  را برای تبادل داده از طریق واحد جانبی UART ارائه می‌کند . برای استفاده از این حالت، باید کارهای زیر را انجام دهیم:

  • فعال کردن وقفه USARTx_IRQn و اجرای ISR ()USARTx_IRQHandler مربوطه.
  • فراخوانی HAL_UART_IRQHandler() در داخل USARTx_IRQHandler(): این کار تمام فعالیت های مربوط به مدیریت وقفه های تولید شده توسط UART را انجام می دهد.
  • استفاده از توابع HAL_UART_Transmit_IT() و HAL_UART_Receive_IT() برای تبادل داده از طریق UART. این توابع همچنین حالت وقفه واحد جانبی UART را فعال می‌کند: به این ترتیب واحد جانبی لاین مربوطه را در کنترل‌کننده NVIC فعال می‌کند تا هنگام وقوع یک رویداد، ISR مربوطه رخ دهد.
  • تنظیم و بررسی کد برنامه برای مقابله با رویدادهای ناهمزمان.

قبل از اینکه کدهای مثال اول را دوباره بنویسیم، بهتر است نگاهی به وقفه های موجود در UART و نحوه طراحی روتین های HAL بیندازیم.

UART Related Interrupts

هر واحد جانبی USART در میکروهای STM32  وقفه های فهرست شده در جدول 6 را ارائه می دهد. این وقفه ها شامل IRQ های مربوط به انتقال داده و خطاهای ارتباطی می شود که آنها را می توان به دو گروه زیر تقسیم کرد:

  • IRQهای تولید شده در حین انتقال: انتقال کامل، CTS یا وقفه خالی بودن رجیستر انتقال داده.
  • IRQهای تولید شده در حین دریافت: شناسایی خط بیکار Idle ، خطای Overrun، عدم خالی بودن رجیستر اطلاعات دریافتی، خطای Parity ، تشخیص LIN break  ، فلگ نویز (فقط در ارتباطات چند بافری) و خطای فریم (فقط در ارتباطات چند بافری).

اگر بیت کنترل مربوطه روی Enable تنظیم شود، این رویدادها یک وقفه ایجاد می کنند (ستون سوم جدول 6). با این حال، MCU های STM32 به گونه ای طراحی شده اند که تمام این IRQ ها فقط به یک ISR برای هر واحد جانبی USART محدود می شوند (شکل 11 را ببینید). به عنوان مثال، USART2 تنها USART2_IRQn را به عنوان IRQ برای تمام وقفه های ایجاد شده توسط این ابزار جانبی تعریف می کند. این به کد کاربر بستگی دارد که فلگ رویداد مربوطه را تجزیه و تحلیل کند تا بفهمد کدام درخواست وقفه را ایجاد کرده است.

این مقاله را با دوستانتان به اشتراک بگذارید!

Be the first to write a review

لطفا با ارسال دیدگاه و امتیاز دهی تیم جبرا را در بهبود کیفیت همیاری کنید

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

Shopping cart
Start typing to see posts you are looking for.

Sign in

No account yet?