در قسمت هفتم از آموزش STM32 با توابع LL، ابتدا در رابطه با کلیت و ذات وقفه صحبت کردیم و گفتیم که به چه دلایلی وقفه مفید است و باید در سیستم وجود داشته باشد، در ادامه در رابطه با وقفه در میکروکنترلرهای STM32 سری F1 صحبت کردیم و طرز کار واحد NVIC رو به طور کامل شرح دادیم. در نهایت هم نگاهمان را معطوف به وقفههای خارجی کردیم و یک وقفهی خارجی که بر روی پینهای میکروکنترلر جانمایی شده است را راهاندازی کردیم.
در این قسمت میخواهیم در رابطه با پروتکل UART در میکروکنترلرهای STM32 صحبت کنیم.
ابتدا تئوری مربوط به پروتکل UART را با جزئیات کامل شرح میدهیم و سپس این پروتکل ارتباطی را در میکروکنترلر STM32 راهاندازی میکنیم.
UART(universal asynchronous receiver-transmitter)
قبل از اینکه در رابطه با این پروتکل صحبت کنم این نکته را یادآور شوم که UART برگرفته از USART است که حرف S درون عبارت USART به معنای Synchronous است. اما چون اغلب از بخش سنکرون این پروتکل استفاده نمیشود، این پروتکل بیشتر با نام UART شناخته میشود و ما هم در این مقاله قصد داریم به UART بپردازیم.
حال شاید از خودتان بپرسید سنکرون (Synchronous) یا آسنکرون (Asynchronous) به چه معناست، در مدارات الکترونیک دیجیتال هر موقع اسم سنکرون را به کار میبریم به قطع یقین در جایی از مدارمان یک سیگنال، به اسم سیگنال کلاک وجود دارد.
به عنوان یادآوری این را بگویم که مدارات الکترونیک دیجیتال به دو دسته کلی ترکیبی و ترتیبی تقسیم میشوند. و مدارات ترتیبی خود به دو دستهی ترتیبی سنکرون و ترتیبی آسنکرون تقسیم میشوند. در ترتیبی سنکرون، مدار همیشه با عاملی به اسم کلاک کار میکند و هماهنگ است. اما در ترتیبی آسنکرون، هیچ کلاکی وجود ندارد و عملکرد مدار بر اساس پارامتر دیگری که در ادامه توضیح میدهم، کار میکند و هماهنگ است.
پس تا اینجا فهمیدیم که UART در دستهی مدارات آسنکرون و USART در دستهی مدارات سنکرون قرار میگیرد.
UART یک پروتکل ارتباطی سریال متشکل از یک فرستنده و یک گیرنده است که در هر لحظه هم میتواند دیتایی را دریافت و هم دیتایی را به نقطهی دیگری ارسال کند که اصطلاحا به این قابلیت Full duplex گفته میشود.
پینهای RX و TX در پروتکل UART

