принимаем файл по USB через съемный носитель

Итак все готово к тому , чтобы реализовать FAT12 ФЛЭШКУ на STM32 (у нас stm32F205VG) . Мы примем файл app.bin , запишем его во внешнюю память AT45DB321E. И самое главное мы отследим как Windows 10 пишет файл на уровне секторов FAT12.

Сначала некоторые нюансы

При удалении с носителя файл на самом деле не удаляется полностью в FAT12, а только помечается на удаление байтом E5.

Также не удаляется содержание самого файла , оно остается разбросанным по секторам (>40). Поэтому рекомендуется делать полное форматирование .

Перед записью конкретной страницы сначала Windows записывает ее полностью нулями. Это похоже проверка , что каждый бит страницы может быть перезаписан.

Дело в том ,что Windows записывая файл пишет , читаем по секторам по одному ей известному алгоритму. Что же для нас будет служить флагом , что файл записан полностью и корректно?

Давайте просто посмотрим на уровне секторов в какой последовательности изменяются данные секторов:

Сначала отформатируем флэшку

При этом в секторах (сектор - это страница 512байт) микросхемы FLASH памяти AT45DB321E у нас пропишется FAT12.

Форматирование надо делать полное, чтобы очистить и содержание всех файлов, разбросанное по секторам.

Также интересно , что Windows для любой даже уже отформатированной правильно флэшки все-равно добавит 3 объекта по известным только ей причинам: 1 каталог System Volume Information и 2 файлов внутри его с GUID. К этому надо отнестись спокойно.

Наша флэшка будет 2200 страниц по 512 бай = 1126400 байт. Для обновления ПО контроллера у которого 1024K Flash более чем достаточно.

фотка 1

Для нашего размера флэшки Windows определит такую структуру FAT12:

Сектор 0 загрузочный сектор
Сектор 1 его (резерв) пустой
Сектор 2 FAT1
Сектор 3 FAT1
Сектор 4 FAT1
Сектор 5 FAT1
Сектор 6 FAT1
Сектор 7 FAT1
Сектор 8 FAT1
Сектор 9 FAT2 - это дубль FAT1 сектора 2
Сектор 10 FAT2
Сектор 11 FAT2
Сектор 12 FAT2
Сектор 13 FAT2
Сектор 14 FAT2
Сектор 15 FAT2
Сектор 16 здесь начинается таблица имен файлов
....
Сектор 48 здесь начинаются данные самих файлов.
.....

Надо понимать , что чуть изменишь размер флэшки и получишь другую структуру.

Номер сектора содержания конкретного файла определяется по значению в секторе 48. У каждого файла в таблице имен файлов (сектор 16) прописывается смещение для таблицы FAT (это начало файла). А в таблице FAT по этому смещению (адресу) расположено значение, что это либо конец файла (0xFFF), либо это адрес(смещение) на следующих адрес (т.е. получается цепочка адресов или еще их называют кластеры). Вот и вся логика.

Показываем как пишется файл app.bin

Обратите внимание у нашего файла есть дата время создания и дата время последнего изменения :

фотка 2

Смотрим только операции записи страниц , и только те когда данные реально изменились, все остальное отфильтровываем ибо траффик огромный для одного маленького файла.

Первое это создание записи в таблице имен (сектор 16):

ГДЕ ДАННЫЕ МЕНЯЛИСЬ ПОКАЗЫВАЕМ ДВУМЯ СТРОЧКАМИ : сначала измененные байты в [xx] и следующая строка ранее существовавший вариант.


      STORAGE_Write_FS [0016]    ReadPage->0 0x0010 [0016] OK  
00002000 :  42   20   00   49   00   6E   00   66   00   6F   00   0F   00   72   72   00  
00002010 :  6D   00   61   00   74   00   69   00   6F   00   00   00   6E   00   00   00  
00002020 :  01   53   00   79   00   73   00   74   00   65   00   0F   00   72   6D   00  
00002030 :  20   00   56   00   6F   00   6C   00   75   00   00   00   6D   00   65   00  
00002040 :  53   59   53   54   45   4D   7E   31   20   20   20   16   00   3D   AD   5A  
00002050 :  8B   50   8B   50   00   00   AE   5A   8B   50   02   00   00   00   00   00  
00002060 : [41] [50] [50] [20] [20] [20] [20] [20] [42] [49] [4E] [20] [18] [10] [E6] [5A] 
00002060 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00002070 : [8B] [50] [8B] [50]  00   00  [E7] [5A] [8B] [50] [05]  00   00   00   00   00  
00002070 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00002080 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00002090 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
000020A0 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  

[41] [50] [50] [20] [20] [20] [20] [20] [42] [49] [4E] имя файла APP.BIN
[20] атрибуты файла = 0010 0000 - архивный
[18] резерв
[10]сотые доли секунды создания файла

