Ошибка запуска карты microSD в режиме SPI

Я пытаюсь подключить карту microSD на 32 ГБ к PIC32MX795F512L , используя связь SPI. Связь SPI работает нормально, так как я проверил ее, замкнув линию ввода и вывода данных и получив обратно данные, которые я отправил. Я следую примерам, приведенным в книге Лусио де Хасио « Программирование 32-битных микроконтроллеров на C ».

Ниже приведены задачи, которые я выполняю:

1. Initializing the SPI Communication in initSPI(), setting the baudrate=76 (250 kHz)
2. Initializing the SD card
   -> CS = 1
   -> Sending 80 clock cycles to wake the card
   -> CS = 0
   -> Sending CMD0 command (0x40, 0x00, 0x00, 0x00, 0x00, 0x95)

Но проблема в том, что я не получаю R1ответ от карты, вместо этого я получаю файл 0xFF. Ниже приведен код:

#define FCY 77000000UL
#define FPB (FCY/2)
#define BAUDRATE    9600
#pragma config POSCMOD=HS,FNOSC=PRIPLL
#pragma config FPLLIDIV=DIV_3, FPLLMUL=MUL_21, FPLLODIV=DIV_1
#pragma config FPBDIV=DIV_2, FWDTEN=OFF

#include <stdio.h>
#include <stdlib.h>
#include <plib.h>
#include <stdbool.h>

#define SDI  _RC4
#define SDCS _RA9
#define enableSD()    SDCS = 0
#define disableSD()   SDCS = 1
#define readSPI() writeSPI(0xFF)

int main(int argc, char** argv)
{
    TRISAbits.TRISA9 = 0;   // CS as output
    TRISCbits.TRISC4 = 1;   // SDI as input
    TRISDbits.TRISD0 = 0;   // SDO as output
    TRISDbits.TRISD10 = 0;  // SCK as output
    AD1PCFG = 0xFFFF;
    DDPCONbits.JTAGEN = 0;
    OpenUART1( UART_EN | UART_NO_PAR_8BIT | UART_1STOPBIT, UART_RX_ENABLE | UART_TX_ENABLE, (FPB/16/BAUDRATE)-1 );
    disableSD(); //CS PIN INITIALLY HIGH TO DISABLE CARD
    initSPI();
    initSD();

    while(1)
    {

    }
    return (EXIT_SUCCESS);
}

void initSPI( void)
{
    SPI1BRG = 76; // FPB/154=250KHZ; (76+1)*2 = 154, so brg = 76
    SPI1CONbits.MSTEN = 1; // MasterEnable
    SPI1CONbits.CKE = 1;   // CKE on
    SPI1CONbits.ON = 1;    // SPI Module ON
}

unsigned char writeSPI(unsigned int b)
{
    SPI1BUF=b; // Write to buffer for TX
    while( !SPI1STATbits.SPIRBF)
        ; // Wait transfer complete
    return(SPI1BUF);
}

int initSD(void)
{
    int i,r,rx;
    int data;

    // Step1: Disable SD card
    disableSD();

    // Step2: Send 80 clock cycles to wake up the card
    for(i=0; i<=9; i++)
    {
        data = writeSPI(0xFF);
    }

    // Step3: Enable SD card
    enableSD();

    // Step4: Send CMD0 command to RESET
    r = sendCMD0();
    disableSD();
    if(r!=1)
    {
        putsUART1("CMD reject\n");
    }
}

int sendCMD0()
{
    int i,r;
    enableSD();

    writeSPI(0x40);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x95); //CMD0 Command

    for( i=0; i<100; i++)
    {
        r = readSPI();
        if ((r & 0x80) == 0)
            break;
    }
    return ( r);
}

Вот схема :

Схемы

Я не получаю R1ответ. Он застрял на шаге 4 в initSD().

Есть ли способ узнать, проснулась ли карта после отправки 80 тактовых импульсов? После 80 тактовых импульсов карта дает ответ 0xFF.

Нужно ли уменьшать тактовую частоту? В настоящее время это 250 кГц.

Почему карта не инициализируется. Как я могу это исправить?

Вы можете сравнить с моей библиотекой на Github ( заголовок , код ) и посмотреть, в чем отличия. Я протестировал приличное количество карт, чтобы решить крайние случаи.
Я поищу ссылку (вчера не видел), но насколько я помню, для SdCard нужен здоровенный развязывающий колпачок - 10 мкФ//100 нФ рядом с держателем. Погуглите следующий "развязывающий конденсатор для SdCard" Вам, наверное, не нужно идти дальше страницы google :-)
вы можете посмотреть на < microchip.com/forums/m530149.aspx >, где есть код на C для доступа к чипу в режиме SPI. Это слишком долго, чтобы публиковать как ответ, или я бы опубликовал его как ответ.
@ user3629249, не могли бы вы снова поделиться ссылкой. Пишет внутренняя ошибка сервера
Вы пробовали другую карту? Также избавьтесь от всех ints. Используйте unsigned char или uint8_t везде. Также заставьте UART распечатать возвращаемые значения r, чтобы мы могли видеть, что происходит.

Ответы (2)

