mercredi 8 juillet 2020

ARDUINO : Stockage en Flash SPI



ARDUINO : Stockage en Flash SPI


Lorsque l'on travaille sur un projet ARDUINO et que l'on désire disposer d'un support de stockage de données, le réflexe est souvent le même :
  • EEPROM
  • carte SD
Sur un ESP8266 ou un ESP32 on dispose de beaucoup plus de moyens. Il existe des possibilités de stockage en FLASH :
  • librairie EEPROM
  • système de fichiers SPIFFS
  • système de fichiers FATFS (ESP32 uniquement)

1. Les supports natifs

1.1. ARDUINO

Sur un ARDUINO la taille de la mémoire EEPROM est de :
  • 1Kb sur un ATMEGA328P
  • 4Kb sur un ATMEGA2560
C'est une mémoire I2C assez lente.

1.2. ESP8266

Sur un ESP8266 la librairie EEPROM autorise la création d'un espace de 4096 octets maximum.

Le système de fichiers SPIFFS peut être configuré à l'aide des options de partitionnement (voir menu Outils/Flash Size).
Sur un classique D1 mini possédant 4Mo de FLASH on pourra réserver jusqu'à 3Mo pour le stockage des données.

Cette partition SPIFFS sera plutôt réservée au stockage de fichiers non modifiables, chargés une fois pour toutes à l'aide du menu Outils/Sketch Data Upload :
  • fichiers de paramètres
  • fichiers HTML, CSS, JS, etc.
Il n'est pas interdit à l'application d'y stocker des données mais il faut savoir que le système de fichiers sera écrasé à chaque rechargement des ressources HTML.
Il faut faire un choix. Il sera difficile de combiner les deux.

1.3. ESP32

Sur un ESP32 l'espace EEPROM est réduit à 512 octets maximum.

Le système de fichier SPIFFS ou FATFS peut être configuré à l'aide des options de partitionnement (voir menu Outils/Flash Size).

Comme pour l'ESP8266 la partition SPIFFS sera plutôt réservée au stockage de fichiers non modifiables, chargés une fois pour toutes à l'aide du menu Outils/Sketch Data Upload :
  • fichiers de paramètres
  • fichiers HTML, CSS, JS, etc.
Une partition FATFS sera plutôt réservée au stockage de fichiers générés par l'application :
  • fichiers logs
  • fichiers de données divers (base de données clients par exemple)
Gros avantage par rapport à l'ESP8266, il est possible d'adopter un schéma de partition combinant les deux systèmes de fichiers SPIFFS et FATFS.
Il sera ainsi possible de séparer les ressources HTML et les fichiers de données.

2. Les supports additionnels

2.1. La carte SD

Dans tous les cas, un support de carte SD sera capable de stocker beaucoup plus de données que l'EEPROM d'un ARDUINO ou la FLASH d'un ESP8266 ou ESP32.

Par contre il faut bien reconnaître que la fiabilité n'est pas toujours au rendez-vous. Outre le fait que certaines SD refusent de fonctionner, il arrive que certains modèles soient source de problèmes.
Voir le paragraphe 9.3. Générer le JAVASCRIPT  à l'aide d'une template sur SD

Après de multiples déboires avec d'autres modèles, j'ai retenu les Sandisk Extreme SDHC (le prix tourne aux alentours de 10€ pour une 16Go).

2.2. L'EEPROM I2C

Les EEPROM I2C ont des capacités faibles et sont lentes. On trouvera des composants allant jusqu'à 256Kbits (32Ko). C'est peu.

2.3. La FRAM

La mémoire FRAM offre des capacité allant jusuqu'à 4Mbits (512Ko). Son prix est très élevé : une trentaine d'euros.

2.4. La flash SPI

Il s'agit du même type de FLASH que sur un ESP8266 ou un ESP32. Ces composants à 8 pattes ont des capacités allant de 512Kbits (64Ko) à 2Gbits (256 Mo).

Comme on peut le voir le choix est large et peut sérieusement augmenter l'espace de stockage d'un ARDUINO ou même d'un ESP32.
Les capacités sont bien moindres que celles des cartes SD mais cela reste tout de même très intéressant.

