MSP430F149: SPI communication with MMC SD Module - for CMD0 , the sd card responds with 0xFF instead of 0x01 for idle state initialization

Part Number: MSP430F149

Tool/software:

Development Environment:

  • Microcontroller: MSP430F149
  • Library: FatFS (with custom disk I/O layer)
  • Interface: SPI
  • Objective: Access an SD card to read/write files using the FatFS library.
  • Component: SD card module (SPI mode)

Problem Description:

Issue: Not able to initialized sd card module, for initialization sending CMD0 following 80 spi clock cycles, sometimes able to successfully initialize the sd card into idle state but sometimes not.

Steps to Reproduce

  1. Initialize the MSP430F149 SPI module with a SPI clock speed of 125 kHz/4MHz/8MHz for SD card initialization.
  2. Perform SD card initialization:
    • Send 74+ clock cycles with CS high and MOSI high (0xFF).
    • Send CMD0 (GO_IDLE_STATE) to enter SPI mode.

Expected Behavior

  • After sending CMD0, the SD card should respond with 0x01

Actual Behavior

  • CMD0 responds 0xFF continuously

Below is the code that handles the disk IO layer,

#include <msp430.h>
#include "mmc_util.h"
#include "diskio.h"



#define CMD0    0x00 // spi mode
#define CMD1    0x01 // Initialization cmd
#define CMD8    0x08
#define CMD17   0x11
#define CMD24   0x18
#define CMD41   0x29
#define CMD55   0x37
#define CMD58   0x3A
// read ocr register to check the working voltage range

void spi_cs_high()
{
    P3OUT |= BIT0;
}

void spi_cs_low()
{
    P3OUT &=~BIT0;
}

uint8_t spi_transfer(uint8_t data) {

     while (!(IFG1 & UTXIFG0));  // Wait for TX buffer ready
     U0TXBUF = data;
     while (!(IFG1 & URXIFG0));  // Wait for TX complete
     return U0RXBUF; // Clear RX flag

}

// as some cards , delay the byte response after the cmd is being sent
uint8_t get_response()
{
    uint8_t i =0;
    uint8_t r ;

    for(;i<8;i++)
    {
        r = spi_transfer(0xFF);
        if(r != 0xFF)
            return r;
    }
    return r;
}
// cmd msb         lsb crc
// 00  00  00  00  00  ff(except CMD0 and CMD8)
void send_cmd(uint8_t cmd,uint32_t arg)
{
    spi_transfer(0x40|cmd);
    spi_transfer(arg>>24);
    spi_transfer(arg>>16);
    spi_transfer(arg>>8);

    uint8_t crc = 0xFF;
    if (cmd == CMD0)
        crc = 0x95;
    else if (cmd == CMD8)
        crc = 0x87;
    spi_transfer(crc);
}

DSTATUS MMC_disk_initialize(void) {
        spi_cs_high();
        uint8_t i;
        for (i = 0; i < 10; i++) spi_transfer(0xFF);  // 80 clock cycles

        // Switching from native mode to spi mode
        spi_cs_low();
        send_cmd(CMD0, 0); // sending cmd 0 to switch from native operating mode to spi mode ,
        // response should be r1 to indicate idle state

        uint8_t res;
        while (1) // Wait for idle state
        {
            res = spi_transfer(0xFF);

            if (res==0x01)
            {
                break;
            }
        }

        spi_cs_high();
        spi_transfer(0xFF);


        spi_cs_low();
        send_cmd(CMD8, 0x000001AA);  // VHS=0x1 (2.7–3.6V), check pattern=0xAA

        uint8_t resp[4];

        while (1)
        {
            res = spi_transfer(0xFF);
            if (res == 0x01)
            {
                uint8_t i = 0;
                for (; i < 4; i++)
                {
                    resp[i] = spi_transfer(0xFF);
                }
                break;
            }
        }

        spi_cs_high();
        spi_transfer(0xFF);
        // r1 response              lower 12 bit to indicate sdc ver 2
        if (res != 0x01 || resp[2] != 0x01 || resp[3] != 0xAA) return 1;  // Invalid response → not SDHC


        uint16_t timeout = 5000;
        do {
                spi_cs_low();
                send_cmd(CMD55, 0); // Send CMD55 before ACMD41
                res = get_response(); // Get response for CMD55
                spi_cs_high();
                spi_transfer(0xFF);

                spi_cs_low();
                send_cmd(CMD41, 0x40000000); // Send CMD41 with HCS bit set
                res = get_response(); // Get response for CMD41
                spi_cs_high();
                spi_transfer(0xFF);

             } while (res != 0x00 && --timeout);

        if (!timeout) return 2; // Timeout waiting for card to exit idle
        return 0;
        // --- CMD58 to read OCR ---
        uint8_t ocr[4];
        spi_cs_low();
        send_cmd(CMD58, 0);

        while (1)
        {
            res = spi_transfer(0xFF);
            if (res == 0x00)
            {
                for (i = 0; i < 4; i++) ocr[i] = spi_transfer(0xFF);
                break;
            }
        }

        spi_cs_high();
        spi_transfer(0xFF);

        // Check if card is SDHC/SDXC by checking CCS bit (bit 30)
        if (ocr[0] & 0x40) {
            return 0; // SDHC card initialized successfully
        } else {
            return 3; // Not SDHC (maybe SDSC)
        }
}

DRESULT MMC_disk_read(unsigned char *buff, uint32_t sector,uint16_t count) {
    if (count != 1) return RES_PARERR;

    spi_cs_low();
    send_cmd(CMD17, sector);

    uint8_t r1;
    r1 = get_response();
    if(r1 !=0)
    {
       spi_cs_high();
       return RES_ERROR;
    }

    uint8_t res=0xFF;
    while (res==0xFF)    // Wait for data token
    {
       res = spi_transfer(0xFF);
    }

    uint16_t i;
    for (i = 0; i < 512; i++) {
        buff[i] = spi_transfer(0xFF);
    }

    spi_transfer(0xFF);  // Dummy CRC
    spi_transfer(0xFF);
    spi_cs_high();
    spi_transfer(0xFF);  // Ncr

    return RES_OK;
}

DRESULT MMC_disk_write(const uint8_t *buff, uint32_t sector, uint16_t count) {
    if (count != 1) return RES_PARERR;

    // Convert LBA to byte address if needed (for standard capacity cards)
    uint32_t addr = sector * 512;

    spi_cs_low();

    // Send CMD24 (WRITE_BLOCK)
    send_cmd(CMD24, addr);
    uint8_t r1 = get_response();
    if (r1 != 0x00) {
        spi_cs_high();
        return RES_ERROR;  // Command not accepted
    }

    // Send one byte gap (Nwr = 1 to 8)
    spi_transfer(0xFF);

    // Send Data Token for single block write
    spi_transfer(0xFE);

    // Send 512 bytes of data
    uint16_t i;
    for (i = 0; i < 512; i++) {
        spi_transfer(buff[i]);
    }

    // Send dummy CRC (not checked in SPI mode)
    spi_transfer(0xFF);
    spi_transfer(0xFF);

    // Read Data Response Token
    r1 = get_response();
    // dataresponse : 0bxxx0sss1 , for successfull datsa write : last four bits : 0101 -> 5
    //              & 0b00011111

    if ((r1 & 0x1F) != 0x05) {
        spi_cs_high();
        return RES_ERROR;  // Data rejected
    }

    // after data response from the card , the card will initiate write after a byte from the host controller
    // Wait for card to finish writing (card holds MISO low during write)
    uint32_t timeout = 0xFFFF;
    while (spi_transfer(0xFF) != 0xFF) {
        if (--timeout == 0) {
            spi_cs_high();
            return RES_ERROR;  // Timeout waiting for card to finish write
        }
    }

    spi_cs_high();
    spi_transfer(0xFF);  // Send extra 8 clock cycles

    return RES_OK;
}


