Часть 8: EEPROM — Создаем мини-файловую систему

У ATmega328P есть всего 1 КБ энергонезависимой памяти EEPROM. Этого достаточно, чтобы хранить настройки, но если мы запишем туда структуры данных, мы сможем эмулировать простую файловую систему с метаданными и проверкой целостности.

Проблема стандартного подхода: Мы просто пишем байты в EEPROM. Если мышь случайно изменит один байт, мы потеряем все настройки, и контроллер будет работать неправильно.

Хак: Создаем «суперблок» — структуру в начале EEPROM, которая хранит:

  • Сигнатуру («ARH» — ARDUINOHACKS).
  • Версию прошивки.
  • CRC-контрольную сумму всех данных.
  • Указатели на записи (как в FAT-таблице).

Код мини-файловой системы:

#include <EEPROM.h>
#include <avr/wdt.h>  // Сторожевой таймер для защиты

// Структура суперблока
struct SuperBlock {
    uint8_t signature[3];   // "ARH"
    uint8_t version;
    uint16_t crc;           // Контрольная сумма всех данных
    uint8_t dataSize;       // Общий размер данных
    uint8_t recordCount;    // Количество записей (до 10)
};

// Структура записи (например, настройки датчика)
struct Record {
    uint8_t id;
    uint16_t value;
    uint8_t enabled;
    uint16_t timestamp;
};

// Глобальные переменные
SuperBlock sb;
Record records[10];

// Функция расчета CRC16 (простой, но надежный)
uint16_t calculateCRC(uint8_t *data, uint8_t len) {
    uint16_t crc = 0xFFFF;
    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

// Загрузка данных из EEPROM
bool loadConfig() {
    // Читаем суперблок
    EEPROM.get(0, sb);
    
    // Проверяем сигнатуру
    if (sb.signature[0] != 'A' || sb.signature[1] != 'R' || sb.signature[2] != 'H') {
        return false;  // EEPROM пуст или поврежден
    }
    
    // Читаем все записи
    for (uint8_t i = 0; i < sb.recordCount; i++) {
        EEPROM.get(sizeof(SuperBlock) + i * sizeof(Record), records[i]);
    }
    
    // Проверяем CRC
    uint16_t computedCRC = calculateCRC((uint8_t*)records, sb.recordCount * sizeof(Record));
    if (computedCRC != sb.crc) {
        return false;  // Повреждение данных!
    }
    
    return true;
}

// Сохранение данных в EEPROM
void saveConfig() {
    // Заполняем суперблок
    sb.signature[0] = 'A';
    sb.signature[1] = 'R';
    sb.signature[2] = 'H';
    sb.version = 1;
    sb.recordCount = 5;  // Сохраняем 5 записей
    
    // Вычисляем CRC
    sb.crc = calculateCRC((uint8_t*)records, sb.recordCount * sizeof(Record));
    
    // Сохраняем в EEPROM (с защитой от сбоев питания)
    uint8_t* sbPtr = (uint8_t*)&sb;
    for (uint8_t i = 0; i < sizeof(SuperBlock); i++) {
        EEPROM.write(i, sbPtr[i]);
        wdt_reset();  // Сбрасываем сторожевой таймер при длительной записи
        _delay_ms(2); // Ждем завершения записи
    }
    
    // Сохраняем записи
    for (uint8_t i = 0; i < sb.recordCount; i++) {
        uint8_t* recPtr = (uint8_t*)&records[i];
        for (uint8_t j = 0; j < sizeof(Record); j++) {
            EEPROM.write(sizeof(SuperBlock) + i * sizeof(Record) + j, recPtr[j]);
            _delay_ms(2);
        }
    }
}

void setup() {
    Serial.begin(115200);
    
    if (loadConfig()) {
        Serial.println("Конфигурация загружена успешно!");
        for (uint8_t i = 0; i < sb.recordCount; i++) {
            Serial.print("Запись ");
            Serial.print(i);
            Serial.print(": ID=");
            Serial.print(records[i].id);
            Serial.print(", Value=");
            Serial.println(records[i].value);
        }
    } else {
        Serial.println("EEPROM поврежден! Создаем новые данные...");
        for (uint8_t i = 0; i < 5; i++) {
            records[i].id = i + 1;
            records[i].value = random(1000);
            records[i].enabled = 1;
            records[i].timestamp = millis() / 1000;
        }
        saveConfig();
        Serial.println("Данные сохранены!");
    }
}

void loop() {
    // Ваш основной код здесь
}

Результат: Вы защищены от случайных сбоев. Если данные повреждены, система создаст новые настройки по умолчанию. Отличное решение для промышленных проектов или метеостанций в диких условиях.

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *