无法使用 STM32F407 上的 Chan FatFs 库通过 SPI 写入 SD 卡文件

Posted

技术标签:

【中文标题】无法使用 STM32F407 上的 Chan FatFs 库通过 SPI 写入 SD 卡文件【英文标题】:Can't write to SD card file with SPI using Chan FatFs library on a STM32F407 【发布时间】:2017-06-19 15:46:40 【问题描述】:

我正在使用带有 STM32F407 芯片的 STM32F4 开发板。为了与 SD 卡通信,我使用 SPI1,并使用 Chan 创建的 FatFs 库。

所以问题的要点是我已经成功地在 SD 卡上创建了一个文件,我可以从中读取。但是,当我尝试写入文件时,它要么损坏文件,要么打印像“46040EDD-C”这样的垃圾数据。如果我查看内存,我可以看到我写的东西,但不知何故,它被写入了错误的内存地址。

我遇到的另一个问题是,第一次创建文件并尝试使用 f_write 对其进行写入时,我得到了回复 FR_DISK_ERR。错误来自试图创建一个集群链,当我通过代码时它工作正常。因此,可能会缺少一些延迟。下次我运行程序时它会工作,并且 f_write 返回 FR_OK。

我不确定这些问题是否相关。我一直在努力让它工作大约两个星期,并且希望能得到任何帮助。谢谢。

代码:

main.c

int main(void)

    int i;

    //SD CARD INIT

    //Fatfs object
    FATFS FatFs;
    //File object
    FIL fil;
    UINT fa;

    FRESULT res_mount, res_open, res_seek, res_write;

    delay(10ms);
    res_mount = f_mount(&FatFs, "", 1);
    if (res_mount == FR_OK) 
        GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
        delay(10ms);
        res_open = f_open(&fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
        if (res_open == FR_OK) 
            GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
            delay(10ms);
            res_seek = f_lseek(&fil, f_size(&fil));
            if(res_seek == FR_OK)
            
                delay(10ms);
                GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
                res_write = f_write(&fil, "Alpha Beta\n", 11, &fa);
                if (fa > 0 && res_write == FR_OK) 
                    GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
                    f_sync(&fil);
                
            
            f_close(&fil);
        
        f_mount(0, "", 1);
    

    while(1);

    //SD CARD INIT END

我编辑的 Chans diskio.c 文件。

#include "diskio.h"     /* FatFs lower layer API */

/* Definitions of physical drive number for each drive */
#define DEV_RAM     0   /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC     1   /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB     2   /* Example: Map USB MSD to physical drive 2 */

static volatile DSTATUS Stat = STA_NOINIT;  /* Disk status */

static BYTE CardType;   /* Card type flags (b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing) */



/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    BYTE pdrv       /* Physical drive nmuber to identify the drive */
)

    if(pdrv) 
        return STA_NOINIT;      // Supports only drive 0

    return Stat;




/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE pdrv               /* Physical drive nmuber to identify the drive */
)

    if (Stat & STA_NODISK) 
        return Stat;    /* No card in the socket? */

    //SLOW

    uint8_t ty = 0;
    ty = SD_CARD_InitialiseCard();

    CardType = ty;

    if(ty)
        Stat &= ~STA_NOINIT;

    return Stat;




/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
    BYTE pdrv,      /* Physical drive nmuber to identify the drive */
    BYTE *buff,     /* Data buffer to store read data */
    DWORD sector,   /* Start sector in LBA */
    UINT count      /* Number of sectors to read */
)

    //DRESULT res;
    //int result;
    if(pdrv || !count)
        return RES_PARERR;
    if (Stat & STA_NOINIT)
        return RES_NOTRDY;

    if(!(CardType & SDCARD_BLOCK))
        sector *= 512;

    if(count == 1)
    
        if((SD_CARD_Cmd(READ_SINGLE_BLOCK, sector) == 0x00) &&  SD_CARD_Read(buff, 512))
            count = 0;
    
    else
    
        if(SD_CARD_Cmd(READ_MULTIPLE_BLOCKS, sector) == 0)
        
            do
            
                if(!SD_CARD_Read(buff, 512))
                    break;
                buff += 512;
            
            while(--count);
            SD_CARD_Cmd(STOP_TRANSMISSION, 0);
        
    

    return count ? RES_ERROR : RES_OK;