Я не вижу никакого кода для того, чтобы сделать вывод MISO входным выводом или сделать вывод SCK выводом. Поскольку вы не используете библиотеку SPI в plib, вам нужно выполнить всю инициализацию выводов самостоятельно. Кроме того, после отправки CMD0увеличьте количество итераций (в вашем forцикле) с 8 до 20 (я использую 100), чтобы обеспечить достаточную свободу действий. Также вместо этого используйте это сравнение:

if ((r & 0x80) == 0) 
   break;
return r;

Также сделайте rвезде unsigned char.

О, спасибо, что предложил это. Думал после инициализации SPI все SDO SDI sck настраиваются автоматически. Теперь я сделаю вывод SDO и SCK и ввод SDI.
Почему вы используете это условие if: if ((r & 0x80) == 0). Можете ли вы объяснить немного.
Первый бит ответа SD-карты всегда равен нулю. Таким образом, нужно проверять первый бит любого полученного байта на наличие нуля. Ваш исходный код делает примерно то же самое, что и мой, но я некоторое время использовал свой с PIC, поэтому я считаю, что мой более надежен :)
uint8_tбыло бы лучше, чем unsigned char.
Я использовал ваше условие if, но карта не отвечает 0x01. Есть ли ошибка в моих функциях для передачи данных.? Можете ли вы взглянуть на него. Есть ли какая-либо другая команда, которую я могу отправить перед CMD0, чтобы проверить что-нибудь еще?
Можете ли вы обновить код в своем вопросе? После того, как все пин-инициализированы?
@TisteAndii Я обновил свой код.
Измените все ваши целые числа на uint8_t. Явно сделайте SPICON1bits.CKP = 0.
Также поместите SPI1CON = 0 в начало init_spi(). А затем попробуйте другую SD-карту. Других идей здесь нет :(
Хорошо, я поставлю SPI1CON = 0 и попробую с этим. Спасибо
И внесите изменения типа и назначение CKP
@TisteAndii Я не очень разбираюсь в программировании SD-карт, но когда мы инициализируем модуль SPI, все соответствующие контакты настраиваются автоматически!
Только если вы используете периферийные библиотеки. Здесь их не используют. Проверьте open_spi.c в файле plib, и вы увидите там те же назначения контактов. Здесь plib не используется для SPI

Внешне выглядит как проблема с электрикой

  • Попробуйте начать с более низкой скорости - возможно, у вас чрезмерная емкость в ваших линиях или sdCard с низкой скоростью.
  • Вы опрашиваете, пока карта не будет готова (см. ниже)
  • Можете ли вы поставить на него прицел, чтобы проверить тайминги, это критично?
  • Попробуйте другую SD-карту или ту же карту в рабочем устройстве.
  • Можете ли вы передавать и получать на другой порт на одном устройстве?
  • Можете ли вы передавать и получать на другую плату, используя одно и то же устройство PIC?
  • Используете ли вы драйверы микрочипов/программные библиотеки?
  • Правильно ли вы подключили MISO/MOSI?
  • Соблюдены ли требования по питанию — развязывающие колпачки и т. д.? 10 мкФ+//100 нФ (некоторые используют 100 мкФ!!)
  • Правильно ли настроены ваши выходные и входные контакты?

На более позднем этапе вам может понадобиться проверить следующее - нет определенного порядка.

  • Проверьте «порядок байтов» вашего SPI
  • Проверьте свой CRC
  • Дважды проверьте свой протокол

https://www.sdcard.org/downloads/pls/simplified_specs/archive/part1_110.pdf

пг 47 говорит, что нужно опрашивать устройство, пока карта не придет в готовность Вы этим занимаетесь?

Я использую 250 кГц, обязательно попробую с меньшей скоростью. Моя карта Micro SD на 32 ГБ класса 10 находится в хорошем рабочем состоянии. PIC32 тоже работает нормально, я делал на нем много других программ. Я не использую библиотеки микрочипов, и да, линии MISO MOSI подключены правильно. Я думаю, что нам нужно только установить линию CS вручную, а остальные линии устанавливаются автоматически. Я тоже загрузил схему. Пожалуйста, взгляните на это. Я не понял Check the 'endianness', можешь немного пояснить
порядок следования байтов — это жаргон, означающий «сначала вы отправляете MSB или LSB». Возможно, проверьте код инициализации вашего оборудования/пин-кода, указанный в библиотеках микрочипов. Большая часть моей работы связана с ARM, поэтому я, к сожалению, не могу помочь с особенностями PIC для скорости выводов, подтягивания / подтягивания и т. Д. Попробуйте установить задержку после установки линии CS.
Вы хотите сказать, что я должен добавить задержку после шага 1 в initSD()
@ user007 да, и опрашивайте его, пока он не будет готов с интервалом <50 мс. увидеть мои правки
Я не опрашиваю, а жду ответа, допуская задержку до 8 байт. Я буду опрашивать его, пока его ответ не будет 0x01. Спасибо
Есть ли ответ от карты после отправки 80 тактовых импульсов
Драйвер, который я использую, ставит ожидание 1 мс после power_on (выбор микросхемы), а затем отправляет CMD8, CMD55, CMD41. Рекомендуется использовать < 24 МГц, если вы не используете «высокоскоростной переключатель». Это режим SDIO (4 бита). Опрос находится в CMD