الكلمة PROGMEM المفتاحية في أردوينو

من موسوعة حسوب
مراجعة 13:53، 1 نوفمبر 2018 بواسطة جميل-بيلوني (نقاش | مساهمات)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

تُستعمَل الكلمة PROGMEM المفتاحية مع المتغيرات عندما يراد تخزين بياناتها في ذاكرة البرنامج (الذاكرة flash) بدلًا من تخزينها في الذاكرة SRAM. لمزيد من المعلومات حول أنواع الذاكرة المتاحة في أردوينو، اطلع على هذه الصفحة.

تصنَّف الكلمة PROGMEM المفتاحية ضمن «مقيدات المتغيرات» (variable qualifier)، ويجب استعمالها مع أنواع البيانات المعرَّفة في المكتبة pgmspace.h فقط. تخبر هذه الكلمة المصرَّف بأنَّ يضع بيانات المتغير التي استعملت معه في الذاكرة flash بدلًا من وضعها في الذاكرة SRAM حيث يجب أن تخزَّن بالحالة الطبيعية.

الكلمة PROGMEM المفتاحية هي جزءٌ من المكتبة pgmspace.h. تُضمَّن هذه المكبتة تلقائيًّا في الإصدارات الحديثة من أردوينو IDE. على أي حال، إن كنت تستعمل إصدارًا أقدم من 1.0 (2011)، فيجب أن تُضمِّن المكتبة يدويًّا في بداية شيفرتك باستعمال include#:

#include <avr/pgmspace.h>

البنية العامة

const dataType variableName[] PROGMEM = {data0, data1, data3…​};

يمثِّل dataType أي نوع من البيانات، و variableName اسم مصفوفة البيانات المراد تخزينها في الذاكرة flash. انتبه إلى أنَّه لمَّا كانت الكلمة PROGMEM المفتاحية مُقيِّدةً لمتغيرٍ، فلا يوجد قاعدة تُحتِّم وجودها في مكان محدَّد أثناء التصريح عن متغير؛ هذا يعني أنَّ المصرِّف في أردوينو يقبل جميع التعريفات المذكورة في المثال الآتي. مع ذلك، أشارت بعض الاختبارات أنَّ PROGMEM، في إصدارات مختلفة من أردوينو (التي تستعمل المصرِّف GCC)، قد تعمل في موضع محدَّد دون غيره عند التصريح عن متغير. جُرُّب المثال الثاني في قسم الأمثلة في الإصدار أردوينو 13؛ قد يعمل المثال في إصدارات سابقة بشكل أفضل إن وُضعَت PROGMEM بعد اسم المتغير.

const dataType variableName[] PROGMEM = {};
const PROGMEM dataType variableName[] = {};
const dataType PROGMEM variableName[] = {};

لمَّا كان بالإمكان استعمال الكلمة PROGMEM المفتاحية مع متغير واحد، فيُستحسَن استعمالها مع مصفوفة (أو أيَّة هيكلية مشابهة في C لتخزين البيانات سواءً ذُكرَت في هذا التوثيق أم لا) لاحتواء البيانات المراد تخزينها في الذاكرة flash إن كانت هذه البيانات كبيرة.

استعمال PROGMEM يتطلب تنفيذ إجراء مكوَّن من خطوتين. فبعد تخزين البيانات في الذاكرة flash (الخطوة الأولى)، يجب استعمال توابع (دوال) خاصة معرَّفة في المكتبة pgmspace.h من أجل قراءة هذه البيانات من ذاكرة البرنامج ووضعها في الذاكرة SRAM، وبذلك يمكننا الاستفادة من هذه البيانات واستعمالها.

أمثلة

توضِّح الشيفرة التالية كيفية كتابة بيانات على الذاكرة flash ثمَّ قراءتها مجدَّدًا منها:

// حفظ بعض الأعداد الصحيحة عديمة الإشارة
const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// حفظ بعض المحارف
const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k;    // متغير عدَّاد
char myChar;


