Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: microSD SDIO
Форум разработчиков электроники ELECTRONIX.ru > Интерфейсы > Форумы по интерфейсам
MiklPolikov
Коллеги, добрый день.

Пытаюсь подключить 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 контроллера ? И перед данными выдаёт старт-бит ?

Заранее спасибо !
MiklPolikov
И ещё, ни как не могу понять, что именно делает CMD7 . В документации написано "SELECT / DESELECT CARD". Ну а как понять, выбрана оказалась карта после очередной CMD7 , или наоборот ? Отпарвляю CMD7 , получаю ответ. Все последующие отправки-ответа нет.
MiklPolikov
Разобрался. Выкладываю свой максимально простой код. Надеюсь что кому-нибудь поможет. Кое-где есть функции операционной системы, ожидающие прерывания от выставления флагов в регистре состояния 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;

}
AHTOXA
Аминьsm.gif
Кстати, у вас в SEND_CMD() не хватает break-а в ветке case RESPONSE_136_BIT. Просто глаз зацепился.
Ну и в тэги [сode]/[сodebox] бы код оформить...
MiklPolikov
Цитата(AHTOXA @ Nov 1 2014, 22:49) *
Ну и в тэги [сode]/[сodebox] бы код оформить...


Сколько раз пробовал, у меня код в этих тегах разъезжается. Перед каждой строчкой появляется непредсказуемое количество пробелов, причём редактировать их количество так что бы становилось ровно не получается.
MiklPolikov
Подскажите, какой командой в режиме SDIO узнают, готова ли карта для приёма следующего блока данных ? При записи нескольких блоков подряд происходит ошибка, а если вставить задержки то всё хорошо.
И то же самое при мультиблочной записи(CMD25) . На втором блоке SDIO выставляет флаг ошибки STA DCRCFAIL . А если блоки разделить задержками, то всё хорошо. Что на самом деле нужно делать между отправками блоков в мультиблочной записи ?
AHTOXA
Перед отправкой команд CMD24/CMD25 нужно дожидаться готовности карты.
Для этого читать статус (CMD13_SEND_STATUS), и проверять бит READY_FOR_DATA.
После этого можно писать. (Между блоками в мультиблочной записи проверять готовность не нужно. По крайней мере, если пишем через DMA).
Точно так же нужно проверять готовность перед командами чтения (CMD17/CMD18).
MiklPolikov
Цитата(AHTOXA @ Nov 4 2014, 10:18) *
Перед отправкой команд CMD24/CMD25 нужно дожидаться готовности карты.
Для этого читать статус (CMD13_SEND_STATUS), и проверять бит READY_FOR_DATA.
После этого можно писать. (Между блоками в мультиблочной записи проверять готовность не нужно. По крайней мере, если пишем через DMA).
Точно так же нужно проверять готовность перед командами чтения (CMD17/CMD18).


Спасибо.
У меня проблема исчезла, после того как вставил CMD13 с проверкой бита READY_FOR_DATA между блоками в мультиблочной передаче(CMD25). Перед CMD25 проверка READY_FOR_DATA не помогла.
MiklPolikov
Цитата(AHTOXA @ Nov 4 2014, 10:18) *
Перед отправкой команд CMD24/CMD25 нужно дожидаться готовности карты.
Для этого читать статус (CMD13_SEND_STATUS), и проверять бит READY_FOR_DATA.
После этого можно писать. (Между блоками в мультиблочной записи проверять готовность не нужно. По крайней мере, если пишем через DMA).
Точно так же нужно проверять готовность перед командами чтения (CMD17/CMD18).


Спасибо.
У меня проблема исчезла, после того как вставил CMD13 с проверкой бита READY_FOR_DATA между блоками в мультиблочной передаче(CMD25). Перед CMD25 проверка READY_FOR_DATA не помогла.
AHTOXA
Я что-то не понял. Вы что, блоки отправляете по одному? Так ведь вся суть команды CMD25 в том, что можно отправить сразу много блоков одной пачкой!
Допустим, у вас 4К данных. Вы ждёте готовности (CMD13), потом даёте команду CMD25, и натравливаете DMA на все свои 4К. И все 4К сразу и передаются.
Где здесь "между блоками"?
MiklPolikov
Цитата(AHTOXA @ Nov 4 2014, 21:49) *
Где здесь "между блоками"?


Почему-то я был уверен, что блоки можно отправлять только по одному, и после каждого надо ждать выставления флагов в SDIO STA . Сейчас попробовал все разом, и действительно, заработало !
GetSmart
Нет ли у кого-нибудь информации о том, в каких типах карт допустима невыравненная мультиблоковая запись? На некоторых SDHC я проверял - работает. Пока неработающую не встретил. А т.к. винда злостно форматирует кластеры на невыравненных адресах, то разбивать 4К кластер на 4+2+1+1 сектор записи вместо одной в 8 секторов - это непозволительная роскошь. Насколько я заметил, стыковка даже мультиблоков делается самой картой (SDHC) и только в момент ухода с какой-то внутренней грануляции карты возникает процесс записи и выдача BUSY. Вопрос насколько старые карты так умеют делать? MMC? SD ver1?

Цитата(AHTOXA @ Nov 4 2014, 10:18) *
... Между блоками в мультиблочной записи проверять готовность не нужно. ...

При невыравненном доступе - нужно. Прямо между блоками может 50 мс BUSY встрять.
MiklPolikov
Цитата(GetSmart @ Mar 27 2015, 22:42) *
Нет ли у кого-нибудь информации о том, в каких типах карт допустима невыравненная мультиблоковая запись?


Не совсем понятно, что значит "невыровненная". Речь идёт о выравнивании адреса на 0x200 байт , или о чём-то ещё ?
И , ради любопытства, зачем Вам это ?
GetSmart
Нееее. У SDHC передаётся сразу номер блока/сектора (т.н. сектор = 512 байт блок).

А невыровненная это когда по адресу блока допустим 0x0015 пишется блок длиной 0x003F блоков. Здесь уже и адрес и длина блока невыровненные. Но длина блока некратная степени двойки это я ещё не проверял. Но , думаю, на SDHC вполне может работать.
MiklPolikov
Цитата(GetSmart @ Mar 27 2015, 23:08) *
по адресу блока допустим 0x0015 пишется блок длиной 0x003F блоков. Здесь уже и адрес и длина блока невыровненные.


Я давно и много работаю с самыми разными SD , но Ваших слов почему-то не понимаю.
GetSmart
ProductManualSDCardv2.2final.pdf

Раздел 5.5 Data Write.
Рисунок 5-5 Multiple Block Write Operation
На блок-схеме видно, что после CMD25 между любым блоком данных возможно появление BUSY.

По поводу грануляции SD-карт есть некоторое пояснение у Чена h__p://elm-chan.org/docs/mmc/mmc_e.html

