Dans la première partie de cet article, nous avons vu comment accéder aux données de l’EEPROM de la carte HAT pour le Raspberry Pi. Voyons maintenant ce qu’elles contiennent et comment insérer nos données spécifiques.
Au sommaire :
Le contenu de l’EEPROM
Ce que vous allez lire dans la mémoire de l’EEPROM est structuré en un entête (le header) et des blocs de données (les atoms).
Le header
Le header qui se trouve en début de la mémoire est constitué des éléments suivants :
- – les 4 premiers octets sont les codes ASCII de ‘R-Pi’
- – un octet pour le numéro de version (actuellement seule la version 1 est définie)
- – un octet réservé pour le futur
- – le nombre d’atomes
- – la longueur totale en octets des données
Les atoms
Chaque bloc de données comprend :
- – un identifiant de type
- – le numéro
- – la taille des données + CRC
- – les données proprement dites
- – le CRC selon la norme CCITT16.
Il y a 3 type d’atoms spécifiques, en principe obligatoires, auxquels l’utilisateur peut ajouter ses propres atoms.
L’atom Vendor
Cet atom est identifié par le type 1 (le type 0 est invalide). Les données spécifiques sont dans l’ordre :
- Un identifiant unique de type UUID
- Un numéro de produit
- Un numéro de version
- La longueur de la chaine de caractères vendeur
- La longueur de la chaine de caractère produit
- La chaine de caractères qui décrit le vendeur
- La chaine de caractères qui décrit le produit
L’atom GPIO
Cet atom est identifié par le type 2. Les données spécifiques sont dans l’ordre :
- Un caractère qui décrit la consommation de la carte
- Un caractère pour décrire si la carte fourni l’alimentation
- 28 caractères, un par GPIO pour spécifier le type des entrées/sorties (utilisé par la carte, pull up ou down…
Les données sont codées sur des bits spécifiques des caractères : voir le lien en référence. Le programme affiche les données brutes en hexadécimal de cet atom. N’ayant pas l’usage je ne suis pas allé plus loin.
L’atom Device Tree
Cet atom est identifié par le type 3.
Il est, d’après les spécifications HAT, obligatoire mais n’est pas présent sur les eeprom que j’ai eu sous la main. Les données de cet atom ont pour but de permettre un support éventuel du matériel par Linux. N’ayant pas l’usage le programme ne traite pas ce type.
L’atom Custom
Cet atom est identifié par le type 4. L’utilisateur stocke ses données dans la zone data d’un atom générique. On peut avoir autant d’atoms que souhaité, le programme n’affichant que le premier
Décodage
Le décodage (procedure ParseBuffer) consiste simplement à découper les zones de la mémoire dans des enregistrements structurés pour pouvoir les manipuler aisément. Ci-dessous un extrait des structures et du décodage :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
TheaGPIO = record aType: UInt16; // 0x0002 = GPIO map count: UInt16; // incrementing atom count dLen: UInt32; // length in bytes of data+CRC bank_drive: byte; // bank drive strength/slew/hysteresis, BCM2835 can only set per bank, not per IO // Bits in byte: // [3:0] drive 0=leave at default, 1-8=drive*2mA, 9-15=reserved // [5:4] slew 0=leave at default, 1=slew rate limiting, 2=no slew limiting, 3=reserved // [7:6] hysteresis 0=leave at default, 1=hysteresis disabled, 2=hysteresis enabled, 3=reserved power: byte; // [1:0] back_power 0=board does not back power Pi // 1=board back powers and can supply up to 1.3A to the Pi // 2=board back powers and can supply up to 2A to the Pi // 3=reserved // If back_power=2 high current USB mode is automatically enabled. // [7:2] reserved set to 0 IO: array[0..27] of byte; // 1 byte per IO pin, bits in each byte: // [2:0] func_sel GPIO function as per FSEL GPIO register field in BCM2835 datasheet // [4:3] reserved set to 0 // [6:5] pulltype 0=leave at default setting, 1=pullup, 2=pulldown, 3=no pull // [ 7] is_used 1=board uses this pin, 0=not connected and therefore not used crc16: UInt16; end; |
Et pour représenter la collection d’atoms :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
TAtoms = record Header: TheHeader; Vendor: TheaVendor; GPIO: TheaGPIO; DeviceTreeBlob: TheaCustom; Customs: array of TheaCustom; end; procedure THat_eeprom_ctlr.ParseBuffer; // set the atoms from content var Offset, pLen, dLen: UInt32; i, aType, Count: UInt16; begin ResetAtoms; try FLastError:= ''; // check signature offset:= 0; pLen:= 4; Move(FeBuf[0], FAtoms.Header.signature, pLen); if FAtoms.Header.signature <> 'R-Pi' then begin FAtoms.Header.numAtoms:= 0; FAtoms.Header.eepLen:= 0; Exit; end; // set Header Offset:= pLen; FAtoms.Header.version:= byte((@FeBuf[Offset])^); Offset:= Offset + SizeOf(FAtoms.Header.version); FAtoms.Header.reserved:= byte((@FeBuf[Offset])^); Offset:= Offset + SizeOf(FAtoms.Header.reserved); FAtoms.Header.numAtoms:= UInt16((@FeBuf[Offset])^); Offset:= Offset + SizeOf(FAtoms.Header.numAtoms); FAtoms.Header.eepLen:= UInt32((@FeBuf[Offset])^); Offset:= Offset + SizeOf(FAtoms.Header.eepLen); … |
A partir de là on peut afficher de façon claire les données de notre eeprom :
1 2 3 4 5 6 7 8 |
procedure TfHatEeprom.ShowAtoms; begin tsHeader.TabVisible:= True; eRPi.Text := Eeprom.Atoms.Header.signature; eVersion.Text := IntToStr(Eeprom.Atoms.Header.version); eNumAtoms.Text := IntToStr(Eeprom.Atoms.Header.numAtoms); eELen.Text := IntToStr(Eeprom.Atoms.Header.eepLen); … |
Ce qui donne :
Inversement l’objet implémente la fonction qui permet de passer des atoms au buffer (procedure AtomsToBuffer) pour pouvoir flasher nos données personnalisées.
Duplication
Si vous dupliquez des eeprom identiques utilisez la fonction NewUUID (bouton New UUID) pour attribuer un identifiant unique à chacune de vos cartes HAT.
Création d’un jeu de données
Le programme permet d’initialiser un jeu de données personnalisé avec une structure minimale : Header, Vendor avec vos identifiants, GPIO vierge. Lancez la commande Atoms > > Init atoms, entrez alors votre description de fabricant et du produit et les identifiants produit et version (nombres entiers) :
Il ne reste plus qu’à lancer la commande Atom > > Validate pour que le programme vous génère toutes les données (longueurs, CRC..) et le buffer prêt à être flashé si vous le désirez.
Utilisation opérationnelle
L’objectif d’avoir un outil pour enregistrer des données propres au matériel en respectant le standard HAT est atteint. Le programme eXplorer s’arrête là, pour implémenter vos atoms custom il vous faudra programmer. L’objet permet avec quelques lignes d’utiliser l’EEPROM pour mes besoins. L’exemple ci-dessous montre un usage typique pour enregistrer un couple de données gain + offset de calibration.
Pour initialiser l’EEPROM avec mes données, équivalent de la fonction Init Atom avec en plus mon atom personnalisé :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
type TCal = record Gain: double; Offset: double; end; var rCal: TCal; Eeprom:= THat_eeprom_ctlr.Create(Self, ec32); Eeprom.InitAtoms('OpenAvionics', 'Primary Flight Display', 1, 1); rCal.Gain:= 1.; rCal.Offset:= 0.; Eeprom.AddCustom(rCal, SizeOf(TCal)); Eeprom.AtomsToBuffer; Eeprom.BufferToEprom; |
Pour récupérer mes données :
1 2 3 4 5 6 7 8 |
If not Assigned(Eeprom) then Eeprom.THat_eeprom_ctlr.Create(Self, ec32); Eeprom.GetFromCustom(0, rCal, SizeOf(TCal)); Et pour modifier les valeurs : If not Assigned(Eeprom) then Eeprom.THat_eeprom_ctlr.Create(Self, ec32); rCal.Gain:= 1.5; rCal.Offset:= 3.6; Eeprom.SaveToCustom(0, rCal, SizeOf(TCal)); } |
La suite est à vous
Le programme Hat Eeprom eXplorer reste limité dans ses possibilités, l’objectif étant la mise au point de l’outil. Le source est disponible et les bonnes volontés sont bien venues pour :
- – gérer l’atom GPIO de façon plus conviviale
- – gérer l’atom Tree
- – vérifier la programmation et les CRC
- – gérer l’initialisation d’atom custom personnalisés
- – gérer les atoms customs multiples
- – corriger mes bugs
- – etc.
Vous pouvez télécharger le programme ainsi que ses sources en cliquant sur ce lien.
Cf :
CRC : Cyclic Redundancy Check
UUID : Universal Unique IDentifier
https://github.com/raspberrypi/hats/blob/master/eeprom-format.md