[E6] [5A] дата создания файла
E6 5A = 0х5AE6 = 0101 1001 1110 0110
ДЕНЬ : 0 0110 = 6 [1–31] (биты 0..4)
МЕСЯЦ :1 111 = 15 [1-12] (биты 5–8)
ГОД :0101 100 = 44 (биты 9-15) +1980 = 2024
странная дата 06-15-2024

[8B] [50] [8B] [50] это какой-то стандартный флаг

[E7] [5A] время последнего изменения файла
[E7] [5A] время = 5A E7 = 1110 0111 0101 1001 =
СЕКУНДЫ :1 1001 = 25*2 = 50 сек [0..29]*2 (биты0..4)
МИНУТЫ :111 010 = 58 мин. [1..59]( биты 5..10)
ЧАСЫ : 1110 0 = 28 [0..23] ( биты 11..15)
28:58:50 странное время

[8B] [50] дата последнего изменения файла
8B 50 = 0х508B = 0100 0000 1000 1010
ДЕНЬ : 0 1010 = 10 [1–31] (биты 0..4)
МЕСЯЦ :0 100 = 4 [1-12] (биты 5–8)
ГОД :0100 000 = 32 (биты 9-15) +1980 = 2012
странная дата 10-04-2012

[05] 00 адрес начального адреса в таблице FAT (50 00) = 0x0005 , т. е. начальный адрес в FAT = 5.


Делается запись в первый сектор FAT2

STORAGE_Write_FS [0009]    ReadPage->0 0x0006 [0009] OK  


      STORAGE_Write_FS [0009]    ReadPage->0 0x0009 [0009] OK  

00001200 :  F8   FF   FF   FF   FF   FF   FF  [6F]  00  [07] [80]  00  [09] [A0]  00  [0B] 
00001200 :  f8   ff   ff   ff   ff   ff   ff   0f   00   00   00   00   00   00   00   00  
00001210 : [C0]  00  [0D] [E0]  00  [0F]  00  [01] [11] [20] [01] [13] [40] [01] [15] [60] 
00001210 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00001220 : [01] [FF] [0F]  00   00   00   00   00   00   00   00   00   00   00   00   00  
00001220 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00001230 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  

Ранее на соседней странице мы объяснили непростой алгоритм записи адресов FAT12 (триплетами) .

В начале F8 FF FF FF FF FF (FF) не учитываем , это спец. запись F8 FF FF (как бы 2 файла) и еще окончания 1 каталога System Volume Information и 2 файлов внутри его(т.е еще +3). Наш файл начинается с адреса 5.

Выделенная цепочка расшифровывается маской yz Zx XY (применяйте к 3 байтам).

У нас получается (выделено) FF 6F 00 , 07 80 00 , 09 A0 00 = FFF 006 007 008 009 00A. Поубивал бы этого шифровальщика...

Теперь мы знаем цепочку всех адресов страниц содержания файла x0048+(0005)+(0006)+...

Далее делается запись в первую страницу FAT1 , копия страницы первой страницы FAT2 . Приводить ее не будем, она такая же.


Заполняется поле [размер файла] у файла в таблице имен файлов (сектор 8)



      STORAGE_Write_FS [0016]    ReadPage->0 0x0010 [0016] OK  
-------printf_sectors_cmp- 16-----

00002000 :  42   20   00   49   00   6E   00   66   00   6F   00   0F   00   72   72   00  
00002010 :  6D   00   61   00   74   00   69   00   6F   00   00   00   6E   00   00   00  
00002020 :  01   53   00   79   00   73   00   74   00   65   00   0F   00   72   6D   00  
00002030 :  20   00   56   00   6F   00   6C   00   75   00   00   00   6D   00   65   00  
00002040 :  53   59   53   54   45   4D   7E   31   20   20   20   16   00   3D   AD   5A  
00002050 :  8B   50   8B   50   00   00   AE   5A   8B   50   02   00   00   00   00   00  
00002060 :  41   50   50   20   20   20   20   20   42   49   4E   20   18   10   E6   5A  
00002070 :  8B   50   8B   50   00   00   E7   5A   8B   50   05   00  [24] [22]  00   00  
00002070 :  8b   50   8b   50   00   00   e7   5a   8b   50   05   00   00   00   00   00  
00002080 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
Размер файла в байтах 24 22 00 00= 0х00002224 = 8740 байт

Пишется содержание файла по секотрам

Для лучшего вросприятия файл целиком забили 0x33


      STORAGE_Write_FS [0051]    ReadPage->0 0x0033 [0051] OK  all 0xFF  

00006600 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00006600 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00006610 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00006610 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00006620 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00006620 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00006630 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00006630 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00006640 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00006640 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  

....

      STORAGE_Write_FS [0068]    ReadPage->0 0x0044 [0068] OK  all 0xFF  

00008800 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00008800 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00008810 : [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] [33] 
00008810 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00008820 : [33] [33] [33] [33] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] 
00008820 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00008830 : [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] 
00008830 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00008840 : [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] 
00008840 :  ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff   ff  
00008850 : [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] 