تصویر بالا دو تراشه که از پروتکل UART پشتیبانی میکنند را نشان میدهد. همانطور که از این تصویر مشخص است، در پروتکل UART علاوه بر GND که در همه جا و همهی پروتکلها وجود دارد و معیاری برای سنجش سایر سیگنالها است، دو پین اصلی دیگر به اسم RX و TX نیز وجود دارد که RX به معنای گیرنده و TX به معنای فرستنده است
اگر به خوبی به این تصویر دقت کنید میبینید که TX دیوایس اول به RX دیوایس دوم، و TX دیوایس دوم به RX دیوایس اول متصل است و دلیل این موضوع هم این است که وقتی دیوایس اول دیتا را بر روی TX میفرستد، در سمت دیگر، دیوایس دوم باید همان دیتا را بر روی RX دریافت کند.
نحوهی انتقال دیتا در پروتکل UART
یکی از مهمترین عوامل در شناخت و اشراف به یک پروتکل، آشنایی با نحوهی انتقال دیتا در آن پروتکل است.
در هر پروتکل یک سری قوانین توسط سازندگان آن پروتکل تدوین شده است که این قوانین نحوهی انتقال دیتا را شرح میدهند.
در پروتکل UART هم یک سری قوانین برای انتقال دیتا وضع شده است که در ادامه این قوانین را بررسی میکنیم.
ابتدا به تصویر زیر دقت کنید:
در تصویر بالا یک پکت دیتا نشان داده شده است که این پکت دارای جزئیاتی به شرح زیر است:
Start bit: این بیت از پکت که مقدار آن صفر منطقی است نشاندهندهی شروع ارسال پکت است و به گیرنده خبر میدهد که ارسال پکت شروع شده است.
Data Frame: پس از اینکه Start bit ارسال شد نوبت این است که دیتای موردنظر ما به سمت گیرنده ارسال شود. دیتا میتواند متشکل از 5 تا 9 بیت باشد اما بیشتر از 8 یا 9 بیت استفاده میشود.
Parity bit: بیت پریتی یا معادل آن در زبان فارسی توازن، معیاری برای سنجش خطا است. این بیت اختیاری است، یعنی پکت هم میتواند شامل بیت پریتی باشد و هم نه. مقدار بیت پریتی در فرستنده و گیرنده محاسبه و با هم مقایسه میشوند، اگر مقدار این بیت در سمت فرستنده و گیرنده برابر نبودند یعنی اینکه خطایی رخ داده است و دیتای دریافتی صحیح نمیباشد.
یک توضیح مختصر در رابطه با بیت پریتی میدهم برای جزئیات بیشتر میتوانید به مراجع مربوطه مراجعه کنید. دو نوع پریتی یعنی پریتی زوج و پریتی فرد وجود دارد.
در پریتی زوج همهی بیتهای Data Frame با هم XOR میشوند تا اگر تعداد 1های بیتهای Data Frame فرد بود، بیت پریتی یک منطقی شود و اگر تعداد 1های بیتهای Data Frame زوج بود، بیت پریتی صفر منطقی شود تا تعداد 1های مجموع بیتهای Data Frame و بیت پریتی زوج باشد.
در پریتی فرد همهی بیتهای Data Frame با هم XNOR میشوند تا اگر تعداد 1های بیتهای Data Frame فرد بود، بیت پریتی صفر منطقی شود و اگر تعداد 1های بیتهای Data Frame زوج بود، بیت پریتی یک منطقی شود تا تعداد 1های مجموع بیتهای Data Frame و بیت پریتی فرد باشد.
Stop bit: این بیت از پکت که مقدار آن یک منطقی است نشاندهندهی پایان ارسال پکت است و به گیرنده خبر میدهد که ارسال پکت به پایان رسیده است. البته همانطور که از تصویر مشخص است بیت پایان میتواند از 1 به 2 بیت نیز افزایش یابد.
خب ما تا اینجا جزئیات یک پکت در پروتکل UART را بررسی کردیم و گفتیم که هر جز از پکت چه کاربردی دارد. اما هنوز یک عامل مهم در این پروتکل را بررسی نکردیم و آن عامل هم چیزی نیست جز Baud rate.
Baud rate در واقع مشخص میکند که در یک ثانیه چند بیت دیتا منتقل میشود. اگر به خاطر داشته باشیم در ابتدای مقاله گفتیم عاملی که باعث کار و هماهنگی مدارات سنکرون میشود کلاک است ولی در مدارت آسنکرون که کلاکی وجود ندارد یک پارامتر دیگر این کار را انجام میدهد که آن پارامتر همین Baud rate است.
در فریم دیتا وقتی ما یک بیت را ارسال میکنیم بسیار مهم است که ارسال این بیت چه مقدار زمانی طول میکشد. در واقع Baud rate است که این مقدار زمان را مشخص میکند.
مقدار Baud rateهای متفاوت و زیادی در این پروتکل بنا به سرعت و کاربرد مورد نیاز ما وجود دارد اما معمولا از دو سرعت 9600 و 115200 استفاده میشود.
پروتکل UART در میکروکنترلرهای STM32
در میکروکنترلرهای STM32 علاوه بر اینکه تمامی مواردی که در رابطه با پروتکل UART بررسی کردیم، پشتیبانی میشود، موارد جانبی دیگری نیز به همراه این پروتکل ارائه شده است که در داکیومنتهای شرکت ST این موارد جانبی را جز قابلیتهای مهم این دسته از میکروکنترلرها به حساب آورده است.
اما هر کدام از این موارد جانبی باید به صورت جداگانه توضیح داده شوند و راهاندازی شوند و از حوصلهی این مقاله خارج است. پس ما در این مقاله قصد داریم که فقط پروتکل UART را در حالت ارسال دیتا راهاندازی کنیم.
خب اجازه بدهید که به نرمافزار STM32CubeMX برویم تا UART را در حالت ارسال داده راهاندازی کنیم.
ابتدا کلاک و دیباگ را مانند گذشته تنظیم میکنیم و سپس از بخش Connectivity که مربوط به پروتکلهای پشتیبانی شده توسط میکروکنترلر است، USART1 را فعال میکنیم.
پس از فعال کردن USART1 میبینم که دو پین مربوط به USART1 به شکل زیر در آمده اند:
اکنون باید مشخص کنیم که میخواهیم از کدام یک از حالتهای سنکرون یا آسنکرون USART1 استفاده کنیم. اگر حالت سنکرون را انتخاب کنیم که معادل همان USART و اگر حالت آسنکرون را انتخای کنیم معادل همان UART میشود.
اما همانطور که گفتم ما میخواهیم از حالت آسنکرون یعنی از UART استفاده کنیم. پس مانند شکل زیر حالت آسنکرون را انخاب میکنیم:
با توجه به پکت مربوط به پروتکل UART، پارامترهای Parity ،Word length ،Baud rate و Stop bit پارامترهای متغیری بودند و ما باید مقدار آنها را از بین چند مقدار موجود تعیین میکردیم.
ابتدا به تصویر زیر دقت کنید:
همانطور که از تصویر بالا مشاهده میکنید مقدار Baud rate را ما 115200 بیت بر ثانیه تنظیم کردیم. این نوع میکروکنترلر Baud rate بین 245 بیت بر ثانیه تا 1000 کیلو بیت بر ثانیه را پشتیبانی میکند.
طول دیتا در این میکروکنترلر از دو مقدار 8 و 9 بیت پشتیبانی میکند که ما مقدار 8 بیت را انخاب کردیم.
طبق توضیحاتی که دادیم بیت پریتی هم میتواند در پکت ارسالی وجود نداشته باشد و هم میتواند وجود داشته باشد و یکی از حالتهای زوج یا فرد باشد. ما در اینجا بیت پریتی را روی حالت None تنظیم کردیم یعنی اینکه میخواهیم بیت پریتی در پکت ارسالی نباشد.
و در نهایت Stop bit هم باید 1 یا 2 بیت باشد که ما در اینجا 1 بیت را انتخاب کردیم.
همچنین چون ما میخواهیم فقط دیتا ارسال کنیم، پس Data direction را بر روی Transmit only تنظیم میکنیم.
پس از انجام تنظیمات بالا از پروژه خروجی میگیریم و وارد محیط برنامهنویسی Keil میشویم.
ما ابتدا یک ثابت (const) آرایهای به اسم Name، با نوع char و به طول 6 تعریف میکنیم و رشتهی “Kamin” را درون این ثابت قرار میدهیم.
همچنین یک متغیر 8 بیتی به نام i و با نوع uint8_t نیز برای شمارنده تعریف میکنیم.
روند کار ما اینگونه است که میخواهیم آرایه Name که رشته “Kamin” در آن قرار دارد را با استفاده از UART میکروکنترلر به کامپیوتر ارسال کنیم و نتیجه را کامپیوتر مشاهده کنیم.
ابتدا به کد زیر که درون حلقه while نوشتیم دقت کنید:
LL_USART_TransmitData8(USART1, Name[i++]); while(!LL_USART_IsActiveFlag_TXE(USART1)); if(i == 6) { i = 0; }
ما ابتدا با استفاده از تابع LL_USART_TransmitData8 کارکتر اولِ Name را با UART1 برای کامپیوتر ارسال میکنیم. برای اینکه بتوانیم دومین کارکتر و به همین ترتیب تا آخرین کارکتر را هم برای کامپیوتر ارسال کنیم، باید بررسی کنیم ببینیم که آیا رجیستر مربوط به دیتای ارسالی خالی است که ما کارکتر بعدی را بفرستیم یا نه.
اجازه بدهید قبل از اینکه توضیح بدهم که چگونه باید بررسی کنیم که آیا رجیستر دیتای ارسالی خالی است یا نه، پشتپردهی انتقال دیتا با استفاده از UART در میکروکنترلر را توضیح بدهم.
زمانی که ما با استفاده از تابع LL_USART_TransmitData8 قصد داریم دیتایی را با استفاده از UART انتقال بدهیم، دیتای ما درون رجیستر Transmit Data Register (TDR) قرار میگیرد. پس از قرار گرفتن دیتا درون این رجیستر، شیفت رجیستری به نام Transmit Shift Register وظیفه دارد که دیتای درون رجیستر Transmit Data را بر روی پین TX میکروکنترلر قرار دهد.

