آموزش میکروکنترلر STM32
در قسمت نهم از آموزش میکروکنترلر STM32 طریقهکار با وقفه رابط سریال را بااستفاده و بدوناستفادهاز توابع کتابخانه hal آموزش داد. در این قسمت از آموزش میکروکنترلر STM32 قصد دارد وارد قسمت آموزشکار با DMA رابط سریال UART شود. در مجموعه آموزشی “آموزش میکروکنترلر STM32” با ما همراه باشید.
DMA در رابط سریال UART
اول از همه بهتر است در چند جملهکوتاه کار و قابلیت این امکان در این رابط سریال را توضیحدهیم. فرضکنید برنامهای داریم که میخواهیم ۵۰۰بایت اطلاعات را به رابط سریال ارسالکنیم، بدوناینکه دست از کارهای دیگرمان بکشیم و وقتی صرف نماییم و یااینکه مثلا ۵۰۰بایت از رابط سریال دریافت کنیم بدون اینکه بخواهیم وقتی صرف کنیم و منتظر دریافت باشیم. در موارد یادشده ما از واحد DMA استفاده خواهیمکرد، بهعلاوه این قابلیت که وقتی عملیات به پایان رسید ازطریق یک وقفه باخبر خواهیمشد و کدهای مناسب را در ادامه اجرا خواهیمکرد. ما برای شروع در این قسمت ابتدا آموزش کار با DMA را در قسمت دریافت قرار میدهیم. برای شروع دوباره به نرمافزار CubeMX میرویم و در قسمت تنظیمات UART1 به پنجره DMA settings میرویم و طبق عکسزیر تنظیمات را انجام میدهیم.
بعداز انتخاب گزینه موردنظر حالا میتوانیم ازطریق گزینه Mode در پایین سمت چپ مشخصکنیم دیتا به چهصورت دریافتشود، بهصورت عادی Normal یا بهصورت Circular و چرخشی. درحالت عادی ما هربار باید بعداز اتمام دریافت اطلاعات دوباره پیکرهبندی را انجامدهیم ولی درحالت چرخشی این عمل لازم نیست و بهصورت خودکار بعداز اتمام دریافت دوباره آماده دریافت اطلاعات میشویم.
و در قسمت Data width هم نوع اطلاعاتی را که میخواهیم منتقلکنیم، مشخص میکنیم:
توجهداشتهباشید در اینجا Byte بهمعنی ۸بیتی و Half Word بهمعنی ۱۶بیتی و Word به معنی دیتا از نوع ۳۲بیتی میباشد. ما در اینجا چون اطلاعاتی که جابجا میکنیم ۸بیتی است همان Byte را انتخاب میکنیم. بعداز ok کردن تنظیمات از CubeMX خروجی میگیریم و به محیط نرمافزار keil میرویم. حال به توضیح فرمانهای کتابخانه hal در این قسمت میپردازیم. برای ارسال یک آرایه اطلاعات به رابط سریال uart ما از فرمانزیر استفاده خواهیم کرد:
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
در آرگومان *huart تابع نام uart مورداستفاده ما قرار میگیرد و در آرگومان *pData رشتهای که قرار است اطلاعاتدریافتی از رابط سریال در داخل آن قرار بگیرد. در آرگومان Size سایز یا تعداد بایتی که قرار است دریافتشود تعیین میشود. درنتیجه ما باید در ابتدای محلی که میخواهیم دریافت اطلاعات شروعشود از این فرمان استفادهکنیم. برای فهم بیشتر مسئله به عکسزیر توجهکنید.
همانطورکه میبینید در قسمت مشخصشده از عکسبالا، ما قرار است ۲۰بایت از رابط سریال huart1 دریافتکنیم و بایتهای دریافتی هم داخل آرایه str ریختهشود. خوب تااینجا مشکلینیست، ولی میخواهیم بدانیم چطور از نتیجهی عملیات باخبر شویم. یعنی اولا از کجا بفهمیم عملیات انتقال تا کجا پیش رفته یعنی چندبایت تاالان دریافتشده و دوم اینکه نصف بایتهایی که میخواستیم دریافتشده و سوماینکه چطور بفهمیم همه ۲۰بایتی که میخواستیم کاملا دریافتشدهاست. در جواب سوال اول، ما میتوانیم با چککردن وضعیت رجیستر CNDTR از رجیسترهای واحد DMA مورداستفاده خودمان متوجهشویم تا الان چند بایت دیگر از ۲۰بایت برای دریافت باقیمانده برای فهمبیشتر به کد زیر توجهکنید:
printf("count data resive=%d\r\n", 20 - hdma_usart1_rx.Instance->CNDTR);
برطبق کدنویسی بالا ۲۰بایتی که میخواستیم در یک مرحله دریافت شود، از تعداد بایتی که برای دریافت باقیمانده کم میشود و نتیجه میشود تعداد بایتی که تاالان دریافتشده بهواقع عملکرد رجیستر CNDTR دقیقا مشابه عملکرد همان رجیستر RxXferCount است که پیشاز این ذکر کردیم. یعنی با هربار استفادهاز فرمان HAL_UART_Receive_DMA این رجیستر با تعداد بایت درخواستی که ما در اینجا همان ۲۰ است پر میشود و با هر واحد (در اینجا بایت) دریافت اطلاعات یکیاز مقدار آن کم میشود تا به صفر برسد و بهواقع عملیات دریافت پایان پذیرد. راهحل دیگری که میتوان ازطریق آن متوجهشد آیا واحد DMA هنوز درحال تبادل اطلاعات است یا کار آن به پایان رسیده، چککردن وضعیت State واحد DMA موردنظر است. برای فهم بیشتر مسئله به عکس کدنویسی زیر توجهکنید.
همانطورکه در عکسبالا میبینید ما چک میکنیم اگر وضعیت State درحالت HAL_DMA_STATE_READY است، یعنی عملیات تمامشده و واحد موردنظر دوباره آماده پذیرش است و در خط ۱۳۳هم اطلاعات دریافتی را نمایش میدهیم . در خط ۱۳۴بافر دریافت را پاک میکنیم، در خط ۱۳۵ بهدلیلاینکه در مد عادی هستیم و از حالت چرخشی استفاده نکردیم دوباره پیکرهبندی را انجام میدهیم. توجه داشته باشید که 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 استفادهکنیم و از توابع Callback که میتوانیم در برگه main.c برنامه خود آنرا جاسازی کنیم استفاده میکنیم. اولین تابع Callback وقفه دریافت کامل است و دیگری مربوطبه دریافت نیمی از اطلاعات درخواستی یعنی وقتی ۲۰بایت میخواهیم از رابط سریال دریافتکنیم بعداز دریافت ۱۰بایت که نیمیاز اطلاعات درخواستی ماست به این تابع میرویم. در عکسزیر میتوانید این توابع را بههمراه کدهای موردنظرمان که میخواهیم داخل این توابع اجرا شود در برگه main.c برنامه ببنید.
همانطورکه در کدنویسیها مشخص است تابع بالا مربوط میشود به دریافت کامل و تابع پایینی مربوط میشود به دریافتی نیمیاز اطلاعات. و در عکس هم توضیح داده شدهاست که فرمان خط ۲۵۱ تنها باید زمانی نوشتهشود که از حالت معمولی استفادهشده و درحالت چرخشی یا Circular احتیاجیبه دوباره نوشتن این فرمان برای دریافت دوباره نیست. برای فهمبیشتر مسئله به تصویری از محیط نرمافزار terminal برای ارتباط سریال با کامپیوتر توجهکنید.
ارسال ازطریق DMA
بعداز آموزش طریقه دریافت بوسیله واحد DMA وارد آموزش ارسال از طریق DMA میگردیم. برای این منظور دوباره به محیط CubeMX برمیگردیم و باز به سراغ پنجره DMA settings میرویم و طبق عکسزیر تنظیمات را انجام میدهیم.
بعداز ok کردن تنظیمات از CubeMX خروجی میگیریم و به محیط نرمافزار keil وارد میشویم. حال میخواهیم ببنیم ازطریق چه فرمانی از کتابخانه hal میتوانیم محتوای یک بافر را ازطریق واحد DMA به رابط سریال ارسالکنیم.
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
در آرگومان *huart تابع نام uart مورداستفاده ما قرار میگیرد و در آرگومان *pData رشتهای که قرار است اطلاعات آن به رابط سریال ارسال شود. در آرگومان Size سایز یا تعداد بایتی که قرار است ارسالشود را تعیین میشود. بعنوانمثال فرمانزیر را تحلیل میکنیم:
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)str, 20);
ازطریق این فرمان ما ۲۰بایت از آرایه str به درگاه سریال huart1 ارسال میکنیم. حال اگر حالت عادی را انتخابکرده باشیم این ارسال یکبار صورت میگیرد و اگر حالت چرخشی را انتخاب کردهباشید بهصورت مداوم اطلاعات به رابط سریال ارسال میگردد. توجهداشتهباشید برای حالت ارسال هم دو وقفه مشابه حالت دریافت بهصورت Callback بکار گرفته میشود. یکی بعداز اتمام نیمیاز بافر با نام HAL_UART_TxHalfCpltCallback و دومی بعداز اتمام همه محتویات بافر با نام HAL_UART_TxCpltCallback منتها درحالتعادی تنها وقفه ارسال نیمیاز بافر قابل دسترسی است، منتها شما خودتان میتوانید با دستکاری تابع UART_DMATransmitCplt در برگه stm32f1xx_hal_uart.c میتوانید برای حالت عادی هم از آن وقفه استفادهکنید.
توجهداشتهباشید در حالت ارسال هم میتوانیم با چککردن رجیستر CNDTR متوجهشویم چند بایت تا الان ارسالشده و همچنین با چککردن وضعیت State بهمانند حالتدریافت میتوانیم از اتمام عملیات باخبر شویم، بعداز توضیح و آموزش دریافت و ارسال توسط DMA توسط رابط UART بد نیست به چند دستور دیگر از کتابخانه hal که معمولا کاربرد دارند اشارهشود:
HAL_UART_DMAPause(UART_HandleTypeDef *huart)
متوقف کردن ادامه انتقال:
HAL_UART_DMAResume(UART_HandleTypeDef *huart)
ادامه انتقالی که قبلا متوقف شده بود:
HAL_UART_DMAStop(UART_HandleTypeDef *huart)
توقف کلی انتقال:
HAL_UART_Abort(UART_HandleTypeDef *huart)
برای توقف انتقالهای جاری که البته درحالت چرخشی مجاز نیست و باعث میشود برنامه بعضا بعداز مدتی قفلکند. برای بخش UART توابع hal زیادی وجوددارد که تحقیقو بررسی آن خارجاز وقت و زمان ماست بهاضافه اینکه این رابط سریال خیلی امکانات دیگری هم دارد که در آموزشهای بعدی به آنها اشاره خواهیم کرد. در قسمت یازدهم از مجموعه آموزش میکروکنترلر STM32 به آموزش تنظیمات نرمافزار CubeMX برای رابط RTC در خانواده CORTEXM3 میپردازیم.
در مجموعه آموزش میکروکنترلر STM32 با ما همراه باشید.
منبع: سیسوگ