Le schede di memoria MMC/SD hanno diverse modalità di comunicazione (alcune opzionali), in particolare le memorie di tipo SD (Secure Digital) supportano sempre il protocollo SPI ed il protocollo nativo ad 1bit.
La comunicazione avviene attraverso l'invio di diversi comandi, ai quale segue sempre una risposta da parte della memoria.
Per entrare in modalità SPI, è sufficiente portare il segnale CS basso durante l'invio del comando CMD0 (Che porta il dispositivo in Idle Mode).
A questo punto, per terminare l'inizializzazione, è necessario inviare il comando ACMD41 finchè la SD non esce dallo stato Idle (lo status è contenuto nel primo byte di una risposta).
Il comando ACMD41, però, è di tipo Application-Specific perciò esso è sempre preceduto dal comando CMD55.
char SDInit() { char i; char status; TRISCbits.TRISC5 = 0; TRISCbits.TRISC4 = 1; TRISCbits.TRISC3 = 0; SSP1STAT = 0x00; SSP1CON1 = 0x30; // SPI master, Fosc/4 PIR1bits.SSP1IF = 0; SD_CS_TRIS = 0; SD_CS = 1; for(i=0; i < 16; i++) spi(0xFF); // Clock extra per il powerup SD_CS = 0; SDCmd(CMD0, 0); // Invia il comando di reset if (SDWait()!=0x01) goto SDError; i = 20; do { SDCmd(CMD55, 0); SDWait(); SDCmd(ACMD41, 0); // Invia il comando ACMD41 per terminare l'inizializzazione if (i-- < 0) goto SDError; } while ((SDWait()&1) != 0x00); // attende l'uscita dallo stato Idle SD_CS = 1; return TRUE; SDError: SD_CS = 1; return FALSE; }
Con il metodo SDInit viene configurato il modulo SPI e vengono effettuate le operazioni appena descritte per inizializzare la memoria SD.
In questo frammento di codice si fa uso di due metodi: SDCmd serve ad inviare un comando alla scheda, mentre con SDWait si attende la risposta.
char SDCmd(char cmd, unsigned long param) { spi(0xFF); // extra clock spi(cmd); // command spi(param >> 24); spi(param >> 16); spi(param >> 8); spi(param); spi(cmd == CMD0 ? 0x95 : 0xFF); // CRC, obbligatorio solo per CMD0 } char SDWait(void) { unsigned short count = 0xFF; char response; while((response=spi(0xFF)) == 0xFF && --count > 0); // attende una risposta diversa da 0xFF, con timeout return response; }
Infine, il metodo che ci permette di leggere dalla memoria, sfruttando il comando CMD17 è SDReadSector.
La lettura deve avvenire a blocchi di n byte, dove n per default vale 512.
char SDReadSector(unsigned long sector) { int i; char* buf_pt = &sd_buffer[0]; char status; SD_CS = 0; SDCmd(CMD17, (sector * SECTOR_SIZE)); // legge a partire dal byte sector*512 if(SDWait()!=0x00 || SDWait()!=0xFE) //attende prima una risposta al comando, poi attende l'inizio del campo dati { SD_CS = 1; sdPresent = FALSE; // se la lettura fallisce, si suppone che la SD sia stata rimossa return FALSE; } for (i = 0; i < SECTOR_SIZE; i++) buf_pt[i] = spi(0xFF); // read block spi(0xFF); spi(0xFF); // flush CRC SD_CS = 1; return TRUE; }
Il settore letto viene memorizzato in un buffer, che per la sua dimensione deve essere collocato esattamente in due banchi RAM, grazie alla direttiva pragma; inoltre questa zona deve essere dichiarata protetta nel linker, affinchè non venga occupata da altre variabili.
Potete realizzarlo da soli (fai-da-te) scaricando tutti i codici sorgenti (Schema elettrico realizzato con Orcad, lista parti, circuito stampato realizzato con Orcad, file gerber, codice sorgente assembler realizzato su piattaforma Microchip).
Il progetto montato e collaudato è disponibile sul nostro store --> FTPmicro