工具/软件:
开发环境:
- 微控制器 : MSP430F149
- 软件库 :FatFS(具有自定义磁盘 I/O 层)
- 缩写 :SPI
- 目标 :使用 FatFS 库访问 SD 卡以读取/写入文件。
- 元件 :SD 卡模块(SPI 模式)
问题描述:
问题 :无法初始化 SD 卡模块,对于初始化发送 CMD0 后 80 个 SPI 时钟周期,有时能够成功地将 SD 卡初始化为空闲状态,但有时不能。
要重现的步骤
- 以 125kHz/SPI/SPI 时钟速度 8MHz 用于 SD 卡初始化时、初始化 MSP430F149 4MHz 模块。
- 执行 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;
}