آموزش WinAVR جلسه هشتم

(آموزش WinAVR جلسه هشتم)

مقدمه:

در جلسه قبل به نحوه ی ارتباط بین AVR و SD/MMC پرداختیم، سپس قسمت های مختلف حافظه های SD/MMC را بررسی نمودیم و با انجام یکسری محاسبات موفق شدیم مکان قرار گیری محتوای یک فایل متنی کوچک را در داخل مموری کارت پیدا کرده و اطلاعات آن را بر روی نمایشگر نشان دهیم. همچنین یکی از کاراکترهای فایل را عوض کردیم. در این جلسه با جدول FAT آشنا می شویم و یک برنامه ی کوچک با WinAVR می نویسیم که در ابتدا محتوای root یک مموری کارت را به ترتیب نمایش می دهد (نام تمام فایلها) و سپس محتوای یک فایل متنی بزرگ (8466B) به نام my_file1.txt را به طور کامل خوانده و برای یافتن کلاسترهای ادامه ی فایل به جدول FAT رجوع می کند و زنجیره ی فایل را به طور کامل می خواند. در نهایت تابعی نوشته ایم که یک فایل متنی به نام atmega16.txt با محتوای A در داخل مموری کارت ایجاد می کند و براحتی به کمک کامپیوتر می توان فایل ایجاد شده را مشاهده و ویرایش کرد.جدول FAT:

همانطوریکه از جلسه ی قبل به یاد دارید هر مموری کارت از بخش های مختلفی تشکیل شده است. این بخش ها که در شکل روبرو مشخص شده است بصورت منطقی می باشد نه فیزیکی. یعنی در واقع یک مموری کارت SD/MMC مجموعه ای از خانه های حافظه است که برای هر گروه از این خانه ها یک نام تعیین شده و محتوای آنها معنای خاصی دارد. در جلسه قبل در موردBoot Sector به طور مفصل توضیح دادیم. بوت سکتور حاوی اعدادیست که به کمک آنها می توان آدرس و اندازه ی خانه های دیگر را پیدا کرد. خودِ بوت سکتور معمولاً در خانه های ابتدایی حافظه قرار گرفته است، از نظر تئوری بایستی در آدرس 0 باشد ولی در عمل در آدرس های بالاتر است. در مموری مورد آزمایش ما در خانه ی 63 قرار گرفته است.

در جلسه ی قبل خاطر نشان کردیم که در بایتهای شماره ۱۴ و ۱۵ داخل بوت سکتور عددی دوبایتی قرار گرفته است که آدرس نسبی شروع جدول FAT1 را نشان می دهد. با جمع این آدرس با آدرس شروع بوت سکتور می توان آدرس دقیق شروع جدول FAT1 را بدست آورد. توجه داشته باشید که در هر مموری کارت دو جدول FAT وجود دارد. ما فقط با جدول FAT1 کار می کنیم و از این پس در این مقاله آن را اختصاراً جدول FAT می نامیم. در مموری 1GB مورد آزمایش ما که با FAT32 فرمت شده است جدول FAT در سکتور شماره 4433 قرار گرفته است:

در شکل بالا محتویات داخل جدول FAT را مشاهده می کنید. در این جدول اعداد در مجموعه های ۳۲ بیتی یا چهار بایتی در کنار هم قرار گرفته اند و هر مجموعه به یکی از کلاسترهای مموری (Clusters) که بعد از Root Directory قرار گرفته اند و محتوای فایل ها را در خود جای داده اند اشاره می کند. (برای نمایش محتوای حافظه بصورت هگز، از نرم افزار رایگانHxD استفاده کرده ایم که فایل آن در کنار پوشه این مقاله قرار داده شده است).

قبل از توضیح بیشتر در خصوص جدول FAT اجازه دهید در ابتدا فایلهای داخل مموری کارت را مشاهده کنیم:

همانطوریکه در شکل بالا ملاحظه می فرمایید در داخل این کارت حافظه، سه فایل متنی، یک پوشه سیستمی و یک فایل سیستمی (در داخل پوشه) قرار گرفته است. در ادامه محتوای Root Directory را به کمک نرم افزار HxD بررسی می کنیم:

همانطوریکه در شکل بالا مشاهده می فرمایید به ازای هر فایل یا پوشه، ۳۲ بایت از جدول دایرکتوری ریشه رزرو شده است. ۱۱ بایت اول هر خط حاوی نام و پسوند فایل بصورت پشت سر هم و حداکثر ۸ کاراکتر برای هر فایل می باشد (بدلیل پیچیدگی، راهکار ثبت نام های طولانی را بررسی نکرده ایم). بایت شماره ۱۱ خصوصیات فایل از قبیل (فایل یا پوشه، مخفی، فقط خواندنی و…) را در خود جای می دهد. بایت شماره ۱۲ تا ۲۵ حاوی خصوصیات کاربر، زمان ساخت، ویرایش و دسترسی فایل و خصویات اشتراک گزاری فایل می باشد. بایت های شماره ۲۶ و ۲۷ شماره ی خانه ای از جدول FAT را نمایش می دهد که در داخل آن خانه آدرس کلاستر بعدی محتوای فایل قرار گرفته است. البته این در صورتی به کار می آید که محتوای فایل بزرگتر از یک کلاستر (در اینجا ۴۰۹۶ بایت) باشد. چهار بایت آخر هم اندازه ی فایل را بر حسب بایت نشان می دهد.

در بین فایل های داخل حافظه، حجم فایل my_file1.txt با ظرفیت ۸۴۶۶ بایت از بقیه بیشتر بوده و ۳ کلاستر را اشغال کرده است. برای بخش اول برنامه (خواندن فایل) بیشتر با این فایل سرو کار خواهیم داشت تا به کمک آن با جدول FAT آشنا شویم.

همانطوریکه ذکر شد از ۳۲ بایتی که در جدول روت دایرکتوری به ازای هر فایل وجود دارد، بایت های ۲۶ و ۲۷ عددی را نمایش می دهد که شماره ی خانه ای در جدول FAT می باشد. این عدد همچنین نمایانگر آدرس کلاستر آغازین محتوای فایل نیز بوده و به کمک دستور زیر می توان آدرس مطلق را محاسبه کرد:

realAddrOfFile = rootDirectory + ( (addrOfFileinFat-2)*sectorPerCluster );

در این دستور rootDirectory آدرس شروع دایرکتوری ریشه، addrOfFileinFat عدد موجود در خانه های ۲۶ و ۲۷ فایل در روت دایرکتوری، sectorPerCluster تعداد سکتورهای موجود در هر کلاستر (در اینجا عدد ۸) و realAddrOfFile نیز آدرس کلاسترِ حاویِ محتوای فایل می باشد. اما ماجرا به همین جا ختم نمی شود. این دستور البته برای فایلهای کوچک که حجم آنها کمتر از اندازه ی یک کلاستر باشد عالی عمل می کند اما برای فایل آزمایشی ما که سه کلاستر را اشغال کرده است (این موضوع را باید با بررسی اندازه ی فایل و مقایسه ی آن با اندازه ی کلاستر متوجه شویم) باید برای پیدا کردن کلاستر بعدی محتوای فایل به جدول FAT مراجعه کنیم.

if (fileSize <= byteInCluster){
    فایل کوچک
}
else
{
    فایل بزرگ و نیاز به مراجعه به جدول تخصیص فایل
}

مجددا جدول FAT را بررسی می کنیم:

خانه های ۲۶ و ۲۷ فایل my_file1.txt در جدول root عدد 0006 را در خود جای داده اند که بیانگر مجموعه ی شماره ۶ از جدول FAT می باشد. اولین مستطیل آبی با محتوای 00000007 همین خانه ی شماره ۶ است که خود به خانه ی شماره ۷ اشاره می کند و خانه ی شماره ۷ به خانه ۸ و بالاخره خانه ۸ به 0FFFFFFF که به معنای آخرین کلاستر فایل است.

