در قسمت سیزدهم آموزش میکروکنترلر STM32F4 به مبحث مهم و کاربردی تایمر ها و سرویس وقفه پرداختیم. در این قسمت از آموزش میکروکنترلر STM32F4 به مبحث سیستمِ فایلِ FatFs بر روی کارت SD میپردازیم. با سیسوگ همراه باشید.
سیستم فایل، یعنی روشی که برای ذخیره فایلها روی وسایل ذخیره ساز استفاده میشود. از مشهورترین و قدیمیترین این سیستمها، Fat میباشد. با استفاده از این سیستم فایل میتوان فایلهایی که روی حافظه ذخیره کردید در دستگاه های دیگر، مانند رایانه شخصی، که از این سیستم فایل پشتیبانی میکنند، مشاهده کرد و تغییر داد. معروفترین سیستم فایل ارائه شده برای سیستمهای میکروکنترلری و تعبیه شده، توسط فردی به نام Chan نوشته شده است (elm-chan.org). این سیستم فایل، سیستم فایل FAT و exFAT برای سیستمهای تعبیه شده میباشد. رایگان و متن باز است و بر اساس C نوشته شده است. در واقع پشتهای نرمافزاری است که بین کاربر و سختافزار قرار میگیرد تا کاربر از درگیر شدن با سختافزار رها شود.
رابط های این نرم افزار عبارتند از:
این دستورات جز FatFs محسوب نمیشود و درباره کار با دستگاه هستند. این دستورات برای دستگاه های مختلف پیاده سازی شدهاند. همان طور که در شکل دیده میشود FatFs با رابطی به آنها دسترسی دارد.
قبل از استفاده از هر فایلی باید ابتدا یک ناحیه کاری (شی سیستم فایل) در درایو منطقی با دستور f_mount ثبت شود. غیر از f_mkfs و f_fdisk سایر تابعها بعد از این فرایند آماده کار میشوند. عملیات باز کردن تابع باید قبل از جداکردن حافظه یا خاموش کردن دستگاه بسته شود وگرنه فایل خراب خواهد شد.
برای باز کردن چند فایل با هم باید به ترتیب زیر عمل کرد. اما باز کردن چندگانه یک فایل با پرچمهای نوشتن متفاوت ممنوع است.
FatFs از باز کردن چندگانه یک فایل حمایت نمیکند مگر اینکه در حالت فقط خواندنی باشند. اگر یک فایل چندباره باز شود، تغییر نام یا پاک کردن آن مجاز نیست. تخطی از این دستورها ممکن است به خرابی فایل منجر شود. می توان از گزینه قفل کردن فایل _FS_LOCK استفاده کرد. با یک کردن این گزینه همه حالت های گفته شده بالا که منجر به خطا میشوند متوقف میشود و با پیغام FR_LOCKED پاسخ داده میشود. اگر تعداد فایل های باز از مقدار _FS_LOCK بیشتر باشد هر دستور جدیدی برای باز کردن یا ایجاد فایل جدید با پیام FR_TOO_MANY_OPEN_FILES رد میشود.
یک file object درست میکند که برای دسترسی به فایل به کار می رود
1 2 3 4 5 | FRESULT f_open ( FIL* fp, /* [OUT] Pointer to the file object STructure */ conST TCHAR* path, /* [IN] File name */ BYTE mode /* [IN] Mode flags */ ); |
که fp اشارهگر به فایل و path مسیر به رشته تهی-پایان است که اسم فایل را نشان می دهد. پرچم حالت میتواند یکی از مقدارهای جدول زیر باشد.
مقدار خروجی این تابع نیز یکی از تعریف های زیر است:
1 | FR_OK, FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_DENIED, FR_EXIST, FR_INVALID_OBJECT, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_LOCKED, FR_NOT_ENOUGH_CORE, FR_TOO_MANY_OPEN_FILES |
در زیر دو مثال دیده میشود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /* Copy a file "file.bin" on the drive 1 to drive 0 */ int main (void) { FATFS fs[2]; /* Work area (file sySTem object) for logical drives */ FIL fsrc, fdST; /* File objects */ BYTE buffer[4096]; /* File copy buffer */ FRESULT fr; /* FatFs function common result code */ UINT br, bw; /* File read/write count */ /* RegiSTer work area for each logical drive */ f_mount(&fs[0], "0:", 0); f_mount(&fs[1], "1:", 0); /* Open source file on the drive 1 */ fr = f_open(&fsrc, "1:file.bin", FA_READ); if (fr) return (int)fr; /* Create deSTination file on the drive 0 */ fr = f_open(&fdST, "0:file.bin", FA_WRITE | FA_CREATE_ALWAYS); if (fr) return (int)fr; /* Copy source to deSTination */ for (;;) { fr = f_read(&fsrc, buffer, sizeof buffer, &br); /* Read a chunk of source file */ if (fr || br == 0) break; /* error or eof */ fr = f_write(&fdST, buffer, br, &bw); /* Write it to the deSTination file */ if (fr || bw < br) break; /* error or disk full */ } /* Close open files */ f_close(&fsrc); f_close(&fdST); /* UnregiSTer work area prior to discard it */ f_mount(NULL, "0:", 0); f_mount(NULL, "1:", 0); return (int)fr; } |
و این مثال:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* Read a text file and display it */ FATFS FatFs; /* Work area (file sySTem object) for logical drive */ int main (void) { FIL fil; /* File object */ char line[82]; /* Line buffer */ FRESULT fr; /* FatFs return code */ /* RegiSTer work area to the default drive */ f_mount(&FatFs, "", 0); /* Open a text file */ fr = f_open(&fil, "message.txt", FA_READ); if (fr) return (int)fr; /* Read all lines and display it */ while (f_gets(line, sizeof line, &fil)) printf(line); /* Close the file */ f_close(&fil); return 0; } |
برای نوشتن دادهها روی فایل به کار میرود.
1 2 3 4 5 6 | FRESULT f_write ( FIL* fp, /* [IN] Pointer to the file object STructure */ conST void* buff, /* [IN] Pointer to the data to be written */ UINT btw, /* [IN] Number of bytes to write */ UINT* bw /* [OUT] Pointer to the variable to return number of bytes written */ ); |
هر بار که تابع با دستور f_open باز میشود یا تابع جدیدی ساخته میشود اشاره گر نوشتن/خواندن در ساختمان فایل که با میدان fptr نشان داده میشود مقدار صفر را دارد و عملیات خواندن/نوشتن از خانه بعد یعنی 1 شروع میشود. بنابراین اگر قصد داریم فایلی را به تدریج یا چند مرحله پر کنیم باید خودمان اشارهگر را به نقطه مورد نظر تنطیم کنیم. برای این کار می توان از تابع f_lseek استفاده کرد. این تابع اشارهگر را در نقطه مورد نظر در فایل برای نوشتن یا خواندن تنظیم میکند. برای افزایش طول فایل نیز میتوان از آن استفاده کرد. ساختار آن به شکل زیر است.
1 2 3 4 | FRESULT f_lseek ( FIL* fp, /* [IN] File object */ FSIZE_t ofs /* [IN] File read/write pointer */ ); |
fp اشارهگر به فایل است و ofs مقدار دویدگی (آفست) را از ابتدای فایل نشان میدهد. مقدار دویدگی یک آدرس مستعار است که میتواند واژه دوتایی (32 بیتی) یا چهارتایی (64 بیتی) باشد.
اگر فایلی حاوی عبارت hello باشد برای اینکه دادهای به انتهای آن اضافه کنیم باید اشارهگر را به عدد 5 تنظیم کنیم که با دستور نوشتن بعدی از خانه 6ام به بعد، نوشتن انجام شود. برای گرفتن مقدار این عدد در فایل ها میتوان از تابع f_size استفاده کرد.
این تابع محتویات یک پوشه را نشان میدهد. با هر بار فراخوانی آن اشارهگر ساختمان فایل بعدی درون پوشه باز میگردد. وقتی به انتهای فهرست فایلها رسید، یک اشارهگر تهی بدون خطا باز میگرداند و دوباره به فایل اول می رود.
1 2 3 4 | FRESULT f_readdir ( DIR* dp, /* [IN] Directory object */ FILINFO* fno /* [OUT] File information STructure */ ); |
برنامه نمونه:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | FRESULT scan_files ( char* path /* STart node to be scanned (***also used as work area***) */ ) { FRESULT res; DIR dir; UINT i; STatic FILINFO fno; res = f_opendir(&dir, path); /* Open the directory */ if (res == FR_OK) { for (;;) { res = f_readdir(&dir, &fno); /* Read a directory item */ /* Break on error or end of dir */ if (res != FR_OK || fno.fname[0] == 0) break; if (fno.fattrib & AM_DIR) { /* It is a directory */ i = STrlen(path); sprintf(&path[i], "/%s", fno.fname); res = scan_files(path); /* Enter the directory */ if (res != FR_OK) break; path[i] = 0; } else { /* It is a file. */ printf("%s/%s\n", path, fno.fname); } } f_closedir(&dir) } return res; } int main (void) { FATFS fs; FRESULT res; char buff[256]; res = f_mount(&fs, "", 1); if (res == FR_OK) { STrcpy(buff, "/"); res = scan_files(buff); } return res; } |
مسیر فایل در FatFs همانند داس/ویندوز میباشد.
1 | "[drive:][/]directory/file" |
نام مورد استفاده قالب 8.3 دارد (sfn) برای استفاده از نامهای بلند باید _USE_LFN را تنظیم کرد. زیرپوشهها را باید با / یا \ جدا کرد. جدا کنندهها ی جفت در نظر گرفته نمیشوند. درایو منظقی با یک عدد و یک دو نقطه نشان داده میشود برای مثال 2: درایو شماره 2 است. وقتی شماره درایو حذف شود درایو پیش فرض (درایو صفر یا درایو جاری) در نظر گرفته میشود. کاراکترهای \0 و \x1f به عنوان پایان مسیر در نظر گرفته میشوند. در حالت LFN ، فاصلهها در نظر گرفته میشود ولی در حالت عادی، پایان مسیر در نظر گرفته میشود. برای مثال مسیر “test path” در حالت عادی “test” در نظر گرفته میشود. فاصلههای انتهای خط و نقطهها در نظر گرفته نمیشوند (در هر دو حالت)، در حالت پیش فرض (_FS_RPATH == 0), مفهوم درایو جاری وجود ندارد. همه اشیا در حجم همواره با مسیر کامل از پوشه ریشه توصیف میشوند. پوشه های «.» و«..» مجاز نیستند. جدا کننده های ابتدای مسیر در نظر گرفته نمیشوند. درایو پیش فرض نیز درایو صفر در نظر گرفته میشود. وقتی مسیرهای نسبی فعال شود (_FS_RPATH >= 1), مسیر بیان شده، از پوشه ریشه ادامه پیدا میکند، اگر یک جدا کننده اول مسیر موجود باشد وگرنه از پوشه جاری درایو که با f_chdir گذاشته شده است ادامه مییابد. نامهای دارای نقطه هم برای مسیرها مجاز هستند. درایو پیش فرض درایو جاری است که توسط f_chdrive تعیین میشود.
وقتی گزینه _STR_VOLUME_ID مشخص شده باشد میتوان از رشتههای از پیش تعیین شده به جای عددها برای شماره درایوها استفاده کرد مثلا “sd:file1.txt”, “ram:swapfile.dat”
سیستمِ فایل، برای هر کدام از درایوهای منطقی (درایو ها می توانند به شکل فیزیکی متفاوت باشند) به file system object نیاز دارد که با دستور f_mount به FatFs اضافه یا حذف (unregister) میشود. این دستور سکتور بوت را میخواند و بررسی میکند که سکتور Fat با سکتور صفر SFD، پارتیشن ها در قالب FDISK باشد.
اگر گزینه مولتی پارتیشن ( MULTI_PARTITION == 1 ) فعال باشد هر درایو منطقی به یک بخش روی درایو فیزیکی محدود است که در جدول مدیریت پارتیشن ها مشخص شده است. این جدول باید توسط کاربر مشخص شود تا ارتباط بین درایوهای منطقی و پارتیشن ها مشخص شود. برای مثال یک نمونه از این جدول در زیر دیده میشود:
1 2 3 4 5 6 7 8 9 | /*Example: Logical drive 0-2 are tied to three pri-partitions on the physical drive 0 (fixed drive) Logical drive 3 is tied to an FAT volume on the physical drive 1 (removable drive) */ PARTITION VolToPart[] = { {0, 1}, /* "0:" ==> Physical drive 0, 1ST partition */ {0, 2}, /* "1:" ==> Physical drive 0, 2nd partition */ {0, 3}, /* "2:" ==> Physical drive 0, 3rd partition */ {1, 0} /* "3:" ==> Physical drive 1, auto detection */ }; |
در شکل زیر دو حالت دیده میشود. شکل سمت راست حالت چندبخشی را نشان میدهد. دو درایو فیزیکی صفر و یک موجودند که درایو صفر به سه بخش تقسیم شده است و در مجموع 4 درایو منطقی داریم.
نکات
در شکل زیر این پشته دیده میشود.
اگر سختافزار جانبی پروتکل spi و یا i2c را پشتیبانی کند این کتابخانه به تنهایی کافی است ولی برای کار کردن با برخی از سختافزارهای دیگر نیاز به تابع های میانی است. نمونه این دو در شکل چپ و راست به ترتیب دیده میشود.
در جدول زیر تابعهای مورد نیاز بنا به کاربردهای خاص دیده میشود.
برخی ویژگیهای این سیستم فایل عبارتند از:
بسته به اینکه از کدام ماژولهای این کتابخانه استفاده کنید میتوانید با تعریفهایی آنها را انتخاب کنید.
همه کدهای FAT نیاز به تابع واسط برای دسترسی به سخت افزار دارند بسته به اینکه از پروتکل spi استفاده شود یا sdio به باس تک بیته یا باس 4 بیته نیاز است. دست کم به سه تابع نیاز دارید. یکی که کارت و کنترلر را آغازسازی کند یکی که یک بلوک را (که معمولا 512 بایت است) به داخل ram بخواند و یکی که یک بلوک را از ram به داخل کارت بنویسد.
در قسمت پانزدهم آموزش میکروکنترلر STM32F4 به مبحث کار کردن با کارت SD در کتابخانه HAL خواهیم پرداخت. با سیسوگ همراه باشید.
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.