برد STM ترفند

آموزش میکروکنترلر STM32 قسمت پانزدهم: واحد DMA و کاربردهای آن

نوشته شده توسط علی زاهدی

ممکن است در طول کد نویسی پروژه های خود با این نوضوع مواجه شده باشید که نیاز بوده است یک کاری بدون وقفه در روند برنامه اصلی انجام شود. برای درک بهتر این مطلب به مثالی که در ادامه آورده شده است توجه کنید. در ارتباط سریال با ماژول هایی مانند GSM ممکن است نیاز به ارسال تعداد زیادی کاراکتر داشته باشید. اما ارسال تعداد زیادی کاراکتر، زمان بر است و ممکن است برنامه دچار وقفه کوتاهی شود. که در صورت تکرار، زمان این وقفه قابل توجه خواهد بود. در این آموزش قصد داریم تا با کمک واحد DMA در میکروکنترلر های STM32 به رفع این مشکل بپردازیم. در ادامه با مرجع تخصصی بردهای امبدد به زبان فارسی، دیجی اسپارک همراه باشید.

 


واحد DMA چیست؟


DMA مخفف کلمه DIRECT MEMORY ACCESS به معنای دسترسی مستقیم به حافظه است. همانطور که از نام این واحد پیداست. این امکان را فراهم می‌سازد، که دستگاه متصل به پریفال میکروکنترلر داده را به صورت مسقیم به حافظه منتقل کند. DMA تکنیکی برای  دسترسی مستقیم  دستگاه‌های I / O به حافظه بدون نیاز به پردازنده است. بنابراین DMA باعث استفاده کارآمدتر و بدون وقفه از دستگاه می‌شود، سرعت انتقال داده را افزایش و هزینه‌های سخت افزاری را کاهش می‌دهد. وجود DMA با CPU می تواند توان آن را به ترتیب بزرگی تسریع کند.

آموزش میکروکنترلر STM32 بخش DMA - دیجی اسپارک

همانطور که گفته شد DMA این امکان را به ما می‌دهد تا بدون درگیر کردن CPU عملیات خاصی را انجام دهیم. برای در تصویر بالا مقدار وروردی قرار گرفته بر روی پایه های ADC خوانده شده و در حافظه میکروکنترلر بدون درگیر شدن CPU ذخیره می‌شود. این عملیات ممکن است برای پریفرال های دیگر مانند UART، SPI، I2C نیاز باشد. کانال های DMA می توانند به هر مکان از حافظه دسترسی داشته باشند ، از جمله:

  • پریفرال های  AHB ، به عنوان مثال مولد CRC
  • حافظه های AHB ، به عنوان مثال SRAM
  • پریفرال های APB ، به عنوان مثال لوازم جانبی USART

 


سخت افزار DMA


اکثر پریفرال های APB را می توان پیکربندی کرد تا درخواست های DMA را تأیید کند. این امر به ویژه  برای پریفرال ها و مبدل های ارتباطی (ADC و DAC) مفید است. دو کنترلر DMA در مجموع دارای ۱۲ کانال (۷ مورد برای DMA1 و ۵ کانال برای DMA2) است که هر کدام به مدیریت درخواست دسترسی به حافظه از یک یا چند پریفرال اختصاص دارد. این یک داور برای رسیدگی به اولویت بین درخواست های DMA دارد.

STM32 DMA Tutorial With Examples For UART And ADC
کنترلر DMA با به اشتراک گذاشتن گذرگاه سیستم با هسته Cortex®-M3 ، انتقال مستقیم حافظه را انجام می دهد. درخواست DMA ممکن است دسترسی CPU به گذرگاه سیستم را برای برخی از چرخه های زمانی که CPU و DMA یک مقصد (حافظه یا پریفرال) را هدف قرار می‌دهند ، متوقف کند. باس ماتریس زمانبندی رفت و برگشت را اجرا می کند ، بنابراین حداقل نیمی از پهنای باند گذرگاه سیستم (اعم از حافظه و پریفرال) را برای CPU را تضمین می کند.

 


انتقال داده در DMA


