Как Вы уже наверное знаете, 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