Geek الکترونیک مقاله

انواع حافظه در میکروکنترلرها

نوشته شده توسط احسان وارسته

تقریبا هیچ کدی وجود نداره که از حافظه استفاده نکنه، کوتاه ترین کد ها مثل شمارنده ها که نیاز به سه دستور جمع(add)، مقایسه(compare) و پرش (jump) دارن هم باید حداقل از یک بایت حافظه برای ذخیره تعداد شمارش استفاده کنن! یا اگه یه چراغ چشمک زن رو هم تصور کنیم، اون هم برای ایجاد زمان تاخیرش نیاز به یک شمارنده داره! بطور کلی میشه گفت هیچ کدی وجود نداره که نیاز به حافظه نداشته باشه مگه اینکه اون کد کاری نکنه!!!

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

بطور کلی در خصوص میکروکنترلر ها ما میتونیم حافظه رو به سه بخش از لحاظ سخت افزاری تقسیم کنیم:

  1. رجیسترهای داخلی پردازنده : این رجیستر ها فارغ از رجیسترهای تنظیم peripheral ها و ادوات میکروکنترلر مثل ADC و UART و … و مختص پردازنده هستن، اینها رجیسترها عمدتا با عدد ها نشون داده میشن و برای اجرای برنامه مورد استفاده قرار میگیرن (ذخیره موقت اطلاعات) و مثلا برای پردازنده های ARM ما تعداد 13 رجیستر داریم که برای استفاده عمومی یا general purpose هستن و از R0 تا R12 نامگذاری شدن. کامپایلر بطور اتوماتیک در طی فرآیند تبدیل کد C/C++ شما به کد اسمبلی از این رجیستر ها در اجرا استفاده میکنه ولی نکته جالب اینه که با روش (هایی) شما میتونین بدون نوشتن کد اسمبلی داخلی برنامه تون و فقط استفاده از کلمه کلیدی یا keywordه register قبل از تعریف متغیرتون به کامپایلر بفهمونین که میخواین این متغیر از نوع رجیستر داخلی پردازنده باشه! فقط توجه کنین که استفاده از این نوع حافظه به این خاطر که خیلی محدوده ممکنه اصولا به صلاح نباشه مگر برای موارد خاص.
    رجیسترهای دیگه ای که داخل پردازنده وجود داره میشه به SP یا Stack Pointer، LR یا Link Register، PC یا Program Counter و APSR یا Application Program Status Register اشاره کرد.
  1. حافظه SRAM یا Static-Random-Access-Memory : ما در کامپیوتر حافظه ای به نام DRAM که D برای Dynamic هست داریم و شاید سوال پیش بیاد چه فرقی با هم دارن؟ از لحاظ نوع دسترسی هر دو RAM هستن، یعنی شما خیلی سریع میتونین به هر نقطه (بسته به اندازه باس – Bus این نقطه میتونه Byte (8بیتی)، Word (32 بیتی برای پردازنده های 32 بیتی)، DWord یا Double Word(64 بیتی) باشه) از حافظه که خواستین دسترسی پیدا کنین. ولی تفاوت عمده بین این دو این هست که SRAM در داخل میکروکنترلر وجود داره ولی DRAM در مادربورد وجود داره و قابلیت کم و زیاد شدن داره. سرعت SRAM بخاطر اینکه ثابته و کنار پردازنده ساخته شده خیلی بیشتر از DRAM هست، از طرفی ساخت SRAM یکم پیچیده تر و سختتر از DRAM هست برای همین مقدارش همیشه خیلی خیلی کمتر از DRAM ه و البته فضای چیپ هم بی تاثیر نیست! این حافظه رو کامپایلر به سه بخش اصلی تقسیم میکنن که عبارتند از: Heap – Stack – Global که در ادامه مطلب راجع بهشون صحبت میکنم.
  2. فلش داخلی یا Internal Flash میکروکنترلر: دو نوع حافظه قبلی که بهشون اشاره شدن حافظه های موقت بودن که بعد از قطع برق این حافظه ها هم اطلاعاتشون از بین میرن ولی در مورد حافظه Flash این حافظه میتونه تا سالها (طبق ادعای سازندگان) اطلاعات رو در خودش بدون تغییر نگه داره! عموم میکروکنترلر ها دارای Flash قابل پاک شدن(Erase) و نوشتن(Write) هستن ولی عمده میکرو هایی که تو تعداد بالا عرضه میشن از نوع ROM یا Read-Only-Memory یا حافظه فقط خواندنی هستن که در حین ساخت یکبار برنامه ریزی یا پروگرم میشن. حافظه Flash عموم میکروکنترلر های در دسترس طبق ادعای سازندگانشون بین هزار تا ده هزار بار قابلیت نوشتن روی هر بلوک رو دارن که معمولا این بلوک ها 2 کیلوبایتی هستن. حالا اینکه چرا حافظه های NAND و NOR باید بصورت بلوکی نوشته بشن به خاطر نحوه ساخت و ساختار داخلیشون هست ولی در مورد خوندن این محدودیت رو ندارن! خب بخاطر محدودیت نوشتن یا Write که این حافظه ها دارن معمولا سعی میشه زیاد تغییری داخلشون انجام نشه و فقط به درد ذخیره کد نهایی ماشین یا باینری میخورن و نهایتا برای ذخیره اطلاعاتی که نخوایم بعد از قطع و وصل شدن تغذیه مدار از بین برن (مثلا میشه ازشون توی بعضی موارد بجای EEPROM استفاده کرد).
  3. حافظه EEPROM یا Electronically Erasable Programmable Read Only Memory: که البته اسم عجیبی داره! حافظه فقط خواندنی قابل برنامه ریزی(!) قابل پاک شدن الکتریکی! علت این اسم عجیبش تاریخچه اش هست. اول ROM بوده که فقط خواندنی بوده، یکبار برنامه ریزی میشده حالا چه بعد از ساخت چه حین ساخت و دیگر تمام! بعدا PROM اومد که قابل برنامه ریزی بود (توسط کاربر) و بعدا نسخه هایی اومد که تحت عنوان EPROM بود که شما توسط نور فرابنفش میتونستی حافظه رو پاک کنی و دوباره پروگرمش کنی. در نهایت EEPROM (یا E2PROM) اومد که شما بجای نور از سیگنال الکتریکی برای این کار (پاک کردن حافظه) استفاده کنی. در میکروکنترلر های کنونی از EEPROM استفاده شده و بیشتر میکروهای AVR دارای این نوع حافظه هستن. یه مزیت بزرگی که این حافظه ها دارن اینه که بعنوان یه حافظه جانبی عالی برای ذخیره اطلاعاتیه که میخوایم زمان زیادی نگه داریم، تا یک میلیون بار قابلیت نوشتن دارن و حداقل تا 100 سال اطلاعات رو بدون تغییر نگه میدارن!

