This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430F149:与 MMC SD 模块的 SPI 通信 — 对于 CMD0、SD 卡响应为 0xFF、而不是 0x01、用于空闲状态初始化

Guru**** 2489685 points
Other Parts Discussed in Thread: MSP430F149

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1550043/msp430f149-spi-communication-with-mmc-sd-module---for-cmd0-the-sd-card-responds-with-0xff-instead-of-0x01-for-idle-state-initialization

器件型号:MSP430F149


工具/软件:

开发环境:

  • 微控制器 : MSP430F149
  • 软件库 :FatFS(具有自定义磁盘 I/O 层)
  • 缩写 :SPI
  • 目标 :使用 FatFS 库访问 SD 卡以读取/写入文件。
  • 元件 :SD 卡模块(SPI 模式)

问题描述:

问题 :无法初始化 SD 卡模块,对于初始化发送 CMD0 后 80 个 SPI 时钟周期,有时能够成功地将 SD 卡初始化为空闲状态,但有时不能。

要重现的步骤

  1. 以 125kHz/SPI/SPI 时钟速度 8MHz 用于 SD 卡初始化时、初始化 MSP430F149 4MHz 模块。
  2. 执行 SD 卡初始化:
    • 在 CS 高电平且 MOSI 高电平 (0xFF) 的情况下发送 74 个以上的时钟周期。
    • 发送 CMD0 (GO_IDLE_STATE) 以进入 SPI 模式。

预期行为

  • 发送 CMD0 后、SD 卡应以 0x01 进行响应

实际行为

  • CMD0 持续响应 0xFF

下面是处理磁盘 IO 层的代码、

#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;
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    当您的代码不支持 MMC 卡时、它确实会让我感到烦恼。 它甚至不支持 SD V1.0。 (CMD8 在 1.0 版本中是非法的)

    首先,检查你的卡。 我注意到在最新版本的 规范 这很好:“ SPI 模式不支持 SDUC 卡。“

    我通常会在放弃之前重试发送 CMD0 几次。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Rubesh:

    参考您 之前 有关这方面的 E2E 博文、我想知道您是否遵循 此流程中的所有要求

    此致、

    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
    }
    

    在这里更新了我的功能来处理 MMC、SDDSC、SDHC 卡,我再次看到从本机模式转换到 SPI 模式的问题。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Rubesh:

    能否详细说明一下您观察到的问题? 是否仍然存在卡没有响应 0x01 的问题?

    此致、

    Owen

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    由于未显示 SPI 初始化代码、因此应仔细检查该代码。 特别是时钟相位和极性。 此外、连接到 SD 卡。 我假设您的 SPI 数据输入引脚上有上拉电阻器、使其不会悬空。

    在放弃之前、我总是重复多次 CMD0 开关。 我似乎记得在十年或两年前计算过一次。 现代的牌可以更好的表现。