حافظی EEPROM که در این پست قصد استفاده از آن را داریم یکی از قسمت هایی است که نسبت به کدویژن دارای پیچیدگی بیشتری است!
___________
ابتدا فایل مربوط به این حافظه را به برنامه اضافه می کنیم
کد:
#include <avr/eeprom.h>
یه ذره توابع موجود و اینا زیاد هست برای همین تو
این لینک می تونید کامل ببینید ولی من اون هایی که نیاز دارم رو توضیح میدم.
اما قبل از هر چیز یه سری متغییر استفاده شده اند که لازم هست توضیح بدم اون هارو!
زیاد پیش میاد که توی برنامه هایی که می بینید از متغییر هایی از نوع "uint8_t" و اینا استفاده شده باشند!
این ها فرقی با قبلی ها ندارن ولی برای اینکه هر کامپایلری بتونه با برنامه کار کنه از این ها استفاده می کنن
توی این فایل که باید به برنامه اضافه کنید
لیست کاملی از این ها اضافه میشه و برای مثال توی C ما از "unsigned char" استفاده می کنیم پس "uint8_t" به اون ترجمه می شه ممکن هست فلان کامپایلر "un ch" باشه پس "uint8_t" به اون تبدیل میشه!
کلا این ها توی این فایل سرامد تعریف شدن!
کد:
Integer types having exactly the specified width
typedef signed char
int8_t
typedef unsigned char
uint8_t
typedef signed int
int16_t
typedef unsigned int
uint16_t
typedef signed long int
int32_t
typedef unsigned long int
uint32_t
typedef signed long long int
int64_t
typedef unsigned long long int
uint64_t
چیز های دیگه ای هم توی این فایل هست که فعلا زیاد کاری باهاشون نداریم ولی توی
این لینک می تونید ببینید
اولین قدم می خوایم از آدرس 60 حافظه یه داده ای رو بخونیم!
کد:
# include <avr / eeprom .h>
void main ( void )
{
uint8_t ByteOfData ;
ByteOfData = eeprom_read_byte (( uint8_t *) 60) ;
}
برای خوندن دو بایت از حافظه یا به عبارتی متغییر int این گونه عمل می کنیم
کد:
uint16_t WordOfData ;
WordOfData = eeprom_read_word (( uint16_t *) 46) ;
این دستور از 46 و 47 داده رو می خونه و توی متغییر ذخیره می کنه
برای متغییر های بزرگ از
و برای اعداد اعشاری از
استفاده می کنیم
برای نوشتن یک بایت در حافظه این گونه عمل می کنیم
کد:
uint8_t ByteOfData ;
ByteOfData = 0 x12 ;
eeprom_update_byte (( uint8_t *) 46, ByteOfData );
حالا متغییر ما رو توی قسمت 46 حافظه ذخیره کرد.
و همانطوری که انتظار دارید برای متغییر های 2 بایتی از
کد:
uint16_t WordOfData ;
WordOfData = 0 x1232 ;
eeprom_update_word (( uint16_t *) 46, WordOfData );
و دو باره برای متغییر های 4 بایتی از
کد:
eeprom_update_dword()
و اعشاری ها
کد:
eeprom_update_float()
حالا ما می تونیم دونه دونه بنویسیم یا بخونیم ولی نیاز هست گاهی که چند تا رو بخونیم مثلا ارایه ها یا یک رشته متنی که ذخیره شده!
برای این کار از این تابع استفاده می کنیم
کد:
void eeprom_read_block ( void * pointer_ram , const void * pointer_eeprom , size_t n)
شاید تعجب کنید که هیچ مقداری رو بر نمی گردونه !
این مقدار در متغییری که ادرسش رو به عنوان اولین ورودی به تابع دادید ذخیره میشه!
برای مثال ما از این برای خواندن 10 بایت پشت سر هم از ادرس 12 رو می بینیم
کد:
uint8_t StringOfData [10];
eeprom_read_block (( void *) StringOfData , ( const void *) 12, 10) ;
حالا شاید از من بپرسید این void این وسط چه فایده ای داره؟!
در جواب می گم در مثال های قبل با اندازه ما مثلا یک متغییر دو بایتی رو خوندیم که ممکن بود که علامت دار باشد یا نباشد و نوع آن را مشخص می کردیم در واقع برای تابع اهمیت داشت که چه مقداری رو می خواند!
اما گاهی ما نمی دانیم که داده ای که درحال خواندن آن هستیم چه نوع داده ای است! شاید هم اصلا مهم نباشد! برای همین از void استفاده می کنیم با این کار تابع فقط مغدار را می خواند و دیگر کاری به نوع داده ی خوانده شده (متن - اعشاری - صحیح - منفی و...) ندارد و عین آن را در خروجی ظاهر می کند در مثال بالا 10 بایت پشت سر هم خوانده شده و در 10 عضو ارایه نوشته می شود.
برای نوشتن در حافظه هم این گونه عمل میشود
کد:
uint8_t StringOfData [10] = " TEST ";
eeprom_update_block (( const void *) StringOfData , ( void *) 12, 10);
اولین ورودی از کلمه ی const استفاده شده است چون دیگر نیازی نیست که تغییری در آن در تابع ایجاد شود و فقط قرار است مقدار آن خوانده شود.
حال از خانه 12 به صورت متوالی برای 10 بایت در حافظه نوشته میشود که 5 تای آن مقداری که در متغییر ما موجود است نوشته میشود و بقیه مقدار 0 می گیرند
_________
حالا ما کار با این حافظه رو یاد گرفتیم ولی حتما این چیزی نیست که ما بخوایم چون وقتی کمی تعداد متغییر ها زیاد میشن دیگه به خاطر سپردن آدرس ها کار ما نیست برای همین نیاز میشه که مثل بقیه متغییر ها ، متغییری برای حافظه تعیین بکنیم تا کار را برای ما ساده تر بکند!
برای این کار مانند تعریف متغییر ها عمل می کنیم با این تفاوت که "EEMEM" را به قبل آن ها اضافه می کنیم که به کامپایلر می گوید متغییر ها در حافظه EEPROM تعریف شوند
برای مثال
کد:
uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];
اما با این حال این به این معنی نیست که مانند متغییر های معمولی به طور مستقیم به آن ها دسترسی داشته باشیم!
برای مثال این کد کار نمی کند!
کد:
uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];
int main ( void )
{
uint8_t SRAMchar ;
SRAMchar = NonVolatileChar ;
}
این ها صرفا یک ادرس هستند برای کار با ان ها باید مثل قبل عمل کنید
کد:
# include <avr / eeprom .h>
uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];
int main ( void )
{
uint8_t SRAMchar ;
uint16_t SRAMint ;
uint8_t SRAMstring [10];
SRAMchar = eeprom_read_byte (& NonVolatileChar );
SRAMint = eeprom_read_word (& NonVolatileInt );
eeprom_read_block (( void *) SRAMstring , ( const void *) NonVolatileString , 10);
}
_________
کلا کامپایلر سه خروجی کامپایل شده به شما میده فایل هگز - فایل مربوط به EEPROM با نام EEP و ELF
در ELF هم اطلاعات مربوط به قسمت فلش هم EEPROM هم اطلاعات فیوزبیت ها ذخیره می شوند
اما در کل شاید ما بخواهیم مقدار پیش فرضی به متغییر خود بدهیم که در این صورت لازم است ما ان را پروگرام کنیم تا برای بار اول داخل EEPROM نوشته شود!
برای این کار کافی است که هنگام تعریف مقداری به آن نسبت دهیم!
کد:
uint8_t EEMEM SomeVariable = 12;
تغییر رو می تونید داخل فایل خروجی مربوط به حافظه مشاهده کنید
دقت کنید که پس از پروگرام کردن اگر مقدار در برنامه تغییر کرد با شروع مجدد میکرو این مقدار ها برای بار جدیدی نوشته نمی شوند و مقدار های قبلی خود را می گیرند!
____________
نمی دونم شاید ادامه داشته باشد! شاید:d
این فایل فشرده هم مجموعه ای از چند آموزش است به زبان شرین انگلیسی حال کردید بخونید!
کد:
EEPROM.pdf
Interrupts.pdf
InterruptUSART.pdf
ManagingLargeProjects.pdf
Progmem.pdf
ProgrammingMethods.pdf
Timers.pdf
USART.pdf
عجله هم نداشتید همه رو خودم هم میگم