PROGMEM در آردوینو

PROGMEM در آردوینو داده‌ها را به‌جای SRAM، در حافظه‌ی (برنامه) flash ذخیره می‌کند. برای توضیحات بیشتر راجع به انواع حافظه‌های موجود در برد آردوینو به اینجا مراجعه کنید. کلمه‌ی کلیدی PROGMEM یک توصیف‌کننده‌ی متغیر است و باید فقط با نوع‌داده‌هایی که در pgmspace تعریف شده‌اند استفاده شود. PROGMEM به کامپایلر می‌گوید: “این داده را در حافظه‌ی فلش قرار بده.” (به‌جای قرار دادن در SRAM که معمولاً داده‌ها به این حافظه می‌روند.) PROGMEM بخشی از کتاب‌خانه‌ی pgmspace.h است که فقط در معماری AVR قابل‌دسترس است. پس اول باید این کتاب‌خانه را به بالای کد اضافه (include) کنید؛ مثل کد زیر:

 

#include <avr/pgmspace.h>

 

سینتکس (نحو)

 

const dataType variableName[] PROGMEM = {data0, data1, data3...};
  • dataType: هر نوع از انواع متغیر
  • variableName: نامی برای آرایه‌ای از داده‌هایتان

توجه کنید که از آن‌جایی که PROGMEM یک توصیف‌کننده‌ی متغیر است، قانون واضحی که کجا باید قرار بگیرد وجود ندارد؛ پس کامپایلر آردوینو همه‌ی تعاریف زیر (که مترادف و مشابه هم هستند) را می‌پذیرد. اما تجربه نشان داده است که PROGMEM در ورژن‌های متفاوت آردوینو (مربوط به ورژن GCC)، ممکن است در جایی کار کند و در جایی دیگر نه. مثال “string table” (جدول رشته) زیر برای کار با آردوینو 13 تست شده است. در ورژن‌های قدیمی‌ترِ IDE، اگر PROGMEM بعد از نام متغیر بیاید، ممکن است بهتر کار کنند.

 

const dataType variableName[] PROGMEM = {};
const PROGMEM dataType variableName[] = {};
// از دو فرمت بالا استفاده کنید اما از فرمت پایین استفاده نکنید.
const dataType PROGMEM variableName[] = {};

درست است که از PROGMEM می‌توان برای یک متغیر هم استفاده کرد، اما فقط هنگامی ارزش دارد که شما بلاک بزرگ‌تری از داده را دارید و نیاز به ذخیره‌ی آن دارید که معمولاً آرایه ساده‌ترین روش ذخیره است. (یا یک ساختمان داده‌ی دیگر C که فراتر از بحث ماست.) استفاده از PROGMEM هم رویه‌ای دو مرحله‌ای است. بعد از گذاشتن داده در حافظه‌ی فلش، برای خواندن داده از حافظه‌ی برنامه و قرار دادن در SRAM، نیاز به متد (فانکشن‌هایی) خاص است که آن‌ها هم در کتاب‌خانه‌ی pgmspace.h تعریف شده‌اند تا بتوانیم با آن کارهای مفید انجام دهیم.

مثال

قطعه کد زیر، نحوه‌ی خواندن و نوشتن char (بایت) و ints (دو بایت) به PROGMEM را نشان می‌دهد:

 

#include <avr/pgmspace.h>

// ذخیره تعدادی داده از نوع
// unsigned ints
const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};

//ذخیره تعدادی داده از نوع
//chars
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);

//کد ست آپ که فقط یک بار اجرا می‌شود را این‌جا قرار دهید.
// خواندن یک اینت دو بایتی
// read back a 2-byte int
for (k = 0; k < 5; k++)
{
displayInt = pgm_read_word_near(charSet + k);
Serial.println(displayInt);
}
Serial.println();
//خواندن یک کر
// read back a char
int len = strlen_P(signMessage);
for (k = 0; k < len; k++)
{
myChar = pgm_read_byte_near(signMessage + k);
Serial.print(myChar);
}

Serial.println();
}

void loop() {
// کد اصلی را که تکرار می‌شود اینجا بنویسید

}

آرایه‌ای از رشته‌ها

هنگامی‌که مشغول کار کردن با حجم زیادی از متن هستید (مثل پروژه‌هایی با نمایشگر LCD) معمولاً راحت‌تر است که آرایه‌ای از رشته‌ها را ایجاد کنید. از آن‌جایی که رشته‌ها خود آرایه هستند، این کار مثالی از رشته‌های دو بعدی است. این‌ها احتمالاً‌ ساختارهای بزرگی هستند پس معمولاً قرار دادنشان در حافظه‌ی برنامه (program memory) مطلوب است. کد زیر این ایده را نشان می‌دهد:

 

/*
PROGMEM string demo
چگونه جدولی (آرایه‌ای) از رشته‌ها را در حافظه‌ی برنامه یا فلش ذخیره و بازیابی کنیم.

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

قرار دادن جدولی (آرایه‌ای) از رشته‌ها در حافظه‌ی برنامه کمی پیچیده است,
اما در اینجا الگوی خوبی وجود دارد.

ایجاد رشته‌ها کاری دو مرحله‌ای است. اول رشته‌ها را تعریف کنید.
*/

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0"; // "String 0"
//و بقیه رشته‌هایی هستند که باید ذخیره شوند.
// change to suit.
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").
دقت کنید که رشته‌ی دریافتی شما در رم به اندازه‌ی کافی بزرگ باشد تا بتواند
هر چیزی که از حافظه‌ی برنامه بازیابی می‌کنید را در خود نگه دارد.
*/

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 );
}
}

نکته

توجه کنید که برای کار با PROGMEM، متغیرها یا باید به‌صورت global و یا با کلمه‌ی کلیدی static تعریف شده باشند. کد زیر کار نخواهد کرد هنگامی‌که درون یک تابع تعریف شده باشد:

 

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

کد زیر کار خواهد کرد حتی اگر درون یک تابع و به‌صورت local تعریف شده باشد:

 

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 ذخیره می‌شود. اگر کد شما چیزهای زیادی را روی مانیتور سریال چاپ می‌کند، شما RAM را پر خواهید کرد. اگر فضای خالی در حافظه‌ی FLASH دارید، می‌توانید به‌راحتی و با کد زیر مشخص کنید که رشته باید در حافظه‌ی فلش ذخیره شود:

 

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

منبع

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

نویسنده و طراح الکترونیکا هستم . سوالی داشتید در کامنت ها یا پیج های اینستاگرام و تلگرام سایت بپرسید .