پس از یک رویداد ، پریفرال یک سیگنال درخواست را به کنترل کننده DMA ارسال می کند. بسته به اولویت های کانال، کنترل کننده DMA به درخواست پاسخ می دهد. به محض دسترسی کنترل کننده DMA به پریفرال، Acknowledge توسط کنترلر DMA به محیط ارسال می شود. پریفرال به محض دریافت تأییدیه از کنترلر DMA، درخواست خود را منتشر می کند. پس از تأیید درخواست توسط پریفرال، کنترل کننده DMA،  شناسه (Acknowledge) را منتشر می کند. اگر درخواست های بیشتری وجود داشته باشد ، پریفرال می تواند معامله بعدی را آغاز کند. به طور خلاصه ، هر انتقال DMA شامل سه عملیات است:

  • بارگیری داده ها از ثبت داده های جانبی یا یک مکان در حافظه که از طریق یک رجیستر آدرس پریفرال/حافظه داخلی جاری آدرس دهی می‌شود. آدرس شروع استفاده شده برای اولین انتقال، آدرس پریفرال اصلی/حافظه است که در رجیستر DMA_CPARx یا DMA_CMARx برنامه ریزی شده است.
  • ذخیره داده های بارگذاری شده در ثبت داده های جانبی یا مکانی در حافظه که از طریق یک رجیستر آدرس داخلی پریفرال/حافظه داخلی جاری آدرس دهی می شود. آدرس شروع استفاده شده برای اولین انتقال ، آدرس پریفرال اصلی/حافظه است که در رجیستر DMA_CPARx یا DMA_CMARx برنامه ریزی شده است.
  • پس از کاهش رجیستر DMA_CNDTRx ، که شامل تعدادی از معاملات است که هنوز باید انجام شود.

درخواست های DMA پریفرال را می‌توان به طور مستقل با برنامه ریزی بیت کنترل DMA در رجیسترهای پریفرال مربوطه فعال/غیرفعال کرد.

 


ویژگی های DMA در STM32F103


واحدهای DMA در STM32F103 دارای ویژگی های زیر است.

  • ۱۲ کانال مستقل (درخواست): ۷ کانال برای DMA1 و ۵ کانال برای DMA2
  • هر یک از ۱۲ کانال به درخواست‌های سخت افزاری اختصاصی DMA متصل است ، ماشه نرم افزاری نیز در هر کانال پشتیبانی می شود. این پیکربندی توسط نرم افزار انجام می شود.
  • اولویت های بین درخواست های کانال های یک DMA ، قابل برنامه ریزی نرم افزاری (۴ سطح متشکل از بسیار بالا ، بالا ، متوسط ​​، پایین) یا سخت افزار در صورت برابری است (درخواست ۱ بر درخواست ۲ اولویت دارد و …)
  • اندازه انتقال منبع و مقصد مستقل (byte, half-word, word) ، شبیه سازی بسته بندی و باز کردن بسته بندی. آدرس مبدا/مقصد باید بر اساس اندازه داده ها تراز شود.
  • پشتیبانی از circular buffer management
  • ۳ پرچم رویداد (DMA Half Transfer ، DMA Transfer complete و DMA Transfer Error) به صورت منطقی با یک دیگر OR شده و دارای یک اینتراپت برای هر کانال هستند.
  • انتقال Memory-to-memory
  • انتقال Peripheral-to-memory و memory-to-peripheral و peripheral-to-peripheral
  • دسترسی به پریفرال های Flash ،SRAM ،APB1 ،APB2 و AHB به عنوان منبع و مقصد
  • تعداد قابل برنامه ریزی داده برای انتقال تا ۶۵۵۳۶
برد STM32F103C8T6 آموزش راه اندازی و کار با آن - دیجی اسپارک

ارسال و دریافت از UART به کمک DMA


در این آموزش قصد داریم تا داده های دریافتی از UART را دریافت و در حافظه ذخیره کنیم. همانطور که گفته شد. زمانی که داده ارسال شده به سمت UART  دارای حجم بسیار زیادی باشد. دریافت آن تنها با وقفه امکان پذیر است. اما به دلیل تعداد زیاد وقفه های دریافتی ممکن است پردازنده در حال انجام کار های مهم دچار تداخل شود. اما زمانی که از DMA در دریافت سریال استفاده شود. مداخله پردازنده بسیار کم می شود. زیرا حتی با وجود وقفه های زیاد فقط وقفه های HT و TC رخ می‌دهند. برای استفاده از DMA در دریافت سریال باید اندازه را برای خواندن از طریق UART تعیین کنید. مشکل این است که وقفه TC زمانی رخ نمی دهد که داده های دریافتی کوچکتر از اندازه تعیین شده باشد. در این حالت ، هنگامی که UART IDLE Interrupt را زیر نظر داریم ، می توانیم مشکل را حل کنیم. بنابراین ، هنگام استفاده از DMA در دریافت سریال ، نظارت بر وقفه HT ، TC و UART IDLE به شدت توصیه می شود.
ارسال و دریافت از UART به کمک DMA - دیجی اسپارک

پیکربندی DMA در STM32


