Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Чтение блока с SD карты
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Evgenius_Alex
Добрый день!

При работе с SD картой столкнулся с проблемой чтения блока.

Карта инициализацию проходит нормально.

После команды чтения одного блока CMD17, приходит корректный ответ (0x00)
Но маркера начала данных(0xFE) не поступает.

Пробовал меня скорость SPI - не помогает

Сталкивался ли кто с таким и как бороться?
ukpyr
попробуйте увеличить время ожидания 0xFE
AndreyVN
Цитата(Evgenius_Alex @ Jan 23 2013, 15:41) *
Добрый день!
При работе с SD картой столкнулся с проблемой чтения блока.


Читаю сектора вот так:
CODE
// Прочитать содержимое буфера в переменную Sector из MMC карты
// N-номер сектора
//-----------------------------------------------------------------------------
void SDReadSector(unsigned long int N)
{
char rsp,rsp1;
short int crc;
SPCR = 0x50; //0x50 fclk/4; 0x51 fclk/16; 0x52 fclk/64
SPSR = 0x00; //
//for(i=0; i<512; i++) Sector[i]=0xEE;
if(!BlockAdressMode) Adr = N*512; //Адрес начала сектора, побайтная адресация
else Adr=N; //Блочная адресация
rsp = MMC_cmd(CMD17, (unsigned int)(Adr>>16),(unsigned int)(Adr&0x0000ffff), 0xFF); //CMD17 Read a block from specify addr
if(rsp){ SD_init(); //Если rsp!=0x00 будим карту снова
rsp = MMC_cmd(CMD17, (unsigned int)(Adr>>16),(unsigned int)(Adr&0x0000ffff), 0xFF); }
if(!rsp) rsp1 = MMC_read(512); //Заполним массив Sector[]
if(!rsp1){ StringPrepare("Block read Err."); Scroll(); ScrollUpdate(); }
PORTB |= MMC_CE_PIN; //MMC-disable
//StringPrepare("CMD17 "); HexToStr(rsp, 7); Scroll();
crc = MMC_crc16(&Sector[0], 512);
if(crc1!=(unsigned char)(crc >> 8) || crc2!=(unsigned char)(crc & 0xff) ){
StringPrepare("CRC Err ");
HexToStr(crc1, 5); HexToStr(crc2, 8); Scroll();
ScrollUpdate(); }
}


PS: Код писал около года назад, помню, что многие проблемы решились после того, как добавил расчет CRC
по спецификации CRC является обязательной только для команд иницилизации, потом может быть отключена.
Genadi Zawidowski
В карту при этом должны вдвигаться все "1" по SPI.
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 23 2013, 20:11) *
В карту при этом должны вдвигаться все "1" по SPI.


не понял насчёт выдвигания "1" по SPI. Поясните, если нетрудно.

