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 通信 — CMD17(单块读取)、SD 卡以 0x00 进行响应(表示命令成功)、但后续的数据读取操作仅返回 0xFF 字节。

Guru**** 2419960 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/1543919/msp430f149-spi-comunication-with-mmc-sd-module---cmd17-single-block-read-the-sd-card-responds-with-0x00-indicating-a-successful-command-but-the-subsequent-data-read-operation-only-returns-0xff-bytes

器件型号:MSP430F149


工具/软件:

开发环境:

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

问题描述

问题 :FatFS 库无法读取 SD 卡的扇区 0(主引导记录,MBR)。 发出 CMD17(单块读取)时、SD 卡会以 0x00 进行响应(表示命令成功)、但后续的数据读取操作仅返回 0xFF 字节。 既未收到预期的数据令牌 (0xFE)、也未收到错误令牌。

要重现的步骤

  1. 以 125kHz/SPI/SPI 时钟速度 8MHz 用于 SD 卡初始化时、初始化 MSP430F149 4MHz 模块。
  2. 执行 SD 卡初始化:
    • 在 CS 高电平且 MOSI 高电平 (0xFF) 的情况下发送 74 个以上的时钟周期。
    • 发送 CMD0 (GO_IDLE_STATE) 以进入 SPI 模式。
    • 发送 CMD8 (SEND_IF_COND) 以验证电压兼容性。
    • 发送 ACMD41 (SD_SEND_OP_COND)、直到卡以 0x00 进行响应。
  3. 发送 CMD17 (READ_SINGLE_BLOCK) 以读取扇区 0 (MBR)。
    • 命令格式:0x51、0x00、0x00、0x00、0x00、 [CRC]
    • 响应:0x00(成功)。
  4. 尝试读取 512 个字节的数据:
    • 预期一个数据令牌 (0xFE) 后跟 512 字节 MBR 数据和 2 字节 CRC。
    • 实际结果:接收到连续 0xFF 字节、无数据令牌或错误令牌。

预期行为

  • 发送 CMD17 后、SD 卡应以 0x00(接受命令)进行响应。
  • 在合理的超时(例如 100ms)内、SD 卡应发送数据令牌 0xFE、后跟 512 字节的 MBR 数据和 2 字节的 CRC。

实际行为

  • CMD17 以 0x00 进行响应、表示命令已被接受。
  • 读取数据块时、仅接收 0xFF 字节、表示 SD 卡处于空闲状态或未使用预期的数据令牌 (0xFE) 或错误令牌进行响应。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您的描述似乎合理、与我所做的相符。 大部分。

    像往常一样、对您认为您的代码所做的事情的描述不如对实际代码有用。

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

    // Below is the code that handles the DISK IO 
    
    #define CMD0    0x00 // spi mode
    #define CMD1    0x01 // Initialization cmd
    #define CMD8    0x08
    #define CMD17   0x17
    #define CMD24   0x24
    #define ACMD41  0x41
    #define CMD55   0x55
    #define CMD58   0x58 // 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;
    }
    
    
    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);
        spi_transfer(arg);
    
        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
            P1OUT |= BIT3;
            while (1) // Wait for idle state
            {
                uint8_t res = spi_transfer(0xFF);
    
                if(res == 0x01)
                {
                    break;
                }
    
            }
    
            spi_cs_high();
            spi_transfer(0xFF);
    
            P1OUT &= ~BIT3;
    
            spi_cs_low();
            send_cmd(CMD8, 0x000001AA);  // VHS=0x1 (2.7–3.6V), check pattern=0xAA
    
            uint8_t resp[4];
            uint8_t res ;
    
            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
    
            // Initialization section
            P1OUT |= BIT3;
    
    
            uint16_t timeout = 5000;
            do {
    
                    spi_cs_low();
                    send_cmd(ACMD41, 0x40000000); // taking longer duration to initialize , sometimes able to initialize it.
                    res = get_response(); // here res idle state should be removed leading to 0x00
                    spi_cs_high();
                    spi_transfer(0xFF);
              } while (res != 0x00 && --timeout);
    
    
            if (!timeout) return 2; // Timeout waiting for card to exit idle
    
            // --- 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;
    }
    
    
    
    

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

    David Schultz 上面的代码逻辑类似于我的描述,请看一下,如果需要任何其他信息,请告诉我

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

    这是不对的。 “ send_cmd (ACMD41、0x40000000);“

    您必须发送 CMD55 才能获取 ACMD 操作。 这里只是 CMD41。 实际上、这也很混乱:

    #define ACMD41  0x41
    #define CMD55   0x55
    #define CMD58   0x58 // read ocr register to check the working voltage range

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

            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);
    

    David Schultz 。正如您所指出的、我更新了逻辑、在 ACMD41 之前发送 CMD55。 但观察 0x05(对于 CMD55)和 0x00(对于 CMD41)的响应  

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

    您是否修复了“#define CMD55  0x55“等问题?

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

    是的、下面是更新的宏  

    #define CMD0    0x00 // spi mode
    #define CMD1    0x01 // Initialization cmd
    #define CMD8    0x08
    #define CMD17   0x17
    #define CMD24   0x24
    #define CMD41   0x41
    #define CMD55   0x55
    #define CMD58   0x58 

    对于 CMD55、我是否需要更改为任何其他值、 在通过 SPI 发送 cmd 时、我正在使用 0x40 执行或操作 cmd

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

    不是。 你让它变得更糟。

    0x55 与 0x15 相同。 又名 CMD21。  

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

    你能详细说明并指出我正确的方向,因为我没有得到什么需要应用的变化. 到目前为止、我要介绍  的是 elm-chan.org/.../mmc_e.html 资源。  

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

    你似乎不理解基数 16 和基数 10 之间的区别。  

    甚至是简单的按位或运算。

    我无法帮您解决这个问题。

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

    尊敬的 Rubesh:
    他的意思是你没有为这些命令设置正确的值。 例如:
    CMD55 将定义为 0x37 -->基址 16、即 3 * 16^3 + 7 * 16^0 = 48 + 7 = 55

    CMD24 将定义为 0x18 -->  基址 16、即 3 * 16^1 + 8 * 16^0 = 16 + 8 = 24

    此致、

    Diego Abad

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

    Diego Abad Sajamin 感谢您的澄清。