DRESULT MMC_disk_ioctl(unsigned char cmd, void *buff) {
    switch (cmd) {
        case CTRL_SYNC:
            return RES_OK;
        case GET_SECTOR_COUNT:
            *(uint32_t*)buff = 0x4000;  // Example: 16 MB card
            return RES_OK;
        case GET_SECTOR_SIZE:
            *(uint16_t*)buff = 512;
            return RES_OK;
        case GET_BLOCK_SIZE:
            *(uint32_t*)buff = 1;
            return RES_OK;
    }
    return RES_PARERR;
}

  • It really bugs me that your code includes references to MMC cards when it does not support them. Heck, it doesn't even do SD V1.0. (CMD8 is illegal for 1.0)

    First, check your card. I noticed in a recent version of the specification this goody: "SPI Mode is not supported by SDUC cards."

    I usually retry sending CMD0 several times before giving up.

  • Hi Rubesh,

    Referring to your previous E2E post regarding this, I was wondering if you are following all of the requirements in this flow.

    Best,

    Owen

  • DSTATUS MMC_disk_initialize(void) {
            spi_cs_high();
    
            // powerup sequence
            uint8_t i;
            for (i = 0; i < 10; i++) spi_transfer(0xFF);  // 80 clock cycles
    
            // Switching from native mode to spi mode
            spi_cs_low();
            send_cmd(CMD0, 0); // sending cmd 0 to switch from native operating mode to spi mode ,
            // response should be r1 to indicate idle state
    
            uint8_t res;
            while (1) // Wait for idle state
            {
                res = spi_transfer(0xFF);
    
                if (res==0x01)
                {
                    break;
                }
            }
    
            spi_cs_high();
            spi_transfer(0xFF);
    
            // checking the card is which category
            // here if the card is rejected with illegal command  (0x05) : sdc v1 / m   mc v3
            spi_cs_low();
            send_cmd(CMD8, 0x000001AA);  // VHS=0x1 (2.7–3.6V), check pattern=0xAA
    
            res = spi_transfer(0xFF);
    
            if (res == 0x05) {
                    // Illegal command → SDv1 or MMC
                    spi_cs_high();
                    spi_transfer(0xFF);
    
                    // Use CMD1 for initialization (MMC/SDv1)
                    uint16_t timeout = 5000;
                    do {
                        spi_cs_low();
                        send_cmd(CMD1, 0);
                        res = get_response();
                        spi_cs_high();
                        spi_transfer(0xFF);
                    } while (res != 0x00 && --timeout);
    
                    if (!timeout) return 2;  // Timeout
                    card_type = CT_MMC;
                    return 0;                // SDv1/MMC initialized
            }
            else if (res == 0x01) {
                    uint8_t resp[4];
                    // Card supports CMD8 → SDv2+
                    for (i = 0; i < 4; i++) {
                        resp[i] = spi_transfer(0xFF);
                    }
                    spi_cs_high();
                    spi_transfer(0xFF);
    
                    // Check echo-back of 0x1AA
                    if (resp[2] != 0x01 || resp[3] != 0xAA) return 1; // Not valid SDv2 card
    
                    // --- SDv2 init sequence with ACMD41 ---
                    uint16_t timeout = 5000;
                    do {
                        spi_cs_low();
                        send_cmd(CMD55, 0);    // Prefix command
                        get_response();
                        spi_cs_high();
                        spi_transfer(0xFF);
    
                        spi_cs_low();
                        send_cmd(CMD41, 0x40000000); // HCS set
                        res = get_response();
                        spi_cs_high();
                        spi_transfer(0xFF);
    
                    } while (res != 0x00 && --timeout);
    
                    if (!timeout) return 2; // Timeout waiting for init
    
                    // --- CMD58: read OCR register ---
                    spi_cs_low();
                    send_cmd(CMD58, 0);
                    uint8_t ocr[4];
                    while (1) {
                        res = spi_transfer(0xFF);
                        if (res == 0x00) {
                            for (i = 0; i < 4; i++) ocr[i] = spi_transfer(0xFF);
                            break;
                        }
                    }
                    spi_cs_high();
                    spi_transfer(0xFF);
    
                    // Check CCS (Card Capacity Status, bit 30)
                    if (ocr[0] & 0x40) {
                        card_type = CT_SD2 | CT_BLOCK; // lba addressing
                        return 0; // SDHC/SDXC initialized
                    } else {
                        card_type = CT_SD1;  // byte addressing
                        return 0; // SDSC (standard capacity SD v2)
                    }
                }
                return 4; // Unknown card / init failed
    }
    

    Here updated my function to handle MMC, SDSC, SDHC cards , I am again observing issue during the transition from native mode to spi mode.

  • Hi Rubesh,

    Could you elaborate on what issue you are observing? Is it still the issue where the card isn't responding with 0x01?

    Best,

    Owen

  • Since the SPI initialization code isn't shown, that should be double checked. Particularly clock phase and polarity. Also, connections to the SD card. I assume that you have a pullup on the SPI data in pin so it doesn't float.

    I always repeat the CMD0 switch many, many times before giving up. I seem to remember counting iterations of that once, a decade or two ago. Modern cards could be better behaved.

**Attention** This is a public forum