• AVR Freaks

Hot!SD initialization with SPI on STM32, ACMD41 problem

Author
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
2020/10/01 14:39:12 (permalink)
0

SD initialization with SPI on STM32, ACMD41 problem

Hi. I have a problem with initialization SD card using SPI on STM32.
I took the followings steps:
-send 10 times 0xff with high CS
- CMD0 with argument 0x00 and CRC 0x95, the response is 0x01 is correct
- CMD8 with argument 0x01aa and CRC 0x87, the response is the same as the argument so it's v2 type card
- ACMD41 with argument 0x40000000 and CRC 0x77, the response is 0x01 for both CMD55 and CMD41, which means that SD card isn't initialized, so I send the ACMD41 and I don't receive the response in 16cycles of CLK and the initialization end up with error. I also have tried to send ACMD41 more times, but I don't receive response every time except the first one.
 
All commands I send with low CS, then I asking for the response 8 times sending 0xff and listening. Next I send 0xff with high CS.
 
Can anyone help me with this problem? I've been looking for the solution for two weeks and on every site I have found the same way of initialization.  I have tested it on four SD cards i I have tried ready-made code.
All my code I use to initialization is below.
 
 

 
/*
* sd_spi.c
*
* Created on: 26.11.2017
* Author: jaras
*/
 
#include "sd_spi.h"
#include <string.h>
 
uint8_t SDSPI_SendCMD(SPI_HandleTypeDef *phandle, uint8_t cmd, uint32_t arg, uint8_t crc) {
uint8_t buf[6];
buf[0] = cmd | 0x40;
buf[1] = (arg >> 24) & 0xff;
buf[2] = (arg >> 16) & 0xff;
buf[3] = (arg >> 8) & 0xff;
buf[4] = arg & 0xff;
buf[5] = crc;
 
if(HAL_SPI_Transmit(phandle, buf, 6, 1000) != HAL_OK) {
return 1;
}
 
return 0;
}
 
uint8_t SDSPI_Response(SPI_HandleTypeDef *phandle, uint8_t *buf, uint16_t size) {
uint8_t tx = 0xff;
uint8_t rx = 0xff;
uint8_t i = 0;
 
while(rx == 0xff) {
if(HAL_SPI_TransmitReceive(phandle, &tx, &rx, 1, 1000) != HAL_OK) {
return 1;
}
i++;
if(i > 8) {
return 2;
}
}
 
*buf = rx;
 
for(uint16_t k = 1; k < size; k++) {
if(HAL_SPI_TransmitReceive(phandle, &tx, &rx, 1, 1000) != HAL_OK) {
return 1;
}
*(buf + k) = rx;
}
 
return 0;
}
 
uint8_t SDSPI_CMD(SPI_HandleTypeDef *phandle, uint8_t cmd, uint32_t arg, uint8_t crc,
uint8_t *response, uint8_t size) {
 
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_RESET);
 
uint8_t res = SDSPI_SendCMD(phandle, cmd, arg, crc);
if(res > 0) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
return 1;
}
 
res = SDSPI_Response(phandle, response, size);
if(res > 0) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
return 2;
}
 
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
uint8_t tx = 0xff;
if(HAL_SPI_Transmit(phandle, &tx, 1, 1000) != HAL_OK) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
return 3;
}
 
return 0;
}
 
uint8_t SDSPI_ACMD(SPI_HandleTypeDef *phandle, uint8_t cmd, uint32_t arg, uint8_t crc,
uint8_t *response, uint8_t size) {
volatile uint8_t value0=10;
volatile uint8_t value1=10;
volatile uint8_t value2=10;
 
uint8_t rx = 0;
 
uint8_t res = SDSPI_CMD(phandle, 55, 0, 0x65, &rx, 1);
value0=rx;
if(res > 0) {
return 1;
}
if((rx & 0xf4) > 0) {
return 2;
}
 
res = SDSPI_CMD(phandle, cmd, arg, crc, response, size);
value1=*response;
value2=res;
if(res > 0) {
return 3;
}
 
return 0;
}
 
