Skip to main content
M N
Associate III
October 10, 2020
Question

STM32_adafriut_sd version detection problem

  • October 10, 2020
  • 3 replies
  • 2990 views

Hi .

Below code has two part :

1- when there is response to CMD8

2- when there is not

And by this response we can determine if it is V1 or V2 but , when it will go for Initializing V2 after sending first ACMD41 then there is an "if" clause ,which will check response to knows if response is "SD_R1_ILLEGAL_COMMAND" then if it is "SD_R1_ILLEGAL_COMMAND" it will send ACMD41 with no argument .

---------------------

So there is two question here for me ?

(Both questions are related to line 947 in attached C file which is :)

if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND)

1- Is second "if clause " for checking ( SDSC ) or ( SDHC and SDXC ) type card ? => what I've got is : It is for initializing V2 - SDSC type card so it will send ACMD41 with no argument (HCS = 0) .(Am I right ?) ( It is what I got from flowchart in "SPI Mode Initilization Flow" part of SD documents ).

2- I read "Physical_Layer_Simplified_Specification" more than ten times but ,I couldn't find description, when it will send "SD_R1_ILLEGAL_COMMAND" in response of ACMD41 (which used in "if clause " after first ACMD41 with argument HCS = 1 ,so by checking response second ACMD41 which is in this "if clause " will send with HCS = 0 because of this ), which page has described this part of ACMD41 in SPI mode ?

response = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87, SD_ANSWER_R7_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND)
 {
 /* initialise card V1 */
 do
 {
 /* initialise card V1 */
 /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_APP_CMD, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 
 /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 }
 while(response.r1 == SD_R1_IN_IDLE_STATE);
 flag_SDHC = 0;
 }
 else if(response.r1 == SD_R1_IN_IDLE_STATE)
 {
 /* initialise card V2 */
 do {
 
 /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 
 /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 }
 while(response.r1 == SD_R1_IN_IDLE_STATE);
//********************************QA**********************************
// below line of code is where I could not find description for it in SD Documentation 
//********************************QA**********************************
 if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND)
 {
 do {
 /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 if(response.r1 != SD_R1_IN_IDLE_STATE)
 {
 return BSP_SD_ERROR;
 }
 /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 }
 while(response.r1 == SD_R1_IN_IDLE_STATE);
 }
 
 /* Send CMD58 (SD_CMD_READ_OCR) to initialize SDHC or SDXC cards: R3 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF, SD_ANSWER_R3_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);
 if(response.r1 != SD_R1_NO_ERROR)
 {
 return BSP_SD_ERROR;
 }
 flag_SDHC = (response.r2 & 0x40) >> 6;
This topic has been closed for replies.

3 replies

M N
M NAuthor
Associate III
October 12, 2020

Up.

(Just because community had problem )

:)

alister
Senior III
October 13, 2020

CMD8 performs three functions:

(a) differentiates v1 and v2 SD cards,

(b) notifies the card the host understands the physical spec v2,

(c) checks the card can handle the host's supply voltage.

>1- when there is response to CMD8

>2- when there is not

From SD Specifications Part 1 Physical Layer Simplified Specification Version 6.00 August 29, 2018,

figure 4-2 shows a "no response" to the CMD8. But that is SD-mode. SPI-mode should always respond to CMD8.

page 175 lists how different versions of SD card respond to the CMD8. Cards that don't support CMD8 respond in the R1 part with illegal command status.

page 203, 210 explain CMD8 and ACMD41.

figure 7-2 explains the ACMD41 argument 0x40000000 (HCS bit) for v2 cards.

>below line of code is where I could not find description for it in SD Documentation

No it's not mentioned. But there are many SD cards. We can only guess the developer of this code had a card that would respond without illegal command to the CMD8, and with illegal command to the ACMD41 with the HCS bit set.

Another good resource is http://elm-chan.org/docs/mmc/mmc_e.html.

M N
M NAuthor
Associate III
October 14, 2020

Thank you . this part was my question :

-------------------------------------

No it's not mentioned. But there are many SD cards. We can only guess the developer of this code had a card that would respond without illegal command to the CMD8, and with illegal command to the ACMD41 with the HCS bit set.

-------------------------------------

And there is another question about this library :

1- for CMD32 it will check SDHC then will multiply to 1 or BlockSize (in case of sdsc), to overcome block oriented type or byte oriented card type :

 /* Send CMD32 (Erase group start) and check if the SD acknowledged the erase command: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_SD_ERASE_GRP_START, (StartAddr) * (flag_SDHC == 1 ? 1 : BlockSize), 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE); 

2- But for CMD33 EndAddr multiply by 512 by default ,then again will check SDHC and maybe another multiply to 512(BlockSize usually) .I know why second 512(BlockSize usually) is necessary, but why is the first default 512 necessary? ( this one -> (EndAddr*512) )

 /* Send CMD33 (Erase group end) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */
 response = SD_SendCmd(SD_CMD_SD_ERASE_GRP_END, (EndAddr*512) * (flag_SDHC == 1 ? 1 : BlockSize), 0xFF, SD_ANSWER_R1_EXPECTED);
 SD_IO_CSState(1);
 SD_IO_WriteByte(SD_DUMMY_BYTE);

