Files
Arduino/14041130/TestLCD/Memory.h
2026-02-19 06:17:44 +03:30

433 lines
11 KiB
C

#ifndef MEMORY_H
#define MEMORY_H
#include <Arduino.h>
#include <SPI.h>
#include "Config.h"
// -------------------- کتابخانه EEPROM داخلی برای STM32 --------------------
#if defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32F4xx)
#include <EEPROM.h>
#endif
// -------------------- متغیرهای خارجی --------------------
extern SPIClass flashSPI;
extern DeviceConfig config;
// -------------------- تنظیمات EEPROM داخلی --------------------
#define EEPROM_CONFIG_START 0 // آدرس شروع در EEPROM
#define EEPROM_SIGNATURE_ADDR 0 // آدرس سیگنچور در EEPROM
#define EEPROM_CONFIG_SIZE sizeof(DeviceConfig)
// -------------------- توابع حافظه SPI Flash --------------------
inline void flashInit() {
pinMode(FLASH_CS, OUTPUT);
digitalWrite(FLASH_CS, HIGH);
flashSPI.begin();
flashSPI.setClockDivider(SPI_CLOCK_DIV4);
delay(100);
}
inline bool flashIsBusy() {
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x05);
uint8_t status = flashSPI.transfer(0);
digitalWrite(FLASH_CS, HIGH);
return (status & 0x01);
}
inline void flashWaitForReady() {
unsigned long start = millis();
while (flashIsBusy()) {
if (millis() - start > 1000) {
Serial1.println("⚠️ Flash wait timeout");
break;
}
delay(1);
}
}
inline bool flashReadBytes(uint32_t addr, uint8_t *data, uint32_t len) {
if (len == 0) return false;
flashWaitForReady();
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x03); // Read command
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
for (uint32_t i = 0; i < len; i++) {
data[i] = flashSPI.transfer(0);
}
digitalWrite(FLASH_CS, HIGH);
return true;
}
inline bool flashWriteBytes(uint32_t addr, uint8_t *data, uint32_t len) {
if (len == 0) return false;
// Write Enable
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x06);
digitalWrite(FLASH_CS, HIGH);
delay(1);
// Page Program
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x02);
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
for (uint32_t i = 0; i < len; i++) {
flashSPI.transfer(data[i]);
}
digitalWrite(FLASH_CS, HIGH);
flashWaitForReady();
return true;
}
inline void flashSectorErase(uint32_t addr) {
// Write Enable
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x06);
digitalWrite(FLASH_CS, HIGH);
delay(1);
// Sector Erase
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x20);
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
digitalWrite(FLASH_CS, HIGH);
flashWaitForReady();
}
inline bool testFlashCommunication() {
Serial1.print("Testing flash communication... ");
// دستور خواندن JEDEC ID
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x9F);
uint8_t manuf = flashSPI.transfer(0);
uint8_t type = flashSPI.transfer(0);
uint8_t capacity = flashSPI.transfer(0);
digitalWrite(FLASH_CS, HIGH);
if (manuf == 0xFF || manuf == 0x00) {
Serial1.println("❌ FAILED (No response)");
return false;
}
Serial1.print("✅ OK (Manuf: 0x");
Serial1.print(manuf, HEX);
Serial1.print(", Type: 0x");
Serial1.print(type, HEX);
Serial1.print(", Capacity: 0x");
Serial1.print(capacity, HEX);
Serial1.println(")");
return true;
}
// -------------------- توابع EEPROM داخلی STM32 --------------------
inline bool eepromIsAvailable() {
#if defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32F4xx)
return true;
#else
return false;
#endif
}
inline bool eepromWriteConfig(const DeviceConfig &cfg) {
if (!eepromIsAvailable()) {
Serial1.println("❌ EEPROM not available on this board");
return false;
}
Serial1.print("Writing config to EEPROM (");
Serial1.print(sizeof(cfg));
Serial1.println(" bytes)...");
uint8_t *data = (uint8_t*)&cfg;
// نوشتن در EEPROM
for (uint16_t i = 0; i < sizeof(cfg); i++) {
EEPROM.write(EEPROM_CONFIG_START + i, data[i]);
}
// ذخیره تغییرات
#if defined(EEPROM_commit)
EEPROM.commit();
#endif
Serial1.println("✅ Config written to EEPROM");
return true;
}
inline bool eepromReadConfig(DeviceConfig &cfg) {
if (!eepromIsAvailable()) {
return false;
}
uint8_t *data = (uint8_t*)&cfg;
// خواندن از EEPROM
for (uint16_t i = 0; i < sizeof(cfg); i++) {
data[i] = EEPROM.read(EEPROM_CONFIG_START + i);
}
// بررسی سیگنچور
if (strcmp(cfg.signature, "CFG") != 0) {
Serial1.println("❌ Invalid signature in EEPROM");
return false;
}
Serial1.println("✅ Config read from EEPROM");
return true;
}
inline bool eepromClearConfig() {
if (!eepromIsAvailable()) {
return false;
}
// پاک کردن با نوشتن 0xFF
for (uint16_t i = 0; i < EEPROM_CONFIG_SIZE; i++) {
EEPROM.write(EEPROM_CONFIG_START + i, 0xFF);
}
#if defined(EEPROM_commit)
EEPROM.commit();
#endif
Serial1.println("✅ EEPROM cleared");
return true;
}
// -------------------- توابع مدیریت کانفیگ هوشمند --------------------
inline void saveConfig() {
Serial1.println("\n💾 SAVING CONFIGURATION");
config.valid = true;
uint8_t buffer[sizeof(DeviceConfig)];
memcpy(buffer, &config, sizeof(DeviceConfig));
bool flashSuccess = false;
bool eepromSuccess = false;
// 1. ذخیره در حافظه SPI Flash (اصلی)
Serial1.println("1. Saving to SPI Flash...");
if (testFlashCommunication()) {
flashSectorErase(CONFIG_ADDRESS);
if (flashWriteBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig))) {
// تأیید
uint8_t verifyBuffer[sizeof(DeviceConfig)];
if (flashReadBytes(CONFIG_ADDRESS, verifyBuffer, sizeof(DeviceConfig))) {
if (memcmp(buffer, verifyBuffer, sizeof(DeviceConfig)) == 0) {
flashSuccess = true;
Serial1.println(" ✅ SPI Flash: Saved and verified");
}
}
}
}
if (!flashSuccess) {
Serial1.println(" ❌ SPI Flash: Failed or not available");
}
else
{
return;
}
// 2. ذخیره در EEPROM داخلی (پشتیبان)
Serial1.println("2. Saving to internal EEPROM...");
eepromSuccess = eepromWriteConfig(config);
if (eepromSuccess) {
Serial1.println(" ✅ EEPROM: Backup saved");
} else {
Serial1.println(" ⚠️ EEPROM: Backup not available");
}
// خلاصه
Serial1.println("\n📊 SAVE SUMMARY:");
Serial1.print(" SPI Flash: ");
Serial1.println(flashSuccess ? "" : "");
Serial1.print(" Internal EEPROM: ");
Serial1.println(eepromSuccess ? "" : "⚠️");
if (flashSuccess || eepromSuccess) {
Serial1.println("✅ Configuration saved successfully");
} else {
Serial1.println("❌ CRITICAL: Could not save config anywhere!");
}
}
inline bool loadConfigFromFlash() {
Serial1.print("Trying to load from SPI Flash... ");
uint8_t buffer[sizeof(DeviceConfig)];
if (!testFlashCommunication()) {
Serial1.println("❌ Flash not responding");
return false;
}
if (!flashReadBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig))) {
Serial1.println("❌ Failed to read from flash");
return false;
}
memcpy(&config, buffer, sizeof(DeviceConfig));
if (strcmp(config.signature, "CFG") != 0) {
Serial1.println("❌ Invalid signature in flash");
return false;
}
config.valid = true;
Serial1.println("✅ Success");
return true;
}
inline bool loadConfigFromEEPROM() {
Serial1.print("Trying to load from internal EEPROM... ");
if (!eepromIsAvailable()) {
Serial1.println("❌ EEPROM not available");
return false;
}
if (!eepromReadConfig(config)) {
Serial1.println("❌ Failed to read from EEPROM");
return false;
}
config.valid = true;
Serial1.println("✅ Success");
return true;
}
inline void createDefaultConfig() {
Serial1.println("Creating default configuration...");
strcpy(config.signature, "CFG");
strcpy(config.deviceId, "");
strcpy(config.serverPhoneNumber, "");
strcpy(config.serverUrl, "https://ghback.nabaksoft.ir");
config.uploadInterval = 5;
config.smsInterval = 10;
config.saveInterval = 60;
config.simType = SIM_UNKNOWN;
config.smsEnabled = false;
config.verified = false;
config.valid = false;
}
inline void readConfig() {
Serial1.println("\n📖 LOADING CONFIGURATION");
bool configLoaded = false;
String source = "";
// استراتژی: اول SPI Flash، اگر نشد EEPROM داخلی
Serial1.println("Strategy: SPI Flash → Internal EEPROM");
// 1. اول از SPI Flash بخوان
if (loadConfigFromFlash()) {
configLoaded = true;
source = "SPI Flash";
// همچنین در EEPROM هم بروزرسانی کن (sync)
//eepromWriteConfig(config);
}
// 2. اگر SPI Flash کار نکرد، از EEPROM داخلی بخوان
else if (loadConfigFromEEPROM()) {
configLoaded = true;
source = "Internal EEPROM (backup)";
// سعی کن دوباره در SPI Flash ذخیره کنی (بازیابی)
Serial1.println("Attempting to restore to SPI Flash...");
uint8_t buffer[sizeof(DeviceConfig)];
memcpy(buffer, &config, sizeof(DeviceConfig));
if (testFlashCommunication()) {
flashSectorErase(CONFIG_ADDRESS);
flashWriteBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig));
Serial1.println("✅ Restored to SPI Flash");
}
}
// 3. اگر هیچ کدام کار نکرد، کانفیگ پیش‌فرض بساز
if (!configLoaded) {
Serial1.println("❌ No valid config found in any memory");
source = "Default";
createDefaultConfig();
saveConfig(); // ذخیره کانفیگ جدید
configLoaded = true;
}
// نمایش نتیجه
Serial1.println("\n📊 LOAD RESULT:");
Serial1.print(" Status: ");
Serial1.println(configLoaded ? "✅ LOADED" : "❌ FAILED");
Serial1.print(" Source: ");
Serial1.println(source);
Serial1.print(" Device ID: ");
Serial1.println(strlen(config.deviceId) > 0 ? config.deviceId : "[Empty]");
Serial1.print(" Verified: ");
Serial1.println(config.verified ? "YES" : "NO");
if (configLoaded) {
Serial1.println("✅ Configuration ready");
}
}
// تابع دیباگ برای نمایش وضعیت حافظه‌ها
inline void memoryStatus() {
Serial1.println("\n🔍 MEMORY STATUS");
Serial1.println("================");
// تست SPI Flash
Serial1.print("SPI Flash: ");
Serial1.println(testFlashCommunication() ? "✅ Available" : "❌ Not available");
// تست EEPROM داخلی
Serial1.print("Internal EEPROM: ");
Serial1.println(eepromIsAvailable() ? "✅ Available" : "❌ Not available");
// وضعیت کانفیگ فعلی
Serial1.print("Config in RAM: ");
Serial1.println(config.valid ? "✅ Valid" : "❌ Invalid");
Serial1.println("================\n");
}
// تابع ریست کامل (برای تست)
inline void resetAllConfig() {
Serial1.println("\n⚠️ RESETTING ALL CONFIGURATION");
// پاک کردن SPI Flash
if (testFlashCommunication()) {
flashSectorErase(CONFIG_ADDRESS);
Serial1.println("✅ SPI Flash erased");
}
// پاک کردن EEPROM
eepromClearConfig();
// ایجاد کانفیگ پیش‌فرض
createDefaultConfig();
saveConfig();
Serial1.println("✅ All configuration reset to defaults\n");
}
#endif // MEMORY_H