Непонятки с FATFs от товарища Чана

Здравствуйте господа.
Столкнулся с проблемой. Попала мне в руки платка на STM32F103 с дисплеем. Воттакая и есть задача, читать с микроСД карты файлик.
Качнул я архив с примерами, в нем проект для Keil в проекте FATFs от Чана и прослойка между картой(которая подключена по SDIO) и это самой ФатФС.
В Keil4 все отлично компилится, заливаю на плату и фиг там. говорит — На карте нет ФС. Хотя ФС там есть и даже есть файл. Вобщем путем шныряния по файлу ff.c и ff.h нашел вот что.
Есть такая сруктура там, которая описывает файловую систему, выглядит так
/* File system object structure */
typedef struct _FATFS {
	WORD	id;				/* File system mount ID */
	WORD	n_rootdir;		/* Number of root directory entries */
	DWORD	winsect;		/* Current sector appearing in the win[] */
	DWORD	sects_fat;		/* Sectors per fat */
	DWORD	max_clust;		/* Maximum cluster# + 1 */
	DWORD	fatbase;		/* FAT start sector */
	DWORD	dirbase;		/* Root directory start sector (cluster# for FAT32) */
	DWORD	database;		/* Data start sector */
#if !_FS_READONLY
	DWORD	last_clust;		/* Last allocated cluster */
	DWORD	free_clust;		/* Number of free clusters */
#if _USE_FSINFO
	DWORD	fsi_sector;		/* fsinfo sector */
	BYTE	fsi_flag;		/* fsinfo dirty flag (1:must be written back) */
	BYTE	pad2;
#endif
#endif
	BYTE	fs_type;		/* FAT sub type */
	BYTE	csize;			/* Number of sectors per cluster */
#if S_MAX_SIZ > 512U
	WORD	s_size;			/* Sector size */
#endif
	BYTE	n_fats;			/* Number of FAT copies */
	BYTE	drive;			/* Physical drive number */
	BYTE	winflag;		/* win[] dirty flag (1:must be written back) */
	BYTE	pad1;
	BYTE	win[512];	/* Disk access window for Directory/FAT */
} FATFS;


И есть функция проверки файловой системы. Выглядит так

static BYTE check_fs (	/* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */
	FATFS *fs,	/* File system object */
	DWORD sect	/* Sector# (lba) to check if it is an FAT boot record or not */
)
{
	if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK)	/* Load boot record */
		return 3;
	if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)		/* Check record signature (always placed at                                               offset 510 even if the sector size is >512) */ 
		return 2;

	if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)	/* Check "FAT" string */
		return 0;
	if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
		return 0;

	return 1;
}

Строка
disk_read(fs->drv, fs->win, sect, 1)
читает нулевой сектор на диске в fs->win, и если чтение прошло успешно возвращает 0. (fs это указатель на структуру которая есть выше).
С чтением проблем нет, все читает. Проблема начинает дальше. при проверке подписи сектора. А именно вот тут
(LD_WORD(&fs->win[BS_55AA]) != 0xAA55

проблемма в том, что в &fs->win[BS_55AA] находится не то что прочиталось с диска, а значение этого BS_55AA
(в заголовке оно #define BS_55AA 510). Причем если поставить не не BS_55AA, а допустим 6 то считываться будет 6.
Высянил я это путем добавления в код вот такой штуки
printf("FAT signature: %i\r\n",LD_WORD(&fs->win[BS_55AA]));

на выходе получаю FAT signature: 510

При этом с диска все читается правильно. Если сделать

char Read_Buffer[512];
Status = SD_ReadBlock(Read_Buffer, 0x00, BlockSize);
printf("Status Read: %i\r\n-------------\r\n",Status);   	
printf("%2X  %2X\r\n",Read_Buffer[0x1fe],Read_Buffer[0x1ff]);


в ответ получаю AA 55
SD_ReadBlock — низкоуровневая функция чтения блока данных с диска.

Почему работа со структурой проходит так странно…
или чего то не так делаю.

Помогите плз.

З.Ы:
В приложении неизмененный проект. и тем не менее у меня не работает
  • 0
  • 24 октября 2011, 17:46
  • Geban
  • 1
Файлы в топике: FATFS V0.08A-SD Card.zip

Комментарии (36)

RSS свернуть / развернуть
в &fs->win[BS_55AA] находится не то что прочиталось с диска, а значение этого BS_55AA
Это указатель, а не то, что прочиталось с диска. В данном случае &fs->win[BS_55AA] — это тоже самое, что и fs->win + BS_55AA.
printf(«FAT signature: %i\r\n»,LD_WORD(&fs->win[0xAA55]));
Может? BS_55AA а не 0xAA55?

tl;dr Либа отличная. Ищи пробщемы у себя.
0
printf(«FAT signature: %i\r\n»,LD_WORD(&fs->win[0xAA55]));

Да действительно ошибся при написании. Но дела не меняет. Знаю что либа отличная.
Тогда получается что строка
if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) 

сравнивает указатель с 0xAA55? А как тогда добыть те 512 байт которые прочитались в fs->win ???

Просто со структурами сильно дел не имел, не пинайте сильно :)
0
Нет. Берётся значение по смещению BS_55AA из считанного сектора и сравнивается с AA55.
Собсна, здесь особой работы со структурами и нет.
Мона переписать так.

