آموزش پیشرفته مدیریت حافظه Flash و Linker Script در STM32

قسمت 29
مشاهده سایر جلسات آموزش
70 بازدید
۱۴۰۴-۱۰-۱۷
11 دقیقه
  • نویسنده: Alireza Abbasi
  • درباره نویسنده: ---

استفاده پیشرفته از لینکر

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

حافظه فلش برای ذخیره‌سازی دائم

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

بیایید با برنامه سریال “Hello World” از فصل ۹ شروع کنیم. ما قصد داریم شمارنده‌ای اضافه کنیم که تعداد دفعات بوت شدن سیستم را نشان می‌دهد و سپس پیامی با تعداد ریست شدن را به دستگاه سریال ارسال کنیم.

طراحی ما بسیار ساده است. قصد داریم ۴ کیلوبایت انتهایی حافظه فلش را برداشته و برای ذخیره اطلاعات پیکربندی استفاده کنیم. یک اسم برای این بخش انتخاب می‌کنیم: CONFIG (تنظیمات). همچنین یک بخش حافظه جدید به نام .config تعریف خواهیم کرد که در آن متغیر شمارنده ریست را قرار می‌دهیم.

کد C برای انجام این کار به این صورت است:

حالا باید اسکریپت لینکر را برای مدیریت بخش جدید خودمان ویرایش کنیم. با تقسیم حافظه فلش به دو بخش شروع می‌کنیم. بخش اول همان حافظه فلش سنتی است که قبلا درباره‌اش صحبت کردیم. بخش دوم، CONFIG، داده‌های پیکربندی ما را نگه می‌دارد، بنابراین باید فایل LinkerScript.ld را ویرایش کنیم و کد زیر را:

با این کد جایگزین می‌کنیم:

با این کار حافظه فلش ۴ کیلوبایت کوچک‌تر می‌شود تا بتوانیم از این فضای جدید برای بخش حافظه‌ای به نام CONFIG استفاده کنیم.

توجه داشته باشید که فلش با حافظه معمولی متفاوت است. در فلش، تنها یک بار می‌توانید اطلاعات را بنویسید و بعد از آن برای نوشتن دوباره باید کل بخش (صفحه) را پاک کنید. در مورد STM32، بخش CONFIG ما باید حداقل اندازه‌ای برابر ۱ کیلوبایت داشته باشد (چون در این میکروکنترلر سایز هر صفحه یا page برابر 1KB یا همان 1024 byte است) و اندازه‌اش باید ضریبی از ۱ کیلوبایت باشد. ما ۴ کیلوبایت را انتخاب کرده‌ایم چون احتمالاً بعدها بخواهیم داده‌های پیکربندی بیشتری در آن ذخیره کنیم.

حالا باید به لینکر دستور دهیم تا بخش .config را درون بلوک حافظه به نام CONFIG قرار دهد. این کار با اضافه کردن کد زیر به بخش SECTIONS فایل LinkerScript.ld انجام می‌شود:

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

برای برنامه‌ریزی مجدد چیپ به مجموعه‌ای از مراحل نیاز است. ما تمام این مراحل را درون تابعی به نام updateCounter قرار داده‌ایم که در کد ۱۱-۴نمایش داده شده است.

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

با وجود بازگشایی حافظه فلش، همچنان نمی‌توانیم مقدار متغیر resetCount را که در این حافظه ذخیره شده است، به طور مستقیم تغییر دهیم. به جای این کار، مقدار resetCount را به یک متغیر موقت با نام newResetCount (که از نوع داده معمولی است) منتقل می‌کنیم، سپس می‌توانیم مقدار newResetCount را به دلخواه تغییر دهیم.

پیش از نوشتن مقدار جدید newResetCount در حافظه فلش، باید حافظه محل ذخیره این متغیر را پاک کنیم. کوچک‌ترین واحد قابل پاک کردن در حافظه فلش، “صفحه” (Page) نامیده می‌شود. برای پاک کردن صفحه موردنظر، ابتدا باید یک ساختار داده را  مقداردهی کنیم، سپس این ساختار را به‌عنوان پارامتر به تابع HAL_FLASHEx_Erase ارسال کنیم تا حافظه صفحه موردنظر پاک شود.

