Перейти к содержанию
    

Ivan.

Участник
  • Постов

    89
  • Зарегистрирован

  • Посещение

  • Победитель дней

    1

Ivan. стал победителем дня 4 марта

Ivan. имел наиболее популярный контент!

Репутация

4 Обычный

Информация о Ivan.

  • Звание
    Частый гость
    Частый гость
  • День рождения 31.10.1979

Контакты

  • Сайт
    Array

Посетители профиля

1 082 просмотра профиля
  1. позволяют, вплоть до 1 бита Спасибо всем, кто помог решить первоначальную задачу
  2. У меня были системы с журналированием событий на MMC, а это минимум 4ГБ. Про настройки - у нас с Вами абсолютно разный подход. и там и там есть преимущества и недостатки. У Вас быстрее (при небольшой структуре), но требует целый сектор для маркера. У меня компактнее, но требует периодической дефрагментации. У Вас для профессионалов, умеющих планировать количество настроек. У меня для требовательных потребителей, которые каждый месяц просят чтото добавить. У Вас для хранения единой структурой. У меня для хранения множества меленьких переменных. У Вас для хранения на одном носителе. У меня на разных. Кстати, Вы писали, что в Вашей системе можно тоже сохранять несколько структур: Т.е. у Вас может быть 2 разных по размеру и наполнению структуры? Допустим сохранили одну структуру, а потом 10 раз вторую и нужно стирать первый сектор? Вы потеряете первую структуру? По одному сектору на каждом носителе (или 2 сектора на одном носителе). Все надежно. У меня 2 копии, а у Вас, как я понял, "скорее всего множество" + целый сектор маркера: В общем это разные системы. хватит их сравнивать
  3. Похожим способом я храню журнал событий, только без кодирования маркеров. Так же циклический безиндексный буфер. Только там размер чистой страницы равен от 1/32 до 1/17 выделенной области, чтобы можно было быстро лопатить гигабайты. Универсальный журнал. 1. ведение циклического журнала различного вида событий; 2. сквозная нумерация события, т.е. при переполнении журнала начальный индекс должен смещаться по мере его затирания; 3. журнал должен быть компактным (упакованным); 4. журнал должен быть самодостаточным, чтобы можно было считать журнал с любого устройства не имея каких то вспомогательных таблиц данных. таких как: номер события 1 - означает перезагрузку устройства; 5. каждое событие должно иметь возможность вставки переменных. например: изменение параметра такого с # на # значение. или сработка при температуре 90°С; 6. подходить как к большим флешкам, так и к маленьким и очень быстро с ними работать; 7. щадить флешку, равномерно используя функцию записи по всему пространству; 8. ну и быть достаточно надежным, чтобы не потерять данные. Для упаковки журнала вместо текста события применяется кодовый идентификатор - типа, код 1 означает перезагрузку устройства. Для этого каждое устройство может передать ту самую таблицу соответствия кодов в текст, чтобы внешняя программа могла сперва считать таблицу кодов, а потом вычитать журнал заменив коды текстами. каждое событие описывается формат строкой по типу printf. например: Изменение параметра \"Адрес устройства\" с #u на #u каждое событие хранит в себе идентификатор события и набор бинарных данных, которые нужно подставить вместо # кодов. формат строка была модифицирована по сравнению с printf под свои задачи. теперь существуют следующие типы значений: #i, #I, #j, #J - вывод знакового целочисленного числа с 1, 2, 4 и 8 байт соответственно; #u, #U, #l, #L - вывод беззнакового целочисленного числа с 1, 2, 4 и 8 байт соответственно; #x, #X, #h, #H - шестнадцатеричное число 1, 2, 4 и 8 байт соответственно; #f, #F - число с плавающей точкой 4 и 8 байт соответственно; Каждый числовой параметр можно модифицировать дополнительными инструкциями, например: #.1i - означает, что целочисленное число должно быть представлено в виде десятичной дроби с 1 знаком после запятой; #/10i - означает, что целочисленное число должно быть поделено на 10; #4X - означает, что число должно быть представлено в шестнадцатеричной системе длинной минимум 4 символа. данные модификации можно комбинировать, например: #.1/10i - поделить на 10 и вывести с одним знаком после запятой. #d - формат даты и времени из 4 байт данных (количество секунд с 2000 года); #c, #C - символ ASCII и UTF-8 соответственно; #s, #S - строка ASCII и UTF-8 соответственно; #e, #E - перечисляемый тип. для перечисляемого типа необходимо перечислить все варианты перечисления: #```None``Even``Odd`e первые 2 кавычки означают, что значение 0 отсутствует если в событии существуют 2 повторяющихся перечисления то их можно упростить: Изменение параметра \"Бит четности интерфейса\" с \"#```None``Even``Odd`e\" на \"#e\" второе значение #e означает, что оно отображается как предыдущее. Также существуют базовые типы отображения: #a, #A - логический (Inactive/Active)(Неактивный/Активный) #b, #B - логический (False/True)(Нет/Да) #o, #O - логический (Off/On)(Выкл/Вкл) это была формат строка. Теперь к самому журналированию: Допустим у нас есть флешка на 10 МБ. запись идет циклически. задача заключается в том, что при старте программы необходимо определить начало и конец журнала, которые находятся где-то в середине флешки. можно записывать в определенное место счетчик. но тогда данная часть флешки быстро выйдет из строя. количество записей в одну страницу возможно в районе 10 - 100 т. раз. точнее не запись, а количество стираний страницы памяти ограничено. Забегая немного вперед - опишу одну единицу записи: Каждая запись хранить следующую информацию: - Порядковый номер события; - Время возникновения события; - Идентификатор события; - Тип события; - Вспомогательные данных; - Проверка валидности записи. Сколько событий в системе может быть? - ну, штук 10, 100, может даже 1000. Можно расставить их по порядку и добавлять порядковый номер по мере их появления в программе. правда появится такая путаница, возможность появления двух событий под одинаковыми номерами. невозможность сортировки по группам. можно вообще применить строковый идентификатор, например: "restart" - но это расточительно. В данном журнале применяется строковый идентификатор сжатый до 3 байт кода путем подсчета hash суммы строкового представления имени события. правда тут возникает вероятность совпадения hash суммы разных событий, но данную проблему можно решить путем проверки совпадений кодов событий один раз при первом старте программы или при выдаче очередного релиза программы. Итак. Теперь упакуем данные в одну запись: Одна запись будет состоять из 8 байт: Байты данных: 7. Crc##0Tp 6. Event### 5. ######## 4. ######## 3. DateTime 2. ######## 1. ######## 0. ######## 4 байта времени события 3 байта идентификатора события 2 бита типа события (0: Info, 1: Warning, 2: Error, 3: Change) 1 бит служебный (0 - указывает, что это событие, 1 - прочие записи) 5 бит CRC записи Другой тип записи. дополнительные данные к событию: 7. Crc##100 6. Data#### 5. ######## 4. ######## 3. ######## 2. ######## 1. ######## 0. ######## В данных записях нигде нет порядкового номера события. я решил не впихивать его в каждое событие, а периодически помечать между событиями следующей записью: 7. Crc##111 6. 00000000 5. 00000000 4. 00000000 3. Index### 2. ######## 1. ######## 0. ######## Пустая запись в которой еще никогда ничего не было записано: 7. 11111111 6. 11111111 5. 11111111 4. 11111111 3. 11111111 2. 11111111 1. 11111111 0. 11111111 так как большинство флешек позволяют писать любой объем данных*, а стирать только постранично - писать в пустую страницу можно не стирая ее предварительно. т.е. мы будем дозаписывать каждое событие не стирая страницу, а стирать только тогда, когда будем производить первую запись в странице. таким образом мы можем писать в страницу памяти гораздо больше раз, чем ее ограничение. ведь ограничение распространяется только на количество стираний страницы. Вернемся к поиску начала и конца журнала. т.е. условные 10 МБ памяти прочитать и пропарсить - это очень длительная операция - я решил разделить память на блоки. например флешку на 20 МБ делим на 20 частей. получается 1 блок = 1 МБ. этот объем может быть практически любым 1 КБ или 100 МБ в зависимости от общего объема. В начале каждого такого блока мы записываем запись со стартовым индексом события в данном блоке, а во всем остальном пространстве блока сами события и данные к ним. при переходе на следующий блок снова ставим метку стартового индекса в данном блоке. как только вся память будет заполнена - мы стираем сразу блок в 1 МБ. Конечно это расточительно, но необходимо для быстрого поиска начала и конца журнала. данный блок по логике программы будет составлять от 1/16 до 1/31 части всей флешки (в зависимости от кратности самой флешки). Теперь при старте программы, компонента журнала пробегает по всем блокам и проверяя в них только первую запись - находит максимальный индекс события. найдя примерный хвост журнала ищем начало журнала. теперь шагаем назад и ищем минимальный индекс блока, который четко совпадает с расчетным индексом. т.е. предыдущий блок должен иметь стартовый индекс равный текущему - размер блока деленный на размер записи. если стартовая запись в блоке испорченная или соответствует ожиданиям - переходим к предыдущей. тем самым мы находим начало журнала, даже если часть данных в них испорчена (что тоже допускается, главное минимизировать вероятность потери всего журнала). Ну и третий шаг - это найти точное положение хвоста журнала внутри блока в 1 МБ. тут пойдем другим путем и методом половинного деления найдем пустую запись. Методом половинного деления мы находим пустую запись всего за 17 чтений записи для размера блока в 1 МБ. ИТОГ: всего за ~80 небольших чтений мы полностью определяем границы журнала для флешки в 32 МБ. для флешки в 1 ГБ - это будет примерно 90 итераций. это займет несколько миллисекунд. Вот так в общем выглядит журнал событий. остается только одна проблема - как очистить целый блок размером в 1 МБ при переходе на очередной блок? это очень долго стирать целый мегабайт. тут система еще хитрее. стираем мы не весь блок, а только те страницы, которые нам потребуются при поиске пустой записи методом половинного деления. т.е. такой обратный метод половинного деления. таким образом при переходе к очередной странице стирания программа сотрет выборочные страницы количество которых не больше 5 - 10 страниц даже для очень больших флешек равномерно размазав время стирания блока по ходу заполнения журнала.
  4. Ладно, Ваша система очень хороша, и моя заслуживает внимания.
  5. а завтра Вам потребовалось добавить переменную в структуру? хана прошлым настройкам? или вы будете добавлять идентификатор версии настроек? и в каждом новом релизе уметь загружать каждую предыдущую версию настроек? В чем же ее масштабируемость? На все переменные. Каждая переменная занимает в среднем по 8 байт *2 копии Не нужно или невозможно? А жирно не будет? Вот к примеру F407: 4 сектора по 16к, а дальше по 128к*. И сколько секторов Вы выделите под настройки? Полагаю максимум 2. И при каждом изменении Вы будете стирать один из двух секторов. А как определить какая из двух копий последняя? счетчик вводить, кто старше, тот и папа? В моем случае 16к мне хватит на 2048 изменений (стандартных параметров по 4 байта) Еще в моем случае количество настроек может быть больше, чем доступный объем носителя. Сохраняются только те настройки, которые были изменены. Допустим у меня есть 50 дискретных входов, каждый из которых можно проинвертировать, сымирировать или перенаправить куда нибудь. Все эти параметры одновременно ни кто настраивать не будет, но возможность изменить и сохранить каждый из них - в моей системе есть. И это не фантазии, у меня реально были такие проекты, где настроек на порядок больше, чем EEPROM в ATMega. Интересно как? Возможно Вы не создавали проекты с тысячами переменных. Калибровку одного температурного сенсора можно и так хранить. Обязательно почитаю
  6. И здесь не обязательно 2 независимых носителя. можно взять 2 сектора на одном носителе А можно краткое описание?
  7. И сколько секторов она занимает? Если Вам потребуется сохранить новую переменную, Вы ее будете писать в следующий сектор или стирать и писать заново в один единственный сектор? В моем случае это по одному-два сектора на каждом носителе. Если стирать - то ее нужно где-то временно хранить. Допустим - это вторая копия, тогда Вы будете каждый раз ее копировать из одной в другую флэшку? А если флэшки разного размера? одна 16к, а втора EEPROM на 2к? Даже просто перезаписать целую страницу - уйдет в десятки раз больше времени, чем дописать новы блок в 8 байт. А ресурс? самое страшное для памяти - это не запись, а стирание страницы.
  8. Описание структуры переменных имеет примерно такой вид: VALUELIST(ValueDescList, // AC, NAME, VALUE, CAPTION, VIEWTYPE, LIMITS... VALUE_DESC(vdNoSaved, currentValue, currentValue, "currentValue", VTDec::vt<3>(), {lacAbsoluteMin, 0_mA}, {lacAbsoluteMax, 24_mA}); VALUE_DESC(vdSavedService, calib4mA, calib4mA, "calib4mA", VTDec::vt<3>(), {lacAbsoluteMin, 2_mA}, {lacAbsoluteMax, 8_mA}); VALUE_DESC(vdSavedService, calib20mA, calib20mA, "calib20mA", VTDec::vt<3>(), {lacAbsoluteMin, 16_mA}, {lacAbsoluteMax, 30_mA}); ... // NAME, OBJ, CAPTION OBJ_DESC(hartParserSlave, hartParserSlave, "Hart HartParserSlave"); OBJ_DESC(variables, variables, "Hart Variables"); OBJ_DESC(identifier, identifier, "Hart Identifier"); ); extern const ValueDescList valueDescList;
  9. Вся компиляция укладывается примерно в 3кБ (на STM32) Вместо PropDesc у меня есть более сложный компонент: ValueDesc. Это уже глобальная структура всех переменных в системе, к которой могут ссылаться протоколы связи, графические меню, хранители параметров, журналы событий. Имеет такую структуру: //3 2 1 0 //Caption# ######## ######## ######## - Caption: Заголовок //Ptr##### ######## ######## ######## - Ptr: Указатель на переменную (объект) //Array### Hash#### ######## ######## - Array: Массив, Hash: Идентификатор //Для переменной //1SEacBit Limits## Bits#### ######## - S: Сохраняемый, Eac: Уровень доступа для изменения, Bit: Стартовый бит, Limits: Количество лимитов, Bits: Размер в битах //Для объекта //0Size### ######## ######## ######## - Size: Размер в байтах // //Viewtype ######## ######## ######## - Viewtype: Указатель на тип отображения (на ValueDescList для объекта) //[ - Лимиты //Bits#Bit MAcAdd## ######## ######## - Bits: Размер переменной в битах, Bit: Стартовый бит, M: Максимальное ограничение, Ac: Максимальный уровень ограничения, Add: Коррекция лимита (не действует, если константа) //Lim##### ######## ######## ######## - Lim: Указатель на переменную ограничения (Константа, если Bit==7, Bits==31) //] Эта структура работает с той же PropSaver, но имеет древовидную систему, проверяет валидность загруженных данных (минимальное, максимальное значение) может иметь сложные зависимости лимитов с гистерезисом, например: var1 не может быть больше var2 - 10. И все это конечно формируется на этапе компиляции, т.е. не занимает ни одного байта кодовой памяти, кроме самой таблицы
  10. Пожалуйста, только не надо трепа, а нафига и что за глупость. Последнюю версию выдать не могу, не потому, что жалко, а потому, что она писалась под специфический TMS контроллер и не оформлена (руки не доходят). Система разделена на 2 части: PropSaver - Занимается записью/чтением данных с виртуальной флэшки (это может быть даже файл в файловой системе) PropDesc - Формирует списки сохраняемых параметров для автоматизации процесса PropSaver //Компонента хранения параметров. //Компонента требует 2 области памяти для надежного хранения данных. //Памяти могут быть разного размера и различной реализации. //Хранение данных производится путем добавления нового блока данных в конец. //При инициализации, компонента проходит по всей памяти и находит конец данных по пустому идентификатору // //Каждый блок данных занимает 4 байта и имеет структуру: //Младший бит ----------- Старший бит //Data#### ######## ######## TpCrc### //Каждый блок данных содержит: //3 байта полезной нагрузки (Data); //2 бита флагов (Tp[0-удалена, 1-Идентификатор, 2-Значение]); //6 бит контрольной суммы (Crc) // //Каждая запись состоит из нескольких блоков данных: //1 блок данных с идентификатором записи //N блоков с данными //Т.е. для хранения 2 байт данных потребуется 8 байт данных: //Id###### ######## ######## 10Crc### //Value### ######## ........ 01Crc### //Для хранения большого объема данных блоки добавляются, например, для хранения 10 байт данных структура будет выглядеть следующим образом: //Id###### ######## ######## 10Crc### //Value### ######## ######## 01Crc### //Value### ######## ######## 01Crc### //Value### ######## ######## 01Crc### //Value### ........ ........ 01Crc### //При сохранении битовых полей данные сохраняются без уплотнения, например, при сохранении 5 бит данных начиная с 4 бита они разместятся следующим образом: //Id###### ######## ######## 10Crc### //....Valu e....... ........ 01Crc### //При чтении/сохранении битовых полей указатель должен быть указан на первый байт данных, а номер бита в диапазоне от 0 до 7 // //При стирании записи в блоке идентификатора стирается тип записи на 00 (это позволяет не перезаписывать всю страницу, а только дозаписать несколько бит данных). //Удаленная запись будет иметь вид: //Id###### ######## ######## 00Crc### //Value### ######## ........ 01Crc### //Даже после удаления данных можно проследить последовательность изменения, пока память не будет дефрагментирована. // //При загрузке параметра компоненте указывается идентификатор параметра и длинна данных в битах, которую необходимо загрузить. //Если цепочка блоков не позволяет загрузить все данные - загрузчик возвращает ошибку. //Благодаря идентификаторам - хранение параметров возможно в произвольном порядке. //Можно сохранять только те параметры, которые били изменены относительно значений по умолчанию, а остальные параметры загружать значениями по умолчанию. //При вводе новых параметров в новых версиях программы - старые параметры остаются доступными и не теряются. // //Последовательность сохранения данных: //1. Сперва производится добавление новой записи в первую память; //1а. Если в этот момент происходит сбой (сброс программы), то при загрузке программы данные будут загружены из второй памяти. //2. Производится стирание одной последней записи до только что записанной (дальнейший поиск совпадений не требуется); //2а. Если в этот момент происходит сбой, то после перезагрузки программы находит новые данные, удаляет все неудаленные блоки данных с таким же идентификатором, после чего дублирует запись во вторую память (тем самым завершает незаконченную процедуру сохранения). //3. Далее производит добавление новой записи во вторую память; //3а. Если в этот момент происходит сбой - компонента выполнит восстановление по пункту 2а. //4. Последним этапом компонента стирает во второй памяти один последний идентификатор до только что сохраненного; //4а. Если в этот момент произойдет сбой - компонента выполнит восстановление по пункту 2а. т.е. данные в первой памяти являются более актуальными. // //Последовательность загрузки данных: //1. Ищет первый с конца соответствующий идентификатор в первой памяти; //2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3. Производит загрузку данных; //3а. Если данные были загружены полностью: //3а1. Ищет такой же идентификатор с конца второй памяти; //3а2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3а3. Сверяет данные с теми, что были загружены из первой памяти; //3а3а. Если данные совпадают: //3а3а1. Возвращает положительный результат. //3а3б. Если данные отличаются: //3а3б1. Сохраняет данные во вторую память; //3а3б2. Возвращает положительный результат не зависимо от того удалось ли сохранить данные во вторую память. //3б. Если не удалось загрузить данные из первой памяти: //3б1. Ищет первый с конца соответствующий идентификатор во второй памяти; //3б2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3б3. Производит загрузку данных; //3б3а. Если данные были загружены полностью: //3б3а1. Сохраняет данные в первую память; //3б3а2. Возвращает положительный результат не зависимо от того удалось ли сохранить данные в первую память. //3б3б. Если не удалось загрузить данные из второй памяти: //3б3б1. Возвращает ошибку загрузки. // //Данные в каждой памяти хранятся асинхронно. //При заполнении одной из памяти она полностью стирается и производится полное копирование данных из резервной памяти в ту, которая только что была очищена. //Перед упаковкой одной памяти в другую компонента рассчитывает сколько места потребуется для добавления новой записи. //Если новая запись не влезает - компонента выполняет 1 из вариантов действий, который был указан при инициализации компоненты: //1. Возврат ошибки записи нового значения; //2. Удаление одной или нескольких самых старых записей, чтобы влезла новая (т.е. самые ранние сохраненные данные могут быть потеряны и при следующем запуске быть загруженными по умолчанию); //3. Удаление целой страницы данных наиболее давней записи; //4. Удаление половины памяти, но не менее одной страницы. В данном варианте блок данных занимает 4 байта, что не подходит для памяти с минимальным блоком данных 8 байт (не помню, по-моему у L4 такая память) В новой реализации я сделал блок данных по 8 байт, в которые входят: //Value### ######## ######## ######## Id###### ######## ######## 10Crc### //Value### ######## ######## ######## ######## ######## ######## 01Crc### Это позволило эффективнее хранить параметры. Первый блок имеет 3-х байтовый идентификатор и 4-х байтовую переменную Каждый последующий блок расширяет данные на еще 7 байт PropDesc //Система сохранения / загрузки списка параметров. //Данная компонента позволяет сформировать список всех полей данных для сохранения, автоматической загрузки всех параметров и восстановления значений по умолчанию при неудачной загрузке. //В список инициализации можно указывать любые поля данных, как глобальных переменных, так и отдельные поля объектов в любом порядке. //Можно указывать как целые структуры данных, так отдельные переменные вплоть до единичных битов. //Компонента позволяет хранить любые объекты размером от 1 бита до 8К байт (в 16 битных системах) и до 512М байт (в 32 битных системах). //Также компонента позволяет хранить массивы данных от 0 до 255 элементов. //Компонента самостоятельно создает идентификатор переменной на основе хеш суммы строкового представления имени параметра. //То есть при указании переменной, которую требуется сохранить компонента преобразует имя переменной в строку, вычислит трехбайтную хеш сумму этой строки и сохранит данные под данным идентификатором. //Это позволяет формировать список в любом порядке, пересортировывать его и вводить новые параметры по мере их появления. //Идентификаторы формируются на этапе компиляции и не расходуют программные ресурсы. Также весь список может быть сохранен в кодовую часть памяти не расходуя оперативную память. //Трехбайтный идентификатор получается своего рода случайным числом. //Вероятность совпадения идентификаторов у разных переменных является достаточно малой для небольших списков порядка 1000 элементов. //Для проверки совпадений идентификаторов и наложения областей памяти существует функция checkOverlap(). //Данную проверку можно выполнять единоразово, перед выпуске релиза и потом исключить из основной программы. //Для восстановления недозагруженных параметров необходимо указать область константной памяти с копией объекта заполненного значениями по умолчанию. //Для эмбеддед архитектуры нет необходимости указывать дефолтный объект, т.к. компонента самостоятельно найдет его в слепке памяти инициализации оперативной памяти. //Если дефолтный объект не указан, или для эмбеддед архитектуры объект находится в области noinit или init0, то при необходимости восстановления он заполняется 0. // //Пример создания списка параметров (в частности пример приведен для эмбеддед архитектуры, где не нужно указывать значения по умолчанию: //Допустим у нас есть некие переменные и объекты: //int16_t var1 = 10; //Единичная переменная имеет значение по умолчанию 10. //int16_t var2[5] = {1, 2, 3, 4, 5}; //Массив переменных из 5 элементов. //int16_t var3; //Переменная var3 не проинициализрована и имеет значение по умолчанию 0. //char str1[10] = "str1"; //Строка из 9 символов и 0 терминатором в конце. //struct Obj { //Структура данных в которой все поля данных проинициализированы значениями по умолчанию. // int16_t field1 = 1; // int8_t field2 = 2; // union { // uint16_t bitFields_1_2 = 0; //Вспомогательное поле для удобства взятия указателя на битовые поля, т.к. данные в структуре могут быть выравнены согласно архитекруте. // struct { // uint16_t bitField1 :5; //Битовое поле размером 5 бит. Битовые поля нельзя инициализировать значениями по умолчанию в месте их объявления. // uint16_t bitField2 :11; //Для этого можно их инициализировать во вспомогательной переменной или constexpr конструкторе. // }; // }; // union { // uint8_t bitArray_4_2 = 0; //Вспомогательное поле для массива битовых полей из 2 элементов по 4 бита каждый. // struct { //Нельзя создавать битовые массивы, для этого представим массив из независимых битовых полей. // uint8_t bitArrayElement0 :4; // uint8_t bitArrayElement1 :4; // }; // }; //} obj1, obj2; //Создадим 2 объекта данной структуры. // //Инициализация списка сохраняемых параметров: //DESCLIST(PropDescList1, //Имя типа списка инициализации. // VARDESC(var1); //Элемент списка, указывающий на переменную var1. хеш сумма вычисляется из строки "var1". // //В случае невозможности загрузки, переменная будет проинициализирована значением 10. // VARDESC(var2); //Элемент списка, указывающий на массив переменных var2. Каждый элемент массива будет сохранена под своим идентификатором. // //Первый элемент будет сохранен под идентификатором "var2.0", второй под "var2.1" и т.д. // VARDESC(var3); //Переменная var3. В случае неудачной загрузки будет заполнена 0 т.к. она не была помещена в область инициализированных переменных. // VARDESC(str1); //Переменная str1 будет сохраняться единым объектом размером 10 байт, а не массивом из 10 переменных т.к. для строковых параметров предусмотрена специальная реализация. // VARDESC(obj1.field1); //Элемент списка указывающий на поле field1 объекта obj1. Переменная будет сохранена под идентификатором "obj1.field1". // BITDESC("obj1.bitField1", &obj1.bitFields_1_2, 0, 5); //Битовое поле bitField1 будет сохранено под идентификатором "obj1.bitField1", которое формируется не на основе имени переменной, а указывается вручную первым аргументом. // //Битовое поле будет размещаться возле переменной obj1.bitFields_1_2 с 0 бита размером 5 бит. // BITDESC("obj1.bitField2", &obj1.bitFields_1_2, 5, 11); //Битовое поле bitField2 будет сохранено под идентификатором "obj1.bitField2". // //Битовое поле будет размещаться возле переменной obj1.bitFields_1_2 с 5 бита размером 11 бит. // BITDESC("obj1.bitArray1", &obj1.bitArray_4_2, 0, 4, 2); //Указатель на массив битовых полей расположенных возле переменной obj1.bitArray_4_2 начиная с 0 бита размером каждого элемента в 4 бита и в количестве 2 элементов. // VARDESC(obj2); //Объект obj2 будет загружаться и сохранятся полностью при изменении любого поля объекта. // VARDESC(obj2.field2); //Элемент указывающий на obj2.field2 бессмысленен, т.к. он ни когда не будет сохранен отдельно, а будет сохраняться целой структурой описанной предыдущей строкой. //); // //Создание списка сохраняемых параметров. //const PropDescList1 propDescList1; //Переменная автоматически разместиться в кодовой памяти для AVR архитектуры, т.к. к структуре применен атрибут PROGMEM. // //Загрузка всех параметров: //propDescList1.load(propSaver1); //компонента загрузит все указанные в списке переменные и восстановит их при необходимости. // //Сохранение отдельных переменных: //propDescList1.save(propSaver1, &obj1.field2); //Компонента найдет какому из элементов списка принадлежит данная память и сохранит ее с помощью объекта PropSaver. //propDescList1.save(propSaver1, &obj2.bitField_1_2, 8); //Компонента найдет какому из элементов списка принадлежит данная память и сохранит ее. // //Данные по адресу obj2.bitField_1_2 начиная с 8 бита принадлежат полю obj2.bitField2 начиная с 5 бита размером 11 бит // //и могли бы быть сохранены отдельно для этой переменной в случае с объектом obj1, // //но объект obj2 описан для сохранения целиком и будет сохранен полностью всем объектом.
  11. Лет 20 назад я придумал интересную систему хранения настроек, и за все время я не потерял ни одного параметра. Конечно за это время она модернизировалась по C++11, флэшки с ECC и флэшки у которых стертое состояние является 0x00, но принцип остался неизменным. Система не занимает ни одного (заранее зарезервированного) байта ОЗУ, кроме стека; Масштабируемая, самовосстанавливающаяся, автоматическая; Работает на любых носителях (с которыми я сталкивался); Журналируемая (по возможности), можно поднять хронологию изменений; Относительно компактная; Щадящая ресурс носителя, увеличивая ресурс в десятки-сотни раз; Устойчивая даже к тому, что если залить совсем другую программу с другими настройками, и даже что-то в ней сохранить, а потом вернуть оригинальную прошивку, то вероятнее всего ничего не потеряется. Если комку интересно - могу описать Из недостатков: иногда может занять длительное время для дефрагментации и тем самым задержать ответ по какому нибудь протоколу. но это решается отложенной обработкой сохранения
  12. Признаю, ошибся, на память формулировал. Конечно же 2 сектора, и желательно на 2-х независимых носителях, например 25 серия и Embedded
  13. При чем тут загрузчик? загрузчик от силы 6к занимает с интерфейсом, протоколом и шифрованием. Проблема в том, что где то нужно хранить настройки, а значит они будут занимать целую страницу, чтобы можно было стирать. Выделять последние 128к - жирно. Использовать нулевой сектор - нельзя, там обязательный старт программы (основной программы или загрузчика - не важно). По этому под настройки просится Sector1, 2 и/или 3. В моем случае это можно решить загрузчиком, размещенным в Sector0, затем настройки, затем основная программа. Но я хотел решить задачу, а не обойти. Могу, но это мне придется делать вручную. А так бы это линковщик сам сделал: не влезла по порядку идущая функция - перескакивает на следующий регион. никакой оптимизации, просто по порядку.
  14. Как же я люблю форумчан за такие великие идеи. Осталось только найти свободные ножки и реализовать на них софтварный I2C. А потом к 1000+ платам приклеить на скочик EEPROM. Вопрос звучал КАК, а не ЗАЧЕМ
  15. Да на самом деле нет, просто хотел идеальный вариант. Вообще к концу разработки в первый сектор ляжет Boot, а App уже куда угодно. Приоритет: 1. Надежность 2. Читаемость исходника 3. Скорость / объем (на выбор)
×
×
  • Создать...