SparkFun Forums 

Where electronics enthusiasts find answers.

Everything ARM and LPC
By slagment
#140924
Hello,

I'm trying into initialize a SD card on the SDIO interface of a STM32F407.

Command 0 returns response 0;
Command 8 returns the check pattern 10101010b;
Command 55 returns response 32;
AppCommand 41 doesn't return a response.

Anyone with SDIO and SD card experience willing to share there initialization code, or find my faults? I'm especially interested in the commands, responses, and arguments.

The below code has been worked over for debugging, so it's not very organized.
Code: Select all
/* Includes */
#include <stdio.h>
#include <stdlib.h>
#include "stm32f4xx_conf.h"
#include "stm32f4xx.h"
#include "IO_Defs_STM32F4.h"
#include "STM32F4_IO_Board.h"

/* Private macro */
#define SDIO_TRANSFER_CLK_DIV     	((uint8_t)0x1)
#define SDIO_INIT_CLK_DIV                  ((uint8_t)0xB2)
#define SD_CHECK_PATTERN           	((uint32_t)0x000001AA)
#define SD_VOLTAGE_WINDOW_SD            ((uint32_t)0x80100000)
#define SD_STD_CAPACITY                 ((uint32_t)0x00000000)
#define SD_CHECK_PATTERN                ((uint32_t)0x000001AA)
#define SD_STD_CAPACITY                 ((uint32_t)0x00000000)
#define SD_HIGH_CAPACITY                ((uint32_t)0x40000000)
#define AMCD41_ARG						(uint32_t)( (1<<30) | (1<<28)| (1<<15) | (1<<0) )


/* Private variables */
/* Private function prototypes */
void SDIO_init1(void);
void SDIO_print_respnse(uint8_t);
void SDIO_GPIO_init(void);
void SDIO_DMA_init(void);
void UART_debug_init(void);
/* Private functions */

/**
**===========================================================================
**
**  Abstract: main program
**
**===========================================================================
*/
int main(void)
{

	char TMP[40];
	//Initialize GPIO pins for LCD display data lines.
	GPIO_init(GPIO_Mode_OUT, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL, LCDDis);
	//Initialize GPIO pins for LCD display control lines.
	GPIO_init(GPIO_Mode_OUT, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL,LCDCtr);

	GPIO_init(GPIO_Mode_IN, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL, 1, GPIO_21);

	LCD_init();
	LCD_init();
	LCD_init();

	UART_debug_init();

	SDIO_GPIO_init();

	SDIO_init1();

	SDIO_DMA_init();



	SDIO_CmdInitTypeDef SDIO_Cmd_struct;					//Structure.
	char Hello [10] = "HELLO!!!!";
	Hello[9] = '\0';

	while (1)
  {

	  SDIO_Cmd_struct.SDIO_CmdIndex= 7;							//CMD7 Select card.
	  SDIO_Cmd_struct.SDIO_Argument= 1;
	  SDIO_SendCommand(&SDIO_Cmd_struct);



	  //Create data unit.
	  SDIO_DataInitTypeDef SDIO_Data;
	  SDIO_Data.SDIO_DataTimeOut 	= 0xFFFFFFFF;
	  SDIO_Data.SDIO_DataLength 	= 10;
	  SDIO_Data.SDIO_DataBlockSize 	= SDIO_DataBlockSize_1b;
	  SDIO_Data.SDIO_TransferDir 	= SDIO_TransferDir_ToCard;
	  SDIO_Data.SDIO_TransferMode	= SDIO_TransferMode_Block;
	  SDIO_Data.SDIO_DPSM 			= SDIO_DPSM_Disable;
	  SDIO_DataConfig(&SDIO_Data);


	  SDIO_Cmd_struct.SDIO_CmdIndex= 24;						//CMD24 Block write.
	  SDIO_Cmd_struct.SDIO_Argument	= 1;
	  SDIO_Cmd_struct.SDIO_CPSM 	= SDIO_CPSM_Enable;
	  SDIO_Cmd_struct.SDIO_Response= SDIO_Response_Short;
	  SDIO_Cmd_struct.SDIO_Wait	=	 SDIO_Wait_No;
	  SDIO_SendCommand(&SDIO_Cmd_struct);
	  SDIO_print_respnse(24);
	  SDIO_WriteData(71);


  }
}