پس از ساخت پروژه جدید در نرم افزار STM32Cube بایستی یکی از UART های میکروکنترلر شده و آن را بر روی مد Asynchrone را فعال کنید. در صورتی که با نحوه ساخت پروژه جدید و فعال سازی UART میکروکنترلر STM32 را ندارید. ابتدا بخش اول آموزش میکروکنترلر STM32 و سپس به ترتیب بخش سوم و  بخش چهارم آموزش میکروکنترلر STM32 را مطالعه کنید. پس از انجام تنظیم پارامتر ها در قسمت Parameter Setting بایستی وارد بخش DMA Setting شده و بر روی گزینه Add کلیک کنید. سپس در قسمتی که عبارت Select نوشته شده است. گزینه USART_RX را انتخاب می‌کنیم. پس از آن بر روی قسمت ایجاد شده کلیک کرده تا تنظیمات در درسترس قرار بگیرد. در ادامه در بخش DMA Request Setting گزینه Mode را بر روی Circular تنظیم کنید. از آنجا که ممکن است داده ها در هر زمان وارد شوند، باید بتواند بدون عملکرد کاربر مکرراً دریافت کند.
پیکربندی DMA در STM32 - دیجی اسپارک

 

در این آموزش برای تست این قابلیت در میکروکنترلر STM32، مراحل بالا را برای ارسال از طریق UART یا UART_TX نیز انجام می‌دهیم. سپس وارد بخش Clock Configuration شده و کریستال میکروکنترلر را بر روی ۷۲ مگا هرتز تنظیم کنید. در اخر پس از انجام تنظیمات بخش Project Manager بر روی GENERATE CODE کلیک کنید و وارد نرم افزار KEIL شوید.


کدنویسی در نرم افزار KEIL


پس از ورود به نرم افزار KEIL بایستی وارد فایل main.c شوید. برنامه اصلی میکروکنترلر در این فایل قرار میگیرد. در این آموزش قصد داریم تا داده هایی که از UART دریافت می شود را ذخیره و داخل حافظه میکروکنترلر ذخیره کنیم. این عملیات در این آموزش به کمک DMA انجام می شود. برای تست پایه RX و TX میکروکنترلر را با استفاده از یک جمپر به یک دیگر متصل کرده ایم. تا داده هایی که از پایه TX ارسال می شود را توسط پایه RX دریافت کرده و داخل حافظه ذخیره کنیم. برای شروع ابتدا بایستی ۲ آرایه برای ارسال از طریق UART و همچنین ذخیره‌ی داده های دریافتی تعریف کنیم. میتوانید آرایه ها را به صورت زیر داخل فایل main.c در یکی از قسمت های مشخص شده با عبارت “USER CODE” بنویسید.

uint8_t Tx_Buff[10]={0,1,2,3,4,5,6,7,8,9};
uint8_t Rx_Buff[10];

توابع DMA

برای ارسال و دریافت به کمک DMA بایستی دستور مربوط به فعال سازی DMA را داخل کد اضافه کنید. برای دریافت از UART به کمک DMA تنها کاری که بایستی انجام دهید این است که دستور HAL_UART_Receive_DMA را در تابع main قبل از حلقه while بنویسید. این دستور دارای ۳ ورودی است. این ورودی ها به ترتیب عبارتند از؛ استراکچر مربوط به انتخاب UART که مورد استفاده قرار داده اید. در این آموزش از UART1 استفاده شده بنا بر این در قسمت مربوط به این ورودی عبارت &huart1 قرار میگیرد. ورودی دوم آرایه بافری است که داده های دریافتی از UART داخل آن قرار میگیرد. ورودی آخر نیز مدت زمان Timeout است. در این آموزش این دستور به صورت زیر داخل کد قرار گرفته است.

HAL_UART_Receive_DMA(&huart1,Rx_Buff,10);

برای ارسال داده از UART به کمک DMA نیز دستور HAL_UART_Transmit_DMA موجود است. این دستور کامل مشابه دستور قبلی است. با این تفاوت که در قسمت ورودی دوم بایستی رشته از داده ها که قصد ارسال آن را داریم، بنویسیم. بنابر این داخل این آموزش است دستور به صورت زیر نوشته خواهد شد.

HAL_UART_Transmit_DMA(&huart1,Tx_Buff,10);

ممکن است در طول کدی که مینویسید نیاز داشته باشید تا متوجه شوید داده های دریافتی چه زمان کامل می شود. تابع زیر از توابع Callback مربوط به UART است که این امکان را به شما میدهد تا پس از دریافت کامل داده از UART عملیاتی را انجام دهید.

void HAL_UART_RXCpltCallback(UART_HandleTypeDef *huart)
{
	
}