uint8_t SDSPI_Init(SPI_HandleTypeDef *phandle) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
HAL_Delay(10);
uint8_t buf[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if(HAL_SPI_Transmit(phandle, buf, 10, 1000) != HAL_OK) {
return 1; //spi error
}
 
uint8_t res = SDSPI_CMD(phandle, 0, 0, 0x95, buf, 1);
if(res > 0) {
return 1; //command error
}
if(buf[0] != 1) {
return 2; //not initialized
}
 
uint8_t type = 0;
uint8_t block = 0;
 
res = SDSPI_CMD(phandle, 8, 0x01aa, 0x87, buf, 5);
if(res > 0) {
type = 1;
}
if(buf[0] != 1) {
type = 1;
}
if((buf[3] & 0x0f) != 1 || buf[4] != 0xaa) {
return 3; //initialization error
}
 
uint8_t stat = 0;
uint32_t tickstart = 0;
 
if(type == 0) {
stat = 1;
tickstart = HAL_GetTick();
while(stat > 0) {
if((HAL_GetTick()-tickstart) >= 1000) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
return 4; //timeout
}
 
res = SDSPI_ACMD(phandle, 41, 0x40000000, 0x77, &stat, 1);
if(res > 0) {
return 5; //not supported
}
}
 
res = SDSPI_CMD(phandle, 58, 0, 0x75, buf, 5);
if(res > 0) {
return 6; //not supported
}
if(buf[0] > 0) {
return 7;
}
if((buf[1] & 0x04) > 0) {
block = 1;
}
 
}
if(type == 1) {
stat = 1;
tickstart = HAL_GetTick();
while(stat > 0) {
if((HAL_GetTick()-tickstart) >= 1000) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
stat = 0;
type = 2;
}
 
res = SDSPI_ACMD(phandle, 41, 0, 0xff, &stat, 1);
if(res > 0) {
stat = 0;
type = 2;
}
}
}
if(type == 2) {
stat = 1;
tickstart = HAL_GetTick();
while(stat > 0) {
if((HAL_GetTick()-tickstart) >= 1000) {
HAL_GPIO_WritePin(SDSPI_CSPORT, SDSPI_CSPIN, GPIO_PIN_SET);
return 8; //timeout
}
 
res = SDSPI_CMD(phandle, 1, 0, 0xff, &stat, 1);
if(res > 0) {
return 9; //error
}
}
}
if(block == 0) {
res = SDSPI_CMD(phandle, 16, 512, 0xff, buf, 1);
if(res > 0) {
return 10; //not supported
}
if(buf[0] > 0) {
return 11; //error
}
}
 
return 0;
}
 
 

 
post edited by danielbr3 - 2020/10/02 01:32:53
#1
LdB_ECM
Super Member
  • Total Posts : 454
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/01 23:05:33 (permalink)
0
It's telling you it isn't a HCS card and as your parameter is only valid for that it fails.
 
These are the bits ... you only sent it a HCS bit which it said go to hell.

#define ACMD41_HCS           0x40000000
#define ACMD41_SDXC_POWER    0x10000000
#define ACMD41_S18R          0x04000000
#define ACMD41_VOLTAGE       0x00ff8000
#define ACMD41_ARG_HC        (ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE)
#define ACMD41_ARG_SC        (ACMD41_VOLTAGE) 

 
What you need to do is send it ACMD41_ARG_HC which is all those bits so it will accept the command and set the OCR and you can check the capacity card and card type.
post edited by LdB_ECM - 2020/10/01 23:07:30
#2
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/02 01:31:45 (permalink)
0
When I try to send the command ACMD41 with that ACMD41_ARG_HC argument, the effect is exactly the same. 
By the way, can you check if I read the correct number of bits in response for the ACMD41 command?
#3
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/02 02:11:40 (permalink)
0
I have just tried another SD card and connected it to 5V instead of 3.3V and it turned out that process of initialization finishes correctly. Could you explain me why I have received correct response for command CMD8 if it should check voltage range and return error if it's incorrect?
#4
LdB_ECM
Super Member
  • Total Posts : 454
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/02 02:30:33 (permalink)
5 (1)
Look at SD flow it's allowed to fail if wrong voltage .. CMD0 and 8 will however work as per spec
It is also why you have to start off at 400Khz to allow for wrong voltage
So you were exiting correctly to unusable card on system because of voltage
 

post edited by LdB_ECM - 2020/10/02 02:37:25
#5
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/05 02:48:29 (permalink)
0
I managed to initialize card correctly and read total size of card, so I have a connection. But when I read free space of card I get 0. What can cause that problem? The card is formatted to FAT32 and 512bytes size of allocation.
#6
LdB_ECM
Super Member
  • Total Posts : 454
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/05 06:01:58 (permalink)
0
You failed to mount the partition correctly ... did you deal with the fact the first partition can be a boot partition or a master partition block?
 