Il existe des modèles CMS, DIP et même des modules :
Un module comme celui-ci équipé d'une W25Q128 coûte 2€, loin du prix d'une SD de qualité.
Quand à la version CMS, on peut trouver des lots de 10 pièces pour un peu plus de 6€ :
Une W25Q16 en bôtier DIP coûte 4€ les 10 pièces, de quoi stocker 2Mo de fichiers pour 40 centimes.
Ce composant est directement utilisable sur breadboard :
Autre avantage, leur faible consommation : 4mA en activité et 1μA au repos.

J'ai choisi de tester le modèle WINBOND W25Q128 (16Mo).

3. Le câblage


Ces mémoires sont des modèles 3.3V. elles nécessitent une adaptation de niveau :

Sur un ESP8266 ou un ESP32 les résistances ne seront pas nécessaires.

4. Les librairies

Il existe différentes librairies :

SPIMemory de Marzogh :

SPIFlash de LowPowerLab :

Ces deux librairies ont une interface analogue à celle de la librairie EEPROM, rien de bien intéressant sauf si l'on envisage de stocker des données sans notion de fichiers, à des adresses connues.

4.1. La librairie AdaFruit

https://github.com/adafruit/Adafruit_SPIFlash

La librairie AdaFruit repose sur la librairie SDFat. Elle hérite donc de l'inerface de cette dernière. Il faut une quantité de mémoire RAM d'au moins 5Ko pour pouvoir l'utiliser. Elle n'est donc pas adaptée aux petits ARDUINOs.

Le programme exemple SdFat_Format requiert 10.5Ko de mémoire RAM, donc une carte MEGA sera incapable de formater la FLASH.

Il faut adopter une ARDUINO DUE pour formater la FLASH, ensuite elle pourra être utilisée sur une MEGA. Cela suppose une organisation lourde, et la MEGA est une carte sans grand intérêt que je n'utilise quasiment jamais.

Quelques remarques pour ceux qui voudraient néanmoins tenter l'expérience :

Dans le fichier Adafruit_SPIFlashBase.cpp la liste des devices supportés ne contient pas la W25Q128. Je l'ai ajouté :

static const SPIFlash_Device_t possible_devices[] = {
    // Main devices used in current Adafruit products
    GD25Q16C,
    GD25Q64C,
    S25FL116K,
    S25FL216K,

    // Only a handful of production run
    W25Q16FW,
    W25Q64JV_IQ,
    W25Q128JV_SQ,

    // Nordic PCA10056
    MX25R6435F,

    // Other common flash devices
    W25Q16JV_IQ,
};

Pour info la liste complète des FLASH se trouve dans le fichier flash_devices.h.
Il est parfaitement possible d'ajouter des descripteurs. La W25Q128 est définie comme suit :

