ARM, STM, STM32, آموزش, توصیه شده, مقاله

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

آموزش میکروکنترلر STM32 : سیسوگ در قسمت هفتم  از  آموزش میکروکنترلر STM32 تنظیمات نرم‌افزار CubeMX برای رابط سریال UART را آموزش داد. در این قسمت از آموزش میکروکنترلر STM32 قصد دارد آموزش ها را در قالب یک مثال عملی نشان دهد. با سیسوگ همراه باشید.   خوب حالا که با وظیفه و نحوه عملکرد تابع HAL_UART_Transmit آشنا شدیم بد نیست یک مثال عملی برای کار با آن در میکروکنترلر STM32 بزنیم:   همین‌طور که در مثال بالا میبینید ما یک رشته 15 کاراکتری را از طریق فرمان یاد شده توسط huart1 در حداکثر زمان 1 میلی ثانیه ارسال می‌کنیم، دلیل گذاشتن (uint8_t *) هم قبل از رشته مورد نظر بخاطر این است که رشته از نوع char است و از طریق این عملگر به نوع آرگومان مشابه مورد نیاز تابع تبدیل می‌شود، تا کامپایلر پیام اخطار ندهد و توجه داشته باشید در آینده نیز از این شکل عملگر زیاد استفاده خواهیم کرد. در صورت نیاز نیز می‌توانیم خروجی تابع را بررسی کنیم تا متعاقباً دستورات لازم در شرایط مختلف صورت پذیرد. فرمان بعدی که باید در کتابخانه hal مورد بررسی قرار دهیم فرمان دریافت یک رشته از رابط سریال است.   در آرگومان *huart تابع نام uart مورد استفاده ما قرار می‌گیرد و در آرگومان *pData رشته‌ای که قرار است از رابط سریال دریافت شود، مشخص می‌شود. در آرگومان Size سایز ، تعداد بایتی که قرار است دریافت شود تعیین می‌شود. آرگومان Timeout حداکثر زمانی را مشخص می‌کند که این تابع باید در این مدت زمان رشته را از رابط سریال دریافت کند. در اینجا هم شکل کار مشابه نحوه عملکرد تابع ارسال می‌باشد و مقدار برگشتی تابع هم از همان نوع می‌باشد. بدین صورت که اگر به بدنه این تابع در برگه stm32f1xx_hal_uart.c نگاه کنیم، با ارسال هر کاراکتر یکی از مقدار متغیر RxXferCount کم می‌شود تا به صفر برسد و دریافت تمام شود. پس در نتیجه ما می‌توانیم با چک کردن وضعیت این متغیر همواره از تعداد بایت دریافت شده باخبر شویم.  
برگه stm32f1xx_hal_uart.c

آموزش میکروکنترلر STM32

  و اگر توجه کنید در بدنه این تابع برخلاف تابع ارسال که پرچم ارسال یعنی پرچم UART_FLAG_TXE چک می‌شد. در اینجا پرچم دریافت با نام UART_FLAG_RXNE چک می‌شود تا اگر دریافت از مدت زمان Timeout بیشتر طول کشید، ادامه عملیات منتفی و برنامه به خط بعد منتقل شود. نکته مهم در اینجا این است که اگر در مدت زمان Timeout فقط مثلا از 10 بایتی که می‌خواهیم دریافت کنیم 7 بایت دریافت شود تابع مقدار HAL_TIMEOUT را برمیگرداند ولیکن ما میتوانیم همان هفت بایت را داخل متغیری که در اشاره گر *pData معرفی کردیم مشاهده کنیم برای این هم که بفهمیم چند بایت دریافت کرده ایم هم که باید همان متغیر RxXferCount را چک کنیم. یعنی اگر مقدار این متغیر 3 بود یعنی از 10 که مقدار اولیه ان بود 7 عدد کم شده و متعاقبا 7 بایت دریافت شده است. در اینجا هم لازم است یک نمونه مثال عملی با این تابع را برای شما قرار دهیم.  
میکروکنترلر STM32