If it helps this is the packed unionize struct you should end up reading .. you can ignore the unionize part just comment out (the first 39 bytes are common to FAT12,16,32)
You should be able to check those fields like OEMNAME, FatSize16, Media, NumFATs etc
struct  FAT_PB_struct {            // OFFSET LOCATIONS
    uint8_t            BS_JmpBoot[3];            // 0-2   (eb xx 90, or e9 xx xx)
    char            BS_OEMName[8];            // 3-10
    uint16_t        BytesPerSector;            // 11-12 (valid 512, 1024, 2048, 4096)
    uint8_t            SectorsPerCluster;        // 13    (valid 1, 2, 4, 8, 16, 32, 64, 128, 32768, 65536)
    uint16_t        ReservedSectorCount;    // 14-15
    uint8_t            NumFATs;                // 16    (2)
    uint16_t        RootEntryCount;            // 17-18 (224 = FAT12, 512 = FAT16, 0 = FAT 32**)
    uint16_t        TotalSectors16;            // 19-20
    uint8_t            Media;                    // 21    (Media descriptor f0: 1.4 MB floppy, f8 : hard disk; etc)
    uint16_t        FATSize16;                // 22-23 (Number of sectors per FAT .. 0 on FAT32**)
    uint16_t        SectorsPerTrack;        // 24-25
    uint16_t        NumberOfHeads;            // 26-27
    uint32_t        HiddenSectors;            // 28-31
    uint32_t        TotalSectors32;            // 32-35
    union {                                    // Unionize the two structures at offset 36
        struct BPBFAT1612_struct fat1612;    // Fat 16/12 structure
        struct BPBFAT32_struct     fat32;        // Fat 32 structure
    } FSTypeData;
};

 
You should already have the sdReadBlock function which reads 512 bytes into a buffer so if this helps that is a dumb down of the partition load
#include <stdbool.h>    // Needed for bool and true/false
#include <stdint.h>        // Needed for uint8_t, uint32_t, uint64_t etc
#include <stdio.h>      // Needed for printf

/*--------------------------------------------------------------------------}
{                        FAT12/16 SPECIFIC STRUCTURE                         }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT16/12 table entry varies this defines it
#pragma pack(1)
struct BPBFAT1612_struct
{
    uint8_t            BS_DriveNumber;            // 36       ( As used in INT 13)
    uint8_t            BS_Reserved1;           // 37
    uint8_t            BS_BootSig;             // 38        (0x29)
    uint32_t        BS_VolumeID;            // 42
    uint8_t            BS_VolumeLabel[11];     // 43-53    (Label or  "NO NAME    ")
    char            BS_FileSystemType[8];   // 54-61    ("FAT16   ", "FAT     ", or all zero.)
};
#pragma pack()

/*--------------------------------------------------------------------------}
{                         FAT32 SPECIFIC STRUCTURE                            }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT32 table entry varies this defines it
#pragma pack(1)
struct BPBFAT32_struct {
    uint32_t        FATSize32;                // 36-39   (Number of sectors per FAT on FAT32)
    uint16_t        ExtFlags;                // 40-41   (Mirror flags Bits 0-3: number of active FAT (if bit 7 is 1) Bit 7: 1 = single active FAT; zero: all FATs are updated at runtime; Bits 4-6 & 8-15 : reserved)
    uint16_t        FSVersion;                // 42-43
    uint32_t        RootCluster;            // 44-47   (usually 2)
    uint16_t        FSInfo;                    // 48-49   (usually 1)
    uint16_t        BkBootSec;                // 50-51   (usually 6)
    uint8_t            Reserved[12];            // 52-63
    uint8_t            BS_DriveNumber;            // 64
    uint8_t            BS_Reserved1;           // 65
    uint8_t            BS_BootSig;                // 66      (0x29: extend signature .. Early FAT32 stop here )
    uint32_t        BS_VolumeID;            // 67-70
    char            BS_VolumeLabel[11];     // 71-81   (Label or  "NO NAME    ")
    char            BS_FileSystemType[8];   // 82-89   ("FAT32   ", "FAT     ", or all zero.)
};
#pragma pack()

/*--------------------------------------------------------------------------}
{          FAT12/16/32 UNIONIZED BIOS PARAMETER BLOCK STRUCTURE                }
{--------------------------------------------------------------------------*/
// Full unionized BPB struct valid for 12/16/32 bit
#pragma pack(1)
struct FAT_BPB_struct {                     // OFFSET LOCATIONS
    uint8_t            BS_JmpBoot[3];            // 0-2   (eb xx 90, or e9 xx xx)
    char            BS_OEMName[8];            // 3-10
    uint16_t        BytesPerSector;            // 11-12 (valid 512, 1024, 2048, 4096)
    uint8_t            SectorsPerCluster;        // 13    (valid 1, 2, 4, 8, 16, 32, 64, 128, 32768, 65536)
    uint16_t        ReservedSectorCount;    // 14-15
    uint8_t            NumFATs;                // 16    (2)
    uint16_t        RootEntryCount;            // 17-18 (224 = FAT12, 512 = FAT16, 0 = FAT 32**)
    uint16_t        TotalSectors16;            // 19-20
    uint8_t            Media;                    // 21    (Media descriptor f0: 1.4 MB floppy, f8 : hard disk; etc)
    uint16_t        FATSize16;                // 22-23 (Number of sectors per FAT .. 0 on FAT32**)
    uint16_t        SectorsPerTrack;        // 24-25
    uint16_t        NumberOfHeads;            // 26-27
    uint32_t        HiddenSectors;            // 28-31
    uint32_t        TotalSectors32;            // 32-35
    union {                                    // Unionize the two structures at offset 36
        struct BPBFAT1612_struct fat1612;    // Fat 16/12 structure
        struct BPBFAT32_struct     fat32;        // Fat 32 structure
    } FSTypeData;
};
#pragma pack()