پس از پاک شدن حافظه، می‌توانیم مقدار جدید newResetCount را در آن بنویسیم. با توجه به اینکه newResetCount یک مقدار 32 بیتی است، در حالی که حافظه فلش فقط می‌تواند 16 بیت را در هر بار نوشتن ذخیره کند، باید از تابع HAL_FLASH_Program برای نوشتن این مقدار به حافظه استفاده کنیم.

کد کامل این فرایند در کد زیر نمایش داده شده است.

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

چندین آیتم پیکربندی

فرض کنید می‌خواهیم بیش از یک متغیر پیکربندی را در حافظه‌ی فلش ذخیره کنیم. مشکل اینجاست که حافظه‌ی فلش، حافظه‌ی معمولی نیست. بعد از اینکه مقداری را در یک متغیر حافظه‌ی فلش ذخیره کردید، دیگر نمی‌توانید آن را به صورت مستقیم تغییر دهید. برای تغییر، باید کل صفحه‌ی حاوی آن متغیر را پاک کنید.

این روش زمانی که شما یک متغیر را در هر صفحه ذخیره می‌کنید (که روشی بسیار غیر بهینه است) کاربرد دارد، اما برای ذخیره‌ی چندین متغیر پیکربندی در یک حافظه‌ی فلش و به‌روزرسانی تنها یک مورد از آن‌ها، به رویکردی متفاوت نیاز داریم. این فرایند که در ادامه آمده کمی پیچیده‌تر است:

  • ذخیره‌ی تمام متغیرهای پیکره‌بندی در RAM (اگر از قبل در flash ذخیره شده اول باید آن متغیرها را خوانده و به RAM انتقال دهیم)
  • به‌روزرسانی مقدار موردنظر در رم: مقدار موردنظر برای تغییر را به‌روزرسانی می‌کنیم.
  • پاک کردن کل بخش پیکربندی در فلش: صفحه‌ی حافظه‌ی فلش که حاوی کل پیکربندی است را پاک می‌کنیم.
  • کپی کل پیکربندی به‌روزرسانی شده از رم به فلش: در نهایت، نسخه‌ی به‌روزرسانی شده‌ی پیکربندی را که در رم قرار دارد، به حافظه‌ی فلش برمی‌گردانیم.

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

کد زیر خلاصه‌ای از کد برای تعریف یک ساختار پیکربندی در بخش .config و به‌روزرسانی یک مقدار در آن ساختار را نشان می‌دهد.

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

یک راه حل برای این مشکل، داشتن دو بخش پیکربندی است، اصلی و پشتیبان، که هرکدام شامل یک کد checksum (کد اعتبارسنجی) هستند. برنامه ابتدا سعی می‌کند پیکربندی اصلی را بخواند. اگر کد اعتبارسنجی خراب باشد، برنامه بخش دوم را می‌خواند. از آنجایی که تنها یک پیکربندی در یک زمان نوشته می‌شود، می‌توانید مطمئن باشید که حتما یکی از پیکربندی اصلی یا پشتیبان صحیح هستند.

مشکل دیگر فلش، فرسودگی حافظه است. تعداد دفعاتی که می‌توانید چرخه‌ی برنامه‌ریزی/پاک کردن را بر روی یک سلول حافظه‌ی فلش انجام دهید، محدود است. بسته به نوع فلش، این تعداد می‌تواند بین ۱۰۰،۰۰۰ تا ۱،۰۰۰،۰۰۰ چرخه باشد. بنابراین، استفاده از حافظه‌ی فلش برای ذخیره‌ی پیکربندی‌ای که انتظار می‌رود ماهی یک بار تغییر کند، مناسب است. اما استفاده از آن برای چیزی که چندین بار در ثانیه تغییر می‌کند، به سرعت حافظه را فرسوده خواهد کرد.

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

مثال سفارشی‌سازی در محل نصب

فرض کنید در شرکتی کار می‌کنیم که دزدگیر تولید می‌کند. این دزدگیرها به شرکت‌های خدمات آلارم فروخته می‌شوند که این شرکت‌ها وظیفه‌ی نصب آن‌ها را در محل نهایی، یعنی خانه یا محل کار مشتریان، بر عهده دارند.

