Перейти к содержанию
    

Работа с SD-картой в SPI-режиме

Просьба к знатокам, помогите разобраться. Перелопатил в Сети массу информации, иногда противоречивой. Проштудировал спецификацию, но не помогло. Проблема на этапе инициализации.

Как рекомендовано, после подачи питания произвожу программный сброс карты (80 клоков при высоком уровне CS, затем команда CD0). Проходит успешно, получаю в ответ R1= 0х01, как и должен. Затем команда CMD8. Определяется как illegal старой версией карты (R1=0x05), но последовательность CMD55 + ACMD41 проходит нормально, в итоге карта инициализируется (R1=0x00). Это карта стандартной ёмкости, 1Гб. Другая старенькая карта, micro-SD ёмкостью 128Мб, команду CMD8 признаёт, отвечает последовательностью R7 (т.е. R1=0x01 и четыре последних байта =0х000001АА), то есть как положено. Затем успешно проходит инициализацию связкой CMD55 + ACMD41 и в итоге тоже готова к работе. Другие команды после этого (например CMD58 или CMD1) воспринимает адекватно, нет проблем.

Зато иная ситуация с относительно новыми micro-SD картами ёмкостью 16Гб, 32Гб и 64Гб. Они словно из другого теста. Сброс также проходит успешно, команда CMD8 тоже. Если после этого применяю для инициализации "устаревшую" команду CMD1, то она проходит, выдаёт R1= 0х01, но сколько ни повторяй, в режим готовности (R1=0x00) не переходит.

Если же после CMD8 пытаюсь применить CMD55 + ACMD41, то уже на этапе ответа на первую часть (CMD55) получаю вместо R1= 0х01 байт 0хF0, за ним 0x1F, и после MISO уже всё время находится в высоком состоянии, карта буквально ни на что не реагирует, включая попытки сброса. ACMD41 подавать уже, разумеется, бесполезно. Уходит в глубокий "отказ". Помогает только отключение питания. Все три ведут себя абсолютно идентично. Причём сами исправны, с компьютера читаются и пишутся (через USB-адаптер, разумеется). Вот осциллограмма реакции на CMD55:

Clipboard02.thumb.jpg.34c4bc3a492549e446840102541efe94.jpg

Если присмотреться, то такое впечатление, что ответ приходит на четыре такта клока позже положенного времени. То есть, если сдвинуть эти два байта (0хF0 и 0x1F) на четыре бита влево, то как раз и получим R1= 0х01, за ним 0xFF. Но как такое может быть?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В стародавние времена был у меня проект, который натаскивал на кучу разных карт. Последовательность инициализации была в результате такая:

1. CMD0

2. CMD8

3. Попытка включить CRC при помощи CMD59, в случае ошибки  запоминаем, что на этом этапе её делать не стоит, и продолжаем

4. CMD58

5. ACMD41

6. CMD59, если не удался шаг 3

7. CMD58

8. CMD9

Попробуйте добавить "необязательные" CMD59 и CMD58.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот фрагмент начала инициализации (после вставки карты) из моего старого проекта с SD. См. FazaInsert():

Скрытый текст

enum CardType {
  CARD_UNKNOWN, CARD_MMC, CARD_SD1, CARD_SD2_BYTE, CARD_SD2_BLOCK, CARD_n};
static CardType cardType = CARD_UNKNOWN;

enum { //Команды SD/MMC
  CM_GO_IDLE_STATE = 0,                //программный сброс
  CM_SEND_OP_COND = 1,                 //начать процесс инициализации
  CMA_SEND_OP_COND = 41 | B7,          //начать процесс инициализации (только SDC)
  CM_SEND_IF_COND = 8,                 //проверка диапазона напряжения (только SDC v2)
  CM_SEND_CSD = 9,                     //считать регистр CSD
  CM_SEND_CID = 10,                    //считать регистр CID
  CM_STOP_TRANSMISSION = 12,           //прекратить чтение данных
  CM_SEND_STATUS = 13,                 //считать статус карты
  CM_SET_BLOCKLEN = 16,                //установка размера блока чтения/записи
  CM_READ_SINGLE_BLOCK = 17,           //считать блок
  CM_READ_MULTIPLE_BLOCK = 18,         //считать множество блоков
  CM_SET_BLOCK_COUNT = 23,             //установка числа блоков для передачи со
                                       //следующей командой многоблочного
                                       //чтения/записи (только MMC)
  CMA_SET_WR_BLK_ERASE_COUNT = 23 | B7,//определить число блоков для предварительного
                                       //стирания для следующей многоблочной команды
                                       //записи (только SDC)
  CM_WRITE_BLOCK = 24,                 //записать блок
  CM_WRITE_MULTIPLE_BLOCK = 25,        //записать множество блоков
  CM_PROGRAM_CSD = 27,                 //записать изменяемые биты CSD
  CM_SET_ERASE_START = 32,             //установить начальный стираемый сектор для CM_ERASE
  CM_SET_ERASE_END = 33,               //установить конечный стираемый сектор для CM_ERASE
  CM_ERASE = 38,                       //стереть цепочку секторов заданную CM_SET_ERASE_START/CM_SET_ERASE_END
  CM_LOCK_UNLOCK = 42,                 //установка/сброс пароля или состояния блокировки
  CM_APP_CMD = 55,                     //стартовая команда из ACMD <n> команд
  CM_READ_OCR = 58,                    //считать OCR
  CM_CRC_ON_OFF = 59                   //вкл.(arg=1) или выкл.(arg=0) проверку CRC команд
};