/*--------------------------------------------------------------------------}
{                    PARTITION DECRIPTION BLOCK STRUCTURE                    }
{--------------------------------------------------------------------------*/
//Structure to access info of a partition on the disk
#pragma pack(1)
struct partition_info {
    uint8_t        status;                            // 0x80 - active partition
    uint8_t        headStart;                        // starting head
    uint16_t    cylSectStart;                    // starting cylinder and sector
    uint8_t        type;                            // partition type (01h = 12bit FAT, 04h = 16bit FAT, 05h = Ex MSDOS, 06h = 16bit FAT (>32Mb), 0Bh = 32bit FAT (<2048GB))
    uint8_t        headEnd;                        // ending head of the partition
    uint16_t    cylSectEnd;                        // ending cylinder and sector
    uint32_t    firstSector;                    // total sectors between MBR & the first sector of the partition
    uint32_t    sectorsTotal;                    // size of this partition in sectors
};
#pragma pack()

/*--------------------------------------------------------------------------}
{                     MASTER BOOT RECORD BLOCK STRUCTURE                        }
{--------------------------------------------------------------------------*/
// Structure to access Master Boot Record for getting info about partitions
#pragma pack(1)
struct MBR_info {
    uint8_t                    nothing[446];        // Filler the gap in the structure
    struct partition_info partitionData[4];        // partition records (16x4)
    uint16_t                signature;            // 0xaa55
};
#pragma pack()


/* you should already have this function */
bool sdReadBlock (uint32_t sector, uint8_t* buf)
{
    // Move to sector given and read sector to buffer
    return true;
}


/*-[ LoadDrivePartition ]---------------------------------------------------}
. Attempts to load the partition on the SD Card. This involves detecting the
. type of partiton and returning the partition data and true if successful.
.--------------------------------------------------------------------------*/
bool LoadDrivePartition (struct FAT_BPB_struct* buf)
{
    if (buf)                                                        // Buffer pointer must be valid
    {
        if (sdReadBlock(0, (uint8_t*) buf) != true) return false;   // Read sector 0
        if (buf->BS_JmpBoot[0] != 0xE9 && buf->BS_JmpBoot[0] != 0xEB)// Check if it is boot sector
        {
            struct MBR_info* mbr = (struct MBR_info*)buf;           // if it is not boot sector, it must be MBR
            if (mbr->signature != 0xaa55) return false;                // if it is not even MBR then it's not FAT
            /* technically you are supposed to walk possibly all 4 looking for the marked active one in status byte */
            /* however on a new formatted card it is always 0 */
            struct partition_info* pd = &mbr->partitionData[0];        // First partition
            /* care with next on 32bit ARM pd->firstSector is unaligned you may need to read as two 16bits and join */
            sdReadBlock(pd->firstSector, (uint8_t*) buf);           // Read first sector of partition
            if (buf->BS_JmpBoot[0] != 0xE9 && buf->BS_JmpBoot[0] != 0xEB)  
                return false;                                        // Not an MBR
        }     
        return true;
    }
    return false;
}