Привожу функцию чтения сектора. Что и где надо добавить?
Мучаюсь уже второй день (((

CODE
unsigned char SD_readSingleBlock(unsigned long startBlock)
{
unsigned char response;
unsigned int i, retry=0;

//передаём в карту байты 0xFF до тех пор , пока карта не начнёт
//отвечать 0xFF, т.е. будет свободна
while(1)
{
if(SPI_transmit(0xFF)==0xff)
break;
}

// эта команда выполнятеся корректно - ответ 0x00
response = SD_sendCommand(READ_SINGLE_BLOCK, startBlock<<9); //read a Block command
//block address converted to starting address of 512 byte Block
if(response != 0x00) //check for SD status: 0x00 - OK (No flags set)
return response;

SD_CS_ASSERT; // CS опускаем в ноль

// а вот тут маркера так и не поступает
while(SPI_receive() != 0xfe) //wait for start block token 0xfe (0x11111110)
if(retry++ > 0xfffe){SD_CS_DEASSERT;
return 1;} //return if time-out

for(i=0; i<512; i++) //read 512 bytes
buffer[i] = SPI_receive();

SPI_receive(); //receive incoming CRC (16-bit), CRC is ignored here
SPI_receive();

SPI_receive(); //extra 8 clock pulses

SD_CS_DEASSERT;

return 0;
}
ukpyr
Цитата
SD_CS_ASSERT; // CS опускаем в ноль
SD_CS_DEASSERT;
а это зачем ? CS должен быть постоянно в нуле
Genadi Zawidowski
Цитата
не понял насчёт выдвигания "1" по SPI. Поясните, если нетрудно.
Значит, что SPI_receive() должна быть определена через SPI_transmit(0xFF) - именно FF и ничего другого.
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 24 2013, 19:13) *
Значит, что SPI_receive() должна быть определена через SPI_transmit(0xFF) - именно FF и ничего другого.


Вы имеете ввиду посылать на карту байты 0xFF, пока не придёт маркер 0xFE?
Если так, то данный ход, к сожалению, не проходит.

Ещё такой момент. Инициализация карты не всегда проходит успешно 3 раза проходит, 1 раз - нет.

Может причину искать в инициализации?

Evgenius_Alex
Цитата(Evgenius_Alex @ Jan 28 2013, 10:34) *
Вы имеете ввиду посылать на карту байты 0xFF, пока не придёт маркер 0xFE?
Если так, то данный ход, к сожалению, не проходит.

Ещё такой момент. Инициализация карты не всегда проходит успешно 3 раза проходит, 1 раз - нет.

Может причину искать в инициализации?


Зацикливание происходит на этапа ожидания ответа на ACMD41
Genadi Zawidowski
Неиспользуемые линии данных карты подтянуты через резисторы к питанию?

Кроме того -
Цитата
shitch CRC check on - required before ACMD41

Это цитата из моих исходников, между прочим. Где вычитал - наверное, в спецификации... Возмите чей-нибудь (хоть мой) рабочий вариант и сравните - всё ли у Вас правильно делается.

CODE
// set MMC in Idle mode
// Вызывается, подразумевая что CS установлен
static char mmcGoIdle(void)
{
unsigned char response;
unsigned long cmd58answer, cmd8answer = 0;

SDCARD_CS_HIGH();
nvram_read_byte(targetsdcard);
SDCARD_CS_LOW();

nvram_read_byte(targetsdcard);
//Send Command 0 to put MMC in SPI mode
mmcSendCmdCRC7(MMC_GO_IDLE_STATE, 0); // CMD0 - Обязательно с правильным CRC
//Now wait for READY RESPONSE
if ((response = mmcGetResponseR1()) != 0x01)
{
return MMC_INIT_ERROR;
}

nvram_read_byte(targetsdcard);

mmcSendCmdCRC7(MMC_SEND_IF_COND, 0x000001aa); // CMD8 - Обязательно с правильным CRC. 3.3 Volt VCC
response = mmcGetResponseR7(& cmd8answer);
if ((response & 0x04) != 0) // illegal command or pattern not match
{
mmcAddressMultiplier = MMC_SECTORSIZE;
mmcCardVersion2 = 0;

// if no responce
// Ver2.00 or later SD Memory Card(voltage mismatch)
// or Ver1.X SD Memory Card
// or not SD Memory Card
debug_printf_P("Ver1.X SD Memory Card or not SD Memory Card\n");

// check voltage range here
nvram_read_byte(targetsdcard);
mmcSendCmd(0x40 + 58, 0); // CMD58
response = mmcGetResponseR3(& cmd58answer);
//debug_printf_P("CMD58 has responce %02x, value = %08lx\n", response, cmd58answer);
if (response == 0x00 || response == 0x01)
{
if ((cmd58answer & (1UL << 21)) || (cmd58answer & (1UL << 20)))
{
debug_printf_P("3.3 volt VCC suitable.\n");
}
else
{
debug_printf_P("3.3 volt VCC NOT suitable.\n");
return MMC_INIT_ERROR;
}
}

for (;;)
{
nvram_read_byte(targetsdcard);
mmcSendCmd(MMC_APP_CMD, 0); // APP_CMD
//Now wait for READY RESPONSE
response = mmcGetResponseR1();
//debug_printf_P("APP_CMD responce R1 = %02x\n", response);
if (response != 0x01 && response != 0x00)
continue;

nvram_read_byte(targetsdcard);
mmcSendCmd(MMC_ACMD41, 0); // ACMD41(0)
//Now wait for READY RESPONSE
response = mmcGetResponseR1();
//debug_printf_P("ACMD41 responce R1 = %02x\n", response);
if (response == 0x00)
break;
}
//debug_printf_P("ACMD41 has responce %02x\n", response);

//return MMC_INIT_ERROR;
}
else if ((cmd8answer & 0xff) != 0xaa)
{
debug_printf_P("Bad CMD8 answer. SD CArd unuseable\n");
return MMC_INIT_ERROR;
}
else
{
mmcCardVersion2 = 1;

// Ver2.00 or later SD Memory Card
debug_printf_P("Ver2.00 or later SD Memory Card\n");

// check voltage range here
nvram_read_byte(targetsdcard);
mmcSendCmd(0x40 + 58, 0); // CMD58
response = mmcGetResponseR3(& cmd58answer);
//debug_printf_P("CMD58 has responce %02x, value = %08lx\n", response, cmd58answer);
if (response == 0x00 || response == 0x01)
{
if ((cmd58answer & (1UL << 21)) || (cmd58answer & (1UL << 20)))
{
debug_printf_P("3.3 volt VCC suitable.\n");
}
else
{
debug_printf_P("3.3 volt VCC NOT suitable.\n");
return MMC_INIT_ERROR;
}
}

nvram_read_byte(targetsdcard);
mmcSendCmd(0x40 + 59, 1); // shitch CRC check on - required before ACMD41
response = mmcGetResponseR1();
//debug_printf_P("CMD59 has responce %02x\n", response);

for (;;)
{
nvram_read_byte(targetsdcard);
mmcSendCmdCRC7(MMC_APP_CMD, 0); // APP_CMD
//Now wait for READY RESPONSE
response = mmcGetResponseR1();
//debug_printf_P("APP_CMD responce R1 = %02x\n", response);
if (response != 0x01 && response != 0x00)
continue;

nvram_read_byte(targetsdcard);

mmcSendCmdCRC7(MMC_ACMD41, 0x40000000); // HCS (High Capacity Support)
//Now wait for READY RESPONSE
response = mmcGetResponseR1();
//debug_printf_P("ACMD41 responce R1 = %02x\n", response);
if (response == 0x00)
break;
}

nvram_read_byte(targetsdcard);
mmcSendCmdCRC7(0x40 + 59, 0); // shitch CRC check off
response = mmcGetResponseR1();
//debug_printf_P("CMD59 has responce %02x\n", response);

// Check CCS bit (capacity) bit
nvram_read_byte(targetsdcard);
mmcSendCmd(0x40 + 58, 0); // CMD58
response = mmcGetResponseR3(& cmd58answer);
//debug_printf_P("CMD58 has responce %02x, value = %08lx\n", response, cmd58answer);
if ((cmd58answer & 0x40000000) != 0) //CCS (Card Capacity Status)
{
debug_printf_P("SDHC or SDXC (High Capacity)\n");
mmcAddressMultiplier = 1; // Для SDHC (адресация идёт в 512-ти байтовых блоках)
}
else
{
debug_printf_P("SDSD - up to 2GB\n");
mmcAddressMultiplier = MMC_SECTORSIZE; // Для обычных SD карт
}

}

//debug_printf_P("mmcGoIdle() done.\n");

nvram_read_byte(targetsdcard);
return (MMC_SUCCESS);
}

Evgenius_Alex
Спасибо, Геннадий!

Неиспользуемые линии были не подтянуты. Подтянул их к "+" питания, но всё равно проблема осталась.
Всё вроде бы делаю правильно. На команду CMD0 приходит корректный ответ - 0x01.
Правда, и 1-й и 2-й байты CRC равны 0xFF.

На команду CMD8 ответ может приходить разный (0x03, 0x07, 0xC0 и т.д.)

А на связке команд CMD55+ACMD41 программа вообще зацикливается.

Ничего не могу понять. Вот мой код функции инициалзации.

CODE
//function инициализация карты памяти
//return 0 - карта инициализирована
unsigned char SD_init(void)
{
unsigned char i, response, CRC1, CRC2;
unsigned char SD_version = 2; //по умолчанию версия SD = 2

int retry=0;

delay_ms(1); //задержка 1 мс, чтобы стабилизировалось напряжение

for(i=0;i<10;i++)
SPI_transmit(0xff); // 10*8 бит = 80 пустых циклов

SD_CS_ASSERT; //вывод CS опустить в ноль

do
{
//команда CMD0
response = SD_sendCommand(GO_IDLE_STATE, 0); //send 'reset & go idle' command
retry++;

if(retry>0xfe) {
return 1;
} //time out
} while(response != 0x01);

// Эти байты всегда равны 0xFF!!!!
CRC1 = SPI_transmit(0xff); //читаем первый байт CRC
CRC2 = SPI_transmit(0xff); //читаем второй байт CRC

SD_CS_DEASSERT;

retry = 0;

//команда CMD8 - приходят разные ответы
while((response = SD_sendCommand(SEND_IF_COND,0x000001AA)) != 0x01 )
{
if(retry++>0xfe)
{
SD_version = 1;
break;
}
}

retry = 0;

// а в этом цикле программа зацикливается и не выходит по команде return 1
do
{
response = SD_sendCommand(APP_CMD,0); //команда CMD55
response = SD_sendCommand(SD_SEND_OP_COND,0x40000000); //команда ACMD41

retry++;

if(retry>0xfffe)
return 1;

} while(response != 0x00);

//читаем регистр OCR, чтобы определить тип карты
retry = 0;
SDHC = 0;
if (SD_version == 2)
{
while((response = SD_sendCommand(READ_OCR,0))!=0x00)
if(retry++>0xfe) break;
}

putchar1('O');
putchar1('K');
putchar1('!');

return 0;
}
Genadi Zawidowski
Ну так если у Вас карта на SDHC, не v2 - вполне может весь этот мусор приходить...
приведите весь код, или хотя бы, получение ответа.
Да, надеюсь, SPI MODE3 используете?
Поймите, весь "огород" я наворотил не зря - как раз для поддержки всех карточек. И пока не встречал тех, с чем не работает.
Судя по коду, Вы взяли за основу пример от TI? Я, во всяком случае, с него начинал - и вот во что превратилось...
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 29 2013, 00:50) *
Ну так если у Вас карта на SDHC, не v2 - вполне может весь этот мусор приходить...
приведите весь код, или хотя бы, получение ответа.
Да, надеюсь, SPI MODE3 используете?
Поймите, весь "огород" я наворотил не зря - как раз для поддержки всех карточек. И пока не встречал тех, с чем не работает.
Судя по коду, Вы взяли за основу пример от TI? Я, во всяком случае, с него начинал - и вот во что превратилось...


Я использую SPI MODE0.... SPI CLOCK RATE - 125 кГц. Может в этом проблема?

Пример кода я брал вот по этой ссылке http://mycontroller.ru/stm32-sd-card-inits...kartyi-pamyati/

А не могли бы Вы привети полностью весь код ?

С описанием всех функций (таких как mmcSendCmdCRC7, mmcGetResponseR1, mmcGetResponseR7 и т.д.)
Genadi Zawidowski
Насчёт описания тяжело... Вот проект.
Пока ничего не меняя смените на mode3 - но у меня выбирать можно было только из mode2 и mode3 - скорее всего, состояние MOSI вне посылки не важно, но полярность клока (по какому перепаду обновлять) важна. Как я понял, к моему работаеюему mode3 допустимая замена mode1.
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 29 2013, 17:28) *
Насчёт описания тяжело... Вот проект.
Пока ничего не меняя смените на mode3 - но у меня выбирать можно было только из mode2 и mode3 - скорее всего, состояние MOSI вне посылки не важно, но полярность клока (по какому перепаду обновлять) важна. Как я понял, к моему работаеюему mode3 допустимая замена mode1.


