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
- Initialize the MSP430F149 SPI module with a SPI clock speed of 125 kHz/4MHz/8MHz for SD card initialization.
- 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; }