Разве никто не делал лог появлений BUSY в мультисекторной записи? У меня на 8-гиговой возникали паузы после первого переданного блока данных в команде CMD25 и чтобы отправить остальные блоки приходится ждать пропадения BUSY. Причём грануляция появления BUSY в переводе на килобайты - от 16 КБ в начале файла коротких BUSY, и далее через грубо 8 раз грануляция увеличивается до 640 КБ и 45 мс BUSY. Карта по какой-то своей логике её меняет. Но блок, после которого формируется длинный BUSY сохраняется - после первого (видимо зависит от форматирования // кластерного сдвига // адреса в команде CMD25). В тесте CMD25 всегда была на 16 блоков.


Но я может быть перемудрил/преувеличил опасения по поводу "невыравненной мультиблоковой записи". Т.к. если в документе ProductManualSDCardv2.2final.pdf нет ограничений на выравнивание адреса, то видимо во всех SD-картах это изначально допускалось. И влияет невыровненность адреса (блока) только на момент формирования длинного BUSY.
aaarrr
Цитата(GetSmart @ Mar 30 2015, 22:03) *
Но я может быть перемудрил/преувеличил опасения по поводу "невыравненной мультиблоковой записи". Т.к. если в документе ProductManualSDCardv2.2final.pdf нет ограничений на выравнивание адреса, то видимо во всех SD-картах это изначально допускалось. И влияет невыровненность адреса (блока) только на момент формирования длинного BUSY.

Ограничений нет (и не было). Влияет, но для каждого типа карты влияние индивидуально, так что в конечном счете это не важно.
AHTOXA
Что касаемо невыровненной записи, то за неё отвечают флаги WRITE_BLK_MISALIGN и WRITE_BL_PARTIAL в CSD.
Первый - определяет, допускает ли карта невыровненный начальный адрес блока, второй - допускает ли карта размер блока меньше чем 512.
Насколько я понял, первый роялит только для не-SDHC карт. (Потому что для SDHC карт не получится передать невыровненный адрес).

Что же до флага BUSY при мультиблоковой записи - не разу его не проверял, и не имел проблем. Возможно, с ним работает аппаратура SDIO контроллера.
aaarrr
Цитата(AHTOXA @ Mar 30 2015, 22:35) *
Насколько я понял, первый роялит только для не-SDHC карт. (Потому что для SDHC карт не получится передать невыровненный адрес).

Да, в HC это убрали, но и на обычных картах никто (или почти никто) такую запись не поддерживал.

Цитата(AHTOXA @ Mar 30 2015, 22:35) *
Что же до флага BUSY при мультиблоковой записи - не разу его не проверял, и не имел проблем. Возможно, с ним работает аппаратура SDIO контроллера.

BUSY, естественно, выставляется - иначе у карты не было бы возможности "притормозить" запись.
GetSmart
Если у кого есть информация, то любопытна и вторая вещь, о которой я писал. В SDHC BUSY может не вырабатываться после завершения мультисекторной записи. То есть карта накапливает непрерывные цепочки. Интересно, как давно и в каких типах такое стало происходить. И в связи с оптимизацией работы ПО с мультисекторной записью SD карт.

PS
Мой термин "невыравненная запись" отличался от документации SD-карт. Невыравненым считался адрес 512-байтного блока (не байта!) относительно грануляции NAND-памяти карты.
ohmjke
Пытаю SDIO на плате SK-STM32F217. Карту пока не подключал, хочу увидеть хоть какую-нибудь осмысленную активность на сигнальных линиях.
Осуществляю инициализацию и отправку команды, но вижу только мусор на линии CLK, на остальных ничего нет.
Может кто-нибудь проверить у себя мой вариант? Или подкинуть 100% рабочий код, без лишних функций.

Код
static SDIO_InitTypeDef s_sdio_param = {
                                         SDIO_ClockEdge_Rising,
                                         SDIO_ClockBypass_Enable,
                                         SDIO_ClockPowerSave_Disable,
                                         SDIO_BusWide_1b,
                                         SDIO_HardwareFlowControl_Disable,
                                         0
                                       };

void bsp_sd_init (void)
{
    SDIO_CmdInitTypeDef SDIO_CmdInitStruct;


    s_gpio_init();

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);

    SDIO_SetPowerState(SDIO_PowerState_ON);
    while (SDIO_GetPowerState() != 0x03)
    {
        continue;
    }

    SDIO_Init(&s_sdio_param);

    SDIO_ClockCmd(ENABLE);

    SDIO_CmdInitStruct.SDIO_CmdIndex = 0xAA;
    SDIO_CmdInitStruct.SDIO_Argument = 0xCCCCCCCC;
    SDIO_CmdInitStruct.SDIO_Response = SDIO_Response_No;
    SDIO_CmdInitStruct.SDIO_CPSM     = ENABLE;
    SDIO_CmdInitStruct.SDIO_Wait     = SDIO_Wait_No;
    SDIO_SendCommand(&SDIO_CmdInitStruct);
}