int main (void)
{
    struct FAT_BPB_struct pd;
    if (LoadDrivePartition(&pd))                                    // Load partition
    {
        uint32_t pSize = 0;                                         // Zero partition size
        if ((pd->FATSize16 == 0) && (pd->RootEntryCount == 0))         // Check if FAT16/FAT32
        {
            // FAT32 capacity
            pSize = pd->TotalSectors32 - pd->ReservedSectorCount - (pd->FSTypeData.fat32.FATSize32 * pd->NumFATs);
            pSize *= pd->BytesPerSector;                            // Partition size in bytes
            printf("FAT32 Volume Label: %s, ID: %08x, size: %lu\n",
                pd->FSTypeData.fat32.BS_VolumeLabel,
                (unsigned int)pd->FSTypeData.fat32.BS_VolumeID,
                pSize);                                             // Basic partition data
        } else {
             // FAT16 capacity
            pSize = pd->TotalSectors32 - (pd->NumFATs * pd->FATSize16) - 33;
            pSize *= pd->BytesPerSector;                             // Partition size in bytes
            printf("FAT12/16 Volume Label: %s, Volume ID %08x, size: %lu\n",
                pd->FSTypeData.fat1612.BS_VolumeLabel,
                (unsigned int)pd->FSTypeData.fat1612.BS_VolumeID,
                pSize);                                             // Basic partition data
        }
    }
    return 0;
}

 
post edited by LdB_ECM - 2020/10/05 08:05:21
#7
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/08 15:46:45 (permalink)
0
I have just received SDIO module and I decided that it will be easier than SPI.
Unfortunately my program actually stops at line:
 while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND))
Can you tell me what does this line mean? It's a part of HAL library. The card is mounted succesfully.
#8
LdB_ECM
Super Member
  • Total Posts : 454
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/08 19:39:12 (permalink)
4 (1)
It's an infinite loop waiting for one of those flags to set before exiting.
 
That is a horrible line of code it should have a timed exit as well but I am guessing it comes after they send a command to the card and waiting for an error or data transfer complete flag to happen and the card has failed to respond.  As the card fails to respond it's stuck in that loop for ever.
 
So basically it's a bug and bad coding in the library and the problem will be before that because the card has stopped responding at all. So you need to walk back and find the last command and its parameters sent to the card before it locks because it clearly doesn't like it. It's likely a bug so check if there is an update or fix for the library from wherever you got it.
 
If you need help going forward we actually need a link to the library code to look at it, you are getting deep into some complex concepts.
post edited by LdB_ECM - 2020/10/08 19:56:07
#9
danielbr3
New Member
  • Total Posts : 6
  • Reward points : 0
  • Joined: 2020/09/30 14:28:03
  • Location: 0
  • Status: offline
Re: SD initialization with SPI on STM32, ACMD41 problem 2020/10/09 01:11:10 (permalink)
0
https://controllerstech.com/interface-sd-card-with-sdio-in-stm32/
I have done everything described on this page, this error turn out in:
Format_SD();
f_opendir(&dir, path);
find_volume(&path, &fs, 0);
check_fs(fs, bsect);
(move_window(fs, sect);
(disk_read(fs->drv, fs->win, sector, 1);
disk_read(disk.lun[pdrv], buff, sector, count);
BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t) (sector), count, SD_TIMEOUT);
and the program break on this moment: https://zapodaj.net/3659f69d2b7e6.png.html
 
Maybe one warning in CubeMX causes that. This warning sounds like: These IPs still have some not configured or wrong parameter values: [FatFS, Verify the platform setting tab]
Here you are my FatFS configuration in CubeMX
https://zapodaj.net/1e1e787664751.png.html
https://zapodaj.net/0be950b4a4f6f.png.html
https://zapodaj.net/cbce0eb90d6d2.png.html
 
#10
Jump to:
© 2020 APG vNext Commercial Version 4.5