شاید برای شما مفید باشد:
راهنمای کامل راه‌اندازی ENC28J60 برای ارسال و دریافت فریم‌های اترنت

حال، تصور کنید شرکتی به نام “آژیرِ مهدی” که در کنار تولید دزدگیر، مغازه‌ی ماهیگیری هم دارد، از دیدن لوگوی شرکت “تولیدکننده‌ی آژیرِ Acme” هنگام راه‌اندازی پنل دزدگیری که نصب کرده، راضی نیست. “آژیرِ مهدی” به برندسازی خود اهمیت می‌دهد و تمایل دارد لوگوی خودش روی پنل نمایش داده شود.

بنابراین، ما باید به مشتریان خود راهی برای شخصی‌سازی لوگوی داخل دستگاه‌هایشان ارائه دهیم.

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

حالا سوال این است که چگونه لوگو را به سیستم وارد کنیم؟

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

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

برنامه‌ریزی لوگو می‌تواند با استفاده از همان سخت‌افزار و نرم‌افزاری انجام شود که برای بارگذاری کد خودمان در حافظه‌ی فلش استفاده می‌کنیم.

همچنین می‌توانیم یک برنامه‌ی داخلی در دستگاه بنویسیم که داده‌ها را از پورت سریال دریافت کند و آن‌ها را در حافظه‌ی لوگو ذخیره کند.

تعویض لوگو یک کار ساده‌ی شخصی‌سازی است. حتی اگر در فرایند جایگزینی مشکلی پیش بیاید و لوگو به درستی ذخیره نشود، سیستم از کار نمی‌افتد. اما تعویض فریمور (firmware) موضوع دیگری است و باید با دقت و احتیاط بیشتری انجام شود.

به‌روزرسانی فریمور (Firmware)

به‌روزرسانی فریمور زمانی که در حال اجراست، کمی پیچیده می‌باشد، اما راه‌های مختلفی برای انجام این کار وجود دارد.

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

  • بوت‌لودر (Bootloader)
  • بخش ۱ برنامه
  • بخش ۲ برنامه

بوت‌لودر یک برنامه‌ی بسیار کوچک است که هرگز به‌روزرسانی نخواهد شد. کار آن نسبتاً ساده است، بنابراین امیدواریم که در همان بار اول آن را به درستی طراحی و پیاده‌سازی کنیم.

دو بخش دیگر حافظه‌ی فلش که با عنوان «بخش ۱ برنامه» و «بخش ۲ برنامه» شناخته می‌شوند، حاوی یک نسخه‌ی کامل از برنامه‌ی اصلی هستند. این بخش‌ها همچنین شامل شماره‌ی نسخه‌ی برنامه و یک کد اعتبارسنجی (checksum) هستند.

وظیفه‌ی بوت‌لودر تصمیم‌گیری درباره‌ی اینکه از کدام بخش برنامه استفاده شود است. بوت‌لودر کد اعتبارسنجی هر دو بخش را بررسی می‌کند و سپس بر اساس محاسبات زیر انتخاب می‌کند که از کدام بخش استفاده کند:

  • اگر کد اعتبارسنجی بخش ۱ خراب باشد و کد اعتبارسنجی بخش ۲ سالم باشد، از بخش ۲ استفاده کن.
  • اگر کد اعتبارسنجی بخش ۱ سالم باشد و کد اعتبارسنجی بخش ۲ خراب باشد، از بخش ۱ استفاده کن.
  • اگر کد اعتبارسنجی هر دو بخش سالم باشد، از بخشی استفاده کن که شماره‌ی نسخه‌ی بالاتری دارد.
  • اگر کد اعتبارسنجی هر دو بخش خراب باشد، چراغ هشدار را روشن کن؛ در این وضعیت دستگاه غیرقابل‌استفاده می‌شود (bricked).

این توضیح، ایده‌ی کلی به‌روزرسانی سیستم‌عامل است، البته ما برخی از مراحل نگه‌داری و مدیریت اطلاعات را نادیده گرفته‌ایم. برای مثال، جدول وقفه (interrupt table) در بخش .isr_vector نیاز به تغییر دارد تا تمام وقفه‌ها به محل مناسب هدایت شوند.

خلاصه

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

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

مسائل برنامه‌نویسی