پس از اینکه دیتای درون Transmit Data Register (TDR) به طور کامل درون شیفت رجیستر قرار گرفت و ارسال دیتا استارت خورد، بیت هشتم از رجیستر Status register به نام TXE مقدارش 1 میشود و نشاندهندهی این است که ما میتوانیم دیتای بعدی را درون رجیستر Transmit Data Register (TDR) قرار بدهیم بدون اینکه هیچ گونه خطایی رخ بدهد.
دقت کنید که بیت TXE از رجیستر Status register فقط خواندنی است و مقدار آن به صورت سختافزاری تغییر میکنید و ما از طریق برنامه یا نرمافزار نمیتوانیم مقدار این بیت را تغییر بدهیم، بلکه فقط میتوانیم مقدار این بیت را بخوانیم.
پس به طور خلاصه زمانی که ما دیتایی را درون رجیستر Transmit Data Register (TDR) مینویسیم بیت TXE به صورت سختافزاری 0 و زمانی که همین دیتا به طور کامل درون شیفت رجیستر قرار گرفت و ارسال دیتا استارت خورد این بیت به صورت سختافزاری 1 میشود.
با توجه به توضیحات بالا ما درون برنامه با استفاده از تابع LL_USART_IsActiveFlag_TXE بررسی کردیم که چه موقع بیت TXE مقدارش 1 میشود و تا زمانی که 1 نشد برنامه منتظر بماند تا دیتا ارسال شود و هر زمانی هم که 1 شد دیتای بعدی درون رجیستر Transmit Data Register (TDR) قرار بگیرد.
همچنین با استفاده از ساختار شرطی if بررسی کردیم که اگر آخرین کاراکتر از رشته ارسال شد، دوباره به اول رشته برگردد. در واقع ما به صورت متوالی رشتهی “Kamin” را با استفاده از UART برای کامپیوتر میفرستیم.
پس از اینکه برنامه را اجرا و مبدل USB به TTL را بین میکروکنترلر و کامپیوتر متصل کردیم، رشته “Kamin” به صورت متوالی مانند تصویر زیر به پورت سریال کامپیوتر فرستاده میشود.
در این قسمت، پروتکل UART و همچنین فرستادن دیتا با استفاده این پروتکل را بررسی کردیم، در قسمت نهم در رابطه با دریافت دیتا با استفاده از پروتکل UART صحبت خواهیم کرد.
منبع:سیسوگ