در آخر بایستی برنامه را کامپایل کرده و بر روی برد Bluepill پروگرام کنید. در صورتی که با نحوه پروگرام کردن میکروکنترلر های Stm32 آشنا نیستید. لطفا بخش اول آموزش های میکروکنترلر Stm32 را مطالعه نمایید. پس از دانلود کد بر روی برد BluePill بایستی یک بار کلید reset را بر روی برد فشار دهید. در صورت که اتصالات را به طور صحیح متصل کرده باشید.

 


جمع بندی


زمانی که حجم کد بالا می‌رود CPU دچار اختلال شده و باعث هنگ کردن دستگاه می شود. در این مواقع برای حل مشکل اولین راهی که به ذهن می‌رسد استفاده از میکروکنترلری با پردازنده قوی تر است. اما راه های دیگری مثل استفاده از بخش DMA در میکروکنترلر های STM32 است. این قابلیت به میکروکنترلر اجازه می دهد تا بدون درگیر کردن CPU یک سری عملیات ها انجام شود. در این آموزش ابتدا با مفهوم DMA آشنا شده. سپس به سخت افزار DMA در میکروکنترلر های STM32 پرداخیتم. در آخر نیز با نحوه پیکربندی این بخش در نرم افزار STM32CubeMX و پس از آن با کدنویسی این بخش در نزم افزار KEIL آشنا شدیم.

 

 

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

 

در پایان نظرات و پیشنهادات خود را با ما درمیان بگذارید و با اشتراک گذاری این آموزش در شبکه های اجتماعی از وبسایت دیجی اسپارک حمایت کنید.

 

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

علی زاهدی

برنامه نویس و طراح سیستم های مبتنی بر میکروکنترلر

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

تبادل نظر و رفع عیب با ثبت دیدگاه

۴ دیدگاه

  • سلام مهندس سپاس از مطلب خوبتون، همونطور که اشاره کردید بدرد SIM800 میخوره که حدود ۱۰۰ کاراکتر داره هنگام قرائت.
    من یه نمونه کد روی STM32F103 داشتم با کاربری پورت سریال عادی برای قرائت و پردازش متن پیام ماژول SIM800 که نسبتا خوب کار میکنه روالشم اینه که اولکد دریافت میشه و دوم شماره و سوم متن پیام چک میشه و بعد اتفاقی که میخوام مثلا روشن شدن خروجی میفته و در انتها اگر عملیات موفق بود بافر و متغیرها بهمراه باکس مسج صفر میشند و آماده برای دریافت پیام بعد که مثلا خاموش شدن همون خروجی باشه.
    وقتی طبق همین آموزش از یک عدد تابع HAL DMA قبل از while استفاده کردم و پایین RECAL انجام دادم در بار اول همه چی خوب طبق قبل پیش رفت اما در پیام دوم که ارسال میکنم کاراکترها مثل بار قبل سریع دریافت نمیشند و باید دو یا سه بار پیام رو مجدد ارسال کنم تا بافر تکمیل شه و بره برای پردازش، مشکل چی میتونه باشه آیا باید بجای یک عدد از چند بدنه تابع استفاده کرد؟ قلق خاصی داره که با پرداخت حق اشتراک در مجموعه بتونم یاد بگیرم؟ یا صرفا از برنامه نویسیه؟ چون برنامه رو اصلا تغییر ندادم و تو نمونه قبل خوب کار میکنه.
    سوال دیگه اینکه برای رفع اشکال در برنامه ها میشه در مجموعه اشتراک گرفت یا صرفا حق اشتراک به مطالب در مورد دوره های آموزشیه؟
    تشکر از سایت خوبتون

  • سلام ممنون از آمو زشتان
    اگر تعداد آرایه مشخص نباشد و بطور مثال یک بار آرایه شش تای است یک و یک بار ۱۰ تایی از چه روشی میتواندیتا را ذخیره سازی کرد من این دستور را استفاده کردم ولی متاسفانه دیتا یکی در میون ثبت میشود
    HAL_UART_Receive_DMA(&huart1,(uint8_t*)CD,1);
    و
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    CU[A]=CD[0];
    A=A+1;
    if(A==1000)A=0;

    }

    • با سلام
      می‌توانید ابتدا برای تعریف آرایه عدد بزرگی را در نظر بگیرید. سپس بعد از دریافت داده ها مقدار Lenth داده دریافتی را با توابع مختلفی که در زبان C موجود است، محاسبه کنید.

  • سلام . ایا نرم افزار پروتیوس واحد DMA رو شبیه سازی میکنه . من از چند تا کد اماده استفاده کردم ولی کار نکرد ولی در اسناد راهنما در قسمت واحد ها پشتیبانی نشده DMA ذکر نشده . ولی مثلا Brown out ذکر شده .

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