آموزش STM32 با توابع HAL, توصیه شده

آموزش STM32 با توابع HAL قسمت نهم: دریافت اطلاعات به وسیله UART

دریافت اطلاعات به وسیله UART

در قسمت قبل با جزییات سخت‌افزاری و نحوه کارکرد واحد USART تا حدودی آشنا شدیم. بررسی کردیم که نحوه ارسال داده توسط فرستنده USART چگونه صورت می‌گیرد. در این بخش می‌بینیم که نحوه دریافت اطلاعات توسط گیرنده USART نیز به فرآیند ارسال بی‌شباهت نیست. واحد USART می‌تواند بسته به مقدار بیت M در رجیستر USART_CR1، یک کلمه 8 یا 9 بیتی را دریافت کند. در دریافت‌کننده اطلاعات، تنها زمانی بیت start تشخیص داده می‌شود که یک الگوی خاص از نمونه‌ها یا بیت‌ها دریافت شود. این الگو باید به ترتیب (از راست به چپ) 1 1 1 0 X 0 X 0 X 0 0 0 0 باشد و اگر این رشته به‌طور کامل دریافت نشود، عمل تشخیص بیت start لغو می‌شود و دریافت‌کننده به حالت idle (یا بیکار) برمی‌گردد. در این حالت هیچ پرچمی فعال نیست و گیرنده منتظر اولین لبه پایین‌رونده می‌ماند.

کلاک بخش گیرنده نیز مانند فرستنده از همان واحد baud rate generator مشترک تأمین می‌شود. بااین‌حال، کلاکی که گیرنده دریافت می‌کند 16 مرتبه از کلاک فرستنده سریع‌تر است. بدین طریق تعداد نمونه‌گیری و دقت بیشتر (تا 16 نمونه) برای bit-time تضمین خواهد شد. در دیاگرام زیر نحوه تشخیص بیت start و شرایط دقیق موردنیاز برای تأیید دریافت بیت start، نشان داده‌شده است:

 

دیاگرام نحوه تشخیص بیت start.

دیاگرام نحوه تشخیص بیت start.

 

در زمان دریافت اطلاعات توسط USART، داده‌ها روی پین RX و (مشابه ارسال داده) از بیت کم‌ارزش شیفت پیدا می‌کنند. همچنین در این زمان، رجیستر RDR مانند یک بافر بین بأس داخلی و شیفت رجیستر دریافت اطلاعات، عمل می‌کند. این موضوع با نگاه کردن به دیاگرام نشان داده‌شده در بخش قبل بهتر مشخص می‌شود:

برای دریافت داده توسط واحد USART مراحل زیر باید طی شوند:

  1. واحد USART با 1 کردن بیت UE در رجیستر USART_CR1، فعال می‌شود.
  2. طول کلمه، با مقدار دادن به بیت M در رجیستر USART_CR1، تنظیم می‌شود.
  3. تعداد بیت stop باید در رجیستر USART_CR2 تنظیم شود.
  4. (در صورت استفاده از ارتباط Multi buffer) DMA به‌وسیله تنظیم رجیستر USART_CR3 تنظیم می‌شود (DMAR).
  5. baud rate موردنظر برای ارتباط، باید در رجیستر USART_BRR تنظیم شود.
  6. با یک کردن بیت RE در رجیستر USART_CR1، گیرنده فعال می‌شود و منتظر تشخیص بیت start می‌ماند

 هنگام دریافت یک کاراکتر، پرچم RXNE فعال می‌شود که نشان‌دهنده‌ی انتقال اطلاعات از شیفت رجیستر به رجیستر RDR است. به‌بیان‌دیگر فعال شدن پرچم RXNE به این معنی است که اطلاعات دریافت شده و آماده خواندن است. همچنین در این زمان، در صورت فعال بودن بیت RXNEIE، یک وقفه تولید خواهد شد.

 در حالت multibuffer، پرچم RXNE بعد از دریافت هر بایت فعال، و با خواندن Data Register توسط DMA غیرفعال می‌شود. در حالت single buffer نیز، غیرفعال سازی بیت RXNE، به‌وسیله خواندن نرم‌افزاری رجیستر USART_DR صورت می‌پذیرد. مانند بیت TXE، بیت پرچم RXNE را نیز می‌توان با نوشتن صفر در آن پاک کرد. ذکر این نکته ضروری است که قبل از آنکه داده بعدی به‌طور کامل دریافت شود، پرچم RXNE باید پاک‌شده باشد. در غیر این صورت، با خطای تداخل (overrun) مواجه می‌شویم.

 در زمان دریافت اطلاعات، بیت RE نباید صفر شود، در غیر این صورت، عملیات دریافت بایت فعلی، لغو خواهد شد.