1. کد پیکربندی را طوری تغییر بده که سگمنت config از ابتدای یک صفحه‌ی حافظه (page boundary) شروع نشود. چه اتفاقی می‌افتد؟

2. کد پیکربندی را طوری تغییر دهید که به جای چاپ یک عدد یک‌رقمی برای بازنشانی، کل عدد را چاپ کند.

3. اسکریپت لینکر تعدادی سمبل (symbol) را برای نشان دادن شروع و پایان یک ناحیه‌ی حافظه تعریف می‌کند. با بررسی اسکریپت لینکر یا نقشه‌ی لینکر، نمادهایی را که شروع و پایان ناحیه‌ی text را تعریف می‌کنند، پیدا کنید. با استفاده از این نمادها، اندازه‌ی ناحیه‌ی text را چاپ کنید. از دستور arm-none-eabi-size برای تأیید نتیجه‌ی خود استفاده کنید.

4. از همین تکنیک‌ها برای چاپ مقدار فضای استک (stack) اختصاص‌یافته استفاده کنید.

5. (پیشرفته) مقدار فضای استک باقی‌مانده را چاپ کنید.برای این کار لازم است مقدار فعلی ثبات stack (رجیستر SP) را با استفاده از کلیدواژه‌ی asm داخل یک متغیر بخوانید.

درک محتوای یک فایل باینری می‌تواند بسیار مفید باشد، و ابزار گنو (GNU toolchain) دارای تعدادی برنامه برای انجام این کار است. مستندات دستورات زیر را بررسی کنید:

  • objdump که اطلاعات فایل آبجکت (object file) را تخلیه می‌کند.
  • nm که لیستی از نمادها (symbols) موجود در یک فایل را نمایش می‌دهد.
  • ar که کتابخانه‌ها (libraries) ایجاد می‌کند یا اطلاعات و فایل‌ها را از آن‌ها استخراج می‌کند.
  • readelf که اطلاعات مربوط به فایل‌های elf (برنامه) را نمایش می‌دهد.
اطلاعات
70
0
1
اشتراک و حمایت
profile نویسنده: Alireza Abbasi متخصص الکترونیک

ویراستار: MasoudHD
مقالات بیشتر

slide

پالت | بازار خرید و فروش قطعات الکترونیک

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

آیسی | موتور جستجوی قطعات الکترونیک

سامانه آی سی سیسوگ (Isee) قابلیتی جدید و کاربردی از سیسوگ است. در این سامانه سعی شده است که جستجو، انتخاب و خرید مناسب تر قطعات برای کاربران تسهیل شود. جستجو در آیسی
family

سیسوگ‌شاپ | فروشگاه محصولات Quectel

فروشگاه سیسوگ مجموعه ای متمرکز بر تکنولوژی های مبتنی بر IOT و ماژول های M2M نظیر GSM، GPS، LTE، NB-IOT، WiFi، BT و ... جایی که با تعامل فنی و سازنده، بهترین راهکارها انتخاب می شوند. برو به فروشگاه سیسوگ
family

سیسوگ فروم | محلی برای پاسخ پرسش‌های شما

دغدغه همیشگی فعالان تخصصی هر حوزه وجود بستری برای گفتگو و پرسش و پاسخ است. سیسوگ فروم یک انجمن آنلاین است که بصورت تخصصی امکان بحث، گفتگو و پرسش و پاسخ در حوزه الکترونیک را فراهم می‌کند. پرسش در سیسوگ فرم
family

سیکار | اولین مرجع متن باز ECU در ایران

بررسی و ارائه اطلاعات مربوط به ECU (واحد کنترل الکترونیکی) و نرم‌افزارهای متن باز مرتبط با آن برو به سیکار
become a writer
نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer
نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله

خانواده سیسوگ

سیسوگ‌شاپ

فروشگاه محصولات Quectel

پالت
سیسوگ فروم

محلی برای پاسخ پرسش‌های شما

سیسوگ جابز
سیسوگ
سیسوگ فروم
سی‌کار

اولین مرجع متن باز ECU در ایران

سیسوگ مگ
آی‌سی

موتور جستجوی قطعات الکترونیکی

سیسوگ آکادمی
پالت

بازار خرید و فروش قطعات الکترونیک

دیدگاه ها

become a writer
نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer
نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله