Electronic – Can’t write to SD card file with SPI using Chan FatFs library on a STM32F407

cmicrocontrollersdspistm32f4

I am using STM32F4-development board with a STM32F407 chip. To communicate with the SD card I use SPI1, and are using the FatFs library created by Chan.

So the gist of the problem is that I have successfully managed to create a file on the SD card, I am able to read from it. But when I try to write to the file it either corrupts the file or prints garbage data like this "{46040EDD-C". If I look at the memmory I can see the stuff I wrote, but somehow it is being written to the wrong memmory address.

Another problem that I have is that the first time I create a file and try to write to it with f_write I get the reply FR_DISK_ERR. The error comes form trying to create a cluster chain, when I step trough the code it works fine. So It might be some delay missing. The next time I run the programm it works, and f_write returns FR_OK.

I am not sure if these problems are related or not. I have been trying to get this to work for about two weeks now and would love any help I can get. Thank you.

The code:

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 file that I have edited.

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

Best Answer

So I found out my problem. As stupid as I am, in my write function I forgot to return success when sending STOP_TRANs token. Here is how the write function looks like after editing.

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