//Ожидание вставки SD-карты и инициализация её затем
static FFaza FazaInsert()
{
  ...
  if (SendCmd(CM_GO_IDLE_STATE) != 1) return (FFaza)FazaUnsupportCard;
  if (SendCmd(CM_SEND_IF_COND, 0x1AA) != 1) {
    cardType = CARD_SD1;
    if (WaitInitSD(CMA_SEND_OP_COND)) {
      if (WaitInitSD(CM_SEND_OP_COND)) return (FFaza)FazaUnsupportCard;
      cardType = CARD_MMC;
    }
  } else if ((GetR3R7() ^ 0x1AA) & B12 - 1) return (FFaza)FazaUnsupportCard;
  else {
    u32 j;
    if (WaitInitSD(CMA_SEND_OP_COND, B30)) return (FFaza)FazaUnsupportCard;
    if (SendCmd(CM_READ_OCR) >> 1) return (FFaza)FazaUnsupportCard;
    LogCatchOut();
    j = GetR3R7();
    LogCR(COL_BLACK "SD-info:" EOL COL_GREEN "OCR: 0x%08X", j);
    cardType = CARD_SD2_BYTE;
    if (j & B30) cardType = CARD_SD2_BLOCK;
    j = j >> 4 & 0xFFFFF;
    if ((i = 31 - (c = CntLeadingZeros(j))) >= 0) {
      c = (i += 17) - CntLeadingZeros(~(j << c));
      LogCR(COL_GREEN "Voltage range: %u.%u-%u.%u V",
        (uint)c / 10, (uint)c % 10, (uint)i / 10, (uint)i % 10);
    }
    LogReleaseOut();
  }
  SendCmd(CM_CRC_ON_OFF, 1);
  while (1) {
    u32 cc1, cc2;
    if (!SendCmd(CM_SEND_CSD))
...
}

//Передача команды SD-карте
//Возвращает ответ R1 который вернула карта или -1 при таймауте.
static int SendCmd(int op, u32 arg = 0)
{
...
}  

//Ожидание завершения процесса инициализации SD-карты.
static int WaitInitSD(int op, u32 arg = 0)
{
  int i;
  u32 t = sysTimer;
  do if ((i = SendCmd(op, arg)) != 1) return i;
  while (!istout(t, ms2tkt(1000)));
  LogsCR(COL_RED "Initialization process timeout!");
  return -1;
}

Работало со всеми имевшимися типами карт (начиная от самой старой MMC 256МБ и до SDHC 32ГБ).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вставлю-ка и я свою болванку)
 

Скрытый текст

/**
 \brief Драйвер SD-карты в режиме SPI
 */

#include "sdcard.hpp"
#include "modules/libs/crc_calcs/crc7.hpp"
#include "drivers/spi/virtual_spi.hpp"
#include "modules\libs\os_abstract_api\os_crit_sec.hpp"
#include "modules\libs\usefulmacro.hpp"
#include "modules\libs\os_abstract_api\os_timers.hpp"
#include "lpc43xx_scu.h"
#include "global_config.hpp"
#include <type_traits>

#define CD_GPIO_INT_(n)         GPIO##n##INT
#define CD_GPIO_INT(n)          CD_GPIO_INT_(n)
#define CD_NVIC_INT_(n)         PIN_INT##n##_IRQn
#define CD_NVIC_INT(n)          CD_NVIC_INT_(n)
#define CD_NVIC_HANDLER_(n)     GPIO##n##_IRQHandler
#define CD_NVIC_HANDLER(n)      CD_NVIC_HANDLER_(n)

SemaphoreHandle_t SdCard::insertEjectSemaphore;
const size_t SdCard::INITIAL_SPEED_KHZ = 100000;
const float SdCard::MAX_SPEED_MULTIPLIER = 1.0;

static const uint16_t sd_crc16_table_a[0x10] =
{
    0x0000, 0x1231, 0x2462, 0x3653,
    0x48c4, 0x5af5, 0x6ca6, 0x7e97,
    0x9188, 0x83b9, 0xb5ea, 0xa7db,
    0xd94c, 0xcb7d, 0xfd2e, 0xef1f
};

static const uint16_t sd_crc16_table_b[0x10] =
{
    0x0000, 0x1021, 0x2042, 0x3063,
    0x4084, 0x50a5, 0x60c6, 0x70e7,
    0x8108, 0x9129, 0xa14a, 0xb16b,
    0xc18c, 0xd1ad, 0xe1ce, 0xf1ef
};
#if( SD_INSERT_HOOK==1)
extern void sd_insert_hook( bool result );
#endif
#if( SD_EJECT_HOOK ==1)
extern void sd_eject_hook();
#endif

//extern "C" void CD_NVIC_HANDLER( SD_CD_GPIO_INT_NUM ) ( ){
//        SdCard::isrService( );
//}

SdCard::descriptor_t::descriptor_t()
    : capacity(0)
    , block_length(0)
    , sector_size(0) {}

SdCard::SdCard( CAbstractSSPDriver * _sspdrv )
    : m_sspdrv(_sspdrv)
    , m_card_version(SD_CARD_NOT_FOUND) {
    m_descriptor = new descriptor_t;
    configASSERT(m_descriptor);
    m_cmd_frame = new host_cmd_format_t();
    configASSERT(m_cmd_frame);
    m_dsknum = FileSystem::NO_DISK;
}