این پیوستگی زنجیر وار همیشه به همین صورت مرتب نیست. تصور کنید ما چندین فایل بزرگ متنی ایجاد کرده ایم و همگی به همین شکل در داخل حافظه قرار گرفته اند. حال تصمیم داریم فایل اول را باز کرده و آن را ویرایش کنیم و مطالبی را به آن اضافه نماییم. در این صورت محتوای آخرین خانه بایستی به جای 0FFFFFFF آدرس کلاستر بعدی را که ممکن است عددی بسیار طولانی مثل 12345678 باشد را در خود جای دهد و زنجیره را بصورت پیوسته نگه دارد. تصور کنید در داخل هارد کامپیوتر که هر روز اطلاعات آن کم و زیاد می شود چه زنجیره ی در هم و برهمی از اطلاعات در داخل جدول FAT ایجاد می شود و سیستم مدیریت فایل برای خواندن یک فایل معمولی چه زحماتی را باید متحمل شود. اگر یک بایت از FAT پاک شود تمام زنجیره خراب شده و عملاً فایل مورد نظر ا زبین می رود، چون دنباله ی آن در میان انبوهی از اطلاعات گم می شود. به همین دلیل در سیستم های مدرن از هر دو جدول FAT1 و FAT2 استفاده می شود و FAT2 همیشه یک کپی از FAT1 را در خود ذخیره می کند تا در صورت بروز آسیب بتوان به کمک نرم افزار های خاص، حافظه را درست کرد (اینکار در سیستم هایی مثل ویندوز احتمالا بصورت خودکار صورت می گیرد، مثل صفحه آبی اصلاح دیسک که گاهی اوقات قبل از شروع در ویندوز XP ظاهر می شود).