static void s_gpio_init (void)
{
    GPIO_InitTypeDef GPIO_InitStructure;


    RCC_AHB1PeriphClockCmd(RCC_CLK |
                           RCC_CMD |
                           RCC_D3  |
                           RCC_D2  |
                           RCC_D1  |
                           RCC_D0, ENABLE);

    GPIO_PinAFConfig(GPIO_CLK, PINSOURCE_CLK, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIO_CMD, PINSOURCE_CMD, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIO_D3,  PINSOURCE_D3,  GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIO_D2,  PINSOURCE_D2,  GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIO_D1,  PINSOURCE_D1,  GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIO_D0,  PINSOURCE_D0,  GPIO_AF_SDIO);

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;

    GPIO_InitStructure.GPIO_Pin = PIN_CMD;
    GPIO_Init(GPIO_CMD, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = PIN_D3;
    GPIO_Init(GPIO_D3,  &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = PIN_D2;
    GPIO_Init(GPIO_D2,  &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = PIN_D1;
    GPIO_Init(GPIO_D1,  &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = PIN_D0;
    GPIO_Init(GPIO_D0,  &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Pin  = PIN_CLK;
    GPIO_Init(GPIO_CLK, &GPIO_InitStructure);
}
ohmjke
Ну что, никто не может помочь?
Мур
Вопрос знатокам SDIO.(Завершаю мост на FPGA SDIO<-> N*UART)

Если по линии CMD кадр начинается с нулевого бита, то как обнаружить начало информационного кадра по DATA[3:0]? Я стал сомневаться, что для SD на входе CLK может отсутствовать частота (А значит именно её появление начинает отсчет информации).
Что наблюдали осциллографом? В Интернете противоречивая информация...
adnega
Цитата(Мур @ Jan 30 2018, 22:24) *
В Интернете противоречивая информация...

Судя по всему, начинается с "0".
Мур
Цитата(adnega @ Jan 30 2018, 23:02) *
Судя по всему, начинается с "0".

Это ценно. Огромное ВАМ спасибо!!!

...остается выяснить CRC вычисляется с учетом этого стартового нуля или ТОЛЬКО данные в учете?
aaarrr
Цитата(Мур @ Jan 31 2018, 11:24) *
...остается выяснить CRC вычисляется с учетом этого стартового нуля или ТОЛЬКО данные в учете?

Только данные, по каждой линии отдельно.
adnega
Цитата(Мур @ Jan 31 2018, 11:24) *
Это ценно. Огромное ВАМ спасибо!!!

...остается выяснить CRC вычисляется с учетом этого стартового нуля или ТОЛЬКО данные в учете?

Рекомендую ознакомиться с SD Specifications Part 1 Physical Layer Simplified Specification Version 3.01:
Цитата
When the wide bus option is used, the data is transferred 4 bits at a time (refer to Figure 3-7). Start and
end bits, as well as the CRC bits, are transmitted for every one of the DAT lines. CRC bits are
calculated and checked for every DAT line individually. The CRC status response and Busy indication
will be sent by the card to the host on DAT0 only (DAT1-DAT3 during that period are don't care).

Более подробно про разные CRC описано в разделе 4.5 Cyclic Redundancy Code (CRC).
Мур
Цитата(aaarrr @ Jan 31 2018, 11:34) *
Только данные, по каждой линии отдельно.

Странная фантазия... 4 вычислителя?... Трудно такую мысль представить у задумщиков SDIO
aaarrr
Цитата(Мур @ Jan 31 2018, 21:53) *
Странная фантазия... 4 вычислителя?... Трудно такую мысль представить у задумщиков SDIO

Странно было бы сделать иначе.
Мур
Цитата(aaarrr @ Jan 31 2018, 22:08) *
Странно было бы сделать иначе.

Хм....
Проще гонять в сдвиговом регистре полином сразу по 4м линиям, чем 4мя по каждой из линий...
aaarrr
Цитата(Мур @ Jan 31 2018, 22:45) *
Проще гонять в сдвиговом регистре полином сразу по 4м линиям, чем 4мя по каждой из линий...

Что может быть проще, чем иметь четыре сдвиговых регистра на выходе сериализатора? Минимум манипуляций с данными, минимум логики.
Мур
Цитата(aaarrr @ Jan 31 2018, 23:11) *
Что может быть проще, чем иметь четыре сдвиговых регистра на выходе сериализатора? Минимум манипуляций с данными, минимум логики.

Смеётесь?....
В 4 раза больше логики в вашем случае! (16 триггеров против 64х).
Есть только одно объяснение этому "безобразию",- вариант применения одной линии данных вместо четырех. Тогда берется для этого один регистр из четырех.
aaarrr
Цитата(Мур @ Feb 1 2018, 10:46) *
Смеётесь?....
В 4 раза больше логики в вашем случае! (16 триггеров против 64х).

16? Т.е. тактировать свой вычислитель с 4x частотой интерфейса - это не безобразие? А логику, которая
будет перекладывать полученное значение CRC в сериализатор, решили и не считать?
adnega
Цитата(Мур @ Feb 1 2018, 10:46) *
Смеётесь?....

А смысл обсуждать это? Есть стандарт - нужно ему следовать.
Если 4 вычислителя не охота делать принципиально, то можно с картой общаться в SPI-режиме, и вообще без какой-либо проверки.
mantech
Цитата(adnega @ Feb 1 2018, 14:45) *
А смысл обсуждать это? Есть стандарт - нужно ему следовать.
Если 4 вычислителя не охота делать принципиально, то можно с картой общаться в SPI-режиме, и вообще без какой-либо проверки.


Если только скорость вообще не важна biggrin.gif
jcxz
Цитата(mantech @ Feb 1 2018, 13:55) *
Если только скорость вообще не важна biggrin.gif

А какой реальный выигрыш от использования SDIO вместо SPI не на коне в вакууме, а на типичном Cortex-M? Загруженном кроме гоняния байтов с SD ещё и другими задачами.
В Мб/сек или в %.
Есть-ли вообще смысл тратить гораздо больше ног МК, которые, как правило, всегда в дефиците?
adnega
Цитата(jcxz @ Feb 1 2018, 15:46) *
А какой реальный выигрыш от использования SDIO вместо SPI не на коне в вакууме

Действительно, реальная карта очень медленная. Т.е. по быстрому интерфейсу ей прилетает команда,
спустя какое-то время карта по быстрому интерфейсу отвечает, а данные сектора выдаст гораааздо позже, но очень быстро.
Применение SDIO в связке с DMA позволяет сильно разгрузить МК, хотя я делал и на SPI+DMA+ISR с деревом из callback-функций.
С учетом того, что карта может приспокойно "задуматься" на полсекунды даже в режиме чтения, jcxz весьма справедлив.
jcxz
Цитата(adnega @ Feb 1 2018, 15:03) *
С учетом того, что карта может приспокойно "задуматься" на полсекунды даже в режиме чтения, jcxz весьма справедлив.

Я даже не об этом говорю, а о том что на практике я писал драйвер SD через SPI на Cortex-M 120МГц. И получал скорость потокового чтения с карты немного больше 1МБ/сек (естественно SPI+DMA).
Даже просто прокачка такого потока через средний Cortex-M сильно его нагрузит. Не говоря уже о том, что наверное МК должен как-то ещё и обрабатывать этот поток.
При более-менее содержательной обработке читаемых/записываемых данных, скорость обработки этих данных вряд-ли будет выше 1МБ/сек, а скорей всего - гораздо ниже.
Тогда такой скорости SPI вполне хватает.
И это при том, что я даже не оптимизировал свой драйвер - возможно получил бы гораздо бОльшую скорость (SPI-флешки у меня работают на SCLK до 40МГц).
Вобщем: при реальной работе с SD, а не мерянии попугаев, сама обработка данных будет больше тормозить, чем обмен с SD по SPI. Имха.
Так что польза от SDIO на типичных Cortex-M - сомнительна. Тоже - имхо.
_4afc_
Цитата(jcxz @ Feb 1 2018, 15:46) *
А какой реальный выигрыш от использования SDIO вместо SPI не на коне в вакууме, а на типичном Cortex-M? Загруженном кроме гоняния байтов с SD ещё и другими задачами.
В Мб/сек или в %.
Есть-ли вообще смысл тратить гораздо больше ног МК, которые, как правило, всегда в дефиците?


Есть проект на Cortex-M7 вида eMMC -> M7 -> HSUSB, без двойной буверизации, т.е. блок читается с флешки и отдаётся в USB.

При подключении по 4 проводам SDIO средня скорость чтения файла длиной 1Гб, в зависимости от частоты шины SDIO, составила:

24.43МГц - 6.50М/с;
25.00МГц - 7.18М/с;
50.00МГц - 10.56М/с.

Насколькоя я знаю скорость 50.00МГц предусмотрена стандартом только в режиме SDIO.

Цитата(adnega @ Feb 1 2018, 16:03) *
С учетом того, что карта может приспокойно "задуматься" на полсекунды даже в режиме чтения, jcxz весьма справедлив.


Моя карта имеет право задуматься на 250мс в режиме чтения.
jcxz
Цитата(_4afc_ @ Feb 1 2018, 15:32) *
Насколькоя я знаю скорость 50.00МГц предусмотрена стандартом только в режиме SDIO.

Просто передаёт с SD на USB? Это карт-ридер? rolleyes.gif
Конечно, если делать карт-ридер, то здесь имеет смысл. Я же писал про использование SD для нужд хранения и содержательной обработки данных самой программой МК.
Единственный вариант: это если данные медленно накапливать на SD при работе устройства, а потом быстро слить в комп не вынимая карты.

Цитата(_4afc_ @ Feb 1 2018, 15:32) *
Моя карта имеет право задуматься на 250мс в режиме чтения.

Да уж.... crying.gif
_4afc_
Цитата(jcxz @ Feb 1 2018, 16:47) *
Единственный вариант: это если данные медленно накапливать на SD при работе устройства, а потом быстро слить в комп не вынимая карты.

Да что-то подобное.

Цитата(jcxz @ Feb 1 2018, 16:47) *
Просто передаёт с SD на USB? Это карт-ридер? rolleyes.gif

В режиме чтения накопленной или обработанной информации - что-то типа карт-ридера. И естественно, чем быстрее считается - тем удобнее.

Цитата(jcxz @ Feb 1 2018, 16:47) *
Конечно, если делать карт-ридер, то здесь имеет смысл. Я же писал про использование SD для нужд хранения и содержательной обработки данных самой программой МК.


Для нужд записи - тоже имеет смысл при ограничении по потреблению и пульсациям по питанию.

Вот ток потребления в режиме SPI:
Нажмите для просмотра прикрепленного файла

А вот в режиме SDIO:
Нажмите для просмотра прикрепленного файла

Видно, что среднее потребление при записи - в режиме SDIO в 2.5 раза меньше, за счёт меньшего времени передачи данных.
jcxz
Цитата(_4afc_ @ Feb 1 2018, 16:09) *
Для нужд записи - тоже имеет смысл при ограничении по потреблению и пульсациям по питанию.
...
Видно, что среднее потребление при записи - в режиме SDIO в 2.5 раза меньше, за счёт меньшего времени передачи данных.

Зато судя по картинкам - пульсация тока в режиме SDIO в ~2 раза больше rolleyes.gif
Да и судя по длительностям осциллограмм - сравниваете SDIO работающий на высокой скорости и SPI работающий на медленной скорости - так сравнивать не корректно.
Вы сами писали, что скорость SDIO у вас максимум была 10МБ/сек. А SPI может работать с SCLK до 25МГц - т.е. около 3МБ/сек. Разница в длительности картинок должна быть соответственно 1/3 или 1/4 (судя по кол-ву линий IO).
Возможно, что если запустите SPI на макс. скорости, то получите сравнимые показатели тока потребления.
mantech
Цитата(jcxz @ Feb 1 2018, 17:17) *
Вы сами писали, что скорость SDIO у вас максимум была 10МБ/сек. А SPI может работать с SCLK до 25МГц - т.е. около 3МБ/сек. Разница в длительности картинок должна быть соответственно 1/3 или 1/4 (судя по кол-ву линий IO).
Возможно, что если запустите SPI на макс. скорости, то получите сравнимые показатели тока потребления.


Только заметьте разницу - "корость SDIO у вас максимум была 10МБ/сек" и "А SPI может работать с SCLK до 25МГц - т.е. около 3МБ/сек" - только первое реально работает, а второе - только ваш прогноз, который не учитывает обработку команды в карте и накладные расходы, а именно больше полутора мегов в сек не получите по спи.
Другое дело - если задача скинуть файл пару кб раз в сек. то какой интерфейс - без разницы.
aaarrr
Цитата(_4afc_ @ Feb 1 2018, 16:32) *
Насколькоя я знаю скорость 50.00МГц предусмотрена стандартом только в режиме SDIO.

Нет, режим High-Speed распространяется в том числе и на подключение через SPI.

P.S. SDIO и SD - это все-таки совершенно разные вещи.
Мур
Цитата(aaarrr @ Jan 31 2018, 11:34) *
Только данные, по каждой линии отдельно.

Тут есть путаница,
.....хотя бы потому, что для вывода всего CRC16(CCITT) по ОДНОЙ линии требуется 16 тактов, хотя на эпюрах на это выделяется 4 такта. Неувязочка явная!

Ответ на этот вопрос резко бы упростился, если кто-нибудь дал пример РЕАЛЬНОЙ последовательности в четыре провода от начала до конца.
adnega
Цитата(Мур @ Feb 2 2018, 10:49) *
Ответ на этот вопрос резко бы упростился, если кто-нибудь дал пример РЕАЛЬНОЙ последовательности в четыре провода от начала до конца.

Дык, достаточно подключиться логическим анализатором к любому картридеру с картой.
aaarrr
Цитата(Мур @ Feb 2 2018, 10:49) *
.....хотя бы потому, что для вывода всего CRC16(CCITT) по ОДНОЙ линии требуется 16 тактов, хотя на эпюрах на это выделяется 4 такта. Неувязочка явная!

На каких эпюрах?

Цитата из Physical Layer Simplified Specification Version 2.00:
Цитата
When the wide bus option is used, the data is transferred 4 bits at a time (refer to Figure 3-8). Start and
end bits, as well as the CRC bits, are transmitted for every one of the DAT lines. CRC bits are
calculated and checked for every DAT line individually.
Мур
Цитата(aaarrr @ Feb 2 2018, 12:22) *
На каких эпюрах?


1111493779.gif вот это пример самодурства! Я увидел то, что хотел увидеть. ... Спасибо, дружище! Камень с плеч....

Делаю 16 тактов на CRC16(CCITT)!!!
aaarrr
Цитата(Мур @ Feb 2 2018, 16:57) *
Я увидел то, что хотел увидеть. ...

Да, так оно и бывает иногда sm.gif
Мур
Ёханый бабай!... Обращаю внимание http://www.sigmatone.com/utilities/crc_gener
Мужики! Вычисление CRC7 требует установку в исходное состояние регистров сдвигового регистра в "0000000", а не "1111111", как обычно.
Только так все работает
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.