TRetVal SdCard::initSDv1() {
    TRetVal retVal;
    /// TODO Не тестировал, т.к. нет такой старой карты
    m_card_version = SD_CARD_NOT_FOUND;
    kprintf("Пытаемся инициализировать SD ver. 1.x\r\n");
    spi_r3_response_cmd_t response3;
    spi_r1_response_cmd_t response1;
    retVal = command(READ_OCR, 0, response3);
    if (retVal == rvOK) {
        if (response3.r1.data[0] != IDLE_STATE) {
            kprintf("Команда CMD58 не поддерживается, это не SD-карта.\r\n");
            retVal = rvDISK_NOT_FOUND;
        } else if (!( response3.ocr.vdd_3_0__3_1_voltage ||
                      response3.ocr.vdd_3_1__3_2_voltage ||
                      response3.ocr.vdd_3_2__3_3_voltage ||
                      response3.ocr.vdd_3_3__3_4_voltage )) {
            kprintf("Карта не поддерживает диапазон напряжения хоста.\r\n");
            retVal = rvDISK_SD_VDD_WINDOW;
        } else {
            kprintf("Карта поддерживает напряжения и команду.\r\n");
            int acmd41_try_counter = 10;
            do {
                kprintf("Отравляем ACMD41...\r\n");
                retVal = command(APP_CMD, 0, response1);
                if (retVal != rvOK) {
                    kprintf("Ошибка отправки CMD55");
                    continue;
                } else {
                    if (response1.data[0] != IDLE_STATE) {
                        kprintf("После CMD55 мы не в IDLE.\r\n");
                        retVal = rvFAILED;
                        break;
                    } else {
                        retVal = command(APP_OP_COND, 0, response1);
                        if (retVal != rvOK) {
                            kprintf("Ошибка отправки ACMD41.\r\n");
                            break;
                        }
                    }
                }
                delayMs(100); /// Грубо, но таких карт очень мало, если они есть, так, что
                              /// таймату можно вот так в лоб сделать
            }while (response1.fields.in_idle_state && --acmd41_try_counter);
            if (response1.fields.in_idle_state || response1.fields.illegal_command) {
                kprintf("Карта остаётся в IDLE или это не SD-карта.\r\n");
                retVal = rvFAILED;
                m_card_version = SD_CARD_NOT_FOUND;
            } else {
                kprintf("Карта переведена в рабочий режим.\r\n");
                m_card_version = SD_V1;
            }
        }
    }
    return retVal;
}

TRetVal SdCard::initSDv2() {
    kprintf("Пытаемся разобраться это ver. 2.00 or later, SDSC или SDHC\r\n");
    TRetVal retVal;
    spi_r3_response_cmd_t response3;
    spi_r1_response_cmd_t response1;
    m_card_version = SD_CARD_NOT_FOUND;
    retVal = command(READ_OCR, 0, response3);
    if (retVal == rvOK) {
        if (response3.r1.data[0] != IDLE_STATE) {
            kprintf("Команда CMD58 не поддерживается, это не SD-карта.\r\n");
            retVal = rvDISK_NOT_FOUND;
        } else if (!( response3.ocr.vdd_3_0__3_1_voltage ||
                      response3.ocr.vdd_3_1__3_2_voltage ||
                      response3.ocr.vdd_3_2__3_3_voltage ||
                      response3.ocr.vdd_3_3__3_4_voltage )) {
            kprintf("Карта не поддерживает диапазон напряжения хоста.\r\n");
            retVal = rvDISK_SD_VDD_WINDOW;
        } else {
            kprintf("Карта поддерживает напряжения и команду.\r\n");
            /// Таймаут на ожидание готовности карты
            kprintf("Отравляем ACMD41.");
            OsApi::Time::Timeout tmt(2000);
            do {
                kprintf(".");
                retVal = command(APP_CMD, 0, response1);
                if (retVal != rvOK) {
                    kprintf("Ошибка отправки CMD55...");
                    RET_VAL(retVal);
                    continue;
                } else {
                    if (response1.data[0] != IDLE_STATE) {
                        kprintf("После CMD55 мы не в IDLE.\r\n");
                        retVal = rvFAILED;
                        continue;
                    } else {
                        app_op_cond_arg_t arg41;
                        memset(&arg41, 0, sizeof( arg41 ));
                        /// Мы поддерживаем SDHC || SDXC
                        arg41.fields.host_capacity_support = 1;
                        retVal = command(APP_OP_COND, arg41.data, response1);
                        if (retVal != rvOK) {
                            kprintf("Ошибка отправки ACMD41.\r\n");
                            continue;
                        }
                    }
                }
            }while (!tmt.isExperied() && response1.fields.in_idle_state);
            kprintf("\r\n");
            if (response1.fields.in_idle_state) {
                kprintf("Карта остаётся в IDLE.\r\n");
                retVal = rvFAILED;
                m_card_version = SD_CARD_NOT_FOUND;
            } else {
                kprintf("Карта переведена в рабочий режим.\r\n");
                kprintf("Включаем CRC.\r\n");
                retVal = turnCrc(true);
                if (retVal == rvOK) {
                    /// Таймаут на ожидание готовности питания
                    tmt.reset();
                    tmt.setTimeout(2000);
                    do {
                        retVal = command(READ_OCR, 0, response3);
                        if (retVal != rvOK) {
                            kprintf("Ошибка отправления команды CMD58.\r\n");
                            RET_VAL(retVal);
                            continue;
                        }
                    }while (!tmt.isExperied() && !response3.ocr.busy_status);
                    if (!response3.ocr.busy_status) {
                        kprintf("Powerup doesn't set.\r\n");
                        retVal = rvFAILED;
                    } else {
                        if (response3.ocr.card_capacity_status) {
                            kprintf("Hight Capacity or Externded Capacity SD Card.\r\n");
                            m_card_version = SD_V2_HC;
                        } else {
                            kprintf("Standard Capacity SD Card.\r\n");
                            m_card_version = SD_V2;
                        }
                    }
                }
            }
        }
    }
    return retVal;
}

TRetVal SdCard::open( const void * params ) {
    DRV_OPEN_BEGIN();
    powerOff();
    configASSERT(m_sspdrv);
    DRV_RET_VAL(m_sspdrv->open());
    gpioConfig();
    m_responseBuffer = new uint8_t[8];
    configASSERT(m_responseBuffer);
    insertEjectSemaphore = xSemaphoreCreateBinary();
    configASSERT(insertEjectSemaphore);
    configASSERT(xTaskCreate(task, "sddetect", TSK_STACK_SIZE, NULL, TSK_PRIO, NULL) == pdPASS);
    DRV_OPEN_END();
}

TRetVal SdCard::close() {
    powerOff();
    openResult = rvCLOSE;
    return rvOK;
}

std::string SdCard::getDrvName() const {
    configASSERT(m_sspdrv);

    return "sdcard @ " + m_sspdrv->getDrvName();
}