char *sector = fs->win;
if( *(WORD*)(sector + 510) != 0xAA55 )
    ....
0
С этим понятно. спасибо :) Буду смотреть дальше. Пример шел вместе с платой, компилится без ошибок и ворнингов. Но не работет почему то. Не работал и до моего вмешательства
0
printf("%2X  %2X\r\n",Read_Buffer[0x1fe],Read_Buffer[0x1ff]);

в ответ получаю AA 55
Сейчас посмотрел в отладчике.
У меня карточка отвечает win[510]=0x55 win[511]=0xAA.
Т.е. наоборот. Посмотри, ты правильных (больших/маленьких) индейцев объявил?
+1
Индейцев я не трогал. Я честно говоря даже не знаю где они. Все что делал с проектом это втыкал принтфы для того что бы отслеживать выполнение функций и что они отвечают. А Bigendian и его младший брат мне на глаза честно говоря не попадались :)
0
ffconf.h

/*---------------------------------------------------------------------------/
/ System Configurations
/----------------------------------------------------------------------------*/

#define _WORD_ACCESS 0 /* 0 or 1 */
/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS
/ option defines which access method is used to the word data on the FAT volume.
/
/ 0: Byte-by-byte access.
/ 1: Word access. Do not choose this unless following condition is met.
/
/ When the byte order on the memory is big-endian or address miss-aligned word
/ access results incorrect behavior, the _WORD_ACCESS must be set to 0.
/ If it is not the case, the value can also be set to 1 to improve the
/ performance and code size. */

Как выставляется в Кейле порядок байт — не знаю, пользуюсь ИАРом.

Все что делал с проектом это втыкал принтфы для того что бы отслеживать выполнение функций и что они отвечают.
Купи STLink, сбережешь кучу времени и моток нервов. Я вот таким обрезком пользуюсь: we.easyelectronics.ru/STM8/proshivka-mk-pri-pomoschi-stm8l-discovery.html#comment25029
0
ща гляну про индейцев. Есть J-Link им собственно и заливаю а режиме SWD
0
Есть J-Link им собственно и заливаю а режиме SWD
А для отладки почему не пользуешь? Гораздо быстрее и проще открыть fs в ней win и найти 510 и 511 элемент массива, ИМХО.
0
SeregaB прав, согласно стандарту win[510]=0x55 win[511]=0xAA. Либо у вас функия SD_ReadBlock байты переворачивает, MBR криво записан.
0
* либо MBR криво записан (что маловероятно).
0
По идее менять порядок байт эта функция не должна, ведь в стандарте чётко оговорен адрес байта и его значение, т.е. по адресу 0x1FE должно всегда cчитываться значение 0x55.

Можно карту каким-нибудь WinHex посмотреть, проверить MBR.
0
printf("%2X  %2X\r\n",Read_Buffer[0x1fe],Read_Buffer[0x1ff]);

Возвращает 55 AA
Все верно. сходится с дампом из WinHex.
В принципе чтение файла я сделал, используя свою собственную огрызок-версию библиотеки. ФАйл читается нормально используя ту же SD_ReadBlock.
Но почему не хочет работать библиотека от чана… вот вопрос. На горизонте проект где нужно будет использовать её в полной мере.
0
А вот этот результат где был?
Если сделать
char Read_Buffer[512];
Status = SD_ReadBlock(Read_Buffer, 0x00, BlockSize);
printf("Status Read: %i\r\n-------------\r\n",Status);          
printf("%2X  %2X\r\n",Read_Buffer[0x1fe],Read_Buffer[0x1ff]);
в ответ получаю AA 55
0
Позволю предположить, что макрос LD_WORD может работать некорректно. Попробуй переопределить его на свой или проверь, не определен ли он в каконить еще месте
0
вот как он выглядит в оригинале
#define	LD_WORD(ptr)		(WORD)(*(WORD*)(BYTE*)(ptr))
0
Теперь могу поспорить в чем ошибка! Товарищ SeregaB был близок. Если у тебя LP_WORD определен как
#define LD_WORD(ptr)            (WORD)(*(WORD*)(BYTE*)(ptr))

то используется
#define _WORD_ACCESS	1	/* 0 or 1 */

Для адекватной работы библиотеки макрос LP_WORD должен распадаться примерно так
(WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))

Такой запор с библиотекой встречал как-то раньше!) Замени определение и протестируй, должно прокатить! Удачи!!!
0
Дополню, что
#define _WORD_ACCESS	1	/* 0 or 1 */
как раз и заставляет распадаться макрос LP_WORD как
(WORD)(*(WORD*)(BYTE*)(ptr))
.
По идее если определено
#define _WORD_ACCESS	0
, то LP_WORD должен быть как
(WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
0
Не уверен, конечно, пусть Geban проверить. Это относится к вопросу порядка байт. Т.к. сигнатура MBR (0xAA55) little-endian, и по-умолчанию данные в контролере хранятся в little-endian, то оба этих варианта дадут одинаковый результат, но при _WORD_ACCESS равной 1 код будет быстрее. Единственное, что может быть, если используется компиляция с big-endian.
0
ну тогда уж это относиться и к выравниванию слов в памяти, что тож может привести в косяк=) Насчет проверить, то только что сам проверил. У мя не пашет=)
0
В STM32 выравнивание на байт, и little endian.
0
Безотносительно возникшей ошибки: почему в макросах избыточные приведения типов? Достаточно же вместо
#define	LD_WORD(ptr)		(WORD)(*(WORD*)(BYTE*)(ptr))
#define	LD_WORD(ptr)		(WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
определить их так
#define	LD_WORD(ptr)		(*(WORD*)(ptr))
#define	LD_WORD(ptr)		(((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr))
Зубры C, прокомментируете?
0
  • avatar
  • John
  • 25 октября 2011, 03:43
Если написано — значит надо.

P.S.: Просто зная постановку задачи могу сказать что у вас и «исходная» версия написана с ошибкой, так что «оптимизированную» не имеет смысла проверять. Будьте внимательны, не только количество скобочек важно, но и их положение.
0
Так я и хочу знать зачем надо, вдруг тоже надо так писать :)
Ткните, где ошибка в исходной версии? Я её просто скопировал из ff.h.
По крайней мере, обе версии компилируются в одинаковый код.
0
#define LD_WORD(ptr)            (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
Если ptr будет не типа указателя на восьмибайтные данные, а скажем указатель на WORD то при выполнении ((ptr)+1) получаем инкремент к величине указателя ни 1 а 2 (адресная арифметика).
Поправьте если я ошибаюсь. может я зря всё через int / char*void* перевожу всё.
0
*восьмибиртые / однобайтные
0
Правы, конечно. Но этот косяк не мой, а в библиотеке. Нужно так, как написал lleeloo.
Но я имею в виду вот что. Например, в макросе
#define LD_WORD(ptr)            (WORD)(*(WORD*)(BYTE*)(ptr))
сначала указатель приводится к типу указателя на байт, затем указатель на байт приводится к типу указателя на два байта. Потом происходит разыменование, получается тип WORD. Затем тип WORD приводится к типу WORD.
Почему нельзя привести сразу к указателю на WORD и зачем повторно приводить к WORD?
0
Это да, есть такое дело. интересно…
0
Действительно ,SeregaB и lleeloo были правы. Дело было в индейцах :)
Пишет и читает. Всем большое спасибо :)
Закончу проект напишу статью :)

С уважением, Евгений.
0
  • avatar
  • Geban
  • 25 октября 2011, 09:24
Что сделали, можете подробнее написать?
0
в ff.h сделал так


#define _MCU_ENDIAN		1
/* Multi-byte word access macros  */

#if _MCU_ENDIAN == 1	/* Use word access */
#define	LD_WORD(ptr)		(WORD)(*(WORD*)(BYTE*)(ptr))
#define	LD_DWORD(ptr)		(DWORD)(*(DWORD*)(BYTE*)(ptr))
#define	ST_WORD(ptr,val)	*(WORD*)(BYTE*)(ptr)=(WORD)(val)
#define	ST_DWORD(ptr,val)	*(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
#elif _MCU_ENDIAN == 2	/* Use byte-by-byte access */
#define	LD_WORD(ptr)		(WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
#define	LD_DWORD(ptr)		(DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr))
#define	ST_WORD(ptr,val)	*( BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)
#define	ST_DWORD(ptr,val)	*(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)
#else
#error Do not forget to set _MCU_ENDIAN properly!
#endif



До этого, когда не работело, стояло
#define _MCU_ENDIAN		2
0
  • avatar
  • Geban
  • 25 октября 2011, 16:50
Это получается, что LD_WORD (_MCU_ENDIAN == 2) работает неправильно, хотя он должен работать независимо от endian.
0
Да есть там какойто касячек=) Не загоняйтесь, просто с этим макросом вопрос уже поднимался вроде!
0
Таки стоило бы разобраться из-за чего этот косячок. Может, баг компилятора или подобная фигня.
0
Вопрос такой (мб и тупой к сожалению(, я сам должен заполнять эти первые 512 байт, или за меня должна это делать операционная система? или они уже заполнены
0
Что вы подразумеваете под
 я сам должен заполнять эти первые 512 байт

Вы должны реализовать низкоуровневые функции чтения/записи устройства.

 или за меня должна это делать операционная система
FATFs - это не операционная система, это просто библиотека для работы с файловой системой.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.