
8.6.2018
Řekněme, že vstup karty CS mám připojený k pinu RC1 mikrokontroleru. V programu si ho označím jako SD_CS. Na počátku je karta neaktivní, SD_CS tedy nastavím na jedna. Dále si připravím proměnnou pro text odesílaný po seriové lince.
#define SD_CS LATCbits.LATC1
SD_CS=1;
BYTE s[50];
Postup při inicializaci sestává z několika kroků. Nejprve se kartě odešle několik dummy bytů (uvádí se minimálně asi 70 cyklů neboli bitů - v ukázce odesílám 20 bytů), kdy je SD_CS stále na jedničce. Tímto se karta přepne do režimu SPI a bude reagovat na zasílané příkazy. Kartu aktivujeme nastavením SD_CS na nulu.
Následuje série příkazů, jejichž cílem je zjistit typ karty. V mém případě je to SDHC a kód níže by měl v proměnné type vrátit hodnotu 3.
BYTE type;
for (BYTE i=0;i<20;i++)
{
spiWriteSD(0xFF);
}
SD_CS=0;
sendSerialString("\nsending CMD0\n");
while ((status=sendCommandSD(CMD0,0))!=0x01)
;
sprintf(s,"status=%d\n",status);
sendSerialString(s);
sendSerialString("\nsending CMD8\n");
if ((status=sendCommandSD(CMD8,0x1AA)) & 0x04)
{
type=1;
}
else
{
unsigned long status2=0;
for (BYTE i=0;i<4;i++)
{
status2=((status2<<8)+spiReadSD());
}
sprintf(s,"status2=0x%lX\n",status2);
sendSerialString(s);
type=2;
}
sprintf(s,"status=%d\n",status);
sendSerialString(s);
sprintf(s,"type=%d\n",type);
sendSerialString(s);
unsigned long arg=((type==2) ? 0x40000000 : 0);
sendSerialString("\nsending ACMD41\n");
int count=0;
while ((status=sendACommandSD(ACMD41,arg))!=0x00)
;
sprintf(s,"status=%d\n",status);
sendSerialString(s);
if (type==2)
{
sendSerialString("\nsending CMD58\n");
sendCommandSD(CMD58,0);
if ((spiReadSD() & 0xC0)==0xC0)
type=3;
for (unsigned char i=0;i<3;i++)
spiReadSD();
sprintf(s,"type=%d\n",type);
sendSerialString(s);
}
Kartě ještě můžu prostřednictvím příkazu číslo 16 sdělit, že chci mít velikost bloku 512 bytů. Taktéž můžu zvýšit rychlost SPI komunikace.
sendSerialString("\nsending CMD16\n");
status=sendCommandSD(CMD16,0x200);
sprintf(s,"status=%d\n",status);
sendSerialString(s);
SSP2CON1bits.SSPM=0b0000;
Výstup programu vypadá po inicializační části následovně:

Víme, že karta je logicky rozčleněna do bloků (sektorů) po 512 bytech. Každý z těchto bloků jednoduše určíme jeho pořadovým číslem počínaje nulou. Při čtení a zápisu je možné pracovat buď s jediným blokem nebo číst/zapisovat do více následných bloků jedním příkazem. Budeme se zabývat pouze první variantou.
Čtení bloku se zahájí odesláním příkazu číslo 17 s argumentem obsahujícím adresu požadovaného bloku. Následně karta zašle byte obsahující hodnotu 0xFE, čímž signalizuje, že další odeslaná data již bude 512 bytů čteného bloku. Načteme tedy těchto 512 bytů a následně ještě dva byty CRC.
BYTE data[512];
void readBlockSD(unsigned long blocknum,BYTE *data)
{
if (sendCommandSD(CMD17,blocknum)!=0x00)
{
while (spiReadSD()!=0x00)
;
}
while (spiReadSD()!=0XFE)
;
for (int i=0;i<512;i++)
data[i]=spiReadSD();
spiReadSD();
spiReadSD();
}
Zápis dat probíhá podobně. Odešleme příkaz číslo 24 společně s číslem bloku, do kterého chceme zapisovat. Dále pošleme byte 0xFE a hned potom 512 bytů dat. Nakonec 2 byty CRC, které nemusíme počítat, stačí poslat hodnotu 0xFFFF. Karta hned poté odešle jeden byte s informací o tom, jak bude naloženo s přijatými daty. Jsou tři možnosti, data budou zpracována, data nebudou zpracována z důvodu chyby CRC nebo chyby zápisu. Informace o bezproblémovém přijetí dat má podobu bytu 0bxxx00101, kde na místě x může být cokoli. Jakákoli jiná hodnota znamená chybu. Dále odešleme příkaz číslo 13 s argumentem 0xFF a čekáme na to, než karta data zapíše neboli než vrátí hodnotu 0xFF.
void writeBlockSD(unsigned long blocknum,BYTE *data)
{
if (sendCommandSD(CMD24,blocknum)!=0x00)
{
while (spiReadSD()!=0x00)
;
}
spiWriteSD(0xFE);
for (int i=0;i<512;i++)
spiWriteSD(data[i]);
spiWriteSD(0xFF);
spiWriteSD(0xFF);
status=spiReadSD();
sprintf(s,"status=%X\n",status);
sendSerialString(s);
sendSerialString("\nsending CMD13\n");
status=sendCommandSD(CMD13,0xFF);
status=spiReadSD();
sprintf(s,"status=%X\n",status);
sendSerialString(s);
while ((status=spiReadSD())!=0xFF)
;
sprintf(s,"status=%X\n",status);
sendSerialString(s);
}
Výstup programu po zápisu dat vypadá takto:

V tomto případě má stavový byte hodnotu 0xE5 neboli 0b11100101, což znamená, že data byla přijata k zápisu.