/*!
 * Команды управления диском.
 *
 * @param _cmd команда.
 * @param _buff указтель на буфер с ответом.
 *
 * @return см. файл RetVals.hpp.
 */
TRetVal SdCard::ioctl( uint8_t _cmd, void * _buff ) {
    TRetVal rv = rvDISK_NOT_FOUND;
    DWORD * value = reinterpret_cast<DWORD *>(_buff);
    if (isCardReady()) {
        rv = rvOK;
        switch (_cmd) {
        case CTRL_SYNC:
            /// We have no cache, so do nothing
            break;
        case GET_SECTOR_COUNT:
            *value = m_descriptor->capacity / getFatSectorSize();
            break;
        case GET_SECTOR_SIZE:
            *value = getFatSectorSize();
            break;
        case GET_BLOCK_SIZE:
            *value = 1;
        case CTRL_TRIM:
            rv = rvFAILED;
            break;
        default:
            rv = rvPARAM_INCORRECT;
        }
    }
    return rv;
}


/*!
 * Желаемые действия по монтированию раздела.
 *
 * \return TRetVal
 */
TRetVal SdCard::mount() {
    return rvFAILED;
}
/*!
 * Желаемые действия по отмонтированию раздела.
 *
 * \return TRetVal
 */
TRetVal SdCard::umount() {
    return rvFAILED;
}
/*!
 * Читает статус карты по пину с её разъёма.
 *
 * \return CardStatus
 */
SdCard::Status SdCard::readCardDetectPin() {
    static const size_t SAMPLE_COUNT = 100;
    uint32_t inserted_counter = 0;
    for ( int i = 0; i < 50; i++ ) {
        if (GPIO_ReadValue(CD_GPIO) & LSHIFT(1, CD_GPIO_BIT))
            inserted_counter = 0;
        else
            inserted_counter++;
        delayMs(1);
    }
    return ( inserted_counter >= ( SAMPLE_COUNT / 2 ) ) ? Inserted : Ejected;
}

void SdCard::powerOn() {
    GPIO_SetOutput(SdCard::PE_GPIO, SdCard::PE_GPIO_BIT, SdCard::PE_ACTIVE_VALUE);
}

