Для хранения именованных настроек особого усложнения структур формата данных не потребуется. Примем следующий формат — одна настройка = один файл. К примеру, нам необходимо хранить скорость подключения по UART и допустим, задержку в миллисекундах мигания светодиода. Создадим (в любом текстовом редакторе) в нашей файловой системе файлы UART_SPD.SET и DELAY_BL.SET. Запишем в них значения: соответственно 115200 и 1000.
Чтобы получить эти данные, обратившись к нашей внутренней FAT12 потребуется либо взять стандартную библиотеку HAL FatFs и попытаться ее приспособить под внутреннюю flash. Или пойти более простым и удобным способом — написать свою функцию чтения файловых данных FAT12, что мы и сделаем:
uint8_t f12_read_data (
char *file_name, // имя файла для поиска в формате 11 символов - "NAME TXT"
char **file_data, // здесь будут данные найденного файла
char *file_list, // здесь будет список файлов
uint16_t file_list_size) { // размер для списка файлов
// FAT12 - http://elm-chan.org/docs/fat_e.html
uint16_t BPB_BytsPerSec;
uint8_t BPB_NumFATs;
uint16_t BPB_RootEntCnt;
uint16_t BPB_RsvdSecCnt;
uint16_t BPB_FATSz16;
uint8_t BPB_SecPerClus;
uint16_t BS_BootSign;
uint8_t found=0;
uint16_t cluster_no;
char *fname;
uint32_t SEEK_FAT12_NAMES_OFFSET;
uint32_t SEEK_FAT12_NAMES_OFFSET_END;
uint32_t SEEK_DATA_OFFSET;
uint32_t fs;
// Получим данные FAT12
BPB_BytsPerSec=uint16_t_from_internal_flash(PAGE_ADDR+11);
BPB_NumFATs=uint8_t_from_internal_flash(PAGE_ADDR+16);
BPB_RootEntCnt=uint16_t_from_internal_flash(PAGE_ADDR+17);
BPB_RsvdSecCnt=uint16_t_from_internal_flash(PAGE_ADDR+14);
BPB_FATSz16=uint16_t_from_internal_flash(PAGE_ADDR+22);
BPB_SecPerClus=uint8_t_from_internal_flash(PAGE_ADDR+13);
BS_BootSign=uint16_t_from_internal_flash(PAGE_ADDR+510);
if (BS_BootSign != 0xAA55) { // неверная сигнатура boot сектора FAT
return FAT12_ERR_BAD_FAT;
}
SEEK_FAT12_NAMES_OFFSET = (BPB_RsvdSecCnt+(BPB_FATSz16 * BPB_NumFATs)) * BPB_BytsPerSec; // отступ на начало имен
SEEK_FAT12_NAMES_OFFSET_END=SEEK_FAT12_NAMES_OFFSET +
(((32 * BPB_RootEntCnt + BPB_BytsPerSec - 1) / BPB_BytsPerSec) * BPB_BytsPerSec); // отступ на конец имен
SEEK_DATA_OFFSET= BPB_BytsPerSec * BPB_RsvdSecCnt+ // Boot сектор + резерв
((BPB_FATSz16 * BPB_NumFATs) * BPB_BytsPerSec) + // Размер FAT
BPB_RootEntCnt*32; // Размер имен и хар-к файлов
if (file_list_size > 0) // обнулить строку списка файлов
file_list[0]=0;
while (SEEK_FAT12_NAMES_OFFSET < SEEK_FAT12_NAMES_OFFSET_END) {
fname = char_from_internal_flash(PAGE_ADDR+SEEK_FAT12_NAMES_OFFSET);
cluster_no = uint16_t_from_internal_flash(PAGE_ADDR + SEEK_FAT12_NAMES_OFFSET + 0x1A);
fs = uint32_t_from_internal_flash(PAGE_ADDR + SEEK_FAT12_NAMES_OFFSET + 0x1C);
if ((file_list_size > 0) &&
(strlen(file_list)+20 < file_list_size) &&
(cluster_no > 0)) {
sprintf(file_list,"%s%11s %06d bytes\r\n",
file_list,fname, fs); // дополним полный список файлов
}
for (int i=0;i<9;i++) { // упрощенный поиск по имени файла
if (fname[i] != file_name[i]) {
found=0;
break;
}
found=1;
}
if (found==1)
break;
SEEK_FAT12_NAMES_OFFSET+=0x20;
}
if (found == 0) {
return FAT12_ERR_FILE_NOT_FOUND;
}
if ( *file_data != 0 ) {
*file_data=char_from_internal_flash(PAGE_ADDR+(cluster_no-2) *
(BPB_SecPerClus * BPB_BytsPerSec) + // Размер кластера
SEEK_DATA_OFFSET);
}
return 0;
}
Теперь — получаем значение необходимой настройки:
f12_read_data("UART_SPDSET" /*имена без точек, дополнение по 11 символов - пробелами*/,&fdata, file_list, 200);
В массиве fdata будет находится строка «115200», которую можно перевести в целое используя функцию atoi():
Проект с исходниками можно взять здесь.
На этом — всё. Спасибо за внимание!
Комментарии (12)
IgorPie
06.03.2019 01:16Так понимаю, что к предыдущей статье и к текущей — один репозиторий на гитхабе и он немного оброс чтением однокластерных файлов fat12?
VladimirKuzmin Автор
06.03.2019 10:32Репозиторий один. Насчет кластеров — да. Пишем любые, читаем однокластерные.
besitzeruf
06.03.2019 02:55Простите, но почему выбор пал на FAT12 а не… например NTFS? <sarcasm />
IgorPie
06.03.2019 05:01Fat открытая спецификация и структура диска весьма примитивная. К тому же на все про все выдано 64 сектора по 512 байт, или вроде того. Это даже не дискета в 360 килобайт.
VladimirKuzmin Автор
06.03.2019 10:23Вообще — из-за простоты и доступности решения. Чтобы хранить данные на внутреннем флеше, собственно, вообще никакой файловой системы не требуется. Можно формировать и заливать образы диска 65К с компьютера. Но это не очень удобно пользователям. Файловая система упрощает работу редактирования настроек. Например это можно делать вообще со смартфона. А файловая система fat12 — упрощает работу программиста :)
FGV
06.03.2019 05:31хм. в "упрощенный поиск по имени файла"
found=1;
надо бы вынести перед циклом, иначе все время будет возвращать данные первого попавшегося файла.
Ну и вот это:
if (found==1) {
found=1;
break;
}
это зачем?VladimirKuzmin Автор
06.03.2019 10:13Вы правы, found=1 перед break — остатки от рефакторинга :) Это можно убрать.
А вот выносить перед циклом — не надо.
IgorPie
06.03.2019 15:55+1Собрал исходники из предыдущей статьи под CooCox'ом для stm32f103cbt6, работает.
Спасибо за статью и сорцы, повеселился с флэшкой на 12 килобайт.
Портировалось без проблем, разве что кокос (arm gcc) с разными видами оптимизаций выдает код значительно длиннее 24576 байт, пришлось подвинуть начало «флэшки» с 0x08006000 значительно дальше, иначе при форматировании затиралась сама рабочая программа. Даже при оптимизации «по объему», вышло 36 кило.
Ну и вещи типа «for(int i=...» gcc не любит.VladimirKuzmin Автор
06.03.2019 16:58Пожалуйста. Можно кстати вполне увеличить объем «флешки» — увеличив соответственно количество STORAGE_BLK_NBR c 0x80 до 0xB7. Получим флешку 92К.
(131072(128К ) — 36864(36К) = 94208 = 184 (B7) сектора по 512 байт )
hardex
Форматировать код вам религия не позволяет?
VladimirKuzmin Автор
Есть отличные инструменты, как пример:
www.tutorialspoint.com/online_c_formatter.htm
в т.ч. с различными настройками.
К сожалению воспользоваться не получилось — пытался делать читабельным в HTML