آموزش میکروکنترلر STM32

  در مثال بالا ابتدا ما یک آرایه از نوع char تعریف می‌کنیم و در فرمان بعد هم یک ساختار به نام Response از نوع HAL_StatusTypeDef ، تا بتوانیم خروجی تابع را بررسی کنیم و در خط 117 با فرمان دریافت به مدت زمان حداکثر 5000 میلی ثانیه صبر میکنیم تا 5 بایت از رابط سریال دریافت شود و بایتهای دریافتی را هم در داخل ارایه pData دریافت خواهیم کرد. که ما میتوانیم با توجه به خروجی تابع بررسی کنیم که آیا در مدت زمان مجاز 5 بایت دریافت شده است یا نه که به دو صورت میتوان این مسئله را چک کرد. یکی اینکه تابع خروجی HAL_OK را بدهد و یا اینکه مقدار متغیر RxXferCount که در اینجا زیر مجموعه ساختار huart1 است مساوی صفر قرار گیرد. در خط 120 مقدار 5 بایت دریافت شده داخل آرایه به نمایش در خواهد امد و در خط 122 چک می‌شود اگر تابع در مدت زمان مجاز نتوانسته تعداد بایت درخواستی ما را دریافت کند چه دستوراتی اجرا شود. در خط 125 چک می‌کنیم که اگر حداقل چند بایت از بایت‌های درخواستی ما هم دریافت شده همان‌ها را برای ما نمایش دهد. یکی از کاربردهای عمومی که این تابع می‌تواند داشته باشد، در زمانی است که ما با یک ماژول جانبی مثل sim800 که به رابط سریال متصل شده است کار می‌کنیم و می‌خواهیم بعد از فرمان دادن به ماژول ببنیم آیا تا یک حداکثر زمان مشخص پاسخ مناسب را به ما می‌دهد یا نه. حالا که یاد گرفتیم چطور می‌توانیم توسط توابع کتابخانه hal عملیات ارسال و دریافت از رابط سریال uart را داشته باشیم بهتر است به سراغ آموزش دو فرمان عمومی ارسال و دریافت از رابط سریال برویم، یعنی فرمان‌های printf و فرمان scanf .توجه داشته باشید شما در حالت عادی نمی‌توانید از این فرمان‌ها استفاده کنید مگر بعد از معرفی توابع خواندن و ارسال کارکتر به برنامه. عملیات معرفی این فرمان‌ها مطابق عکس‌های زیر می‌باشد. ابتدا باید طبق عکس زیر توابع را در فایل main.c به برنامه معرفی کنید :    
میکروکنترلر STM32

آموزش میکروکنترلر STM32

    و در پایین فایل main.c هم متن توابع را به این شکل طرح برای کار با UART1 آماده می‌کنیم:  
آماده‌سازی توابع برای کار با UART1

آموزش میکروکنترلر STM32

  برای واحدهای دیگر رابط UART در میکروکنترولر تنها لازم است عدد آنها را تغییر دهیم ، شما اگر به مثال‌های خود شرکت st یا مثال‌های داخل اینترنت نگاه کنید، آنها از فرمان‌های خود کتابخانه hal برای ارسال و دریافت تک کاراکتر استفاده کرده‌اند ، که کاملا یک کار غیرحرفه‌ای و غیر لازم است و بهترین راه همین مستقیم کار کردن بارجیستر و پرچم‌ها با خلاصه ترین شکل کدنویسی است. کلاً توصیه من به شما این است که در برنامه‌نویسی تا حد ممکن از فرمان‌های کتابخانه hal استفاده نکنید و خودتان مستقیماً با رجیسترها کار کنید تا کیفیت کد نهایی مطلوب باشد.   در قسمت نهم آموزش میکروکنترلر STM32 قصد داریم طریقه کار با وقفه رابط سریال را با استفاده از توابع کتابخانه hal را توضیح دهیم. با سیسوگ همراه باشید.

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

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

نوشته های مشابه

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

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

