|
|
|
microSD SDIO, Помогите начать |
|
|
|
Oct 30 2014, 17:11
|
Гуру
Группа: Свой
Сообщений: 2 015
Регистрация: 23-01-07
Из: Москва
Пользователь №: 24 702
|
Коллеги, добрый день.
Пытаюсь подключить microSD карту к интерфейсу SDIO STM32L151RDT Возникла проблема с чтением.
Инициализация проходит. На CMD2 , CMD3 карта отвечает. Посылаю CMD7, получаю ответ с верным CRC Посылаю CMD17 , получаю ответ с верным CRC Перевожу SDIO в режим чтения. Он выдаёт ~40 клоков , после чего выставляет бит ошибки "не получен старт бит" .
Вопрос 1: правильно ли я понимаю, что "старт бит" должен быть выдан картой одновременной по всем линиям D0-D3 ?
Вопрос 2: Вижу по осциллографу, что на линии D0 во время пачки клоков что-то есть, на остальных высокий уровень(они все подтянуты к питанию) . Можно ли предположить, что карта в SPI режиме ?
Вопрос 3 : Если карта на самом деле в SPI режиме, то как она должна попасть в SDIO режим ? Если это происходит путём подачи CMD0 в то время как линия DAT3/CS в высоком уровне, то это условие у меня выполняется, т.к. линии D0-D3 подтянуты к питанию.
Вопрос 4 : Правильно ли я понимаю алгоритм чтения в режиме SDIO ? Проинициализировались - > CMD7 c верным RCA -> CMD17 с одресом -> и карта начинает выдавать данные. Не совсем понятно, надо ли в ответах на CMD7 CMD17 проверять ещё что-то, помимо того что они просто получены ? "карта начинает выдавать данные" - в ответ на клоки SDIO контроллера ? И перед данными выдаёт старт-бит ?
Заранее спасибо !
--------------------
Если у Вас нет практического опыта в данной теме- не вступайте в дискуссию и не пишите никаких теоретических рассуждений! Заранее спасибо !
|
|
|
|
|
Nov 1 2014, 17:34
|
Гуру
Группа: Свой
Сообщений: 2 015
Регистрация: 23-01-07
Из: Москва
Пользователь №: 24 702
|
Разобрался. Выкладываю свой максимально простой код. Надеюсь что кому-нибудь поможет. Кое-где есть функции операционной системы, ожидающие прерывания от выставления флагов в регистре состояния SDIO. Их легко заменить простой проверка этого регистра в бесконечном цикле, прерывание при этом надо отключить.
Ноги процессора настраиваются так. Принципиально, что на всех подтяжка к + //////////////////// void SD_INTERFACE_INIT(void) { //включаем тактирование порта RCC->AHBENR |= RCC_AHBENR_GPIOCEN; RCC->AHBLPENR|=RCC_AHBLPENR_GPIOCLPEN;
RCC->AHBENR |= RCC_AHBENR_GPIODEN; RCC->AHBLPENR|=RCC_AHBLPENR_GPIODLPEN; __dsb(15); //скорость порта максимальная GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8|GPIO_OSPEEDER_OSPEEDR9|GPIO_OSPEEDER_OSPEEDR10|GPIO_OSPEE DER_OSPEEDR11| GPIO_OSPEEDER_OSPEEDR12; GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2; //SD1 SDIO GPIOC->MODER &= ~GPIO_MODER_MODER8; GPIOC->MODER |= GPIO_MODER_MODER8_1; GPIOC->MODER &= ~GPIO_MODER_MODER9; GPIOC->MODER |= GPIO_MODER_MODER9_1; GPIOC->MODER &= ~GPIO_MODER_MODER10; GPIOC->MODER |= GPIO_MODER_MODER10_1; GPIOC->MODER &= ~GPIO_MODER_MODER11; GPIOC->MODER |= GPIO_MODER_MODER11_1; GPIOC->MODER &= ~GPIO_MODER_MODER12; GPIOC->MODER |= GPIO_MODER_MODER12_1; GPIOD->MODER &= ~GPIO_MODER_MODER2; GPIOD->MODER |= GPIO_MODER_MODER2_1; GPIOC->PUPDR &=~ GPIO_PUPDR_PUPDR8; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR8_0; GPIOC->PUPDR &=~ GPIO_PUPDR_PUPDR9; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR9_0; GPIOC->PUPDR &=~ GPIO_PUPDR_PUPDR10; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR10_0; GPIOC->PUPDR &=~ GPIO_PUPDR_PUPDR11; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR11_0; GPIOC->PUPDR &=~ GPIO_PUPDR_PUPDR12; GPIOC->PUPDR |= GPIO_PUPDR_PUPDR12_0;
GPIOD->PUPDR &=~ GPIO_PUPDR_PUPDR2; GPIOD->PUPDR |= GPIO_PUPDR_PUPDR2_0; GPIOC->AFR[1]|=(12<<0); GPIOC->AFR[1]|=(12<<4); GPIOC->AFR[1]|=(12<<8); GPIOC->AFR[1]|=(12<<12); GPIOC->AFR[1]|=(12<<16); GPIOD->AFR[0]|=(12<<8); }
SDIO настраивается так. Включены прерывания по всем важным флагам по итогам передачи команды и данных. PLL настроен так, что частота процессора 32МГц, промежуточная частота PLL, от которой тактируется SDIO, 48МГц
//////////////////////////настраиваем SDIO для SD /////////////////////////////////////////////////////// void SD_SDIO_INIT(void) { RCC->APB2ENR|=RCC_APB2ENR_SDIOEN; // включаем тактирование RCC->APB2LPENR|=RCC_APB2LPENR_SDIOLPEN;
RCC->APB2RSTR|=RCC_APB2RSTR_SDIORST; RCC->APB2RSTR&=~RCC_APB2RSTR_SDIORST; //сброс настроек
SDIO->CLKCR|=SDIO_CLKCR_HWFC_EN; //HW Flow Control is enabled SDIO->CLKCR|=SDIO_CLKCR_WIDBUS_0; // 4-wide bus mode: SDIO_D[3:0] used ++ SDIO->CLKCR|=SDIO_CLKCR_PWRSAV; //SDIO_CK is only enabled when the bus is active SDIO->CLKCR|=SDIO_CLKCR_CLKEN; //SDIO_CK is enabled ++
SDIO->DCTRL|=SDIO_DCTRL_SDIOEN; SDIO->DCTRL|=SDIO_DCTRL_RWMOD; //Read Wait control using SDIO_CK ///Непонятно, что это SDIO->DCTRL|=SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DBLOCKSIZE_3; // размер блока 512 ++ SDIO->DCTRL|=SDIO_DCTRL_DMAEN; //DMA enabled. ++ ///////включаем прерывания SDIO->MASK|=SDIO_MASK_STBITERRIE; SDIO->MASK|=SDIO_MASK_DATAENDIE; SDIO->MASK|=SDIO_MASK_CMDRENDIE; SDIO->MASK|=SDIO_MASK_DTIMEOUTIE; SDIO->MASK|=SDIO_MASK_CTIMEOUTIE; SDIO->MASK|=SDIO_MASK_DCRCFAILIE; SDIO->MASK|=SDIO_MASK_CCRCFAILIE; SDIO->MASK|=SDIO_MASK_CMDSENTIE;
NVIC_SetPriority(SDIO_IRQn, 11); NVIC_EnableIRQ(SDIO_IRQn);
SDIO->POWER|=SDIO_POWER_PWRCTRL ; //Включаем SDIO
}
функция передачи CMD команды. После начала передачи функция ждёт прерывания от выставления флагов в SDIO STA . У меня операционка, но её можно выкинуть, заменив просто поллингом регистра STA, или передачей флага из прерывания через глобальную переменную.
//////////////////////////////////////// __inline char SEND_CMD(char sd_n, unsigned char cmd, unsigned long int argument, unsigned char response_type, unsigned int* ansver ) { unsigned int SDIO_CMD_MASK; EventBits_t SDIO_STA_MASK; //Clear the Command Flags SDIO->ICR=0xFFFFFFFF; //(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT | SDIO_STA_CMDREND | SDIO_STA_CMDSENT); SDIO->ARG=argument; //First adjust the argument (because I will immediately enable CPSM next) SDIO_CMD_MASK=cmd; //The last argument is to enable CSPM SDIO_CMD_MASK |=SDIO_CMD_CPSMEN; //The last argument is to enable CSPM switch(response_type){ case RESPONSE_0_BIT : SDIO_CMD_MASK &=~SDIO_CMD_WAITRESP_0; SDIO_CMD_MASK &=~SDIO_CMD_WAITRESP_1; break; case RESPONSE_48_BIT : SDIO_CMD_MASK |=SDIO_CMD_WAITRESP_0; SDIO_CMD_MASK &=~SDIO_CMD_WAITRESP_1; break; case RESPONSE_136_BIT : SDIO_CMD_MASK |=SDIO_CMD_WAITRESP_0; SDIO_CMD_MASK |=SDIO_CMD_WAITRESP_1; default : SDIO_CMD_MASK |=SDIO_CMD_WAITRESP_0; SDIO_CMD_MASK |=SDIO_CMD_WAITRESP_1; }
xEventGroupClearBits(x_SDIO_Transmit_EventGroup,0xFFFFFFF); SDIO->CMD=SDIO_CMD_MASK; //запускаем передачу команды if (response_type==RESPONSE_0_BIT) { SDIO_STA_MASK=xEventGroupWaitBits(x_SDIO_Transmit_EventGroup, SDIO_STA_CMDSENT | SDIO_STA_CTIMEOUT, pdTRUE, pdFALSE, 5); if(SDIO_STA_MASK & SDIO_STA_CTIMEOUT) return SD_ERROR; return SD_OK; } else //SHRESP or LNRESP or R3RESP { SDIO_STA_MASK=xEventGroupWaitBits(x_SDIO_Transmit_EventGroup, SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL, pdTRUE, pdFALSE, 5); if(SDIO_STA_MASK & SDIO_STA_CTIMEOUT) return SD_ERROR; if(SDIO_STA_MASK& SDIO_STA_CCRCFAIL) { if(cmd==CMD41) { ansver[0]=SDIO->RESP1; ansver[1]=SDIO->RESP2; return SD_OK; } else return SD_ERROR; } if(SDIO_STA_MASK & SDIO_STA_CMDREND) { ansver[0]=SDIO->RESP1; ansver[1]=SDIO->RESP2; if(response_type==RESPONSE_136_BIT) { ansver[2]=SDIO->RESP3; ansver[3]=SDIO->RESP4; } return SD_OK; } } return SD_ERROR; }
Функция настройки DMA для передачи SDIO <> буфер памяти /////////////////////////////////////////////////////////////////////////////////// void SD_DMA_INIT(void) { RCC->AHBENR|=RCC_AHBENR_DMA2EN; //включили тактирование DMA RCC->AHBLPENR|=RCC_AHBLPENR_DMA2LPEN;
RCC->AHBRSTR&=~RCC_AHBRSTR_DMA2RST;//перестаём сбрасывать настройки DMA
//передача и приём DMA2_Channel4->CCR&=~DMA_CCR4_EN;// выключили канал DMA2_Channel4->CCR|=DMA_CCR4_TCIE;// прерывание при конце передачи DMA2_Channel4->CCR|=DMA_CCR4_MSIZE_1; //размер памяти 32 DMA2_Channel4->CCR|=DMA_CCR4_PSIZE_1; //размер периферии 32 } }
Функции запуска DMA для чтения и для записи ///////////////////////////////запускаем чтение SD по DMA ///////////////////////////////// void SD_DMA_START_READ(char sd_n, unsigned char *p_buf_r, int data_size) { // "периферия" -откуда берутся данные // "память" -куда идёт данные if(sd_n==SD1) { DMA2_Channel4->CCR&=~DMA_CCR4_EN; // канал приёма и передачи DMA2_Channel4->CNDTR=data_size/4; //т.к. data_size в байтах, а размер регистра 32 байта DMA2_Channel4->CMAR=(uint32_t)p_buf_r; // сюда идут данные DMA2_Channel4->CPAR=(uint32_t)(&SDIO->FIFO); // отсюда идут данные DMA2_Channel4->CCR|=DMA_CCR4_MINC; DMA2_Channel4->CCR &=~ DMA_CCR4_PINC;
DMA2_Channel4->CCR|=DMA_CCR4_EN;
}
}
///////////////////////////////запускаем запись в SD по DMA ///////////////////////////////// void SD_DMA_START_WRITE(char sd_n, unsigned char *p_buf_w, int data_size) { // "периферия" -откуда берутся данные // "память" -куда идёт данные DMA2_Channel4->CCR&=~DMA_CCR4_EN; // канал приёма и передачи DMA2_Channel4->CNDTR=data_size/4; //т.к. data_size в байтах, а размер регистра 32 байта DMA2_Channel4->CPAR=(uint32_t)p_buf_w; // сюда идут данные DMA2_Channel4->CMAR=(uint32_t)(&SDIO->FIFO); // отсюда идут данные DMA2_Channel4->CCR|=DMA_CCR4_PINC; DMA2_Channel4->CCR&=~DMA_CCR4_MINC; DMA2_Channel4->CCR|=DMA_CCR4_EN; }
Инициализация карты. Работает только на картах больше 2Гб. См. комментарии. ///// Как это работает написано в документе Part_1_Physical_Layer_Simplified_Specification_Ver3.01_Final_100518-1.pdf страница 115 char SD_initialize (char sd_n) { unsigned int cmd_ansver[4]; char result; unsigned long int time; unsigned int C_SIZE; time=xTaskGetTickCount()+(2000/portTICK_RATE_MS); SD_SCK_DIV_60(sd_n) SEND_CMD(sd_n, CMD0,0,RESPONSE_0_BIT,&cmd_ansver[0]); SEND_CMD(sd_n, CMD0,0,RESPONSE_0_BIT,&cmd_ansver[0]); //почему то c одним CMD0 не работает result=SEND_CMD(sd_n, CMD8,0x1AA,RESPONSE_48_BIT,&cmd_ansver[0]); if(result==SD_OK) //карта ответила на CMD8 { if((cmd_ansver[0] & 0x1FF)!=0x1AA) //контрольная цифра не верна { result= FR_DISK_ERR; } else { while(1) { result=SEND_CMD(sd_n, CMD55,0,RESPONSE_48_BIT,&cmd_ansver[0]); if(result==SD_OK) result=SEND_CMD(sd_n, CMD41, /*(1<<31)*/ 0x80000000 |(1<<20)|(1<<30)|(1<<28) ,RESPONSE_48_BIT,&cmd_ansver[0]); //|(1<<28) включает макс.скорость if(result==SD_OK) { if((cmd_ansver[0] & /*(1<<31)*/ 0x80000000)!=0) //инициализация пройдена { if((cmd_ansver[0] & (1<<30))!=0) //CCS=1 CardType[sd_n]=SDHC_SDXC; else //CCS=0 CardType[sd_n]=SDSC;
result=SEND_CMD(sd_n, CMD2,0,RESPONSE_136_BIT,&cmd_ansver[0]); if(result==SD_OK) result=SEND_CMD(sd_n, CMD3,0,RESPONSE_48_BIT,&cmd_ansver[0]); if(result==SD_OK) sd_RCA[sd_n]=(cmd_ansver[0])>>16; break; } } else { result= FR_DISK_ERR; break; } if(xTaskGetTickCount()>time) { result= FR_DISK_ERR; break; } } } } else //карта не ответила на CMD8 { while(1) { result=SEND_CMD(sd_n, CMD55,0,RESPONSE_48_BIT,&cmd_ansver[0]); if(result==SD_OK) result=SEND_CMD(sd_n, CMD41, /*(1<<31)*/ 0x80000000 |(1<<20)|(1<<28) ,RESPONSE_48_BIT,&cmd_ansver[0]); if(result==SD_OK) { if((cmd_ansver[0] & /*(1<<31)*/ 0x80000000)!=0) //инициализация пройдена { CardType[sd_n]=SDSC; result =FR_OK; break; } } else { result= FR_DISK_ERR; break; } if(xTaskGetTickCount()>time) { result= FR_DISK_ERR; break; } } }
//Включаем частоту шины в соответствии с классом скорости карты. Надо бы сделать получение класса скорости от карты. SD_SCK_DIV_8(sd_n)
//Узнаём количество секторов на карте if(result==SD_OK) result=SEND_CMD(sd_n, CMD9, sd_RCA[sd_n]<<16, RESPONSE_136_BIT, &cmd_ansver[0]); if(CardType[sd_n]==SDHC_SDXC) { C_SIZE =((cmd_ansver[1] & 0x1F)<<17) + ((cmd_ansver[2] & 0xFFFF0000)>>16); //биты 69-48 sd_total_sectors[sd_n]=C_SIZE*1024; } else { sd_total_sectors[sd_n]=0; //тут надо бы сделать обработку старых карт } if(result==SD_OK) result=SEND_CMD(sd_n, CMD7, sd_RCA[sd_n]<<16, RESPONSE_48_BIT, &cmd_ansver[0]); if(result==SD_OK) result=SEND_CMD(sd_n, CMD55, sd_RCA[sd_n]<<16 , RESPONSE_48_BIT, &cmd_ansver[0]); if(result==SD_OK) result=SEND_CMD(sd_n, CMD6, 0x02, RESPONSE_48_BIT, &cmd_ansver[0]); //выбираем шину 4 бита
return result; }
Функция чтения ///////////////////////////////////////////////////////////////////// __inline DRESULT TRY_disk_read ( BYTE Drive, /* Physical drive number */ BYTE* pBuffer, /* Pointer to the read data buffer */ DWORD SectorNumber, /* Start sector number */ BYTE SectorCount /* Number of sectros to read */ ) { unsigned long int sd_adress,number_of_block; unsigned long int i; unsigned char result; char sd_n =Drive; EventBits_t SDIO_STA_MASK; unsigned int cmd_ansver[4]; if(CardType[sd_n]==SDHC_SDXC) sd_adress=SectorNumber; else sd_adress=SectorNumber*0x200;
number_of_block=SectorCount; for(i=0;i<number_of_block;i++) { result=SEND_CMD(sd_n, CMD17, sd_adress+(i*512), RESPONSE_48_BIT, &cmd_ansver[0]); if(result!=SD_OK) { ADD_EVENT_MESSAGE_SDx(sd_n,"#!!! ERROR: TRY_disk_read: CMD18: Ошибка 2#"); return RES_ERROR; } //xSemaphoreTake(x_SD_Multi_Transmit_Complite[sd_n],0);// xEventGroupClearBits(x_SDIO_Transmit_EventGroup,0xFFFFFFF); SD_DMA_START_READ(sd_n,pBuffer+(i*512),512);
SDIO->ICR=0xFFFFFFFF; SDIO->DTIMER= 0xffffff; SDIO->DLEN=512; SDIO->DCTRL=SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DBLOCKSIZE_3; // размер блока 512 SDIO->DCTRL|=SDIO_DCTRL_DMAEN; //DMA enabled. SDIO->DCTRL|=SDIO_DCTRL_DTDIR; //Direction. //направление передачи из дарты в процессор SDIO->DCTRL|=SDIO_DCTRL_RWSTART; // SDIO->DCTRL|=SDIO_DCTRL_DTEN; //DPSM is enabled //запускаем передачу данных //Ждём выставления флагов в прерывании. По результатам судим о успехе/не успехе передачи SDIO_STA_MASK=xEventGroupWaitBits(x_SDIO_Transmit_EventGroup, SDIO_STA_DBCKEND | SDIO_STA_STBITERR /*| SDIO_STA_DATAEND */ | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL, pdTRUE, pdFALSE, 10); if(SDIO_STA_MASK & SDIO_STA_CTIMEOUT) return RES_ERROR; if(SDIO_STA_MASK & (SDIO_STA_STBITERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL )) return RES_ERROR; } return RES_OK; }
И очень похожая функция записи ///////////////////////////////////////////////////////////////////// __inline DRESULT TRY_disk_write( BYTE Drive, /* Physical drive number */ unsigned char* pBuffer, /* Pointer to the read data buffer */ DWORD SectorNumber, /* Start sector number */ BYTE SectorCount /* Number of sectros to read */ ) { char sd_n=Drive; char result; unsigned long int i; unsigned long int sd_adress,number_of_block; EventBits_t SDIO_STA_MASK; unsigned int cmd_ansver[4]; if(CardType[sd_n]==SDHC_SDXC) sd_adress=SectorNumber; else sd_adress=SectorNumber*0x200;
number_of_block=SectorCount; for(i=0;i<number_of_block;i++) { result=SEND_CMD(sd_n, CMD24, sd_adress+(i*512), RESPONSE_48_BIT, &cmd_ansver[0]); if(result!=SD_OK) { ADD_EVENT_MESSAGE_SDx(sd_n,"#!!! ERROR: TRY_disk_write: CMD24: Ошибка 2#"); return RES_ERROR; } xEventGroupClearBits(x_SDIO_Transmit_EventGroup,0xFFFFFFF); SD_DMA_START_WRITE(sd_n,pBuffer+(i*512),512);
SDIO->ICR=0xFFFFFFFF; SDIO->DTIMER= 0xffffff; SDIO->DLEN=512; SDIO->DCTRL=SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DBLOCKSIZE_3; // размер блока 512 SDIO->DCTRL|=SDIO_DCTRL_DMAEN; //DMA enabled. SDIO->DCTRL &=~ SDIO_DCTRL_DTDIR; //Direction. //направление передачи из процессора в карту SDIO->DCTRL|=SDIO_DCTRL_DTEN; //DPSM is enabled //запускаем передачу SDIO_STA_MASK=xEventGroupWaitBits(x_SDIO_Transmit_EventGroup, SDIO_STA_DBCKEND | SDIO_STA_STBITERR /*| SDIO_STA_DATAEND */ | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL, pdTRUE, pdFALSE, 10); if(SDIO_STA_MASK & SDIO_STA_CTIMEOUT) return RES_ERROR; if(SDIO_STA_MASK & (SDIO_STA_STBITERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL )) return RES_ERROR; } return RES_OK; }
--------------------
Если у Вас нет практического опыта в данной теме- не вступайте в дискуссию и не пишите никаких теоретических рассуждений! Заранее спасибо !
|
|
|
|
|
|
4 чел. читают эту тему (гостей: 4, скрытых пользователей: 0)
Пользователей: 0
|
|
|