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

آموزش میکروکنترلر STM32 : واحد DMA در رابط سریال UART

آموزش میکروکنترلر STM32 ; سیسوگ در قسمت نهم از آموزش میکروکنترلر STM32  طریقه کار با وقفه رابط سریال را با استفاده و بدون استفاده از توابع کتابخانه hal  آموزش داد. در این قسمت از آموزش میکروکنترلر STM32 قصد دارد وارد قسمت آموزش کار با DMA رابط سریال UART شود. در مجموعه آموزشی ” آموزش میکروکنترلر STM32 ” با سیسوگ همراه باشید.

 

DMA در رابط سریال UART

اول از همه بهتر است در چند جمله کوتاه کار و قابلیت این امکان در این رابط سریال را توضیح دهیم. فرض کنید برنامه‌ای داریم که میخواهیم 500 بایت اطلاعات را به رابط سریال ارسال کنیم، بدون اینکه دست از کارهای دیگرمان بکشیم و وقتی صرف نماییم و یا اینکه مثلا 500 بایت از رابط سریال دریافت کنیم بدون اینکه بخواهیم وقتی صرف کنیم و منتظر دریافت باشیم. در موارد یاد شده ما از واحد DMA استفاده خواهیم کرد، به علاوه این قابلیت که وقتی عملیات به پایان رسید از طریق یک وقفه با خبر خواهیم شد و کدهای مناسب را در ادامه اجرا خواهیم کرد. ما برای شروع در این قسمت ابتدا آموزش کار با DMA را در قسمت دریافت قرار می‌دهیم. برای شروع دوباره به نرم افزار CubeMX می‌رویم و در قسمت تنظیمات UART1 به پنجره DMA settings می‌رویم و طبق عکس زیر تنظیمات را انجام می‌دهیم.

 تنظیمات UART1 پنجره DMA settings

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

بعد از انتخاب گزینه مورد نظر حالا می‌توانیم از طریق گزینه Mode در پایین سمت چپ مشخص کنیم دیتا به چه صورت دریافت شود، بصورت عادی Normal یا به صورت Circular و چرخشی. در حالت عادی ما هر بار باید بعد از اتمام دریافت اطلاعات دوباره پیکره‌بندی را انجام دهیم ولی در حالت چرخشی این عمل لازم نیست و بصورت خودکار بعد از اتمام دریافت دوباره آماده دریافت اطلاعات می‌شویم.

تنظیمات UART1 پنجره DMA settings

و در قسمت Data width هم نوع اطلاعاتی را که می‌خواهیم منتقل کنیم، مشخص می‌کنیم:

تنظیمات UART1 پنجره DMA settings

توجه داشته باشید در اینجا Byte به معنی 8 بیتی و Half Word به معنی 16 بیتی و Word به معنی دیتا از نوع 32 بیتی می‌باشد. ما در اینجا چون اطلاعاتی که جابجا می‌کنیم هشت بیتی است همان Byte را انتخاب می‌کنیم. بعد از ok کردن تنظیمات از CubeMX خروجی می‌گیریم و به محیط نرم افزار keil می‌رویم.

حال به توضیح فرمان‌های کتابخانه hal در این قسمت می‌پردازیم. برای ارسال یک آرایه اطلاعات به رابط سریال uart ما از فرمان زیر استفاده خواهیم کرد:

 

در آرگومان *huart تابع نام uart مورد استفاده ما قرار می‌گیرد و در آرگومان *pData رشته ای که قرار است اطلاعات دریافتی از رابط سریال در داخل آن قرار بگیرد. در آرگومان Size سایز یا تعداد بایتی که قرار است دریافت شود تعیین می‌شود. در نتیجه ما باید در ابتدای محلی که می‌خواهیم دریافت اطلاعات شروع شود از این فرمان استفاده کنیم. برای فهم بیشتر مسئله به عکس زیر توجه کنید.

 ارسال اطلاعات به رابط سریال uart

همانطور که میبینید در قسمت مشخص شده از عکس بالا، ما قرار است 20 بایت از رابط سریال huart1 دریافت کنیم و بایت‌های دریافتی هم داخل آرایه str ریخته شود. خوب تا اینجا مشکلی نیست، ولی می‌خواهیم بدانیم چطور از نتیجه عملیات باخبر شویم. یعنی اولا از کجا بفهمیم عملیات انتقال تا کجا پیش رفته یعنی چند بایت تا الان دریافت شده و دوم اینکه نصف بایت‌هایی که می‌خواستیم دریافت شده و سوم اینکه چطور بفهمیم همه 20 بایتی که می‌خواستیم کاملا دریافت شده است. در جواب سوال اول، ما می‌توانیم با چک کردن وضعیت رجیستر CNDTR از رجیستر‌های واحد DMA مورد استفاده خودمان متوجه شویم تا الان چند بایت دیگر از 20 بایت برای دریافت باقیمانده برای فهم بیشتر به کد زیر توجه کنید:

 

 

بر طبق کد‌نویسی بالا 20 بایتی که می‌خواستیم در یک مرحله دریافت شود، از تعداد بایتی که برای دریافت باقیمانده کم می‌شود و نتیجه می‌شود تعداد بایتی که تا الان دریافت شده بواقع عملکرد رجیستر CNDTR دقیقا مشابه عملکرد همان رجیستر RxXferCount است که پیش از این ذکر کردیم. یعنی با هر بار استفاده از فرمان HAL_UART_Receive_DMA این رجیستر با تعداد بایت درخواستی که ما در اینجا همان 20 است پر می‌شود و با هر واحد (در اینجا بایت) دریافت اطلاعات یکی از مقدار آن کم می‌شود تا به صفر برسد و بواقع عملیات دریافت پایان پذیرد.

راه حل دیگری که می‌توان از طریق آن متوجه شد آیا واحد DMA هنوز در حال تبادل اطلاعات است یا کار آن به پایان رسیده، چک کردن وضعیت State واحد DMA مورد نظر است. برای فهم بیشتر مسئله به عکس کدنویسی زیر توجه کنید.

چک کردن وضعیت State واحد DMA

همانطور که در عکس بالا میبینید ما چک می‌کنیم اگر وضعیت State در حالت HAL_DMA_STATE_READY است، یعنی عملیات تمام شده و واحد مورد نظر دوباره آماده پذیرش است و در خط 133 هم اطلاعات دریافتی را نمایش می‌دهیم . در خط 134 بافر دریافت را پاک میکنیم، در خط 135 بدلیل اینکه در مد عادی هستیم و از حالت چرخشی استفاده نکردیم دوباره پیکره بندی را انجام می‌دهیم. توجه داشته باشید که State در ابتدای شروع متن تابع HAL_UART_Receive_DMA به حالت HAL_UART_STATE_BUSY_RX می‌رود و تا پایان عملیات دریافت در همین وضعیت باقی می‌ماند و اصولا تابع HAL_UART_Receive_DMA هم تنها زمانی اجرا می‌شود که State در وضعیت ازاد یا همان HAL_DMA_STATE_READY باشد. برای فهم بیشتر متن تابع مورد نظر را در برگه مربوطه مطالعه نمائید. بیاد داشته باشید این قائده تنها مخصوص این تابع نمی‌باشد، بلکه خیلی توابع دیگر از کتابخانه hal از این رویه پیروی می‌کنند. خوب حالا بسراغ توابع وقفه رابط DMA می‌رویم که این وقفه به اجبار در همان نرم افزار CubeMX انتخاب شده بوده بدنه روتین وقفه در همان برگه stm32f1xx_it.c وجود دارد که در عکس زیر مشاهده مینمائید.

توابع وقفه رابط DMA

در اینجا ما می‌خواهیم از دو وقفه مختلف برای دریافت رابط DMA استفاده کنیم و از توابع Callback که می‌توانیم در  برگه main.c برنامه خود ان را جاسازی کنیم استفاده می‌کنیم. اولین تابع Callback وقفه دریافت کامل است و دیگری مربوط به دریافت نیمی از اطلاعات درخواستی یعنی وقتی 20 بایت می‌خواهیم از رابط سریال دریافت کنیم بعد از دریافت 10 بایت که نیمی از اطلاعات درخواستی ماست به این تابع می‌رویم. در عکس زیر می‌توانید این توابع را به همراه کدهای مورد نظرمان که می‌خواهیم داخل این توابع اجرا شود در برگه main.c برنامه ببنید.

تابع Callback وقفه رابط DMA

همانطور که در کدنویسی‌ها مشخص است تابع بالا مربوط می‌شود به دریافت کامل و تابع پایینی مربوط می‌شود به دریافتی نیمی از اطلاعات. و در عکس هم توضیح داده شده است که فرمان خط 251 تنها باید زمانی نوشته شود که از حالت معمولی استفاده شده و در حالت چرخشی یا Circular احتیاجی به دوباره نوشتن این فرمان برای دریافت دوباره نیست. برای فهم بیشتر مسئله به تصویری از محیط نرم افزار terminal برای ارتباط سریال با کامپیوتر توجه کنید.

 محیط نرم افزار terminal برای ارتباط سریال با کامپیوتر

ارسال از طریق DMA

بعد از آموزش طریقه دریافت بوسیله واحد DMA وارد آموزش ارسال از طریق DMA می‌گردیم. برای این منظور دوباره به محیط CubeMX برمی‌گردیم و باز به سراغ پنجره DMA settings می‌رویم و طبق عکس زیر تنظیمات را انجام می‌دهیم.

 پنجره DMA settings در محیط CubeMX

بعد از ok کردن تنظیمات از CubeMX خروجی می‌گیریم و به محیط نرم افزار keil وارد می‌شویم. حال می‌خواهیم ببنیم از طریق چه فرمانی از کتابخانه hal می‌توانیم محتوای یک بافر را از طریق واحد DMA به رابط سریال ارسال کنیم.

 

 

در آرگومان *huart تابع نام uart مورد استفاده ما قرار می‌گیرد و در آرگومان *pData رشته‌ای که قرار است اطلاعات آن به رابط سریال ارسال شود. در آرگومان Size سایز یا تعداد بایتی که قرار است ارسال شود را تعیین می‌شود. بعنوان مثال فرمان زیر را تحیلیل می‌کنیم.

 

 

از طریق این فرمان ما 20 بایت از آرایه str به درگاه سریال huart1 ارسال می‌کنیم. حال اگر حالت عادی را انتخاب کرده باشیم این ارسال یکبار صورت می‌گیرد و اگر حالت چرخشی را انتخاب کرده باشید بصورت مداوم اطلاعات به رابط سریال ارسال می‌گردد . توجه داشته باشید برای حالت ارسال هم دو وقفه مشابه حالت دریافت بصورت Callback بکار گرفته می‌شود. یکی بعد از اتمام نیمی از بافر با نام HAL_UART_TxHalfCpltCallback و دومی بعد از اتمام همه محتویات بافر با نام HAL_UART_TxCpltCallback منتها در حالت عادی تنها وقفه ارسال نیمی از بافر قابل دسترسی است، منتها شما خودتان می‌توانید با دستکاری تابع UART_DMATransmitCplt در برگه stm32f1xx_hal_uart.c می‌توانید برای حالت عادی هم از آن وقفه استفاده کنید.

تنظیملت DMA در رابط سریال UART

توجه داشته باشید در حالت ارسال هم می‌توانیم با چک کردن رجیستر CNDTR  متوجه شویم چند بایت تا الان ارسال شده و همچنین با چک کردن وضعیت State به مانند حالت دریافت میتوانیم از اتمام عملیات باخبر شویم ، بعد از توضیح و اموزش دریافت و ارسال توسط DMA توسط رابط UART بد نیست به چند دستور دیگر از کتابخانه hal که معمولا کاربرد دارند اشاره شود.

 

 

متوقف کردن ادامه انتقال

 

 

ادامه انتقالی که قبلا متوقف شده بود

 

 

توقف کلی انتقال

 

 

برای توقف انتقال‌های جاری که البته در حالت چرخشی مجاز نیست و باعث می‌شود برنامه بعضا بعد از مدتی قفل کند.

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

 

سیسوگ در قسمت یازدهم از مجموعه آموزش میکروکنترلر STM32 به آموزش تنظیمات نرم‌افزار CubeMX برای رابط RTC  در خانواده CORTEXM3 می‌پردازد. در مجموعه آموزش میکروکنترلر STM32 با سیسوگ همراه باشید.

 

 

 

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

6 دیدگاه در “آموزش میکروکنترلر STM32 : واحد DMA در رابط سریال UART

  1. سروش گفت:

    با عرض سلام و احترام
    سوالی از آموزشتون برام پیش اومده بود که ممنون خواهم شد اگر جواب بدین.
    در قسمت پنجره Dma setting یه قسمت هست به نام priority که Low گذاشتین . از کجا می شه فهمید باید High گذاشت یا Low . چون من در مثال های دیگه ای که مثل همین بود حالتهای مختلفی دیدم.یک جا هر دو را Low گذاشته بودند یک جای دیگه High برای RX و Low برای TX گذاشته بودند.
    از جناب آقای زئوس بزرگ هم کمال تشکر را دارم . موفق و سربلند باشید!

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

      سلام دوست عزیز، ممنون از لطف شما
      خوب همونطور که از اسم priority برمیآد ، برای مشخص کردن اولویت کاربرد داره ، اما اولویت چی بر چی ؛ ببینید توی میکروکنترلر یه باس RAM در هر لحظه میتونه میزبان تراکنش باشه (البته باس رم خیلی سریعه و فکر میکنم سرعتی در حدود گیگاهرتز داره تو مدل m4) ولی به هر حال ، حالا فرض کنید هم زمان شما دوتا کانال Dma رو فعال کردید ، و شرایطی پیش میآد که دقیقا نیاز در یک لحظه مشخص به حافظه RAM دسترسی پیدا بکنند ، چه اتفاقی می تونه بیفته ؟
      شما میتونید با دادن اولویت مشخص کنید که کدام کانال DMA از اولویت بالاتری برخورداره و زودتر به RAM دسترسی پیدا بکنه ! البته میگم فقط توی چنین شرایطی !

    2. سروش گفت:

      با سلام و عرض ادب
      واقعا ممنون توضیحتون مشکلم رو حل کرد.
      یک دنیا ممنون.
      موفق و سربلند باشید.

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

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

  2. سروش گفت:

    سلام آقا مهدی
    خسته نباشید . امیدوارم همینطور به آموزشتون ادامه بدید.
    خیلی خیلی ممنون.
    از سایت سیسوگ هم تشکر ویژه – علم برای همه رایگان –
    می خواستم اگه امکانش هم بود در مورد حالت mem to mem واحد dma هم یه قسمت آموزشی بزارید.
    موفق و سر بلند باشید.

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

      خواهش میکنم دوست عزیز
      خوشحالیم اگر توانسته باشیم کمکی هر چند کوچک کرده باشیم

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

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