20 دیدگاه در “آموزش میکروکنترلر STM32 قسمت هشتم: مثال عملی رابط سریال UART

  1. tariq گفت:

    سلام مهندس
    وقت بخیر
    اگر بخواهیم یک printf برای usart1 و یک prinf برای usart2 داشته باشیم باید چکار کنیم؟مثلا موقع تعریف printf یوزارت هم تعریف کنیم مثل:

    (printf(&husart2,”HELLO”%d,variable

    1. زئوس Zeus زئوس Zeus گفت:

      برای این کار اول باید با استریم ها توی سی آشنا بشید.
      ابن کار شدنی است – میتونید با تعریف استریم مسیر داده خروجی دستور printf رو مشخص کنید
      فکر کنم از این لینک شروع کنید بد نباشه

  2. سلام مهندس من یک برد دیسکاوری تهیه کردم با پردازنده F1 و از طریق برنامه cube mx تنظیمات رو انجام دادم .
    میخوام با دستور HAL_UART_Transmit که ابتدای جزوه توضیح مختصری دادید متن “hi” رو که داخل بافر هست به برنامه ای مثل hercules در کامپیوتر ارسال کنم اما هر بار بطور ثابت اطلاعات ناخوانا و مبهم منتقل میشوند چیزی تقریبا بدین صورت : ec
    مشکل از کجا میتونه باشه بنظرتون؟ میتونم اطلاعات رو براتون ایمیل کنم؟ خیلی ساده و کم حجمه

    1. زئوس Zeus زئوس Zeus گفت:

      سلام دوست عزیز
      احتمالا باوود ریت درست تنظیم نشده – چند تا دلیل میتونه داشته باشه
      مقدار دهی اشباهی رجیستر ها که اگر داربد کیوب مکس استفاده میکنید احتمالش ضعیفه
      احتمال قوی تر انتخاب کلاک اشتباه با تنطیمات هست. مثلا ببینید فرکانس کریستال روی بردتون چقدره شما هم مقدار کریستال خارجی رو همین تنظیم کردید ؟
      یا مقادیر pll رو بررسی کنید و از دست احتمالات
      آها انشالله که زمین مدار و مبدل سریال رو که یکی کردید دیگه ؟

      1. سلام و عرض ادب
        خدمت دوستان عارضم که من با پورت سریال در انواع PLC ، نرم افزارهای مانیتورینگ و برنامه LabVIEW قبلا در سطح متوسط که سنسوری بخونیم یا خروجی روشن کنیم و از این کارها ، کار کردم و زمینه پایه ای لازم رو دارم.

        در پاسخ شما که لطف کردید جواب دادید باید عرض کنم:
        تنظیمات در هر دو سمت و پورت کامپیوتر در device manager درست و یکسان تنظیم شده.

        کریستال خارجی روی برد تا 24 مگا هرتر میتونه تنظیم شه که من در CUBE تمام پریفرال ها و قطعات رو روی 16 تنظیم کردم.

        بله اگر زمین وصل نباشه که اصلا ارتباطی نخواهم داشت.

        کابلم db9 female هستش که پایه 2 ، 3 و 5 رو استفاده کردم rx = 2 , tx = 3 , gnd = 5

        سیم های رنگی کتابی سوکت خورده رو از یک سر به سیم مربوطه لحیم کردم و مادگی رو به پایه های ای که روی برد مشخص کردم زدم و از اون طرف با یک مبدل usb to serial به کامپیوتر وصل کردم.

        برنامه ای که نوشتم کلمه hi رو از یک بافر از نوع unsigned char به طول 3 از طریق فرمت دستور hal_uart_transmit بصورت کست شده داخل حلقه while با یک تاخیر 250ms دایما به سمت گیرندهع میفرسته و من اون سمت هر 250ms بجای کلمه hi کلمه ناخوانای ec رو دارم ، جالب اینکه همین تست رو بجای برنامه Hercules روی PLC هم انجام دادم نتیجه همین بود عبارت ناخوانی ec بصورت دایم هر 250ms میومد.

        خودم تو شک هستم که آیا نویزه ؟!

        این فایل 10 مگی آپلود شده ی پروژست از قبیل پیکر بندی CUBE و برنامه arm اگر کسی تونست رهنمایی در حل بکنه جبران مالی هم بر حسب وظیفه میکنم.
        با تشکر

        1. قبلی رو تایید نکنید لطفا اشتباه شد
          این آدرس دانلود فایله که یادم رفت بگذارم :
          http://s12.picofile.com/file/8399043118/usart1_send.rar.html

        2. زئوس Zeus زئوس Zeus گفت:

          خوب بسیار عالی – ببینید دوست عزیز چند تا سوال میپرسم لطف کنید اینا رو توضیح بدید تا بشه نظر داد
          ۱. کریستال خارجی میکروکنترلر شما چند مگاهرتزه ؟
          ۲. از چه بردی استفاده میکنید خودتون طراحی کردید یا آماده است ؟
          ۳. آیا روی یوارت میکروکنترلر آیسی مبدل سطح مثل rs232 وجود داره یا نه ؟
          ۴. مدل مبدل سریال شما ورودی ttl میگیره یا منطق ۲۳۲ ؟
          همونطور که خودتون هم گفتید سریال چیز پیچیده ای نداره من فکر میکنم باوود ریت شما درست نیست که این عبارت رو دارید دریافت میکنید یا منطق ولتاژی یکسان نیست که احتمالش کمتره – برای همین به سوالات جواب بدید ممکنه هرچند ساده به نظر بیاد ولیی برای من که بردتون رو ندیدم لازمه که این جزییات رو بدونم اگه قراری کمکی بکنم.

          1. سلام
            1- کریستال خارجی 8MHZ
            2- برد آماده خریداری شده این هم مدلش که در اینترنت فراوونه دیتا شیتش و جزو رایج ترین بردهای آماده برای تازه کارهاست : STM32VLDISCOVERY
            3- به گمونم داره چون در آموزش ارایه شده برای STM32F1 با این برد در ویدئو مستقیما برد به ماژول Sim800L متصل بود و ارتباط میگرفت
            4- مبدلی که دارم USB به DB9 Male هستش مدل OMEGA که اونم دیتا شیتش در ینترنت زیاده و جزو رایج ترین های با کیفیت در ایرانه ، وقتی داره از USB کامپیوتر میگیره حدس میزنم ولتاژ TTL داشته باشه بیشتر از این نمیدونم اما به راحتی با هر دستگاهی باهاش ارتباط گرفتیم بین کامپیوتر و اون.

            در مورد باودریت هم مجددا چک کردم در هر دو سمت بصورت یکسان ب انواع و اقسام امتحان کردم و پاسخ صحیح نگرفتم و کلاک هم از 16 به 8 تغییر دادم همینطور بود.
            اینها رو من اضافه کردم فقط به فایلی که برنامه کیوب تولید کرده در فایل main نمیدونم آیا چیز دیگه لازمه اضافه کنم یا نه:

            تعریف بافر :
            unsigned char Buffer[3]={“hi”};

            حلقه تکرار حاوی دستور ارسال:

            while (1)
            {
            HAL_UART_Transmit(&huart1,(unsigned char*)Buffer,3,100);
            HAL_Delay(250);
            }

            اینم از تنظیمات پورت که برنامه کیوب تو فایل main آورده :
            huart1.Instance = USART1;
            huart1.Init.BaudRate = 9600;
            huart1.Init.WordLength = UART_WORDLENGTH_8B;
            huart1.Init.StopBits = UART_STOPBITS_1;
            huart1.Init.Parity = UART_PARITY_NONE;
            huart1.Init.Mode = UART_MODE_TX;
            huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
            huart1.Init.OverSampling = UART_OVERSAMPLING_16;

            ممنونم از وقتی که میگذارید انشا… بتونم جبران کنم.

          2. زئوس Zeus زئوس Zeus گفت:

            ببخشید که دارم دیر پاسخ میدم – خوب مشکل معلوم شد – برد شما خروجی ttl میده و مبدل شما نیاز به ورودی با منطق rs232 داره (یعنی ۱۲ ولت) برای این تبدیل میتونید از آیسی max232 استفاده کنید یا از مبدل با ورودی ttl استفاده کنید.

  3. پیام گفت:

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

    1. زئوس Zeus زئوس Zeus گفت:

      خوب برای مقایسه رشته شما اول باید یک بافر درست کنید که کارکترهای دریافتی رو توی اون بافر ذخیره کنید و بعد عملیات مقایسه رشته رو روی بافر انجام بدید در غیر این صورت این کار شدنی نیست دوست عزیز.

  4. kourosh گفت:

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

    1. زئوس Zeus زئوس Zeus گفت:

      سلام خواهش میکنم
      ببخشید متوجه منظورتون نشدم – پاک کردن فلگ دریافت در مود اینتراپت توسط خود کتابخانه hal فکر میکنم انجام بشه و لازمه این اتفاق بیفته چون اگر این اتفاق نیفته وقفه مداما تکرار می شود.

  5. سعید گفت:

    سلام مهندس گرامی
    ممنون از مطالب خوب و مفید شما

    من از طریق پورت سریال و ترمینال با ماژول sim808 ارتباط برقرار کردم و میخوام با استفاده از اینترنت اشیاء دیتا رو روی گوشی از طریق اپلیکیشن mqttdash نمایش بدم.
    با برنامه ترمینال تونستم با سرور ارتباط برقرار کنم اما توی ارسال دیتا به مشکل برخوردم.

    topic رو چطوری میتونم از این طریق معرفی کنم؟کلا از طریق ترمینال و رابط سریال این کار انجام میشه؟

    ممنون میشم کمک کنید.

    1. زئوس Zeus زئوس Zeus گفت:

      سلام دوست عزیز
      خوب همونطور که میدونید sim808 یه شکل مستقیم از mqtt پشتیبانی نمیکنه
      برای این کار باید از کتابخونه های آماده ای که برای ارتباط با mqtt نوشته شده است استفاده کنید
      میشه توضیح بدید چطور از طریق ترمینال وصل شدید ؟

  6. Porya گفت:

    سلام مهندس
    وقت بخیر
    خیلی ممنون از آموزش هاتون
    ببخشید به یه مشکل حین کار با stm32f407 خوردم
    موقع کار با spi همه تنظیماتو انجام میدم

    ولی زمان ارسال یا دریافت رجیستر dr ظاهرا اصلا دیتایی دریافت یا ارسال نمیکنه

    تننظیمات هم کاملا درسته
    مد master انتخاب شده
    بیت BR با مقدار مناسب تنظیم شده
    بیت های cpol و cpha با مقدار درست مقدار دهی شدن
    بعد بیت spe ست شده
    مد ارسال ۸ بیتیه
    بعدش هم یه دیتا تویه رجیستر drنوشته میشه.
    برنامه منتظر میمونه تا txe یک بشه، بعد rxne بعد bsy.
    عجیبه با خوندن بیت txe برای چک شرط یک شدنش، بیت rxne صفر میشه خودکار!!
    و درنتیجه در شرط بعدی که چک کردن بیت rxneه کد گیر میکنه.
    اینکه rxne یک میشه ولی باز بافر دریافت خالیه و dr هیچ مقداری جز صفر نداره هم عجیبه!!
    بعد چک های متوالی برنامه هم از طریق توابع hal و هم کتابخونه هایی که خودم نوشتم، متوجه شدم بیت bsy هم یک نمیشه اصلا!
    پس انگار کلا واحد spi درگیر ارسال و دریافت نمیشه! ولی اگه اینجوریه چرا rxne تغییر حالت میده؟!
    هرچی چک میکنم و میخونم نتیجه ایی نمیگیرم، نمیدونم چیه مشکل واقعا

    اگه راهنماییم کنید ممنون میشم

    1. زئوس Zeus زئوس Zeus گفت:

      سلام
      امم ، اولین چیزی که به ذهنم میرسه اینه که چک کنید آیا کلاک SPI مورد استفاده رو تامین کردید یا خیر (موارد زیادی برای خودم پیش اومده که کلاک رو تنظیم نکرده بودم)
      اگر امکان داره تیکه کد کانفیگ و تیکه کدی که دیتا رو ارسال میکنید رو اینجا قرار بدید که بشه بررسی کرد. کارایی که برای ارسال انجام میدید ظاهرا درسته
      برای ارسال اول باید چک کنید که بافر ارسال خالی باشه ، بعد دیتا رو قرار بدید ؛ بعد منتظر بشید که بافر دریافت پر بشه و فرایند رو اینطور پیش ببرید

    2. زئوس Zeus زئوس Zeus گفت:

      آقای عبدالهی زحمت کشیدند و این تابع رو آماده کردند

      uint8_t SPI_ReadWriteByte(uint8_t num)
      {
      uint8_t retry=0;
      while((SPI1->SR&1<<1)==0) { retry++; if(retry>200)return 0;
      }
      SPI1->DR=num;
      retry=0;
      while((SPI1->SR&1<<0)==0) { retry++; if(retry>200)return 0;
      }
      return SPI1->DR;
      }

  7. وحید گفت:

    درود
    با تشکر از آموزش هاتون یک درخواست داشتم اگه مقدور هست لطف بفرمایید و مثال های عملی رو طوری پیش ببرید که بشه تست کرد تا مطالب بهتر جابیفته . آموزش هاتون خیلی خوبه ازتون ممنونم .
    باتشکر

    1. زئوس Zeus زئوس Zeus گفت:

      سلام دوست عزیز
      احتمالا سری دوم آموزش ها همینطور که پیشنهاد میدید پیش خواهد رفت
      موفق باشید