К сожалению, изменение режима SPI не помогло. Пробовал все четыре режима SPI.

Придётся адаптировать Ваш код под ATMega128.

Правда, я смотрю, у Вас в коде типы данных есть непонятные. Я с ARM-ами никогда не работал.
Например uint_fast8_t, uint_fast32_t, uint_fast64_t. Чем их можно заменить?

И ещё не могут быть такие траблы из-за электрически неправильно собранной цепи?

Правда, у меня всё стандартно.
Согласующие резисторы между картой и МК на всех линиях - 510 Ом.
Подтягивающие резисторы к плюс питания - 51 кОм.
На всех линиях, кроме MISO стоят защитные стабилитроны на 3.3В.
Genadi Zawidowski
непонятные типы - это из stdint.h - смысл типов надеюсь понятен.
А что, у Вас ешё и процессор не от 3.3 питается? Что за 510 Ом?
Антизвонные на 22..56 ом я ещё понимаю...
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 30 2013, 19:15) *
непонятные типы - это из stdint.h - смысл типов надеюсь понятен.
А что, у Вас ешё и процессор не от 3.3 питается? Что за 510 Ом?
Антизвонные на 22..56 ом я ещё понимаю...


Привожу Вам схему соединения ATMega128 и SD карт.
.
Atmega128 питается от 5 В, SD карта - от 3.3 В

