// BOARD >> STM32F103C8T6 DEK KIT V1.0 - سحه1
#include <SPI.h>

#define PIN_CS   PA4    // Chip Select pin (SPI1)
#define LED_TEST PC13   // LED چشمک‌زن هنگام تست
#define LED_OK   PB4    // نتیجه موفق (Active HIGH)
#define LED_ERR  PB3    // نتیجه خطا (Active HIGH)

#define CMD_JEDEC_ID       0x9F
#define CMD_READ_STATUS1   0x05
#define CMD_WRITE_ENABLE   0x06
#define CMD_PAGE_PROGRAM   0x02
#define CMD_READ_DATA      0x03
#define CMD_SECTOR_ERASE   0x20

// مشخصات W25Q32
#define PAGE_SIZE          256
#define SECTOR_SIZE        4096
#define TOTAL_SIZE_BYTES   (4UL * 1024UL * 1024UL)
#define TOTAL_SECTORS      (TOTAL_SIZE_BYTES / SECTOR_SIZE)

void setup() {
  pinMode(PIN_CS, OUTPUT);
  digitalWrite(PIN_CS, HIGH);

  pinMode(LED_TEST, OUTPUT);
  pinMode(LED_OK, OUTPUT);
  pinMode(LED_ERR, OUTPUT);
  digitalWrite(LED_TEST, LOW);
  digitalWrite(LED_OK, LOW);
  digitalWrite(LED_ERR, LOW);

  Serial.begin(115200);
  SPI.begin();
  delay(100);

  Serial.println("\n=== W25Q32 Flash Test ===");

  uint8_t m, t, c;
  readJedecID(&m, &t, &c);

  Serial.printf("JEDEC ID: %02X %02X %02X\n", m, t, c);
  if (m != 0xEF || t != 0x40 || c != 0x16) {
    Serial.println("❌ Unknown Flash or not detected!");
    showError();
    return;
  }

  Serial.println("✅ W25Q32 Detected");
  printFlashInfo();

  const char pattern[] = "STM32 SPI Flash Test Data!";
  char verify[sizeof(pattern)] = {0};

  for (uint32_t i = 0; i < 3; i++) {
    uint32_t addr = i * SECTOR_SIZE;
    Serial.printf("\n[Sector %lu / Addr 0x%06lX]\n", i, addr);

    blinkLed(LED_TEST, 4, 100);  // چشمک زدن حین عملیات

    sectorErase(addr);
    waitForReady();

    writeEnable();
    pageProgram(addr, (uint8_t*)pattern, sizeof(pattern));
    waitForReady();

    readData(addr, (uint8_t*)verify, sizeof(pattern));

    if (memcmp(pattern, verify, sizeof(pattern)) == 0) {
      Serial.println("✅ Write/Read OK");
    } else {
      Serial.println("❌ Data Mismatch!");
      showError();
      return;
    }
  }

  Serial.println("\n✅ All test sectors passed successfully!");
  showSuccess();
}

void loop() {}

// ----------------------
// توابع کمکی
// ----------------------

void blinkLed(uint8_t pin, uint8_t times, uint16_t delayMs) {
  for (uint8_t i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(delayMs);
    digitalWrite(pin, LOW);
    delay(delayMs);
  }
}

void showSuccess() {
  digitalWrite(LED_TEST, LOW);
  digitalWrite(LED_ERR, LOW);
  digitalWrite(LED_OK, HIGH);  // LED موفق روشن شود
}

void showError() {
  digitalWrite(LED_TEST, LOW);
  digitalWrite(LED_OK, LOW);
  digitalWrite(LED_ERR, HIGH); // LED خطا روشن شود
}

void readJedecID(uint8_t *m, uint8_t *t, uint8_t *c) {
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(CMD_JEDEC_ID);
  *m = SPI.transfer(0x00);
  *t = SPI.transfer(0x00);
  *c = SPI.transfer(0x00);
  digitalWrite(PIN_CS, HIGH);
}

void printFlashInfo() {
  Serial.println("--- Flash Information ---");
  Serial.printf("Manufacturer: Winbond (0xEF)\n");
  Serial.printf("Device: W25Q32 (0x4016)\n");
  Serial.printf("Total Size: %lu bytes (%.1f MB)\n", TOTAL_SIZE_BYTES, TOTAL_SIZE_BYTES / 1024.0 / 1024.0);
  Serial.printf("Sector Size: %d bytes\n", SECTOR_SIZE);
  Serial.printf("Page Size: %d bytes\n", PAGE_SIZE);
  Serial.printf("Total Sectors: %lu\n", TOTAL_SECTORS);
}

void writeEnable() {
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(CMD_WRITE_ENABLE);
  digitalWrite(PIN_CS, HIGH);
}

void waitForReady() {
  uint8_t status;
  do {
    digitalWrite(PIN_CS, LOW);
    SPI.transfer(CMD_READ_STATUS1);
    status = SPI.transfer(0x00);
    digitalWrite(PIN_CS, HIGH);
  } while (status & 0x01);
}

void sectorErase(uint32_t addr) {
  writeEnable();
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(CMD_SECTOR_ERASE);
  SPI.transfer((addr >> 16) & 0xFF);
  SPI.transfer((addr >> 8) & 0xFF);
  SPI.transfer(addr & 0xFF);
  digitalWrite(PIN_CS, HIGH);
  Serial.println("Sector Erased");
  waitForReady();
}

void pageProgram(uint32_t addr, const uint8_t *buf, uint16_t len) {
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(CMD_PAGE_PROGRAM);
  SPI.transfer((addr >> 16) & 0xFF);
  SPI.transfer((addr >> 8) & 0xFF);
  SPI.transfer(addr & 0xFF);
  for (uint16_t i = 0; i < len; i++) {
    SPI.transfer(buf[i]);
  }
  digitalWrite(PIN_CS, HIGH);
}

void readData(uint32_t addr, uint8_t *buf, uint16_t len) {
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(CMD_READ_DATA);
  SPI.transfer((addr >> 16) & 0xFF);
  SPI.transfer((addr >> 8) & 0xFF);
  SPI.transfer(addr & 0xFF);
  for (uint16_t i = 0; i < len; i++) {
    buf[i] = SPI.transfer(0x00);
  }
  digitalWrite(PIN_CS, HIGH);
}