void SdCard::powerOff() {
    GPIO_SetOutput(SdCard::PE_GPIO, SdCard::PE_GPIO_BIT, !SdCard::PE_ACTIVE_VALUE);
}
template <typename T>
TRetVal SdCard::command( cmd_t cmd, uint32_t arg, T &resp, cs_t cs ) {
    configASSERT(m_responseBuffer);
#if 0
    kprintf("\r\nCMD: 0x%02x, ARG: 0x%02x\r\n", cmd, arg);
#endif
    CAbstractSSPDriver::TMsgType msg;
    if (std::is_same<spi_r1_response_cmd_t, T>::value)
        msg.sdcard.resp = SdCardTypes::Resp_t::r1;
    else if (std::is_same<spi_r2_response_cmd_t, T>::value)
        msg.sdcard.resp = SdCardTypes::Resp_t::r2;
    else if (std::is_same<spi_r3_response_cmd_t, T>::value) {
        msg.sdcard.resp = SdCardTypes::Resp_t::r3;
    } else if (std::is_same<spi_r7_response_cmd_t, T>::value)
        msg.sdcard.resp = SdCardTypes::Resp_t::r7;
#if 0
    kprintf("RESP: %d\r\n", msg.sdcard.resp);
#endif
    msg.timeout_ms = 700;
    msg.sdcard.use_sdcard_mode = true;
    msg.sdcard.sdcmd.fields.command_index = cmd;
    msg.sdcard.sdcmd.fields.argument = arg;
    msg.sdcard.sdcmd.fields.crc7 = CRC7::calc(&msg.sdcard.sdcmd.data[0], 5);
    msg.rxbuffer = m_responseBuffer;
    TRetVal retVal = m_sspdrv->txrx(&msg);
    if (retVal == rvOK) {
        if (!( *( m_responseBuffer + 0 ) & 0x80 )) {
            switch (msg.sdcard.resp) {
            case SdCardTypes::Resp_t::r1:
                memcpy(&resp, m_responseBuffer, sizeof( resp ));
                break;
            case SdCardTypes::Resp_t::r3:
                memcpy(&resp, m_responseBuffer, sizeof( resp ));
                break;
            case SdCardTypes::Resp_t::r7:
                memcpy(&resp, m_responseBuffer, sizeof( resp ));
                break;
            default:
                configASSERT(0);
            }
        } else
            retVal = rvDISK_NO_RESPONSE;
    }
    return retVal;
}
uint16_t SdCard::crc16( const uint8_t * buff, size_t length ) {
    uint16_t crc = 0x00;
    while (length--) {
        uint8_t data = *buff++ ^ ( crc >> 0x08 );
        crc = ( sd_crc16_table_a[( data & 0xf0 ) >> 4] ^ sd_crc16_table_b[data & 0x0f] ) ^ ( crc << 8 );
    }
    return crc;
}
TRetVal SdCard::writeFatSector( uint32_t _sector, const uint8_t * const _src, uint32_t _count ) {
#if 0
    kprintf("writeFatSector : _sector %d, _count %d\r\n", _sector, _count);
#endif
    TRetVal rv = rvDISK_NOT_FOUND;
    const uint8_t * p_src = _src;
    if (isCardReady()) {
        while (_count--) {
            rv = dataWrite(WRITE_BLOCK_SINGLE, _sector++ * getFatSectorSize(), p_src, getFatSectorSize());
            p_src += getFatSectorSize();
        }
    }
    return rv;
}
TRetVal SdCard::dataWrite( SdCard::cmd_t cmd, uint64_t address, const uint8_t * txbuf, size_t length ) {
    configASSERT(txbuf);
    configASSERT(m_responseBuffer);
    uint16_t txCrc16;
#if 0
    kprintf("CMD: 0x%02x, ADDR: 0x%08x, LEN: %d\r\n", cmd, address, length);
#endif
    CAbstractSSPDriver::TMsgType msg;
    msg.sdcard.resp = SdCardTypes::Resp_t::r1;
    msg.sdcard.use_sdcard_mode = true;
    msg.sdcard.sdcmd.fields.command_index = cmd;
    msg.sdcard.sdcmd.fields.argument = make_argument(( m_card_version == SD_V2_HC ) ? ( address / getFatSectorSize() ) : address);
    msg.sdcard.sdcmd.fields.crc7 = CRC7::calc(&msg.sdcard.sdcmd.data[0], 5);
    msg.rxbuffer = m_responseBuffer;
    msg.sdcard.length = length;
    msg.sdcard.txdata = txbuf;
    txCrc16 = crc16(txbuf, length);
    msg.sdcard.crc16 = &txCrc16;
    msg.timeout_ms = 5000;
    TRetVal retVal = m_sspdrv->txrx(&msg);
    if (retVal == rvOK) {
        if (*( m_responseBuffer + SdCardTypes::RESP_ERROR_OFFSET ) == 0xff)
            retVal = rvDISK_NO_RESPONSE;
        else {
            spi_r1_response_cmd_t * p_r1resp = reinterpret_cast<spi_r1_response_cmd_t *>(&m_responseBuffer[0]);
            if (p_r1resp->fields.illegal_command) {
                kprintf("dataRead illegal command.\r\n");
                retVal = rvFAILED;
            }
            if (retVal == rvOK) {
                uint8_t data_response = *( m_responseBuffer + SdCardTypes::TOKEN_ERROR_OFFSET );
                if (( ( data_response >> 1 ) & SdCardTypes::DATA_RESPONSE_TOKEN_MASK ) != SdCardTypes::DATA_RESPONSE_TOKEN_ACCEPTED) {
                    retVal = rvDISK_WRITE_ERROR;
                }
            }
        }
    }
    return retVal;
}
TRetVal SdCard::readFatSector( uint32_t _sector, uint8_t * _dst, uint32_t _count ) {
#if 0
    kprintf("readFatSector : _sector %d, _count %d\r\n", _sector, _count);
#endif
    TRetVal rv = rvDISK_NOT_FOUND;
    if (isCardReady()) {
        /// Да, я знаю, что есть CMD18, но лень реализовать мультиблочное чтение
        while (_count--) {
            rv = dataRead(READ_BLOCK_SINGLE, _sector++ * getFatSectorSize(), _dst, getFatSectorSize());
            _dst += getFatSectorSize();
            if (rv != rvOK)
                break;
        }
    }
    return rv;
}
TRetVal SdCard::dataRead( SdCard::cmd_t cmd, uint64_t address, uint8_t * rxbuf, size_t length ) {
    configASSERT(rxbuf);
    configASSERT(m_responseBuffer);
    uint16_t recvCrc16;
#if 0
    kprintf("CMD: 0x%02x, ADDR: 0x%08x, LEN: %d\r\n", cmd, address, length);
#endif
    CAbstractSSPDriver::TMsgType msg;
    msg.sdcard.resp = SdCardTypes::Resp_t::r1;
    msg.sdcard.use_sdcard_mode = true;
    msg.sdcard.sdcmd.fields.command_index = cmd;
    msg.sdcard.sdcmd.fields.argument = make_argument(( m_card_version == SD_V2_HC ) ? ( address / getFatSectorSize() ) : address);
    msg.sdcard.sdcmd.fields.crc7 = CRC7::calc(&msg.sdcard.sdcmd.data[0], 5);
    msg.rxbuffer = m_responseBuffer;
    msg.sdcard.length = length;
    msg.sdcard.rxdata = rxbuf;
    msg.sdcard.crc16 = &recvCrc16;
    msg.timeout_ms = 5000;
    TRetVal retVal = m_sspdrv->txrx(&msg);
    if (retVal == rvOK) {
        if (*( m_responseBuffer + SdCardTypes::RESP_ERROR_OFFSET ) == SdCardTypes::NO_CMD_RESPONSE)
            retVal = rvDISK_NO_RESPONSE;
        else {
            spi_r1_response_cmd_t * p_r1resp = reinterpret_cast<spi_r1_response_cmd_t *>(m_responseBuffer);
            if (p_r1resp->fields.illegal_command) {
                kprintf("dataRead illegal command. 0x%02x\r\n", p_r1resp->data[0]);
                retVal = rvFAILED;
            }
            if (retVal == rvOK) {
                if (*( m_responseBuffer + SdCardTypes::TOKEN_ERROR_OFFSET ) != SdCardTypes::SBR_START_TOKEN_VALUE) {
                    retVal = rvDISK_READ_ERROR;
                } else {
                    uint16_t calcCrc16 = crc16(rxbuf, length);
                    if (calcCrc16 != recvCrc16) {
                        retVal = rvCRC_FAILED;
                    }
                }
            }
        }
    }
    return retVal;
}

SdCard::spi_r2_response_cmd_t SdCard::getStatus() {
    spi_r2_response_cmd_t r2;
    command(SEND_STATUS, 0x00, r2);
//    dprintf("R2 %x %x\r\n", r2.data[0], r2.data[1]);
    return r2;
}

void SdCard::gpioConfig() {
    /* Input */

    scu_pinmux(SdCard::CD_PORT, SdCard::CD_PORT_BIT, MD_EZI, SdCard::CD_PORT_FUNC); // CD
    GPIO_SetDir(SdCard::CD_GPIO, 1 << SdCard::CD_GPIO_BIT, 0);
    /* Output */
    scu_pinmux(SdCard::PE_PORT, SdCard::PE_PORT_BIT, MD_EHS, SdCard::PE_PORT_FUNC); // POW
    GPIO_SetDir(SdCard::PE_GPIO, 1 << PE_GPIO_BIT, 1);

    powerOff();

//  GPIO_IntSet(CD_GPIO_INT(SD_CD_GPIO_INT_NUM), SdCard::CD_GPIO, SdCard::CD_GPIO_BIT, MODE_EDGE, 2);
//  NVIC_SetPriority(CD_NVIC_INT(SD_CD_GPIO_INT_NUM), CARD_DETECT_IRQ_PRIORITY);
//  GPIO_ClearInt(CD_GPIO_INT(SD_CD_GPIO_INT_NUM));
//  NVIC_ClearPendingIRQ(CD_NVIC_INT(SD_CD_GPIO_INT_NUM));
//  NVIC_EnableIRQ(CD_NVIC_INT(SD_CD_GPIO_INT_NUM));
}

TRetVal SdCard::spiSyncExchange( const uint8_t * txbuf, uint8_t * rxbuf, size_t length, cs_t cs ) {
    CAbstractSSPDriver::TMsgType msg;
    msg.length = length;
    msg.txbuffer = txbuf;
    msg.rxbuffer = rxbuf;
    msg.cs = cs;
    msg.use_dma = false;
    msg.blocking = false;
    msg.sdcard.use_sdcard_mode = true;
    auto r = m_sspdrv->txrx(&msg);
    return r;
}
size_t SdCard::getFatSectorSize() const {
    return 512;
}
void SdCard::task( void * _params ) {
    static SdCard::Status oldStatus = FirstRun;
    static SdCard::Status newStatus = FirstRun;
    for (;;) {
        if (newStatus != FirstRun) {
            newStatus = SdCard::instance()->readCardDetectPin();
            if (newStatus != oldStatus)
                oldStatus = newStatus;
            else {
                delayMs(500);
                continue;
            }
        }
        switch (newStatus) {
        case Ejected:
            SdCard::instance()->ejectProcessing();
            break;
        case Inserted:
            SdCard::instance()->insertProcessing();
            break;
        case FirstRun:
            SdCard::instance()->m_card_version = SD_CARD_NOT_FOUND;
            SdCard::instance()->powerOff();
            newStatus = SdCard::instance()->readCardDetectPin();
            if (newStatus == Ejected)
                oldStatus = newStatus;
            break;
        default:
            configASSERT(0);
        }
    }
}
void SdCard::ejectProcessing() {
    m_card_version = SD_CARD_NOT_FOUND;
    if (m_dsknum != FileSystem::NO_DISK) {
        FileSystem::VfsPool::umount(m_dsknum);
    }
#if( SD_EJECT_HOOK ==1)
    sd_eject_hook();
#endif
    powerOff();
    kprintf("SD-карта извлечена...\r\n");
}
void SdCard::insertProcessing() {
    spi_r1_response_cmd_t response1;
    kprintf("SD-карта вставлена. Выставляем частоту шины %d Гц.\r\n", INITIAL_SPEED_KHZ);
    CAbstractSSPDriver::Setup_t spi_config(INITIAL_SPEED_KHZ, CAbstractSSPDriver::Mode0);
    TRetVal retVal = m_sspdrv->setup(spi_config);
    bool initOk = false;
    if (retVal == rvOK) {
        bool firstInit = true;
        for ( int tryCounter = 0; ( tryCounter < 4 ) && !initOk && ( readCardDetectPin() == Inserted ); tryCounter++ ) {
            if (firstInit)
                firstInit = false;
            else {
                powerOff();
                delayMs(1500);
            }
            powerOn();
            delayMs(700); /// На доводку карты до упора пользователем
            kprintf("Отправляем пустые циклы.\r\n");
            if (( retVal = sendDummyCycles() ) == rvOK) {
                /* Сброс карты памяти */
                retVal = command(GO_IDLE_STATE, 0x00, response1);
                if (retVal != rvOK) {
                    kprintf("Ошибка отправки команды: ");
                    RET_VAL_PRINT(retVal);
                    continue;
                }
                kprintf("Карта ответила: 0x%02x\r\n", response1.data);
                if (response1.data[0] != IDLE_STATE) {
                    kprintf("Ошибка иницилизации\r\n");
                    continue;
                }
                /* Задание диапазона наряжения питания карты и анализ ответа */
                send_if_cond_arg_t arg8;
                arg8.fields.voltage_accepted = SD_2_7__3_6_VOLTAGE;
                arg8.fields.echo_back = CMD8_PATTERN;
                spi_r7_response_cmd_t response7;
                retVal = command(SEND_IF_COND, arg8.data, response7);
                if (retVal != rvOK) {
                    kprintf("Ошибка отправки команды: ");
                    RET_VAL_PRINT(retVal);
                    continue;
                }
                if (response7.value.fields.echo_back != arg8.fields.echo_back) {
                    kprintf("CMD8 pattern mistmatch! Recieved 0x%02x, send 0x%02x\r\n", response7.value.fields.echo_back, arg8.fields.echo_back);
                }
                if (!response7.r1.fields.in_idle_state) {
                    kprintf("CMD8 card is not in IDLE\r\n");
                }
                if (response7.r1.fields.illegal_command) {
                    kprintf("CMD8 is illegal command\r\n");
                }
                if (!response7.value.fields.voltage_accepted) {
                    kprintf("CMD8 voltage range!\r\n");
                }
                if (response7.value.fields.echo_back != arg8.fields.echo_back ||
                    !response7.r1.fields.in_idle_state ||
                    response7.r1.fields.illegal_command ||
                    !response7.value.fields.voltage_accepted) {
                    kprintf("Очень похоже, что это карта ver. 1.x\r\n");
                    retVal = initSDv1();
                    RET_VAL_PRINT(retVal);
                } else {
                    kprintf("Очень похоже, что это карта ver. 2.0 or later\r\n");
                    retVal = initSDv2();
                    RET_VAL_PRINT(retVal);
                }
                if (retVal == rvOK) {
                    if (m_card_version == SD_V1 || m_card_version == SD_V2) {
                        kprintf("Устанавливаем длину блока 512 байт...");
                        retVal = command(SET_BLOCKLEN, make_argument(0x200), response1);
                        RET_VAL_PRINT(retVal);
                        if (response1.data[0]) {
                            kprintf("CMD16 response error 0x%02x\r\n", response1.data[0]);
                            retVal = rvFAILED;
                        }
                    }
                    if (retVal == rvOK) {
                        kprintf("Запрашиваем CID...");
                        retVal = getCidRegister(m_cidRegister);
                        RET_VAL_PRINT(retVal);
                        if (retVal == rvOK) {
                            printCidRegister(m_cidRegister);
                            kprintf("Запрашиваем CSD...");
                            retVal = getCsdRegister(m_csdRegister);
                            RET_VAL_PRINT(retVal);
                            if (retVal == rvOK) {
                                m_descriptor->capacity = m_csdRegister.get_device_size();
                                printCsdRegister(m_csdRegister);
                            }
                        }
                    }
                }
                if (retVal != rvOK)
                    continue;
                else
                    initOk = true;
            }

        }
    }
    if (!initOk) {
        m_dsknum = FileSystem::NO_DISK;
        kprintf("Ошибка доступа к карте, выньте, вставьте снова, либо выбросите...");
        RET_VAL_PRINT(retVal);
    } else {
        kprintf("Карта успешно инициализирована и готова к работе!\r\n");
        kprintf("Версия карты: %s.\r\n", cardVersion2string().c_str());
        size_t up_speed = static_cast<size_t>(m_csdRegister.get_max_data_transfer_rate() * MAX_SPEED_MULTIPLIER);
        kprintf("Повышаем скорость до %d Гц...", up_speed);
        spi_config = CAbstractSSPDriver::Setup_t(up_speed, CAbstractSSPDriver::Mode0);
        retVal = m_sspdrv->setup(spi_config);
        RET_VAL_PRINT(retVal);
        if (retVal == rvOK) {
            kprintf("Монтируем диск...");
            retVal = FileSystem::VfsPool::mount(FileSystem::AbstractFs::FsType::FATFS, this, m_dsknum);
            kprintf("Результат монтирования ");
            RET_VAL_PRINT(retVal);
        }
    }
#if( SD_INSERT_HOOK==1)
    sd_insert_hook(retVal == rvOK);
#endif
}
TRetVal SdCard::getCsdRegister( spi_csd_register &csd ) {
    TRetVal retVal = dataRead(SEND_CSD, 0, csd.data, sizeof( csd ));
    if (retVal == rvOK) {
#if 1
        kprintf("csd: ");
        for ( int i = 0; i < sizeof( spi_csd_register ); i++ ) {
            kprintf("%02x", csd.data);
        }
        kprintf("\r\n");
#endif
        uint8_t crc = ( CRC7::calc(csd.data, sizeof( spi_csd_register ) - 1) << 1 ) | 1;
        if (crc != ( csd.data[sizeof( spi_csd_register ) - 1] ))
            retVal = rvCRC_FAILED;
    }
    return retVal;
}
/*!
 * Function: getCidRegister
 * Прочитать CID-регистор.
 *
 * @param cid ссыль на переменную.
 *
 * @return TRetVal
 */