void SDIO_GPIO_init(void)
{

    GPIO_InitTypeDef  GPIO_InitStructure;


    /* GPIOC and GPIOD Periph clock enable */
    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE );
    /* Enable the SDIO APB2 Clock */
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_SDIO, ENABLE );



    /* Configure PC.12 pin: CLK pin */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIOC->BSRRL = GPIO_Pin_12;
    GPIOC->BSRRH = GPIO_Pin_12;
    GPIOC->BSRRL = GPIO_Pin_12;
    GPIOC->BSRRH = GPIO_Pin_12;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOC, &GPIO_InitStructure);


    /* Configure PD.02 CMD line */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);



    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);


}
void SDIO_init1(void)
{
	SDIO_DeInit();
    SDIO_InitTypeDef SDIO_InitStructure;
	char TMP [40];


    SDIO_InitStructure.SDIO_ClockDiv = 120;//240;//120;//SDIO_INIT_CLK_DIV;
    SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
    SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
    SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
    SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
    SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
    SDIO_Init(&SDIO_InitStructure);

    /*!< Set Power State to ON */
    SDIO_SetPowerState(SDIO_PowerState_ON);


    /*!< Enable SDIO Clock */
    SDIO_ClockCmd(ENABLE);

	_delay_us(0x4FFFFF);



	SDIO_CmdInitTypeDef SDIO_CmdInitStructure;					//Structure.

	
  //  	USART_SendData(USART1, 0xF0F0);

		


		
  



	
		//CMD0 Go idle.
	
		SDIO_CmdInitStructure.SDIO_Argument = 0x0;
		SDIO_CmdInitStructure.SDIO_CmdIndex = 0;
		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(0);
		
		
		
		
		
	
  
while ( (uint8_t)SDIO_GetResponse(SDIO_RESP1) != 170)
    {	
	
	
		//CMD 8
		SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
		SDIO_CmdInitStructure.SDIO_CmdIndex = 8;
		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(8);		
		_delay_us(0x4FFFFF);	
	}			
	
	while ( (uint8_t)SDIO_GetResponse(SDIO_RESP1) != 32)
    {	
      	// CMD55
        SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 55;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(55);		
		_delay_us(0x4FFFFF);
	}

	while ( (uint8_t)SDIO_GetResponse(SDIO_RESP1) != 99)
    {		
		//ACMD41
        SDIO_CmdInitStructure.SDIO_Argument = AMCD41_ARG;//SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY  ;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 41;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		_delay_us(0x4FFFFF);
		SDIO_print_respnse(41);
	}
	

        //Send CMD2 ALL_SEND_CID
        SDIO_CmdInitStructure.SDIO_Argument = 0x0;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 2;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(2);


        //Send CMD3 RCA
        SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 3;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(3);
   
	













    SDIO_CmdInitStructure.SDIO_Argument = 1 << 16;
    SDIO_CmdInitStructure.SDIO_CmdIndex = 9;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    SDIO_print_respnse(9);

}

void SDIO_print_respnse(uint8_t CMD)
{
	LCD_clear();
	char TMP [40];
	LCD_SetCursor ( 0, 0 );
//	_delay_us(0x4FFFFF);
/*
		sprintf( TMP, "CMD %d - %d %d %d %d", CMD,
		(int)SDIO_GetResponse(SDIO_RESP1),  (int)SDIO_GetResponse(SDIO_RESP2),
		(int)SDIO_GetResponse(SDIO_RESP3),  (int)SDIO_GetResponse(SDIO_RESP4));
		LCD_write_str((unsigned char *)TMP);
*/
		sprintf( TMP, "CMD %d-%d %d %d", CMD,
		(uint8_t)SDIO_GetResponse(SDIO_RESP1),(uint8_t)SDIO_GetResponse(SDIO_RESP2),(uint8_t)SDIO_GetResponse(SDIO_RESP3));
		LCD_write_str((unsigned char *)TMP);


		LCD_SetCursor ( 0, 1 );
		sprintf( TMP, "CMD Resp %d", SDIO_GetCommandResponse());
		LCD_write_str((unsigned char *)TMP);


//	_delay_us(0x4FFFFF);

}

void SDIO_DMA_init(void)
{
 	SDIO_CmdInitTypeDef SDIO_Cmd_struct;					//Structure.
	char Hello [10] = "HELLO!!!!";
	Hello[9] = '\0';
	  //9. Configure DMA.  Page 725 RM0090 and directions in stm32f4xx_dma.c
	  RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);	//Enable DMA clock.
	  DMA_InitTypeDef SDIO_DMA_struct;						//Settings structure.
	  DMA_StructInit(&SDIO_DMA_struct);						//Populate with default values.
	  //SDIO specific settings.
	  SDIO_DMA_struct.DMA_Channel				= DMA_Channel_4;
	  SDIO_DMA_struct.DMA_PeripheralBaseAddr	= SDIO_BASE;				// TRY SDIO_BASE + 0x80
	  SDIO_DMA_struct.DMA_DIR					= DMA_DIR_MemoryToPeripheral;
	  SDIO_DMA_struct.DMA_Memory0BaseAddr		= (uint32_t)Hello;
	  SDIO_DMA_struct.DMA_PeripheralInc			= DMA_PeripheralInc_Disable;
	  SDIO_DMA_struct.DMA_MemoryInc				= DMA_MemoryInc_Enable;
	  SDIO_DMA_struct.DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Word;
	  SDIO_DMA_struct.DMA_MemoryDataSize		= DMA_MemoryDataSize_Word;
	  SDIO_DMA_struct.DMA_PeripheralBurst		= DMA_PeripheralBurst_INC4;

	  DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);

	  DMA_Init(DMA2_Stream3,&SDIO_DMA_struct);				//Set stream with settings.


	  //10. Enable stream.
	  DMA_Cmd(DMA2_Stream3, ENABLE);						//This may conduct the transfer.

	  DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);
	  SDIO_DMACmd(ENABLE);

	  SDIO_Cmd_struct.SDIO_CmdIndex= 7;							//CMD7 Select card.
	  SDIO_Cmd_struct.SDIO_Argument= 1;
	  SDIO_SendCommand(&SDIO_Cmd_struct);



	  //Create data unit.
	  SDIO_DataInitTypeDef SDIO_Data;
	  SDIO_Data.SDIO_DataTimeOut 	= 0xFFFFFFFF;
	  SDIO_Data.SDIO_DataLength 	= 10;
	  SDIO_Data.SDIO_DataBlockSize 	= SDIO_DataBlockSize_1b;
	  SDIO_Data.SDIO_TransferDir 	= SDIO_TransferDir_ToCard;
	  SDIO_Data.SDIO_TransferMode	= SDIO_TransferMode_Block;
	  SDIO_Data.SDIO_DPSM 			= SDIO_DPSM_Disable;
	  SDIO_DataConfig(&SDIO_Data);


	  SDIO_Cmd_struct.SDIO_CmdIndex= 24;						//CMD24 Block write.
	  SDIO_Cmd_struct.SDIO_Argument	= 1;
	  SDIO_Cmd_struct.SDIO_CPSM 	= SDIO_CPSM_Enable;
	  SDIO_Cmd_struct.SDIO_Response= SDIO_Response_Short;
	  SDIO_Cmd_struct.SDIO_Wait	=	 SDIO_Wait_No;
	  SDIO_SendCommand(&SDIO_Cmd_struct);
	  SDIO_print_respnse(24);
	  SDIO_WriteData(71);


}

void UART_debug_init(void)
{
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	 
	 
    GPIO_InitTypeDef  GPIO_InitStructure;


    /* GPIOB Periph clock enable */
    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE );

	 
	 
	/* Configure PC.12 pin: CLK pin */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
	

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART1);
  
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate				= 9600;
	USART_InitStructure.USART_WordLength			= USART_WordLength_8b;
	USART_InitStructure.USART_StopBits				= USART_StopBits_1;
	USART_InitStructure.USART_Parity				= USART_Parity_No;
	USART_InitStructure.USART_Mode					= USART_Mode_Tx;
	USART_InitStructure.USART_HardwareFlowControl	= USART_HardwareFlowControl_None;

	
	USART_Init(USART1, &USART_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
	

	
	
	
	
	 
}
By UhClem
#140929
I got my take on the SDIO code working several weeks ago but I did not use the stm libraries to do it.

ACMD41 does not return a valid CRC so perhaps the library is flubbing that somehow.

It looks like for the voltage range you only set one bit indicating 2.7 to 2.8 volts on the power supply. Is this in fact what you are providing? If the card doesn't support that operating voltage that could be the reason for no response.
By slagment
#140959
Thanks for the reply,

Not using the STM library is a feat, congrats. Are you speaking for CMD8 or ACMD41?

For CMD8:
From page 51 of the Physical Layer Simplified Specification
https://www.sdcard.org/downloads/pls/si ... 100518.pdf

(VHS) bits 19:16 0001b === 2.7-3.6V

The supplied voltage is 3.3V. Also, the card echoes back the check pattern, signifying it can operate at the supplied voltage.
When the card is in Idle state, the host shall issue CMD8 before ACMD41. In the argument, 'voltage
supplied' is set to the host supply voltage and 'check pattern' is set to any 8-bit pattern.
The card checks whether it can operate on the host's supply voltage. The card that accepted the
supplied voltage returns R7 response. In the response, the card echoes back both the voltage range
and check pattern set in the argument. If the card does not support the host supply voltage, it shall not
return response and stays in Idle state.
Concerning the response for CMD 55, a response of R1 32 indicates illegal address. The error puzzles me, because this isn't a data operation, and the card hasn't been given a RCA and should be using default RCA 0x0000.

Any thoughts? Thanks.
By UhClem
#140962
slagment wrote:Thanks for the reply,

Not using the STM library is a feat, congrats. Are you speaking for CMD8 or ACMD41?
ACMD41
Concerning the response for CMD 55, a response of R1 32 indicates illegal address. The error puzzles me, because this isn't a data operation, and the card hasn't been given a RCA and should be using default RCA 0x0000.

Any thoughts? Thanks.
32 (aka 0x00000020) is APP_CMD indicating that the card will interpret the next command as application specific.

Oh, your loop to send ACMD41 is wrong. If it fails you need to send CMD 55 again before retrying ACMD41. I cheated and set a bit so that my send command routine could handle CMD55 automagically. My code to handle this part of initialization is:
Code: Select all
  /* 
     Send ACMD41 and repeat for up to 1 second until card indicates it is
     ready. Set HCS bit and indicate 3.2-3.3V power.
  */
static int initialize(int cap_bit)
{
  int i;
  for(i = 0; i <=100; i++)
    {
      delay(10);   // wait 10ms
      /*
        The R3 response does not have a valid CRC so a check for a CRC
        error is needed here. The CRC error bit is then an indication that
        the command worked. Go figure.
      */
      if((SendCmd(ACMD41, 0x300000 | (cap_bit ? 0x40000000: 0)) & SDIO_STA_CCRCFAIL) == 0)
        continue;
      if(SDIO->RESP1 & 0x80000000) // this bit set when card ready
        break;
    }
  if(i >= 100)
    return -3;
  if(cap_bit && (SDIO->RESP1 & 0x40000000L))  // check card capacity bit
    CardType = SDHC;
  else
    CardType = SD;
  return 0;
}

I include a delay of 10ms because I didn't see any need to bang away on the card as fast as possible since it will take some time to initialize.
By slagment
#141023
Thanks again,

I made some changes to my code, and looked at the responses. You're right, CMD 55 was getting the correct response, but ACMD41 never brings bit 31 high, no matter how many times it is issued. Watching with a logic analyzer, the card response twice to ACMD 41, both times bit 31 is low, then never responds again. Not even to CMD 55.

I'm using this for the argument to ACMD41 0b01000000000000100000000000000000.

The voltage on VCC of card holder is the 2.97V, 2.86 on the GPIO lines from the micro-controller.

The response from ACMD41 is:
1 090 486 272 = 0b1000000111111111000000000000000.

The card is ADATA 8GB Micro ST HC. I've also tried a low capacity card.

ACMD41 Loop.
Code: Select all
    while(!(SDIO->RESP1 & 0x80000000))
	{
	
		_delay_us(0x4FFFFF);
		//CMD55
	    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 55;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(55);			
		_delay_us(0x4FFFFF);

		//ACMD41
        SDIO_CmdInitStructure.SDIO_Argument = AMCD41_ARG;//SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY  ;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 41;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(41);
		_delay_us(0x4FFFFF);

		if( SDIO_STA_CCRCFAIL == 0)
			continue;
		
		if (SDIO->RESP1 & 0x80000000)
			break;			
		
	}	
Complete Code
Code: Select all
/* Includes */
#include <stdio.h>
#include <stdlib.h>
#include "stm32f4xx_conf.h"
#include "stm32f4xx.h"
#include "IO_Defs_STM32F4.h"
#include "STM32F4_IO_Board.h"

/* Private macro */
#define SDIO_TRANSFER_CLK_DIV     	((uint8_t)0x1)
#define SDIO_INIT_CLK_DIV                  ((uint8_t)0xB2)
#define SD_CHECK_PATTERN           	((uint32_t)0x000011AA)
#define SD_VOLTAGE_WINDOW_SD            ((uint32_t)0x80100000)
#define SD_STD_CAPACITY                 ((uint32_t)0x00000000)
#define SD_CHECK_PATTERN                ((uint32_t)0x000001AA)
#define SD_STD_CAPACITY                 ((uint32_t)0x00000000)
#define SD_HIGH_CAPACITY                ((uint32_t)0x40000000)
#define AMCD41_ARG						(uint32_t)( 0b01000000000000100000000000000000)
													
/* Private variables */
/* Private function prototypes */
void SDIO_init1(void);
void SDIO_print_respnse(uint8_t);
void SDIO_GPIO_init(void);
void SDIO_DMA_init(void);
void UART_debug_init(void);
/* Private functions */

/**
**===========================================================================
**
**  Abstract: main program
**
**===========================================================================
*/
int main(void)
{

	char TMP[40];
	//Initialize GPIO pins for LCD display data lines.
	GPIO_init(GPIO_Mode_OUT, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL, LCDDis);
	//Initialize GPIO pins for LCD display control lines.
	GPIO_init(GPIO_Mode_OUT, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL,LCDCtr);

	GPIO_init(GPIO_Mode_IN, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL, 1, GPIO_21);

	LCD_init();
	LCD_init();
	LCD_init();

	UART_debug_init();

	SDIO_GPIO_init();

	SDIO_init1();

	SDIO_DMA_init();



	SDIO_CmdInitTypeDef SDIO_Cmd_struct;					//Structure.
	char Hello [10] = "HELLO!!!!";
	Hello[9] = '\0';

	while (1)
  {

	  SDIO_Cmd_struct.SDIO_CmdIndex= 7;							//CMD7 Select card.
	  SDIO_Cmd_struct.SDIO_Argument= 1;
	  SDIO_SendCommand(&SDIO_Cmd_struct);



	  //Create data unit.
	  SDIO_DataInitTypeDef SDIO_Data;
	  SDIO_Data.SDIO_DataTimeOut 	= 0xFFFFFFFF;
	  SDIO_Data.SDIO_DataLength 	= 10;
	  SDIO_Data.SDIO_DataBlockSize 	= SDIO_DataBlockSize_1b;
	  SDIO_Data.SDIO_TransferDir 	= SDIO_TransferDir_ToCard;
	  SDIO_Data.SDIO_TransferMode	= SDIO_TransferMode_Block;
	  SDIO_Data.SDIO_DPSM 			= SDIO_DPSM_Disable;
	  SDIO_DataConfig(&SDIO_Data);


	  SDIO_Cmd_struct.SDIO_CmdIndex= 24;						//CMD24 Block write.
	  SDIO_Cmd_struct.SDIO_Argument	= 1;
	  SDIO_Cmd_struct.SDIO_CPSM 	= SDIO_CPSM_Enable;
	  SDIO_Cmd_struct.SDIO_Response= SDIO_Response_Short;
	  SDIO_Cmd_struct.SDIO_Wait	=	 SDIO_Wait_No;
	  SDIO_SendCommand(&SDIO_Cmd_struct);
	  SDIO_print_respnse(24);
	  	_delay_us(0x4FFFFF);
	  SDIO_WriteData(71);
	  


  }
}

void SDIO_GPIO_init(void)
{

    GPIO_InitTypeDef  GPIO_InitStructure;


    /* GPIOC and GPIOD Periph clock enable */
    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE );
    /* Enable the SDIO APB2 Clock */
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_SDIO, ENABLE );



    /* Configure PC.12 pin: CLK pin */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIOC->BSRRL = GPIO_Pin_12;
    GPIOC->BSRRH = GPIO_Pin_12;
    GPIOC->BSRRL = GPIO_Pin_12;
    GPIOC->BSRRH = GPIO_Pin_12;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOC, &GPIO_InitStructure);


    /* Configure PD.02 CMD line */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);



    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);


}
void SDIO_init1(void)
{
	SDIO_DeInit();
    SDIO_InitTypeDef SDIO_InitStructure;
	char TMP [40];


    SDIO_InitStructure.SDIO_ClockDiv = 120;//240;//120;//SDIO_INIT_CLK_DIV;
    SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
    SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
    SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
    SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_4b;
    SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
    SDIO_Init(&SDIO_InitStructure);

    /*!< Set Power State to ON */
    SDIO_SetPowerState(SDIO_PowerState_ON);


    /*!< Enable SDIO Clock */
    SDIO_ClockCmd(ENABLE);

	_delay_us(0x4FFFFF);



	SDIO_CmdInitTypeDef SDIO_CmdInitStructure;					//Structure.

	
  //  	USART_SendData(USART1, 0xF0F0);

		


		
  



	
		//CMD0 Go idle.
	
		SDIO_CmdInitStructure.SDIO_Argument = 0x0;
		SDIO_CmdInitStructure.SDIO_CmdIndex = 0;
		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(0);
		
		
		
		
		
	
  
while (SDIO_GetResponse(SDIO_RESP1) != 426)
    {	
	
	
		//CMD 8
		SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
		SDIO_CmdInitStructure.SDIO_CmdIndex = 8;
		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(8);		
		_delay_us(0x4FFFFF);	
	}			
	

/*		while(SDIO_GetResponse(SDIO_RESP1) != 288)
		{	
	
	    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 55;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(55);	
		_delay_us(0x4FFFFF);	
		}	
*/	

    while(!(SDIO->RESP1 & 0x80000000))
	{
	
		_delay_us(0x4FFFFF);
		//CMD55
	    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 55;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(55);			
		_delay_us(0x4FFFFF);

		//ACMD41
        SDIO_CmdInitStructure.SDIO_Argument = AMCD41_ARG;//SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY  ;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 41;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
		SDIO_SendCommand(&SDIO_CmdInitStructure);
		SDIO_print_respnse(41);
		_delay_us(0x4FFFFF);

		if( SDIO_STA_CCRCFAIL == 0)
			continue;
		
		if (SDIO->RESP1 & 0x80000000)
			break;			
		
	}	
		
	
	

        //Send CMD2 ALL_SEND_CID
        SDIO_CmdInitStructure.SDIO_Argument = 0x0;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 2;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
			_delay_us(0x4FFFFF);
        SDIO_print_respnse(2);
	_delay_us(0x4FFFFF);

        //Send CMD3 RCA
        SDIO_CmdInitStructure.SDIO_Argument = 0x00;
        SDIO_CmdInitStructure.SDIO_CmdIndex = 3;
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&SDIO_CmdInitStructure);
			_delay_us(0x4FFFFF);
        SDIO_print_respnse(3);
   	_delay_us(0x4FFFFF);
	













    SDIO_CmdInitStructure.SDIO_Argument = 1 << 16;
    SDIO_CmdInitStructure.SDIO_CmdIndex = 9;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
	_delay_us(0x4FFFFF);
    SDIO_print_respnse(9);
	_delay_us(0x4FFFFF);
}

void SDIO_print_respnse(uint8_t CMD)
{
	LCD_clear();
	char TMP [40];
	LCD_SetCursor ( 0, 0 );
//	_delay_us(0x4FFFFF);
/*
		sprintf( TMP, "CMD %d - %d %d %d %d", CMD,
		(int)SDIO_GetResponse(SDIO_RESP1),  (int)SDIO_GetResponse(SDIO_RESP2),
		(int)SDIO_GetResponse(SDIO_RESP3),  (int)SDIO_GetResponse(SDIO_RESP4));
		LCD_write_str((unsigned char *)TMP);
*/
		sprintf( TMP, "CMD%d-%d %d %d", CMD,
		(uint32_t)SDIO_GetResponse(SDIO_RESP1),(uint8_t)SDIO_GetResponse(SDIO_RESP2),(uint8_t)SDIO_GetResponse(SDIO_RESP3));
		LCD_write_str((unsigned char *)TMP);


		LCD_SetCursor ( 0, 1 );
		sprintf( TMP, "CMD Resp %d", SDIO_GetCommandResponse());
		LCD_write_str((unsigned char *)TMP);


//	_delay_us(0x4FFFFF);

}

void SDIO_DMA_init(void)
{
 	SDIO_CmdInitTypeDef SDIO_Cmd_struct;					//Structure.
	char Hello [10] = "HELLO!!!!";
	Hello[9] = '\0';
	  //9. Configure DMA.  Page 725 RM0090 and directions in stm32f4xx_dma.c
	  RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);	//Enable DMA clock.
	  DMA_InitTypeDef SDIO_DMA_struct;						//Settings structure.
	  DMA_StructInit(&SDIO_DMA_struct);						//Populate with default values.
	  //SDIO specific settings.
	  SDIO_DMA_struct.DMA_Channel				= DMA_Channel_4;
	  SDIO_DMA_struct.DMA_PeripheralBaseAddr	= SDIO_BASE;				// TRY SDIO_BASE + 0x80
	  SDIO_DMA_struct.DMA_DIR					= DMA_DIR_MemoryToPeripheral;
	  SDIO_DMA_struct.DMA_Memory0BaseAddr		= (uint32_t)Hello;
	  SDIO_DMA_struct.DMA_PeripheralInc			= DMA_PeripheralInc_Disable;
	  SDIO_DMA_struct.DMA_MemoryInc				= DMA_MemoryInc_Enable;
	  SDIO_DMA_struct.DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Word;
	  SDIO_DMA_struct.DMA_MemoryDataSize		= DMA_MemoryDataSize_Word;
	  SDIO_DMA_struct.DMA_PeripheralBurst		= DMA_PeripheralBurst_INC4;

	  DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);

	  DMA_Init(DMA2_Stream3,&SDIO_DMA_struct);				//Set stream with settings.


	  //10. Enable stream.
	  DMA_Cmd(DMA2_Stream3, ENABLE);						//This may conduct the transfer.

	  DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);
	  SDIO_DMACmd(ENABLE);

	  SDIO_Cmd_struct.SDIO_CmdIndex= 7;							//CMD7 Select card.
	  SDIO_Cmd_struct.SDIO_Argument= 1;
	  SDIO_SendCommand(&SDIO_Cmd_struct);



	  //Create data unit.
	  SDIO_DataInitTypeDef SDIO_Data;
	  SDIO_Data.SDIO_DataTimeOut 	= 0xFFFFFFFF;
	  SDIO_Data.SDIO_DataLength 	= 10;
	  SDIO_Data.SDIO_DataBlockSize 	= SDIO_DataBlockSize_1b;
	  SDIO_Data.SDIO_TransferDir 	= SDIO_TransferDir_ToCard;
	  SDIO_Data.SDIO_TransferMode	= SDIO_TransferMode_Block;
	  SDIO_Data.SDIO_DPSM 			= SDIO_DPSM_Disable;
	  SDIO_DataConfig(&SDIO_Data);


	  SDIO_Cmd_struct.SDIO_CmdIndex= 24;						//CMD24 Block write.
	  SDIO_Cmd_struct.SDIO_Argument	= 1;
	  SDIO_Cmd_struct.SDIO_CPSM 	= SDIO_CPSM_Enable;
	  SDIO_Cmd_struct.SDIO_Response= SDIO_Response_Short;
	  SDIO_Cmd_struct.SDIO_Wait	=	 SDIO_Wait_No;
	  SDIO_SendCommand(&SDIO_Cmd_struct);
	  SDIO_print_respnse(24);
	  SDIO_WriteData(71);


}

void UART_debug_init(void)
{
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	 
	 
    GPIO_InitTypeDef  GPIO_InitStructure;


    /* GPIOB Periph clock enable */
    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE );

	 
	 
	/* Configure PC.12 pin: CLK pin */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
	

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART1);
  
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate				= 9600;
	USART_InitStructure.USART_WordLength			= USART_WordLength_8b;
	USART_InitStructure.USART_StopBits				= USART_StopBits_1;
	USART_InitStructure.USART_Parity				= USART_Parity_No;
	USART_InitStructure.USART_Mode					= USART_Mode_Tx;
	USART_InitStructure.USART_HardwareFlowControl	= USART_HardwareFlowControl_None;

	
	USART_Init(USART1, &USART_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
	

	
	
	
	
	 
}
By UhClem
#141036
slagment wrote: I'm using this for the argument to ACMD41
0b01000000000000100000000000000000.
In order for me to verify that this number is reasonable I would have to count a bunch of zeros. That is never going to happen.