/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_write (
    BYTE pdrv,          /* Physical drive nmuber to identify the drive */
    const BYTE *buff,   /* Data to be written */
    DWORD sector,       /* Start sector in LBA */
    UINT count          /* Number of sectors to write */
)

    //DRESULT res;
    //int result;

    if (pdrv || !count)
        return RES_PARERR;
    if (Stat & STA_NOINIT)
        return RES_NOTRDY;
    if (Stat & STA_PROTECT)
        return RES_WRPRT;

    if(!(CardType & SDCARD_BLOCK))
        sector *= 512;

    if(count == 1)
    
        if((SD_CARD_Cmd(WRITE_SINGLE_BLOCK, sector) == 0x00) &&  SD_CARD_Write(buff, 0xFE))
            count = 0;
    
    else
    
        if (CardType & SDCARD_SDC)
            SD_CARD_Cmd(SET_WR_BLOCK_ERASE_COUNT, count);

        if(SD_CARD_Cmd(WRITE_MULTIPLE_BLOCKS, sector) == 0)
        
            do
            
                if(!SD_CARD_Write(buff, 0xFC))
                    break;

                buff += 512;
            
            while(--count);

            if (!SD_CARD_Write(0, 0xFD))    /* STOP_TRAN token */
                count = 1;
        
    

    return count ? RES_ERROR : RES_OK;




/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)

    //DRESULT res;
    //int result;

    //NOT NEEDED AT THE MOMENT

    return RES_PARERR;


DWORD get_fattime (void)

    /* Pack date and time into a DWORD variable */
    return    ((DWORD)(2017 - 1980) << 25)
            | ((DWORD)1 << 21)
            | ((DWORD)1 << 16)
            | ((DWORD)0 << 11)
            | ((DWORD)0 << 5)
            | ((DWORD)0 >> 1);

SD_CARD.c:

#include "SD_CARD.h"

/*
SPI1(GPIOA):    -   Type:   -   SD CARD:
Pin4            -   CS      -   Pin2
Pin5            -   SCLK    -   Pin5
Pin6            -   MISO    -   Pin7
Pin7            -   MOSI    -   Pin3
*/
uint8_t SD_CARD_InitialiseCard()

    INT i = 0;

    SPI1ENABLE();

    ChipSelect(SPI1, HIGH);
    for(i = 0;i < 16;i++)
        SPI_Send(SPI1, 0xFF);

    for(i = 0;i < 0xFFFF;i++);

    while(SD_CARD_Cmd(GO_IDLE_STATE, 0) != R1_IDLE_STATE); //CMD0

    uint32_t r = SD_CARD_Cmd(SEND_IF_COND, 0x1AA); //CMD8
    if(r == 0x1AA)
        return SD_CARD_InitialiseCardV2();
    else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND))
        return SD_CARD_InitialiseCardV1();
    else
        return SDCARD_FAIL;


uint8_t SD_CARD_WriteRead(INT arg)

    SPI_Send(SPI1, arg);
    uint8_t test = SPI_Read(SPI1);
    return test;


uint8_t SD_CARD_InitialiseCardV1()

    uint8_t cmd;
    INT i = 0;

    if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) <= 1) //ACMD41 - set to 3V, use 0x40200000 for 3V3
        cmd = SD_SEND_OP_COND;
    else
        cmd = SEND_OP_COND;

    for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
    
        if(SD_CARD_Cmd(cmd, 0) == 0) //CMD1 or ACMD41
           
            // Set block length to 512 (CMD16)
            if(SD_CARD_Cmd(SET_BLOCKLEN, 512) != 0) //CMD16
                return SDCARD_FAIL;

            //Init: SEDCARD_V1
            if(cmd == SD_SEND_OP_COND)
                return SDCARD_V1;
            else
                return SDCARD_MMCV3;
        
    

    //Timeout waiting for v1.x card
    return SDCARD_FAIL;


uint8_t SD_CARD_InitialiseCardV2()

    INT i = 0;
    INT j = 0;

    for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
    
        for(j = 0;j < 0xFF;j++);
        if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) == 0) //ACMD41 - set to 3V, use 0x40200000 for 3V3
        
            uint32_t ocr = SD_CARD_Cmd(READ_OCR, 0); //CMD58
            return (ocr & 0x40000000) ? SDCARD_V2 | SDCARD_BLOCK : SDCARD_V2;
        
    

    //Timed out waiting for v2.x card
    return SDCARD_FAIL;