#define W25Q128JV_SQ                                                           \
  {                                                                            \
    .total_size = (1 << 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,                                               \
  }

Ajouter la W25Q256 ou W25Q512 ne devrait pas être bien complexe. Il suffit de lire la datasheet.

Pour une W25Q256 :

    .memory_type = 0x40, .capacity = 0x19, .max_clock_speed_mhz = 133,         \

Pour une W25Q512 :

    .memory_type = 0x71, .capacity = 0x19, .max_clock_speed_mhz = 133,         \

J'ai tout de même essayé de formater la FLASH avec un ARDUINO DUE :

Adafruit SPI Flash FatFs Format Example
Flash chip JEDEC ID: 0xEF4015
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This sketch will ERASE ALL DATA on the flash chip and format it with a new filesystem!
Type OK (all caps) and press enter to continue.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Creating and formatting FAT filesystem (this takes ~60 seconds)...
Setting disk label to: EXT FLASH
Formatted flash!
Error, failed to mount newly formatted filesystem!

Comme on le voit l'identifiant 0xEF4015 (W25Q16) est bien lu, donc la communication SPI se fait bien, mais la suite échoue. Une W25Q128 donne le même résultat.

Abandon de la librairie AdaFruit.


4.2. La librairie de Paul Stoffregen

https://github.com/PaulStoffregen/SerialFlash.git

La librairie de Paul Stoffregen offre une interface du type système de fichiers :
  • création ou ouverture d'un fichier
  • écriture / lecture / effacement
  • positionnement
  • parcours de la liste des fichiers
Parmi les inconvénients on peut citer :
  • il n'y a pas de répertoires
  • les fichiers sont créés avec une taille fixe
J'ai décidé d'adopter cette librairie car elle est légère et adaptée aux petits ARDUINOs.

4.2.1. Les fichiers de données

Si l'application crée un fichier pour y stocker des données, la taille fixe est un handicap.
En effet le fichier est créé au départ avec un contenu effacé (une suite de bytes ayant la valeur 0xFF).
Il sera assez difficile d'en faire un fichier de données, à moins d'effectuer pas mal d'opérations manuellement :
  • ouvrir le fichier
  • se positionner à la fin des données écrites précédemment (il faut mémoriser cet index)
  • écrire les données
  • sauvegarder le nouvel index
  • fermer le fichier
Où stocker l'index ? en EEPROM ?
Ce n'est pas d'une élégance démesurée.

Bien entendu il est impossible de réécrire par dessus des données déjà écrites. La mémoire FLASH s'efface par bloc. Si l'on souhaitait réécrire une partie du fichier il faudrait :
  • mémoriser le bloc
  • l'effacer
  • le réécrire en incorporant les nouvelles données
Or sur ce type de FLASH on ne effacer que par blocs de mémoire d'au minum 4Ko (16 pages de 256 octets). Il est donc impossible de sauvegarder un bloc dans la mémoire RAM d'un ARDUINO, à moins qu'il ne s'agisse d'un MEGA2560.

4.2.2. Les fichiers ressources

Nous allons aborder ici la gestion des fichiers ressources :
  • fichiers de paramètres
  • fichiers HTML, CSS, JS
  • fichiers images, logos
  • etc.
Ce cas est plus facile à traiter :
  • création du fichier à la bonne taille
  • ouverture du fichier
  • écriture les données
  • fermeture du fichier
Pour faire cela, que nous manque t'il ?
Il nous faut un logiciel de transfert de fichiers. Il en existe un certain nombre :
  • FTP
  • XMODEM, ZMODEM
  • etc.
Malheureusement ils sont assez peu compatibles avec la taille mémoire d'un ARDUINO.

4.2.3. Le logiciel de transfert

J'ai développé une librairie permettant le transfert de fichiers par le câble USB entre le PC et l'ARDUINO. Elle utilise la librairie de Paul Stoffregen.

Côté PC, un script PYTHON se charge de l'envoi des commandes.

Il est possible de transférer des fichiers texte ou binaires.
A la fin du transfert le contenu du fichier est vérifié.

4.2.3.1. Les commandes

Pour être à même de transférer un fichier il faut charger l'exemple upload dans l'ARDUINO.

Il faut également installer PYTHON 2.7 et PYSERIAL sur le PC.

L'utilitaire serflash.py est un logiciel en ligne de commande.

usage: serflash.py [-h] [-s] [-e] [-l] [-d] [-g] device file [file...]

Les commandes du logiciel PYTHON sont les suivantes :

Option Rôle
device    Port de communication
file
Fichier à transférer
-h
Aide
-s
Affiche la taille de la FLASH en Mo
-b
Affiche la taille d'un bloc en octets
-e
Efface la FLASH
L'effacement d'une W25Q128 prend 40 secondes
-l
Affiche la liste les fichiers dans la FLASH
-d
Affiche un dump du contenu du ou des fichiers
-g récupère de fichier sur le disque (avec l'extension .dump)

Voici des exemples. 3 fichiers (text, bin, big) sont présents dans le répertoire python :

Les 2 premières commandes affichent la taille totale et la taille d'un bloc.
La 3ème commande efface la FLASH.
Les 3 commandes suivantes transfèrent les fichiers.
La 7ème commande liste les fichiers.
La 8ème commande fait un dump des fichiers text et bin.
La 9ème commande récupère les fichiers text et bin.

$ ./serflash.py -s /dev/ttyUSB1
16 Mbytes (16777216 bytes)
$ ./serflash.py -b /dev/ttyUSB1
65536 bytes
$ ./serflash.py -e /dev/ttyUSB1

$ ./serflash.py /dev/ttyUSB1 text
Uploading 1 file(s) ...
text (26 bytes)
26 bytes sent
End of transfer
verified: OK
Files:
text : 26 bytes
$ ./serflash.py /dev/ttyUSB1 bin
Uploading 1 file(s) ...
bin (17 bytes)
19 bytes sent
End of transfer
verified: OK
Files:
text : 26 bytes
bin : 17 bytes
$ ./serflash.py /dev/ttyUSB1 big
Uploading 1 file(s) ...
big (3026 bytes)
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
128 bytes sent
82 bytes sent
End of transfer
verified: OK
Files:
text : 26 bytes
bin : 17 bytes
big : 3026 bytes
$ ./serflash.py -l /dev/ttyUSB1
Files:
text : 26 bytes
bin : 17 bytes
big : 3026 bytes
$ ./serflash.py -d /dev/ttyUSB1 text bin
text:
0000 61 7a 65 72 74 79 75 69 6f 70 71 73 64 66 67 68    azertyuiopqsdfgh
0010 6a 6b 6c 6d 77 78 63 76 62 6e                                  jklmwxcvbn
bin:
0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10     ................
0010 11                                                 .
$ ./serflash.py -g /dev/ttyUSB1 text bin
text.dump: Done
bin.dump: Done


Sur un PC Windows il faudra remplacer /dev/ttyUSB1 par COMX.

4.2.3.2. Le sketch

La librairie de transfert de fichiers peut être intégrée facilement dans un sketch :

#include <SerialFlash.h>
#include <serial-flash-upload.h>

#define CSPIN 10
#define LED 2 // Optional (can be set to ZERO)

Uploader uploader(CSPIN);

void setup()
{
Serial.begin(115200);
if (uploader.begin(LED) == false) {
Serial.println("ERR_FLASH");
}
}

void loop()
{
if (Serial.available()) {
uploader.shell();
}
}

Le sketch d'exemple upload est peu volumineux.

12216 octets (39%) de l'espace de stockage de programmes.
281 octets (13%) de mémoire dynamique.

Il suffit d'ajouter le code pour ses propres besoins dans les fonctions setup() et loop(), avec les précautions habituelles (pas d'attentes bloquantes, de delay() dans la fonction loop()).

On peut également utiliser le sketch exemple upload uniquement pour le transfert des fichiers, et écrire un sketch sans cette librairie, pour l'application.
Il faudra simplement recharger le sketch upload avant chaque transfert.

On peut enfin disposer de deux cartes ARDUINO, dont une dédiée au transfert, chargée avec le sketch upload. Dans ce cas la FLASH SPI peut être soudée sur une carte amovible.
Pour cet usage les modules du commerce sont particulièrement adaptés :


La librairie SerialFlash n'est pas très gourmande. Un sketch simple d'écriture de données occupe peu de place :

7726 octets (25%) de l'espace de stockage de programmes.
204 octets (9%) de mémoire dynamique.

Comparativement un sketch exemple ReadWrite de la librairie SDFat occupe :

9374 octets (30%) de l'espace de stockage de programmes.
1057 octets (51%) de mémoire dynamique

La librairie SerialFlash de SerialFlash est donc nettement plus économique en RAM que la librairie SDFat.


5. Téléchargements

Ce projet est téléchargeable ici :


6. Conclusion

Les FLASH SPI me permettront à l'avenir de remplacer avantageusement les cartes SD, et ceci pour un coût très avantageux.


Cordialement
Henri

6 commentaires:

  1. Bonjour, concernant la RAM nécessaire pour modifier un bloc, j'ai pris le parti de réserver un bloc de la mémoire flash comme zone de transit. Plutôt que de copier en RAM le bloc à effacer, je me copie dans cette zone de 4K, que je modifie à la volée avant de la recopier à l'emolacement cible.
    C'est de ma gymnastique, mais ça marche avec un Pro Mini ;-)

    RépondreSupprimer
  2. Très intéressant ... Si l'on veut stocker des tableaux hexadécimaux pour faire de l'animation sur un Oled, quand est-il de la vitesse de lecture, c'est acceptable ou il faut passer son chemin?

    RépondreSupprimer
    Réponses
    1. Un ARDUINO 16Mhz pourra adopter une vitesse SPI de 8Mbit/s, donc 1Mbyte/s. Il serait étonnant que cela ne convienne pas à une animation, dont la vitesse doit de toutes façons être adaptée à l’œil humain.

      Supprimer
  3. Bonjour,
    Article très intéressant pour un projet en cours afin de remplacer ma carte micro SD.
    Est il possible d'utiliser le format CSV et de le récupérer sur le PC ?
    (pour des données de capteurs)

    RépondreSupprimer
    Réponses
    1. La question n'a pas de sens. Un fichier est un fichier. A partir du moment où l'on est capable de transférer un fichier texte ou binaire, tous les types de fichiers peuvent être pris en charge.

      Supprimer