Code: Select all
        SDIO_SendCommand(&SDIO_CmdInitStructure);
        SDIO_print_respnse(55);
      _delay_us(0x4FFFFF);
The call to SDIO_print_response() may or may not get valid data. During SD initialization where the SD clock is under 400KHz and if the ARM processor is running with a fast clock, it will read the results before they have arrived every time. Never a good thing. The problem is that the library functions never check the SDIO status register to see if the Command Path State Machine is finished let alone wait for it to finish.
By hsutherl
#141042
I was working with SDIO a few months ago. I had a version (nearly) working that was a fairly faithful copy of the ST SDIO example, except that it doesn't use the StdPeriph lib, and has hooks for interrupt-driven data xfer rather than busy-wait. But I sort of ran out of steam before getting it into a useful form. Anyway, here is the section under discussion:
Code: Select all
#define SD_VOLTAGE_WINDOW_SD            ((uint32_t)0x80100000)
#define SD_MAX_VOLT_TRIAL               ((uint32_t)0x0000FFFF)
...
  uint32_t count = SD_MAX_VOLT_TRIAL;
  uint32_t hcs = 0; // Host Capacity Support bit for ACMD41
...
  e = CmdResp7(SDIO_SEND_IF_COND, SD_CHECK_PATTERN, ci); // CMD8
  if (e == SD_OK) {
    ...
    hcs = 1; //indicate support for high density (ACMD41)
  }
  ...
  sendstrz(strzai); // SD_PowerOn(): checking voltage range (over and over?)
  while(!ci->ocr.ocr.ready && count--) {
    e = CmdResp1(SDIO_APP_CMD, 0, ci); // SEND CMD55 APP_CMD with RCA as 0
    if (e != SD_OK) return(e);
    e = CmdResp3(SDIO_SD_APP_OP_COND, SD_VOLTAGE_WINDOW_SD | hcs << 30, ci); //ACMD41
    if (e != SD_OK) return(e);
  }
  if (count == 0) return(SD_INVALID_VOLTRANGE);

The points of interest are
  • argument to ACMD41: 0xc0100000 for hd, 0x8010000 otherwise. (I'll have to review the spec to understand this.)
  • The author of the ST example code chose to try 65535 times before giving up. I assume he had a reason for this. I had lots of problems getting things to work, but I don't remember a problem in this area. I may try to have it spit out the count on a few cards and report back.
By slagment
#141268
If you don't clear the CRC flag after ACMD41, the Respx registers lock up. The card stops responding after two ACMD41 attempts, if it has initialized. The errata does explain the CRC fail for CMD5, but not ACMD41. Documentation didn't say the Respx registers stop updating.

So,

1. ACMD41, CRC fails like it should.
2. ACMD41 again, checking for the busy bit, which isn't updated in the Respx because the CRC flag wasn't cleared.
3. ACMD41 again, and the card no longer responds to that command.

The error was finally discovered by counting out the 1's and 0's on the logic analyzer, and noticing the actual response didn't match the Respx registers.

I hope ST updates the SDIO driver, to not lock up the Respx registers on CRC fail.

Thanks for your help.
By UhClem
#141280
That is something that bit me as well. It took quite a while before I figured out that if peripheral flags are set then bad things happen. Like the hardware doesn't respond at all. But I didn't notice that the CRC fail flag is not on the list of flags cleared by the library code just before writing the command register. On the other hand, have you read the disclaimer at the start of the standard peripheral library files?
Code: Select all
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
Just one of the reasons that I avoided using those libraries.

If you look at the SD spec you will see that ACMD41 returns a R3 response. The field of the R3 response that would normally have a CRC is reserved. I have no idea why it needs to be special.
long long title how many chars? lets see 123 ok more? yes 60

We have created lots of YouTube videos just so you can achieve [...]

Another post test yes yes yes or no, maybe ni? :-/

The best flat phpBB theme around. Period. Fine craftmanship and [...]

Do you need a super MOD? Well here it is. chew on this

All you need is right here. Content tag, SEO, listing, Pizza and spaghetti [...]

Lasagna on me this time ok? I got plenty of cash

this should be fantastic. but what about links,images, bbcodes etc etc? [...]