Je ne vais pas refaire toute la documentation d’une carte SD mais je vous présente tout de suite comment faire pour connecter une carte SD à un microcontroleur. Les codes sont utilisables directement avec XC8 de Microchip.
Cette article portera sur le hardware pour connecter physiquement la carte au micro et sur la partie software. A ce propose, c’est une libraire extra-light que je vous propose car elle a besoin d’environ 600 octets de RAM.
Le Hardware
La carte SD fonctionne sous une tension max de 3.3V et utilise un port SPI. Il arrive que le PIC (ou le micro) soit alimenté en 5V (si si ça arrive encore) et que du coup, ça marche pas bien. Soit vous mettez un convertisseur de niveau (level shiffter) soit on le fait simple.
Si on le fait simple, ça donne:
Parler à une carte SD
Et oui, c’est comme pour nous. Avant de se lancer dans un dialogue, il faut connaitre le modèle de carte SD. Et à ce jour il y en a trois (la MMC, la V1 et la V2). Pour le savoir il faut lui poser des questions et en fonction des réponses on le découvre. La séquence est la suivante:
Note : la documentation allégée est dispo ici
Normalement à la fin de cette séquence, la carte est disponible pour demander une lecture ou une écriture (ou tout autre commande mais c’est pas le sujet)
Passons au code
Oui la procédure c’est bien, le code c’est mieux. Je ne vais pas présenter le bus SPI, wikipedia l’a déjà fait. Mais avant de présenter tout le code (qui est plus bas) il faut expliquer un peu le fonctionnement. Pour dialoguer avec la carte SD, il faut lui envoyer des commandes et observer si elle est disponible. Pour cela on a besoin de quelques fonctions et quelques paramètres.
Les commandes
Il y a toute une série de commande qu’on peut envoyer pour effectuer des actions sur la carte SD. Je me suis inspiré de l’article de LUCKY RESISTOR pour l’énumération. Voici la liste des commandes qu’on utilise le plus souvent:
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 |
typedef enum { Cmd_GoIdleState = 0x40, // CMD0 Go idle state. Cmd_SendOpCond = 0x41, //CMD1 Send Op Condition Cmd_SwitchFunc = 0x46, // CMD6 Check switchable function (mode 0) and swtches card function (mode 1) Cmd_SendIfCond = 0x48, // CMD8 Verify SD Memory Card interface operating condition. Cmd_SendCSD = 0x49, // CMD9 Ask to send the card specific data Cmd_SendCID = 0x4A, // CMD10 Ask to send the card identification Cmd_StopTransmission = 0x4C, // CMD11 Stop reading blocks. Cmd_SendStatus = 0x4D, // CMD13 Ask to send the status Cmd_SetBlockLenght = 0x50, // CMD16 Set the block length Cmd_ReadSingleBlock = 0x51, // CMD17 Read one block. Cmd_ReadMultiBlock = 0x52, // CMD18 Read multiple blocks. Cmd_SetBlockCount = 0x53, // CMD23 Set block count Cmd_WriteSingleBlock = 0x58, // CMD24 set address to write data Cmd_WriteMultiBlock = 0x59, // CMD25 write multiple blocks Cmd_WriteCSD = 0x5B, // CMD27 write CSD Cmd_EraseStartBlock = 0x60, // CMD32 Address of the first write block to be erased Cmd_EraseEndBlock = 0x61, // CMD33 Address of the last write block to be erased Cmd_Erase = 0x66, // CMD38 Erase the define range Cmd_AppCmd = 0x77, // CMD55 Defines to the card that the next cmd is an application specific cmd Cmd_GenCmd = 0x78, // CMD56 transfert DATA block or get DATA block Cmd_ReadOCR = 0x7A, // CMD58 Retrieve the OCR register. Cmd_CRCOnOff = 0x7B, // CMD59 Turns the CRC On or off ACmd_SDStatus = 0x4D, // ACMD13 Send the SD Status ACmd_SendOpCond = 0x69 // ACMD41 Sends host capacity support information and activates the card's initialization process. }Command; |
Il y en a d’autre mais déjà là on ne les utilise pas toutes.
Arguments et réponses
Certaines commandes vont nécessiter l’envoi d’arguments et une fois que la carte SD a effectuée son traitement, elle va nous renvoyer les données. Tout cela nécessite 32 bits. Dans cette application, j’ai besoin d’optimiser la place en RAM et je vais donc utiliser la même variable pour les arguments et les données en réponses. Pour me simplifier l’utilisation je vais également construire une structure qui me permettra d’avoir soit un mot de 32bits soit un tableau de 4 octets. La déclaration est faite par:
1 2 3 4 5 6 7 |
typedef union { unsigned long L32bit; unsigned char C8bit[4]; }type_32; /* SD card argument 4octets allocation */ type_32 SDArgument; |
Quand on envoie des commandes, la carte SD retourne également un octet de status. Ce status indique s’il y a eut des erreurs d’adresse, de CRC, de séquences ou si la carte en est attente. Cet octet est stocké dans la variable SDResponse.
1 |
unsigned char SDResponse; |
Etat de la carte
La carte peut être dans différents états et de différents types. Pour que que le code soit un peu structuré, je vais créer un registre qui contiendra les différents flag
1 2 3 4 5 6 7 8 9 10 11 12 13 |
typedef union { unsigned char _Reg; struct { unsigned char FAILED:1; //Flag that an error occurs unsigned char SDV1 :1; unsigned char SDV2 :1; unsigned char MMC :1; unsigned char CCS :1; unsigned char READY :1; unsigned char WP :1; //SD card in Write Protect unsigned char CD :1; //SD card in slot }; }sd_flag; |
Activer et désactiver le bus SPI
Pour que le code soit un peu plus lisible, je vais créer une fonction SPI_SWITCH() qui va permettre d’activer ou de désactiver le bus SPI
1 |
#define SPI_SWITCH(ONOFF) {SSPCONbits.SSPEN = ONOFF;} |