ما در برنامه ی کوچکی که با WinAVR برای میکروی ATmega16 نوشته ایم در ابتدا آدرس بوت سکتور را یافته و از آنجا آدرس های دایرکتوری ریشه و جدول تخصیص فایل را پیدا کرده ایم. سپس رکوردهای root را برای فایل my_file1.txt جستجو کرده ایم و پس از یافتن آن، حجم فایل را در متغیر fileSize قرار داده ایم. در ادامه با کمک دستوری که در صفحه قبل آورده شد حجم فایل را با اندازه کلاسترهای مموری مقایسه کرده ایم و در صورتی که حجم فایل بیشتر بود ابتدا کلاستر اول را بطور کامل خوانده و سپس با دستور mmcRead(fatAddr, buffer) جدول FAT را در بافر ذخیره کرده ایم. از جدول فت محتوای زنجیره را خوانده و هر بار آدرس کلاستر مربوطه را در متغیر nextAddrOfFileinFat قرار داده و آدرس واقعی کلاستر را محاسبه و محتوای آن را به طور کامل خوانده ایم. در هر بار خواندن عدد (۴۰۹۶) را که اندازه ی کلاستر ها در این حافظه می باشد از اندازه فایل کم کرده ایم و این کار را تا زمانی که اندازه فایل بزرگتر از صفر است ادامه داده ایم:while(fileSize>0){

بدین ترتیب هر فایلی با هر حجمی براحتی قابل خواندن می باشد و تنها مشکل موجود کم بودن SRAM میکروکنترلر می باشد که اجاره نمی دهد تمام محتوای FAT را در داخلSRAM قرار دهیم تا خواندن فایل با سرعت بالاتری انجام پذیرد. برای ایجاد یک سیستم مدیریت فایل واقعی باید به نکات خیلی زیادی توجه داشته باشید. از جمله کپی کل محتوای FAT در داخل RAM (ما در اینجا فقط ۵۱۲ بایت اول FAT را بررسی کرده ایم که این کار برای فایل های بیشتر ایجاد مشکل می کند. نکته دوم اینست که همیشه آخرین کلاستر به طور کامل مربوط به فایل نمی باشد و ممکن است فقط چند بایت اول آن حاوی اطلاعات فایل باشد که این امر باید با شمارش تعداد کاراکتر های خوانده شده و مقایسه آن با اندازه ی فایل مشخص شود.

در بخش بعدی برنامه یک فایل به نام atmega16.txt ایجاد کرده ایم و در داخل آن کاراکتر A را نوشته ایم که این فایل براحتی در داخل کامپیوتر نیز قایل مشاهده می باشد. برای ایجاد یکی فایل متنی در ابتدا جدول FAT را از ابتدا بررسی کرده ایم و اولین ۳۲ بیت خالی را پیدا و آدرس نسبی آن را محاسبه و در متغیر tmp ذخیره کرده ایم.

این مدخل مربوط به فایل ما خواهد بود و چون فایل ما کوچکتر از اندازه ی یک کلاستر می باشد این مدخل را با 0FFFFFFF پر کرده ایم چون نیازی نیست به جایی اشاره کند و آدرس اولین و آخرین کلاستر حاوی محتوای فایل می باشد. در قدم بعدی محتوای Root Directory را به طور کامل از ابتدا جستجو کرده ایم و این جستجو را برای پیدا کردن اولین ۳۲ بایت خالی ادامه داده ایم. به محض پیدا کردن اولین ۳۲ بایت خالی بلافاصله نام فایل را در ۱۱ بایت ابتدایی نوشته و بایت های بعدی را نیز با خصوصیات و تاریخ ساخت فایل پر کرده ایم (به دلیل اهمیت پایین، بایت خصوصیات و تاریخ را از یک فایل متنی دیگر کپی کرده ایم، در صورتی که سیستم شما مجهز به ساعت داخلی باشد می توانید زمان و تاریخ دقیق را بصورت میلادی محاسبه کرده و با رعایت فرمت خاص که در آدرس https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system نوشته شده است در بایت های مربوطه قرار دهید. اما دو بایت مهم ۲۶ و ۲۷ که مربوط به آدرس شروع فایل در جدول FAT می باشد را با متغیر tmp که در مرحله قبل بدست آورده بودیم پر کرده ایم. توجه داشته باشید که متغیر tmp یک متغیر ۱۶ بیتی است که برای تبدیل آن به دو متغیر ۸ بیتی کافیست طبق دستور زیر عمل کنیم:

u08 c1 = tmp & 0x00FF; // lsb
u08 c2 = tmp >> 8; // msb

بدین ترتیب بایت کم ارزش تر در متغیر c1 و بایت پر ارزش تر در متغیر c2 قرار خواهد گرفت که آنها را در جای مناسب خود در buffer قرار داده ایم. چهار بایت آخر نیز مربوط به اندازه فایل می باشد. ما از پیش می دانیم که فایل ما قرار است فقط حاوی کاراکتر A باشد، پس اندازه آن ۱ بایت خواهد بود. اما در حالت واقعی باید اندازه فایل متنی نوشته شده در سیستم خود را محاسبه کرده آن را در فیلد های آخر قرار دهید. ترتیب ارزش فیلدها نیز مشخص است: فیلدهای ابتدایی کم ارزش تر و فیلدهای انتهایی بایت پر ارزش تر را در خود جای می دهند.

هنوز یک مرحله دیگر باقی مانده و آن نوشتن محتوای فایل (کاراکتر A) در کلاستر مربوطه می باشد. آدرس نسبی این کلاستر در جدول FAT در متغیر tmp قرار گرفته بود که با استفاده از فرمول:
realAddrOfFirstFile = rootDirectory + ( (tmp-2)*sectorPerCluster );
آدرس واقعی اولین (و آخرین) کلاستر فایل را بدست می آوریم. در نهایت محتوای بافر دستکاری شده دایرکتوری ریشه را در جای خود بازنویسی کرده و پس از خالی کردن بافر از کاراکترهای اضافه و نوشتن کاراکتر A در اولین خانه بافر، محتوای بافر را نیز در کلاستر مربوط به فایل بازنویسی کرده ایم. بدین ترتیب فایل ما با موفقیت در داخل مموری کارت به ثبت می رسد و می توانیم آن را توسط هر دستگاه استانداردی از قبیل کامپیوتر مشاهده نماییم. توجه داشته باشید که این روش برای فایل های کوچک مناسب است. در صورتی که محتوای فایل شما بیشتر از اندازه یک کلاستر باشد بایستی پس از نوشتن اولین کلاستر، مجدداً به جدول FAT رجوع کنید و اولین مدخل خالی را پیدا کرده و آدرس آن را در مدخل قبلی (tmp) بنویسید و به طور موقت مدخل جدید را با 0FFFFFFF پر کنید و آدرس نسبی آن را در tmp قرار دهید و آدرس واقعی کلاستر را به کمک tmp پیدا کرده و ادامه ی فایل را در آن بنویسید و در نهایت اندازه فایل را بر حسب کاراکتر در رکورد مربوطه در جدول Root Directory کامل نمایید.
کد کامل پروژه در محیط WinAVR:

 

 

 

شماتیک:

شماتیک پروژه ارتباط با مموری کارت را در شکل زیر ملاحظه می فرمایید که با استفاده از نرم افزار قدرتمند و رایگان کیکد (KiCad) ترسیم شده است. توجه داشته باشید که شماتیک این جلسه با جلسه قبلی تفاوت دارد، اول اینکه بدلیل نیاز به حجم بالا از میکروی ATmega16 استفاده شده است و دوم اینکه برای آزمایش LCD در مد ۴ بیت، برنامه این جلسه با LCD در این مد نوشته شده و فقط یک پورت به نمایشگر اختصاص یافته است.

سخت افزار:

تصویری از پروژه در حال تست را در شکل زیر مشاهده می فرمایید:

آنچه در این مقاله و مقاله ی قبلی نوشته شد شرح مختصری بود بر نحوه ی عملکرد حافظه ها و آدرس دهی آنها. مطمئناً نوشتن برنامه های سیستم فایل و مدیریت حافظه کار آسانی نیست و بنا به گفته ی پروفسور تنن باوم، مدیریت فایل بخشی از یک سیستم عامل به حساب می آید. پس انتظار نداشته باشید که یک سیستم مدیریت فایل بصورت تمام و کمال توسط یک شخص و در مدت کوتاهی نوشته شود و بصورت رایگان و پشتیبانی کامل برای استفاده منتشر گردد. برای استفاده عملی از مموری کارت ها در پروژه های میکروکنترلری بهتر است از سیستم های مدیریت فایلِ از پیش نوشته شده و معتبر و یا تجاری استفاده کنید تا آسیبی به دستگاه و فایلهای شما نرسد و تا حد قابل قبولی قابل اعتماد نیز باشد. هدف از این سری مقالات بیشتر آشنایی با دستورات زبان C و آموزش کامپایلر رایگان WinAVR می باشد. هدف اصلی ما هرگز نوشتن یک سیستم مدیریت فایل جامع نیست. امید است با بررسی این برنامه ها با دستورات بیشتری در خصوص این کامپایلر آشنا شوید. طبق قرار قبلی جلسه بعدی به مباحث تکمیلی WinAVR از قبیل (متغیرها، وقفه ها، EEPROM، ترفندهای C) و غیره اختصاص خواهد یافت و جلسه دهم نیز به مثالهای کاربردی اختصاص می یابد.

منابع:https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
http://aminelec.persianblog.ir/post/4/
http://electrorc.blogfa.com/post-28.aspx
http://www.c-jump.com/CIS24/Slides/FAT/lecture.html
http://en.wikipedia.org/wiki/Secure_Digital

– طراحی و پیاده سازی سیستم های عامل (کتاب MINIX)، پروفسور اندرو اس. تنن باوم، آلبرت اس. وودهال، ترجمه و تالیف دکتر ابوالفضل طرقی حقیقت نشر همای دانش
(توجه: این آموزش بر اساس مقاله dominoی مهندس علی تروشه نوشته شده که بدینوسیله از زحمات ایشان قدردانی می شود)
http://electrorc.blogfa.com
این مقاله به همراه سایر مقالات منابع بالا در پوشه این آموزش قرار گرفته است.

 دانلود PDF این مقاله

بازدیدها: 145