uint32_t SD_CARD_Cmd(INT cmd, INT arg)

    struct command_fields com;
    com.start_bit = 0;
    com.transmitter_bit = 1;
    com.index = cmd;
    com.argument = arg;
    if(cmd == GO_IDLE_STATE)
        com.crc = 0x4A;
    else if(cmd == SEND_IF_COND)
        com.crc = 0x43;
    else
        com.crc = 0x7F;
    com.end_bit = 1;

    if(cmd == SD_STATUS | cmd == SET_WR_BLOCK_ERASE_COUNT | cmd == SD_SEND_OP_COND) //ACMDx
        SD_CARD_Cmd(APP_CMD, 0); //CMD55

    SD_CARD_WriteCom(&com);

    if(cmd == SEND_IF_COND)
        return SD_CARD_RecieveR7();
    else if(cmd == READ_OCR)
        return SD_CARD_RecieveR3();
    else
        return SD_CARD_RecieveR1();


void SD_CARD_WriteCom(struct command_fields *com)


    ChipSelect(SPI1, LOW);
    SPI_Send(SPI1, 0xFF);
    SPI_Send(SPI1, (0xFF & ((com->start_bit << 7) | (com->transmitter_bit << 6) | com->index)));
    SPI_Send(SPI1, (0xFF & (com->argument >> 24)));
    SPI_Send(SPI1, (0xFF & (com->argument >> 16)));
    SPI_Send(SPI1, (0xFF & (com->argument >> 8)));
    SPI_Send(SPI1, (0xFF & com->argument));
    SPI_Send(SPI1, (0xFF & ((com->crc << 1) | com->end_bit)));


INT SD_CARD_Write(const BYTE *buffer, BYTE token)

    INT i = 0;
    ChipSelect(SPI1, LOW);

    while(SD_CARD_WriteRead(0xFF) != 0xFF);

    // indicate start of block
    SPI_Send(SPI1, token);

    if(token != 0xFD)
    
        // write the data
        for(i = 0;i < 512;i++)
        
            SPI_Send(SPI1, *buffer);
            buffer++;
        

        // write the checksum
        SPI_Send(SPI1, 0xFF);
        SPI_Send(SPI1, 0xFF);

        // check the repsonse token
        if(((SD_CARD_WriteRead(0xFF)) & 0x1F) != 0x05)
        
            ChipSelect(SPI1, HIGH);
            SPI_Send(SPI1, 0xFF);
            return SUCCESS;
        
    

    // wait for write to finish
    while(SD_CARD_WriteRead(0xFF) != 0xFF);

    ChipSelect(SPI1, HIGH);
    SPI_Send(SPI1, 0xFF);
    return ERROR;


INT SD_CARD_Read(BYTE *buffer, INT length)

    INT i = 0;
    ChipSelect(SPI1, LOW);

    for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
    
        // read until start byte (0xFF)
        if(SD_CARD_WriteRead(0xFF) == 0xFE)
        
            // read data
            for(i = 0;i < length;i++)
                buffer[i] = SD_CARD_WriteRead(0xFF);

            SPI_Send(SPI1, 0xFF); // checksum
            SPI_Send(SPI1, 0xFF);

            ChipSelect(SPI1, HIGH);
            SPI_Send(SPI1, 0xFF);
            return SUCCESS;
        
    

    return ERROR;


uint8_t SD_CARD_RecieveR1()

    INT i;
    uint8_t response = 0xFF;
    for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
    
        response = SD_CARD_WriteRead(0xFF);
        if((response == 0x00) || (response == 0x01))
        
            ChipSelect(SPI1, HIGH);
            SPI_Send(SPI1, 0xFF);
            return response;
        
    
    ChipSelect(SPI1, HIGH);
    SPI_Send(SPI1, 0xFF);
    return 0xFF;


uint32_t SD_CARD_RecieveR7()

    INT i = 0, j = 0;
    for(i = 0;i < (SD_COMMAND_TIMEOUT * 1000);i++)
    
        uint8_t response[5];
        response[0] = SD_CARD_WriteRead(0xFF);
        if(!(response[0] & 0x80))
        
                for(j = 1;j < 5;j++)
                
                    response[j] = SD_CARD_WriteRead(0xFF);
                
                ChipSelect(SPI1, HIGH);
                SPI_Send(SPI1, 0xFF);
                return ((response[1] << 24) | (response[2] << 16) | (response[3] << 8) | response[4]);
        
    
    ChipSelect(SPI1, HIGH);
    SPI_Send(SPI1, 0xFF);
    return 0xFFFFFFFF; // timeout