конец файла в последнем секторе дозабивается нулями до конца сектора.

Файл расположен последовательно с сектора 0051 до сектора 0068.

Заполняется поле [время последнего изменения] у файла в таблице имен файлов (ОПЯТЬ сектор 16)


      STORAGE_Write_FS [0016]    ReadPage->0 0x0010 [0016] OK  

00002000 :  42   20   00   49   00   6E   00   66   00   6F   00   0F   00   72   72   00  
00002010 :  6D   00   61   00   74   00   69   00   6F   00   00   00   6E   00   00   00  
00002020 :  01   53   00   79   00   73   00   74   00   65   00   0F   00   72   6D   00  
00002030 :  20   00   56   00   6F   00   6C   00   75   00   00   00   6D   00   65   00  
00002040 :  53   59   53   54   45   4D   7E   31   20   20   20   16   00   3D   AD   5A  
00002050 :  8B   50   8B   50   00   00   AE   5A   8B   50   02   00   00   00   00   00  
00002060 :  41   50   50   20   20   20   20   20   42   49   4E   20   18   10   E6   5A  
00002070 :  8B   50   8B   50   00   00  [41] [58]  8B   50   05   00   24   22   00   00  
00002070 :  8b   50   8b   50   00   00   e7   5a   8b   50   05   00   24   22   00   00  
00002080 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00  
00002090 :  00   00   00   00   00   00   00   00   00   00   00   00   00   00   00   00 

Время

[41] [58] время = 58 41 = 0101 1000 0100 0001 =

СЕКУНДЫ : 0 0001 = 1*2 = 2 сек [0..29]*2 (биты0..4)
МИНУТЫ :000 010 = 2 мин. [1..59]( биты 5..10)
ЧАСЫ : 0101 1 = 11 [0..23] ( биты 11..15)

Дата

8B 50 = 0х508B = 0101 0000 1000 1011

ДЕНЬ : 0 1011 = 11 [1–31] (биты 0..4)
МЕСЯЦ : 0 100 = 4 [1-12] (биты 5–8)
ГОД : 0101 000 = 40 (биты 9-15) +1980 = 2020

Итого все правильно 11.04.2020г. 11:02:02

Предварительные выводы

Получается сначала делается заготовка в секторе имен файлов : имя файла, фиксируется ему дата/время изменения и дата создания.
Потом в FAT записывается цепочка адресов всего файла.
Потом записывается размер файла в сектор 8.
Потом записывается содержание файла по секторам >48
Потом записывается ОПЯТЬ дата/время изменения

Например можно сигналом окончания закачки файла использовать время/дату нашей прошивки. То есть надо всегда файлу прошивки устанавливать одну дату / время. Или только время устанавливать.

Но это не есть удобно.

Также выясняется , что порядок записи FAT, таблицы имен файлов и секторов с содержанием файла мягко говоря не совсем последовательно стабильны.

Поэтому будем использовать задержку в виде таймера с периодом примерно 100мс, чтобы отследить, что процесс записи завершен. Следим за записью секторов, в таблице имен файлов появляется наша запись с размером файла больше 0 и включаем таймер, который вызывается допустим 40 раз и если не было ни разу еще одной операции записи (и заодно чтения), то считываем цепочку секотров в FAT , пишет во внутреннюю флэш.

Алгоритм проверки полной закачки файла может быть такой

Отслеживаем начало записи файла

1. Это запись страницы имен файлов 18 : STORAGE_Write_FS [0016] .
Проверяем появление записи 'APP BIN'.
Выставляем флаг в глобальной переменной.
Запоминаем время/дату посл. изменения файла (там странное время/дата).

2. Опять ждем запись страницы имен файлов 18 : STORAGE_Write_FS [0016] .
Проверяем появление в записи 'APP BIN' поля размер файла.
Выставляем флаг в глобальной переменной.

3. Опять ждем запись страницы имен файлов 18 : STORAGE_Write_FS [0016] .
Проверяем изменение в записи 'APP BIN' в поле время файла.
Выставляем флаг в глобальной переменной.

4. Ждем чтобы процесс немного устаканился

5. Проверяем цепочку адресов файла в таблице FAT1.

6. Читаем сам файл и делаем с ним далее все что вам нужно.

То есть весь основной код в анализе записываемой страницы имен файлов.

Кое-какие еще нюансы

Т.к. одна страница (сектор) у нас 512 байт, а номер адреса в FAT 3 байта и 512/3 не делится ровно выясняется , что Windows пишет часть байт триплета в конце одной страницы FAT и продолжает в начале следующей. Это надо учитывать обязательно.

На самом деле STORAGE_Init_FS не самая первая вызываемая функция в обмене с хостом. Оказывается , что STORAGE_Read_FS вызывается раньше для страницы 0, 2 и 9, а потом уже STORAGE_Init_FS.