Interfaçage SPI d’une mémoire NOR Flash

J’ai compris quelques trucs dans l’utilisation de la librairie Adafruit_SPIFlash.

Premièrement, on peut créer une interface SPI avec les commandes suivantes :

SPIClass SPI_2(NRF_SPIM0, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI);

Le nrf52840 a 4 interfaces SPI, NRF_SPIM0-3. On peut déclarer les pins correspondantes, MISO, SCK et MOSI. Si tout est branché correctement, on peut envoyer la commande 9F qui permet d’imprimer le JEDEC ID (4 bytes suivant la commande) :

#include <SPI.h>

#define CS_PIN 6

SPIClass SPI_2(NRF_SPIM0, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI);

void setup() {
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
  Serial.begin(115200);
  while(!Serial) delay(100);

  SPI_2.begin();
  SPI_2.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); //10MHz
  digitalWrite(CS_PIN, LOW);
  Serial.println(SPI_2.transfer(0x9F)); //JEDEC ID
  Serial.println(SPI_2.transfer(0x00));
  Serial.println(SPI_2.transfer(0x00));
  Serial.println(SPI_2.transfer(0x00));
  Serial.println(SPI_2.transfer(0x00));
  digitalWrite(CS_PIN, HIGH);
  SPI_2.endTransaction();
  SPI_2.end();
}

void loop() {

}

Cela imprime les chiffres suivants :

159
239
64
24
0

239 = EFh, 64 = 40h, et 24 = 18h. Le JEDEC est donc 0xEF4018, ce qui correspond bien à celui du datasheet.

Pour utiliser la librairie Adafruit_SPIFlash, il faut se créer un nouveau device SPIFlash_Device. Le manufacturer ID correspond au premier byte du JEDEC, (EFh ici), ensuite la memory type au deuxième (40h ici) et finalement la capacité au troisième (18h ici). Les autres paramètres sont trouvés depuis le datasheet et à partir d’une chip similaire. J’utilise la SPIClass pour lui passer la bonne définition des pins de l’interface, avec le CS aussi. Voici le code complet :

#include <SPI.h>
#include <SdFat.h>

#include <Adafruit_SPIFlash.h>

#define CS_PIN 6

SPIFlash_Device_t const W25Q128JVSIQ{
  .total_size = (1UL << 24), /* 16 MiB */                                    
  .start_up_time_us = 5000, 
  .manufacturer_id = 0xef,                     
  .memory_type = 0x40, 
  .capacity = 0x18, 
  .max_clock_speed_mhz = 133,         
  .quad_enable_bit_mask = 0x02, 
  .has_sector_protection = false,              
  .supports_fast_read = true, 
  .supports_qspi = true,                         
  .supports_qspi_writes = true, 
  .write_status_register_split = false,        
  .single_status_byte = false, 
  .is_fram = false,                             
};

SPIClass SPI_2(NRF_SPIM0, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI);
Adafruit_FlashTransport_SPI flashTransport(CS_PIN, SPI_2);
Adafruit_SPIFlash flash(&flashTransport);

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(100); // wait for native usb
  }

  Serial.println("Adafruit Serial Flash Info example");

  Serial.println(flash.begin(&W25Q128JVSIQ, 1));

  Serial.print("JEDEC ID: 0x");
  Serial.println(flash.getJEDECID(), HEX);
  Serial.print("Flash size: ");
  Serial.print(flash.size() / 1024);
  Serial.println(" KB");
}

void loop() {
  // nothing to do
}

En utilisant cette définition, c’est possible d’utiliser tous les programmes de la librairie, comme SdFat, etc. et créer un système de fichier haut niveau sur la mémoire Flash, sans avoir à repasser par le low level du SPI et des commandes du registre de la mémoire.