uint32_t SD_CARD_RecieveR3()

    uint32_t ocr = 0;
    INT response;
    for(int i=0; i < SD_COMMAND_TIMEOUT; i++)
    
        response = SD_CARD_WriteRead(0xFF);
        if(!(response & 0x80))
        
            ocr = SD_CARD_WriteRead(0xFF) << 24;
            ocr |= SD_CARD_WriteRead(0xFF) << 16;
            ocr |= SD_CARD_WriteRead(0xFF) << 8;
            ocr |= SD_CARD_WriteRead(0xFF);
            ChipSelect(SPI1, HIGH);
            SPI_Send(SPI1, 0xFF);
            return ocr;
        
    
    ChipSelect(SPI1, HIGH);
    SPI_Send(SPI1, 0xFF);
    return 0xFFFFFFFF; // timeout


INT SD_CARD_InitialiseDisk()

    if(SD_CARD_InitialiseCard() == SDCARD_FAIL)
        return SDCARD_FAIL;

    SPI_SetSpeed(SPI1, SPI_SPEED_1300KHz);
    return SUCCESS;

SPI.c:

#include "SPI.h"

void SPI1ENABLE()

    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;
    static uint8_t SPI1_ENABLED = 0;
    if(SPI1_ENABLED)
        return;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

    GPIO_InitStruct.GPIO_Pin =  SPI1_SCLK | SPI1_MISO | SPI1_MOSI;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin =  SPI1_CS;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_Cmd(SPI1, ENABLE);

    SPI1_ENABLED = 1;


uint8_t SPI_Read(SPI_TypeDef* SPIx)

    while(SPI1->SR & SPI_I2S_FLAG_BSY);         // wait until SPI is not busy anymore
    while(!(SPI1->SR & SPI_I2S_FLAG_RXNE));    // wait until receive complete
    return SPI_ReceiveData(SPIx);


void SPI_Send(SPI_TypeDef* SPIx, unsigned char Data)

    while(SPI1->SR & SPI_I2S_FLAG_BSY);         // wait until SPI is not busy anymore
    while(!(SPI1->SR & SPI_I2S_FLAG_TXE));    // wait until transmit complete
    SPI_SendData(SPIx, Data);
    while(!(SPI1->SR & SPI_I2S_FLAG_RXNE));    // wait until receive complete

【问题讨论】:

我认为您最好将问题发送到electronics.stackexchange.com 谢谢,会做的! 【参考方案1】:

所以我发现了我的问题。像我一样愚蠢,在我的写入函数中,我在发送 STOP_TRANs 令牌时忘记返回成功。下面是 write 函数编辑后的样子。

INT SD_CARD_Write(const BYTE *buffer, BYTE token)

    INT i = 0;
    ChipSelect(SPI1, LOW);

    while(SD_CARD_WriteRead(0xFF) != 0xFF);

    // indicate start of block
    SPI_Send(SPI1, token);

    if(token != 0xFD)
    
        // write the data
        for(i = 0;i < 512;i++)
        
            SPI_Send(SPI1, *buffer);
            buffer++;
        

        // write the checksum
        SPI_Send(SPI1, 0xFF);
        SPI_Send(SPI1, 0xFF);

        // check the repsonse token
        uint8_t resp = 0x00;
        do
        
            resp = SD_CARD_WriteRead(0xFF);
        
        while(resp == 0x00);

        if((resp & 0x1F) != 0x05)
        
            // wait for write to finish
            while(SD_CARD_WriteRead(0xFF) != 0xFF);

            SPI_Send(SPI1, 0xFF);
            ChipSelect(SPI1, HIGH);
            SPI_Send(SPI1, 0xFF);

            return SUCCESS;
        
    

    // wait for write to finish
    while(SD_CARD_WriteRead(0xFF) != 0xFF);

    SPI_Send(SPI1, 0xFF);
    ChipSelect(SPI1, HIGH);
    SPI_Send(SPI1, 0xFF);

    if(token == 0xFD)
        return SUCCESS;

    return ERROR;

【讨论】:

感谢您的回答。我试图了解为什么我的 STM32F103 项目无法将多个扇区写入 SD 卡,这个答案让我检查了 CubeMX 生成的 user_diskio.c 文件中的 xmit_datablock 函数。在您的示例中,该函数始终在 STOP_TRANS 令牌之后返回 false,因此每个多扇区写入都会失败。

以上是关于无法使用 STM32F407 上的 Chan FatFs 库通过 SPI 写入 SD 卡文件的主要内容,如果未能解决你的问题,请参考以下文章

stm32f407boot0在哪

STM32F407之搭建工程

STM32F407接上外部晶振后无法运行程序

stm32f407移植ucos怎么把ucos放在stm32上?

stm32f407H库和stm32f103H库有区别吗

STM32F407 - SDIO | FATFS - 将文件保存到 SD 卡