TRetVal SdCard::getCidRegister( spi_cid_register &cid ) {
    TRetVal retVal = dataRead(SEND_CID, 0, cid.data, sizeof( cid ));
    if (retVal == rvOK) {
#if 1
        kprintf("sid: ");
        for ( int i = 0; i < sizeof( spi_cid_register ); i++ ) {
            kprintf("%02x", cid.data);
        }
        kprintf("\r\n");
#endif
        uint8_t crc = ( CRC7::calc(cid.data, sizeof( spi_cid_register ) - 1) << 1 ) | 1;
        if (crc != ( cid.data[sizeof( spi_cid_register ) - 1] ))
            retVal = rvCRC_FAILED;
    }
    return retVal;
}
void SdCard::printCidRegister( spi_cid_register cid ) {
    kprintf("Manufacturer ID: 0x%01x.\r\n", cid.cid_register.manufacturer_id);
    kprintf("OEM/Application ID: 0x%02x.\r\n", cid.cid_register.oem_id.get());
    char product_name[6];
    memset(product_name, 0, sizeof( product_name ));
    memcpy(product_name, cid.cid_register.product_name, 5);
    kprintf("Product name: %s.\r\n", product_name);
    kprintf("Product Revision: 0x%01x.\r\n", cid.cid_register.product_revision);
    kprintf("Product Serial Number: 0x%04x.\r\n", cid.cid_register.product_serial_number.get());
    kprintf("Manufacturing Date: 0x%04x.\r\n", cid.cid_register.manufacturing_date);
    kprintf("Crc7: 0x%01x.\r\n", cid.cid_register.crc7);

}
void SdCard::printCsdRegister( spi_csd_register csd ) {
    kprintf("CSD Structure: 0x%02x.\r\n", csd.get_structure());
    kprintf("Data read access time1: %.09f s.\r\n", csd.get_data_read_access_time1());
    kprintf("Data read access time2: %d clocks.\r\n", csd.get_data_read_access_time2());
    kprintf("Max data transfer rate: %d bit/s.\r\n", csd.get_max_data_transfer_rate());
    uint16_t ccc = csd.get_card_command_classes();
    kprintf("Card command classes (0x%04x):", ccc);
    for ( int i = 0; i < 12; i++ ) {
        kprintf(" %d: %s.", i, ( ccc & LSHIFT(1, i) ) ? "Yes" : "No");
    }
    kprintf("\r\n");
    kprintf("Max read data block length: %d bytes.\r\n", csd.get_max_read_data_block_length());
    kprintf("Partial blocks for read allowed: %s.\r\n", csd.is_partial_blocks_read_allowed() ? "Yes" : "No");
    kprintf("Write block misalignment: %s.\r\n", csd.is_write_block_misalignment() ? "Yes" : "No");
    kprintf("Read block misalignment: %s.\r\n", csd.is_read_block_misalignment() ? "Yes" : "No");
    kprintf("DSR Implemented: %s.\r\n", csd.is_dsr_implemented() ? "Yes" : "No");
    kprintf("Device size: %" PRIu64 " bytes.\r\n", csd.get_device_size());
    kprintf("Device size multiplier: %d.\r\n", csd.get_device_size_multiplier());
    kprintf("Erase single block enable: %s.\r\n", csd.is_erase_single_block_enable() ? "Yes" : "No");
    kprintf("Erase sector_size size: %d blocks.\r\n", csd.get_erase_sector_size());
    kprintf("Write protect group size: %d sectors.\r\n", csd.get_write_protect_group_size());
    kprintf("Write protect group enable: %s.\r\n", csd.is_write_protect_group_enable() ? "Yes" : "No");
    kprintf("Write spped factor: %d.\r\n", csd.get_write_speed_factor());
    kprintf("Max data write block length: %d bytes.\r\n", csd.get_max_write_block_length());
    kprintf("Partial blocks for write allowed: %s.\r\n", csd.is_partial_blocks_for_write_allowed() ? "Yes" : "No");
    kprintf("File format group: %d.\r\n", csd.get_file_format_group());
    kprintf("Is copy flag: %s.\r\n", csd.is_copy_flag() ? "Yes" : "No");
    kprintf("Is permanent write protection: %s.\r\n", csd.is_permanent_write_protection() ? "Yes" : "No");
    kprintf("Is temporary write_protection: %s.\r\n", csd.is_temporary_write_protection() ? "Yes" : "No");
    kprintf("File format: %d.\r\n", csd.get_file_format());
    kprintf("Crc7: 0x%02x.\r\n", csd.get_crc7());
}
TRetVal SdCard::turnCrc( bool _onoff ) {
    spi_r1_response_cmd_t response1;
    TRetVal retVal = command(CRC_ON_OFF, 0x0001, response1);
    if (retVal == rvOK) {
        if (response1.data[0]) {
            retVal = rvFAILED;
        }
    }
    return retVal;
}
/*!
 * Function: sendDummyCycles
 * После включения питания отправляет флешке не менее 74 клоков при выставленных в 1
 * MOSI, CS. После этого карта должна быть готова к работе.
 *
 * @return TRetVal
 */