R2-R6 - 51 кОм подтягивающие к +3.3В
R7-R10 - 510 Ом резисторы для согласования уровней 5В и 3.3В
VD1-VD3 - защитные стабилитроны на 3.3 Вольт
Genadi Zawidowski
Цитата(Evgenius_Alex @ Jan 31 2013, 11:58) *
Привожу Вам схему соединения ATMega128 и SD карт...



Так, для справки: нормированная ёмкость стабилитрона BZX84C3V3 на 3.3 вольта при нулевом обратном смещении - 450 пикофарад.
И процессор начинает эти 450 пикофарад через 510 ом заряжать... Ёмкость, разумеется, упадёт до сотни пикофарад при полутора вольтах (когда дойдём до порога переключения логики в карте)... Если ещё где-то нормированы скорости нарастания на клоке...
Я даже не хочу предсказывать, как всё это будет работать в "боевом" применении. И не поможет никакое снижение скорости чередования импульсов - уже завалены фронты сигнала.

Не говоря про то, что минимальный уровень логической "1" на входе ATMEGA128 должен быть 0.6 * VCC - что при 5.0 вольтах питания равно 3.0 вольта. Скажем так, запас никакой.

Сделайте нормально всё здесь. Самое простое - снизить питание процессора до 3.3 вольта и выкинуть весь этот "огород".
Если нужны 16 МГц - найти слегка другой процессор или применить трансляторы уровней вроде sn74lvc1t45 или что-ибудь специальное, для карточек.
ut1wpr
Цитата(Genadi Zawidowski @ Jan 31 2013, 22:53) *
Так, для справки...
Я тоже ходил по этим граблям. Явно видно осциллографом завал фронта клока. Отказался от стабилитронов. Сделал просто делитель, причем ток через него выбирал побольше, входную емкость карточки тоже надо учитывать. Помогало, но не очень. Были сбои. Поставил параллельно верхнему плечу делителя форсирующую емкость в пару десятков пик - фронт заметно улучшился. Увеличивал емкость электролита возле карточки. Читать-писать стало заметно лучше. Но все равно проблемно. Вывод - или сопрягать уровни микросхемой-согласователем, или переводить МК на 3.3 Как только перевел - все менингиты снялись. В одну телегу впрячь не должно коня и трепетную лань.. sm.gif Гена прав.
MiklPolikov
Цитата(ut1wpr @ Feb 1 2013, 02:45) *
Я тоже ходил по этим граблям...


УУУххх....

Вместо всех этих делителей и стабилитронов надо использовать резисторы + диоды шоттки закинутые на +питания карточки.
Или вообще не использовать диоды, наверняка они и так в карточке для защиты стоят.
Evgenius_Alex
Цитата(Genadi Zawidowski @ Jan 31 2013, 23:53) *
Так, для справки: нормированная ёмкость стабилитрона BZX84C3V3 на 3.3 вольта при нулевом обратном смещении - 450 пикофарад.
И процессор начинает эти 450 пикофарад через 510 ом заряжать... Ёмкость, разумеется, упадёт до сотни пикофарад при полутора вольтах (когда дойдём до порога переключения логики в карте)... Если ещё где-то нормированы скорости нарастания на клоке...
Я даже не хочу предсказывать, как всё это будет работать в "боевом" применении. И не поможет никакое снижение скорости чередования импульсов - уже завалены фронты сигнала.

Не говоря про то, что минимальный уровень логической "1" на входе ATMEGA128 должен быть 0.6 * VCC - что при 5.0 вольтах питания равно 3.0 вольта. Скажем так, запас никакой.

Сделайте нормально всё здесь. Самое простое - снизить питание процессора до 3.3 вольта и выкинуть весь этот "огород".
Если нужны 16 МГц - найти слегка другой процессор или применить трансляторы уровней вроде sn74lvc1t45 или что-ибудь специальное, для карточек.


Спасибо, Геннадий, за Ваш совет! Он оказался верным.

Действительно, убрал стабилитроны и всё заработало. Правда, питание не менял, оставил 5В для Меги и 3.3В для карты.
Инициализация проходит 100% без сбоев, чего раньше на наблюдалось.
Данные пишутся тоже нормально. С чтением, правда, проблемы - с карты 512Мб читается, с карты 2Гб - не читается. Надо разбираться.

А по поводу перехода процессора на 3.3В. Дело в том, что это довольно сложное устройство, уже отлаженное, неизвестно как оно
поведёт себя при пониженном напряжении. Над этим надо ещё подумать. Может действительно проще поставить микросхему-согласовватель уровней и оставить всё как есть.
Genadi Zawidowski
У HC карточек адресация не смещением, а номером блока. В моём проекте это есть...
МП41
Так 2Гб карточка не должа быть HC.
Genadi Zawidowski
Я видел 2GB и HC и не HC.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.