اکنون‌که با مفاهیم مربوط به دریافت اطلاعات توسط واحد USART آشنا شدیم، می‌خواهیم به سراغ توسعه یک پروژه برویم و به‌وسیله واحد USART موجود در میکروکنترلر STM32، از پورت سریال داده دریافت کنیم.

 

ایجاد پروژه

برای ایجاد پروژه این قسمت، مانند قسمت پیشین از تب Pinout & Configuration و بخش Connectivity، ‌USART1 را انتخاب می‌کنیم. سپس باید از قسمت Mode گزینه Asynchronous را انتخاب کنیم. این بار از قسمت Parameter Settings، پارامتر Data Direction را روی حالت Receive and Transmit تنظیم می‌کنیم. بقیه‌ی پارامترها ازجمله Buad Rate، طول کلمه و Parity را می‌توانیم بدون تغییر رها کنیم.

 

 

همچنین یک پایه خروجی را برای یک LED چشمک‌زن تنظیم می‌کنیم. برای این منظور می‌توانیم از همان LED متصل به پایه PC13 استفاده کنیم. اکنون می‌توانیم پروژه را ایجاد کنیم و به سراغ فایل کد برویم.

 

نوشتن کد پروژه

در کتابخانه HAL، تابع مورد استفاده برای دریافت داده توسط واحد USART، به صورت زیر است:

 

همان‌طور که توضیحات تابع مشخص است، این تابع چهار پارامتر دارد که پارامتر اول اشاره‌گر به ساختار حاوی اطلاعات مربوط به تنظیمات ماژول UART، پارامتر دوم اشاره‌گر به بافر داده، پارامتر سوم طول داده و پارامتر چهارم مدت‌زمان Timeout است. این تابع مانند تابع HAL_UART_Transmit، در حالت blocking عملیات خود را انجام می‌دهد. به این معنی که پردازنده در زمان انجام این عملیات، تمامی عملیات دیگر را متوقف می‌کند و تا زمانی که مقدار داده مورد انتظار دریافت نشده، یا مدت‌زمان Timeout سپری نشده باشد، عملیات دیگری انجام نخواهد شد. این روند، به دلیل اینکه همه عملیات مهم دیگر را متوقف می‌کند، تنها در برنامه‌ای قابل‌استفاده است که فقط بخواهیم از ورودی داده دریافت کنیم. استفاده از این نوع دریافت داده، یعنی روش polling، علاوه بر ایرادی که به آن اشاره شد، مشکلات دیگری نیز دارد که در ادامه باهم بررسی می‌کنیم.

اکنون یک بافر برای نگهداری پیام ارسالی تعریف می‌کنیم:

حالا می‌توانیم با استفاده تابع معرفی شده برای دریافت داده با USART، اطلاعات را از پورت سریال دریافت کنیم. برای این منظور کد زیر را در حلقه while(1) می‌نویسیم:

در این کد برای دریافت داده، 100 میلی‌ثانیه زمان Timeout تعیین کرده‌ایم. وضعیت LED متصل به پایه PC13 را نیز در هر بار اجرا معکوس می‌کنیم. به این طریق، می‌توانیم تأثیر زمان Timeout در دستور دریافت داده را بر روی چشمک زدن LED ببینیم. به‌بیان‌دیگر، به این طریق تأثیر یک دستور دریافت در حالت blocking، در کنار دستورات دیگر، دیده خواهد شد. بعد از دستورات مربوط چشمک زدن LED، اطلاعات بافر را توسط UART ارسال می‌کنیم تا در پورت سریال، داده دریافت شده را مشاهده کنیم. اکنون از پروژه build می‌گیریم و کد را روی میکرو دانلود می‌کنیم. در اینجا نیز مانند قسمت قبلی، برای نمایش اطلاعات و همچنین ارسال داده به پورت سریال، از نرم‌افزار RealTerm استفاده می‌کنیم.

در نرم‌افزار RealTerm از تب Port پورت COM موردنظر، که مبدل TTL to USB به آن متصل شده است را انتخاب می‌کنیم و Baud Rate را روی عدد 115200 تنظیم می‌کنیم. سپس باید روی کلید Change کلیک کنیم.

 

تنظیم ترمینال سریال RealTerm.

 

اکنون در تب Send، چهار کاراکتر یا بایت 1234 را در کادر مربوط به ارسال می‌نویسیم و سپس کلید Send ASCII را در مقابل کادر می‌زنیم.

 

ارسال داده به پورت سریال.

 

مشاهده می‌کنیم که تنها 1 بایت از 4 بایت ارسالی دریافت شده است. برای اینکه بتوانیم هر 4 بایت را دریافت کنیم، زمان Timeout را از 100 میلی‌ثانیه به 1 ثانیه افزایش می‌دهیم. بدین منظور آرگومان مربوط به زمان Timeout را در دستور دریافت USART تغییر می‌دهیم:

دوباره از کد، Build می‌گیریم و روی میکرو دانلود می‌کنیم. مجدداً از ترمینال سریال همان داده 1234 را به پورت ارسال می‌کنیم.

 

دریافت و نمایش 4 بایت در ترمینال سریال.

 

مشاهده می‌کنیم که این بار، هر 4 بایت داده دریافت شده است. اما با نگاه به LED چشمک زدن متوجه می‌شویم که سرعت چشمک زدن آن، به مقدار قابل‌توجه‌ای کاهش‌یافته است. دلیل این امر نیز Blocking بودن دستور دریافت و افزایش زمان Timeout آن به یک ثانیه است. برای اینکه از این مشکلات جلوگیری کنیم. باید از حالت Non-blocking برای دریافت اطلاعات استفاده کنیم. در ادامه این حالت را بررسی می‌کنیم.

 

تغییر تنظیمات پروژه

در صورت استفاده از وقفه برای انتقال اطلاعات، فرآیندهای دیگر تا اتمام کامل انتقال به تعویق نخواهند افتاد. پس به‌عبارت‌دیگر با استفاده از وقفه، از حالت Non-Blocking بهره خواهیم گرفت.

اکنون فایل ioc. پروژه را برای تغییر پیکربندی باز می‌کنیم، به‌منظور فعال کردن وقفه USART، واحد USART1 را انتخاب می‌کنیم و از تب NVIC Settings، وقفه USART1 global را فعال می‌کنیم؛

 

فعال‌سازی وقفه واحد USART1.

فعال‌سازی وقفه واحد USART1.

اکنون مجددا به سراغ فایل کد می‌رویم.

 

تغییرات کد

خط کد مربوط به دریافت اطلاعات در حالت polling را کامنت می‌کنیم؛

همچنین خط مربوط به اعلان بافر را کامنت می‌کنیم تا بافر را به‌صورت گلوبال و خارج از تابع Int main تعریف کنیم؛

قبل از حلقه while(1) دستور دریافت داده به‌صورت وقفه را می‌نویسیم؛

اکنون باید به سراغ تابع Rx Complete Callback برویم و دستور دریافت مجدد داده را در آن بنویسیم. زیرا در تکمیل دریافت داده این تابع صدا زده می‌شود و اگر بخواهیم دریافت اطلاعات بیش از یک بار انجام شود باید در بدنه این تابع مجدداً دستور دریافت اطلاعات نوشته شود. پس تابع Rx Complete Callback را به‌صورت زیر و درون فایل main.c تعریف می‌کنیم:

دوباره از کد Build می‌گیریم و روی میکرو دانلود می‌کنیم. مجدداً از ترمینال سریال همان داده 1234 را به پورت ارسال می‌کنیم؛

 

 

این بار مشاهده می‌کنیم که علاوه بر اینکه داده به‌صورت کامل دریافت شده است، سرعت چشم زدن LED نیز تغییری نمی‌کند.

در این قسمت از سری آموزش STM32 با توابع HAL، با نحوه دریافت اطلاعات به‌وسیله USART، هم به شکل Polling ‌(Blocking mode) و هم به شکل وقفه (Non-blocking mode) ‌آشنا شدیم و در قسمت‌های بعد، با روش سوم یعنی استفاده از DMA نیز آشنا خواهیم شد. در قسمت بعدی نحوه Redirect کردن توابع کتابخانه stdio را یاد می‌گیریم. با ما همراه باشید.

 

   لینک این پروژه در گیت‌هاب

   منبع توضیحات USART

انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

شما نیز میتوانید یکی از نویسندگان سیسوگ باشید.   همکاری با سیسوگ

بازگشت به لیست

دیدگاهتان را بنویسید

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

4 دیدگاه در “آموزش STM32 با توابع HAL قسمت نهم: دریافت اطلاعات به وسیله UART

  1. ابوالفضل گفت:

    سلام وقتتون بخیر
    چرا در تابع کال بک اتمام دریافت که برای استفاده مجدد در فایل main نوشتیم بعضی دستورات کار نمیکند؟من یک متغیر را مقدار دهی میکنم و یا السیدی را پاک میکنم دستورات انجام نمیشود.

    1. سیاوش گفت:

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

      1. ابوالفضل گفت:

        متغیر هام گلوبال هستند و خارج از تابع main و در قسمت مربوطه مشخص شده مربوط به متغیر های گلوبال تعریف کرده ام.
        منظورم از پاک کردن السیدی هم به این صورت است که من از کتابخونه السیدی استفاده می کنم و تابع مربوط به clear‌ کردن السیدی در بقیه جاهای برنامه استفاده میکنم کاملا کار میکنه اما توی تابع کال بک خیر.

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

        1. سیاوش گفت:

          پس احتمالا باید فلگ‌هارو چک بکنید که ببینید آیا به درستی فعال می‌شن