TRetVal SdCard::sendDummyCycles() {
    static const uint8_t txBuff[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    cs_t cs;
    cs.take = false;
    CAbstractSSPDriver::TMsgType msg;
    msg.length = sizeof( txBuff );
    msg.txbuffer = txBuff;
    msg.cs = cs;
    msg.use_dma = false;
    msg.blocking = false;
    auto retVal = m_sspdrv->txrx(&msg);
    return retVal;
}
SdCard::CardVersion SdCard::getCardVersion() const {
    CardVersion cv;
    OsApi::CritSecSmart cs;
    cv = m_card_version;
    return cv;
}
std::string SdCard::cardVersion2string() {
    switch (getCardVersion()) {
    case SD_CARD_NOT_FOUND:
        return "SD-карта не обнаружена!";
        break;
    case NOT_SD_CARD:
        return "Не SD-карта!";
        break;
    case SD_V1:
        return "SDSC ver. 1.0.";
        break;
    case SD_V2:
        return "SDSC ver. 2.x.";
        break;
    case SD_V2_HC:
        return "SDHC(EC) ver. 2.x.";
        break;
    case MMC:
        return "MMC-карта.";
        break;
    default:
        configASSERT(0);
    }
    configASSERT(0);
    return "Error!";
}
bool SdCard::isCardReady() const {
    CardVersion cv = getCardVersion();
    return cv == SD_V1 || cv == SD_V2 || cv == SD_V2_HC;
}
inline uint32_t SdCard::make_argument( uint32_t _arg ) {
    uint32_t result;
    uint8_t * src = reinterpret_cast<uint8_t *>(&_arg);
    uint8_t * dst = reinterpret_cast<uint8_t *>(&result);
    dst += 3;
    for ( int i = 0; i < sizeof( _arg ); i++ ) {
        *dst-- = *src++;
    }
    return result;
}
//void SdCard::isrService() {
//    configASSERT(insertEjectSemaphore);
//    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
//    xSemaphoreGiveFromISR(insertEjectSemaphore, &xHigherPriorityTaskWoken);
//    GPIO_ClearInt(CD_GPIO_INT(SD_CD_GPIO_INT_NUM)); // Очищаем interrupt status register
//    __DSB();
//    NVIC_ClearPendingIRQ(CD_NVIC_INT(SD_CD_GPIO_INT_NUM)); // Очищаем флаг
//    __DSB();
//    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
//}

  

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ого, даже не ожидал такого... Спасибо, коллеги. Боюсь только, что это пока не мой уровень. Попробую разобраться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 6/11/2020 at 11:50 PM, aaarrr said:

Попробуйте добавить "необязательные" CMD59 и CMD58.

А Вы знаете, помогло!

Для того, чтобы команда CMD55 (а за ней, соответственно, и АCMD41) стала проходить, достаточно оказалось после команды CMD8 поместить CMD59 с отменой проверки CRC (которая и так в SPI-режиме не проверяется). Если же командой CMD59 проверку CRC установить, то тогда в ответ на CMD58 приходит сообщение об ошибке CRC, хотя сам CRC верный. Но то уже побочный эффект, можно забить.

Механизм этого волшебства остаётся для меня загадкой, но важно, что работает. Кто бы мог предположить...

Спасибо большое!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 hours ago, Herz said:

Спасибо большое!

А может пришпилить эту тему в шапке форуме? Очень полезные куски кода тут пробегали, многим может пригодится

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Таких во многих темах бывало, всё нее пришпилишь. Думаю, поиском легко найдётся, если кто захочет. Название темы конкретное.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

бывают поддельные карточки, раньше transcend, последнее время sandisk, у них sd-контроллер работает нормально, а SPI дурака валяет
мы ставим или kingston или innodisk с ними проблем пока не случалось

Изменено пользователем gridinp

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...