alister
Senior III
October 14, 2020

flag_SDHC is the variable at the bottom of the code snippet in your first post.

SD Specifications Part 1 Physical Layer Simplified Specification Version 6.00 August 29, 2018, table 7-4, point 10.

It applies all commands with an address argument. The older SDSC are byte-addressed. The newer SDHC and SDXC (and larger) are 512-byte-block-addressed as they'll need the extra address bits and the various file systems are dependent on the 512-byte block size anyway.

M N
M NAuthor
Associate III
October 19, 2020

Thank you , And I'm not using FatFs ,At this time I just am using this library nothing else .

And in picture below there is an example of "Physical_Layer_Simplified_Specification"

0693W000004KIRBQA4.jpg 

So,Still here have this question not answered :

-----------------------------------------------------------------------

I knew different between SDSC byte address oriented card and SDHC-SDXC block address .

The problem is with those two function maybe with an example it will be better explanation .

Imagine flag_SDHC = 1 ; -> so the card is block address type .

Now we will call SD_SendCmd function :

1- For start erase address it would be like this :

//The Second parameter will replace with 1 because we want to erase from first or block number one and because "lag_SDHC = 1" so because of ternary operator "?" block number 1 * 1 will be 1 :
SD_SendCmd(SD_CMD_SD_ERASE_GRP_START, 1 , 0xFF, SD_ANSWER_R1_EXPECTED);

which has no problem and everything seems fine .

2- For end erase address it would be like this :

//The Second parameter will replace with 5( as an example we want block number 5 will be our end erase address ) and because of "flag_SDHC = 1" and because of ternary operator "?" resault for second function parameter will be (5 * 512)*(1) which is block 2560 !!! :
SD_SendCmd(SD_CMD_SD_ERASE_GRP_END, (5*512) * (1), 0xFF, SD_ANSWER_R1_EXPECTED);

But here is the problem it will multiply our EndAddress with 512 ! why should we do this ? why like start address it is not just block number as in last picture sd specification example said (without any multiplication to 512) ?

For CSD v2 in sector_size it has said it is not related to erase operation and AU size has affect on this ? Isn't it related to this 512 ?

alister
Senior III
October 19, 2020

>But here is the problem it will multiply our EndAddress with 512 !

Compare it with the FatFs implementation at http://elm-chan.org/fsw/ff/ffsample.zip. You'll find it in stm32\mmc_stm32f1_spi.c.

@Amel NASRI​, could you escalate this please? The stm32_adafruit_sd.c of https://github.com/STMicroelectronics/STM32CubeF1/tree/master/Drivers/BSP/Adafruit_Shield and other repositories (F4, etc) have implemented erase incorrectly.

M N
M NAuthor
Associate III
October 19, 2020

Ok . But I hate FatFs, It is like below picture :

0693W000004KJqgQAG.jpg0693W000004KJqRQAW.jpg:grimacing_face: