Как Вы уже наверное знаете, SDIO — это интерфейс для передачи данных в/из карт памяти.
В этой статье речь пойдет о работе с microSD флеш картой памяти по SDIO, который имеется в контроллере stm32f407vgt6 платы stm32f4 discovery.
Работать с картами флеш памяти можно при помощи SPI интерфейса, чем большинство радиолюбителей и занимались последние пару лет (в следствии чего, если вы поищите как подключить карту памяти к контроллеру, сразу получите результат в виде схемы подключения SPI). Но техника не стоит на месте и у нас в руках не дорогие 32 битные контроллеры, которые имеют на борту модуль специально предназначенный для работы с картами памяти — SDIO, который существенно упрощает и ускоряет работу.
Сначала о контактах.

| MMC Pin |
SD Pin |
miniSD Pin |
microSD Pin |
Имя | I/O | Logic | Описание |
|---|---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
Не используется |
|
|
|
|
|
|
|
OD |
Command, Response |
|
|
|
|
|
|
|
Ground | |
|
|
|
|
|
|
|
|
Power |
|
|
|
|
|
|
|
|
Serial Clock |
|
|
|
|
|
|
|
|
Ground |
|
|
|
|
|
|
|
|
SD Serial Data 0 |
|
|
|
|
nIRQ |
O |
OD |
Не используется (memory cards) Interrupt (SDIO cards) (Negative Logic) |
|
|
|
|
|
|
|
|
Не используется | |
|
|
|
|
|
Зарезервировано | |||
|
|
|
|
|
Зарезервировано |
С картой можно работать по SDIO в режиме однобитной шины данных и 4ех битной (вроде бы уже есть и 8). Далее речь пойдет об одно битной. Никакой принципиальной разницы, кроме количества используемых проводников, это не играет.
Как видно контактов на флешке довольно много. Из них нам кроме питания и земли понадобятся только три.
- CLK — тактирование карты.
- CMD — по этой линии передаются команды.
- DAT0 — линия данных (в случае 4-ех битной шины их будет 4).
Соединяем с контроллером:
- PC8 — SDIO_D0 (DAT0)
- PC12 — SDIO_CK (CLK)
- PD2 — SDIO_CMD (CMD)
Я не использовал никаких подтяжек и все работало нормально. Правда иногда случаются сбои и карта не инициализируется.
Теперь перейдем к принципу работы.
Все черные дела за нас делает модуль SDIO. Мы просто должны знать команды, которые нужно посылать и читать данные.
Передача данных с/на карту неотъемлемо связана с вычислением контрольных сумм. Это происходит при каждой передаче.
Рассмотрим функцию инициализации модуля SDIO и GPIO:
void SD_LowLevel_Init(void) {
uint32_t tempreg;
GPIO_InitTypeDef GPIO_InitStructure;
// GPIOC and GPIOD Periph clock enable
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
//Initialize the pins
GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);
// Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ; // | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11; // раскомментируйте для 4ех битной шины
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// Configure PD.02 CMD line
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
// Configure PC.12 pin: CLK pin
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//Enable the SDIO APB2 Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);
// Enable the DMA2 Clock
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
//Инициализируем SDIO (с начальным тактированием HW_Flow Disabled, Rising Clock Edge, Disable CLK ByPass, Bus Width=0, Power save Disable
SDIO->CLKCR=tempreg;
// Включаем SDIO
SDIO->>POWER = 0x03;
Думаю тут все более менее ясно.
Вот функция передачи команды:
static uint32_t SD_Command(uint32_t cmd, uint32_t resp, uint32_t arg) {
//Response must be:
//0,2:No response (expect cmdsent) ->NORESP
//1:Short Response (expect cmdrend and ccrcfail) ->SHRESP
//3:Long Response (expect cmdrend and ccrcfail) ->LNRESP
//Clear the Command Flags
SDIO->ICR=(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT | SDIO_STA_CMDREND | SDIO_STA_CMDSENT);
SDIO->ARG=arg; //First adjust the argument (because I will immediately enable CPSM next)
SDIO->CMD=(uint32_t)(cmd & SDIO_CMD_CMDINDEX) | (resp & SDIO_CMD_WAITRESP) | (0x0400); //The last argument is to enable CSPM
//Block till we get a response
if (resp==NORESP) {
//We should wait for CMDSENT
while (!(SDIO->STA & (SDIO_STA_CTIMEOUT | SDIO_STA_CMDSENT))) {};
}
else {//SHRESP or LNRESP or R3RESP
//We should wait for CMDREND or CCRCFAIL
while (!(SDIO->STA & (SDIO_STA_CTIMEOUT | SDIO_STA_CMDREND | SDIO_FLAG_CCRCFAIL))) {};
}
//Check to see if the response is valid
//We consider all R3 responses without a timeout as a valid response
//It seems CMDSENT and CMDREND are mutually exlusive. (though I am not sure. Check this later)
if (SDIO->STA & SDIO_STA_CTIMEOUT) {
SD_Panic(cmd, "SDIO: Command Timeout Error\n");
} else if ((SDIO->STA & SDIO_FLAG_CCRCFAIL) && (resp!=R3RESP)) {
SD_Panic(cmd, "SDIO: Command CRC Error\n");
}
return SDIO->STA;
}
Тут уже все обстоит несколько сложнее.
На вход функции подаеться необходимая комана (cmd), идентификатор ответа (указывает ждать ли ответ после этой команды) и аргумент (arg).
Дальнейшая часть кода требует длительного «вкуривания» спецификации на SD карты.
Приведу также код функции инициализации карты (да, да.. её нужно еще и инициализировать).
void SD_Init(void){
//uint32_t data;
uint32_t response;
uint32_t TimeOut=0xFFFF;
uint32_t tempreg;
//CMD0: GO_IDLE_STATE (No Response)
SD_Command(CMD0, NORESP, 0);
//CMD8: SEND_IF_COND //Response to CMD8 is R7. But I will ignore that response
SD_Command(CMD8, SHRESP, 0x000001AA); //Non v2.0 compliant sd's will cause panic here due to the timeout
SD_Response(&response, RESP_R7); //AA is the check pattern. If response does not match with it, execution will be blocked in panic
while (1) {
////Send ACMD41
//CMD55
SD_Command(CMD55, SHRESP, 0); //Note that argument should be RCA. But at this point RCA of SD is 0. (It will be changed after cmd3)
SD_Response(&response, RESP_R1);
//ACMD41 (Response is R3 which does not contain any CRC)
//Second argument in the argument indicates that host supports SDHC. We will check acmd41 response if the SD is SC or HC
SD_Command(ACMD41, R3RESP, (uint32_t) 0x80100000 | (uint32_t) 0x40000000);
SD_Response(&response, RESP_R3);
//Check the ready status in the response (R3)
if ((response >> 31) == 1) { //When card is busy this bit will be 0
//Card is now initialized. Check to see if SD is SC or HC
SDType=(response & 0x40000000) >> 30; //1=HC, 0=SC
break;
} else {
TimeOut--;
if (!TimeOut) {
SD_Panic(ACMD41, "SDIO:ACMD41 Timeout\n");
}
}
}
//Now we are in the Ready State. Ask for CID using CMD2
//Response is R2. RESP1234 are filled with CID. I will ignore them
SD_Command(CMD2, LNRESP, 0);
//Now the card is in the identification mode. Request for the RCA number with cmd3
SD_Command(CMD3, SHRESP, 0);
SD_Response(&response, RESP_R6);
//Read the RCA
RCA=response>>16;
//Now the card is in stand-by mode. From this point on I can change frequency as I wish (max24MHz)
//Use cmd9 to read the card specific information
//Response is R2 with CSI. I will ignore the response
SD_Command(CMD9, LNRESP, (RCA << 16));
//Put the Card in the tranfer mode using cmd7. (I will change the clock spped later together with bus width)
//Bus width can only be changed in transfer mode
SD_Command(CMD7, SHRESP, (RCA << 16));
SD_Response(&response, RESP_R1);
//Change the bus-width with cmd6
//CMD55
SD_Command(CMD55, SHRESP, (RCA << 16)); //Note the real RCA in the argument
SD_Response(&response, RESP_R1);
//ACMD6
SD_Command(ACMD6, SHRESP, 0x02);
SD_Response(&response, RESP_R1);
//Configure SDIO->CLKCr for wide-bus width and new clock
tempreg=0; //Reset value
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//tempreg|=(0x01)<<11; //4 bit Bus Width
// ОСТАВЛЯЕМ ПО УМОЛЧАНИЮ 1бит шины
// биты 11-12 = 00
tempreg|=SDIO_CLKCR_CLKEN; //Clock is enabled
//Keep the rest at 0=> HW_Flow:Disabled, Rising Edge, Disable bypass, Power save Disable, Clock Division=0
//As the clock divider=0 => New clock=48/(Div+2)=48/2=24
SDIO->CLKCR=tempreg;
//Now we can start issuing read/write commands
}
В ходе данной функции карта сбрасывается и настраивается для передачи данных.
После этого я выводил данные с карты в USART stm32.
Вот такой вывод:
0 block:
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 ff
01c0: ff ff 0c ff ff ff 00 08 00 00 00 24 76 00 00 00
01d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa
В нулевом секторе всегда должны быть не нулевые данные.
Прикрепляю проект для IAR.
Не пугайтесь названия. Просто все это «выросло» из проекта для USART.
PS. Основа работы с картой в файлах sdcard.c и sdcard.h







Тема вроде по f4xx, а проект по ссылке для f10x