کد نوشته شده ما توسط کامپایلر به کد باینری تبدیل میشه و این کد ها توسط پروگرمر روی حافظه Flash میکرو ریخته میشه. البته قبلش باید اون بلوک هایی که کد قراره روشون نوشته بشه رو Erase یا پاک کنه، پس هر Write روی حافظه Flash همیشه با یک Erase هم همراه هستش.

حافظه SRAM میکرو توسط کامپایلر به سه بخش مهم تقسیم میشه: Heap، Stack، Global .

بخش Heap بخشی از حافظه است که برای Dynamic Memory Allocation مورد استفاده قرار میگیره و وقتی شما نمیدونین دقیقا چقدر حافظه برای یه آرایه میخواین میتونین از malloc استفاده کنین و مقدار حافظه مورد نیازتون رو بصورت متغیر بهش بدین. بعد از استفاده هم از تابع free برای آزادسازی حافظه استفاده بکنین که همه این اتفاقا تو بخشی از حافظه به اسم Heap میوفته. پس اگه زیاد از این مدل حافظه استفاده میکنین بهتره که اندازه این حافظه رو بزرگ انتخاب کنین و حتما از تابع free هم بعد از اتمام استفاده تون از حافظه استفاده کنین وگرنه باعث سرریز شدن یا overflow حافظه میشه که باعث مشکلاتی تو اجرای برنامه تون میشه. مثلا برنامه تون بدون هیچ دلیل بعد از چند دقیقه کار از کار میوفته یا میکرو هنگ میکنه و …

بخش Stack حافظه که بهش پشته هم گفته میشه برای دو منظور بکار میره:

  1. نگهداری آخرین نقطه اجرای برنامه یا مقدار PC در موقع اجرای دستور call : وقتی شما تابعی رو صدا میکنین موقعیت فعلی اجرای برنامه شما رو در این حافظه نگه میداره تا بعد از اجرای تابع مورد نظر شما به نقطه قبلی برگرده و ادامه برنامه رو اجرا بکنه.
  2. برای نگهداری پارامترهای توابع + متغیرهای توابع: پارامترهایی که به توابع پاس داده میشن در این بخش حافظه ذخیره میشن، پس اگه توابع تودرتوی زیادی دارین بهتره حافظه Stack رو زیاد کنین. و همچنین متغیرهایی که داخل هر تابع استفاده میکنین هم در همین بخش حافظه هستن! اصولا اگر توی کدتون متغیرهای زیادی دارین و از توابع زیادی استفاده میکنین بهتره که اندازه این بخش حافظه رو بیشتر از حد پیش فرض خود کامپایلر در نظر بگیرین.

شاید فکر کنین که کامپایلر خودش میتونه حدس بزنه که چقدر حافظه stack نیازه و چه لزومی داره که ما اندازه ش رو تغییر بدیم ولی واقعیت اینه که چون این حافظه رو کامپایلر نمیتونه دقیق حدس بزنه (بخاطر عوامل خیلی زیادی از جمله ورودی های ناخشناخته و تصادفی وقفه ها (interrupts) و متغیر بودن تعداد صدا زدن های توابع تو شرایط غیرقابل پیشبینی توسط کامپایلر و …) خیلی از مواقع علت هنگ کردن میکرو همین کم بودن حافظه stack هستش.

بخش Global که همونطور که از اسمش پیداست برای متغیرهای global که در تمام سورس ها قابل استفاده هستند استفاده میشه. ما تو تعریف این حافظه کاری نمیتونیم انجام بدیم، ما فقط اندازه stack و heap رو مشخص میکنیم و بقیه حافظه میشه global !

 

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

احسان وارسته

یه علاقمند به حوزه علم و فناوری که دوست داره با به اشتراک گذاشتن دانشش قدم کوچکی برای کمک به جامعه فنی برداره :)
حمایت مالی

نظر بگذارید