void setup() {
  Serial.begin(9600);
  while (!Serial);  // انتظار اتصال المنفذ التسلسلي

  // اكتب هنا أي شيء تريد تهيئته وتنفيذه لمرة واحدة
  // إعادة قراءة الأعداد الصحيحة 
  for (k = 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // إعادة قراءة المحارف
  for (k = 0; k < strlen_P(signMessage); k++)
  {
    myChar =  pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }

  Serial.println();
}

void loop() {
  // اكتب هنا برنامجك لتنفيذه بشكل متكرر

}

يُستحسَن عند العمل مع مقدار كبير من النصوص، مثل احتواء المشروع على شاشة LCD، إنشاء وتهيئة مصفوفة من السلاسل النصية لتخزين هذه النصوص فيها. لمَّا كانت السلاسل النصية نفسها هي مصفوفات، فيجب استعمال مصفوفة ثنائية الأبعاد. يوضح المثال التالي فكرة وضع البيانات كبيرة الحجم في ذاكرة البرنامج:

/*
 PROGMEM string demo
 How to store a table of strings in program memory (flash),
 and retrieve them.

 Information summarized from:
 http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

 Setting up a table (array) of strings in program memory is slightly complicated, but
 here is a good template to follow.

 Setting up the strings is a two-step process. First define the strings.
*/

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0";   // هي السلسلة المراد تخزينها. غيرها بما يناسب برنامجك "String 0" السلسلة
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";


// ثم أنشئ جدولًا يشير إلى هذه السلاسل النصية

const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};

char buffer[30];    // تأكد من أن يكون حجم هذه المصفوفة كبيرًا بما يكفي ليستع لأكبر سلسلة نصية

void setup()
{
  Serial.begin(9600);
  while(!Serial); // انتظر حتى يتصل المنفذ التسلسلي
  Serial.println("OK");
}


void loop()
{
  /* يتطلب استعمال جدول السلاسل النصية الموجود في ذاكرة 
     .البرنامج استعمال دوال خاصة لجلب البيانات
      سلسلة نصية من ذاكرة البرنامج strcpy_P تنسخ الدالة
      .(في حالتنا buffer هي ذاكرة المتغير) RAM إلى الذاكرة
      كبير ما يكفي  RAM تأكد من أن الجزء المحجوز في الذاكرة 
      .ليسع البيانات المستعادة من ذاكرة البرنامج
      */


  for (int i = 0; i < 6; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
    Serial.println(buffer);
    delay( 500 );
  }
}

ملاحظات وتحذيرات

انتبه رجاءً إلى أنَّ المتغيرات يجب أن تكون إمَّا متغيرات عامة أو متغيرات محلية ساكنة (عُرِّفت باستعمال الكلمة static المفتاحية) لتتمكن من استعمال الكلمة PROGMEM معها.

لن يعمل المثال التالي عند وضعه ضمن متغير:

const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";

بينما سيعمل التصريح التالي لمتغير إن عُرِّف محليًّا داخل دالة:

const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"

الاستدعاء F()‎ الموسَّع

عندما يُستعمَل أمرٌ مثل:

Serial.print("Write something on  the Serial Monitor");

تُحفَظ السلسلة النصية المراد طباعتها بشكل طبيعي في الذاكرة RAM. إن كانت شيفرتك تطبع الكثير من النصوص على المنفذ التسلسلي، فسيؤدي ذلك إلى امتلاء الذاكرة. إن كان لديك مساحة فارغة في الذاكرة flash، فيمكنك أن تستثمرها بسهولة بتخزين هذه النصوص فيها عبر استعمال الصيغة التالية:

Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));

انظر أيضًا

  • الدالة sizeof()‎: تعيد الحجم المحجوز من الذاكرة بالبايت لمتغير أو مصفوفة معيَّنة.
  • أنواع الذاكرة الموجودة في أردوينو.

مصادر