基于ARM开发板从零开始学习STM32 09-SD卡实验

SDIO模式

SDIO简介

SD/SDIO/MMC 主机接口可以支持 MMC 卡系统规范 4.2 版中的 3 个不同的数据。总线模式:1 位(默认)、4 位和 8 位。在 8 位模式下,该接口可以使数据传输速率达到 48MHz,该接口兼容 SD 存储卡规范 2.0 版。SDIO 存储卡规范 2.0 版支持两种数据总线模式:1 位(默认)和 4 位。

目前的芯片版本只能一次支持一个 SD/SDIO/MMC 4.2 版的卡,但可以同

时支持多个 MMC 4.1 版或之前版本的卡。除了 SD/SDIO/MMC,这个接口完全

与 CE-ATA 数字协议版本 1.1 兼容。

 

SD协议

                             

SDIO的时钟

SDIO_CK 时钟是通过 PC12 引脚连接到 SD 卡的,是 SDIO 接口与 SD 卡用于同步的时钟。

SDIO 选配器挂载到 AHB 总线上,通过 HCLK 二分频输入到适配器得到SDIO_CK 的时钟,这时 SDIO_CK = HCLK/(2+CLKDIV)。其中 CLKDIV 是SDIO_CLK(寄存器)中的 CLKDIV 位。

另外,SDIO_CK 也可以由 SDIOCLK 通过设置 bypass 模式直接得到,这时SDIO_CK = SDIOCLK=HCLK。

通过下面的库函数来配置时钟:

SDIO_Init(&SDIO_InitStructure);

对 SD 卡的操作一般是大吞吐量的数据传输,所以采用 DMA 来提高效率,SDIO 采用的是 DMA2 中的通道 4。在数据传输的时候 SDIO 可向 DMA 发出请求。

SDIO 的命令、数据传输方式

SDIO 的所有命令及命令响应,都是通过 SDIO-CMD 引脚来传输的。

命令只能由 host 即 STM32 的 SDIO 控制器发出。SDIO 协议把命令分成了11 种,包括基本命令,读写命令还有 ACMD 系列命令等。其中,在发送 ACMD命令前,要先向卡发送编号为 CMD55 的命令。

下面的命令格式图,其中的 start bit,transmission bit ,crc7,endbit,都是由 STM32 中的 SDIO 硬件完成,我们在软件上配置的时候只需要设置 command index 和命令参数 argument。Command index 就是命令索引(编号),如 CMD0,CMD1…被编号成 0,1...。有的命令会包含参数,读命令的地址参数等,这个参数被存放在 argument 段。

                                      

                                                                                      SD 卡命令格式 

通过下面的函数来配置、发送命令:

SDIO_SendCommand(&SDIO_CmdInitStructure);

SD 卡对 host 的各种命令的回复称为响应,除了 CMD0 命令外,SD 卡在接收到命令都会返回一个响应。对于不同的命令,会有不同的响应格式,共 7种,分为长响应型(136bit)和短响应型(48bit)。以下图,响应 6(R6)为例:

                                      

                                                                              SD 卡命令响应格式(R6)

SDIO 通过 CMD 接收到响应后,硬件去除头尾的信息,把 command index保存到 SDIO_RESPCMD 寄存器,把 argument field 内容保存存储到SDIO_RESPx 寄存器中。这两个值可以分别通过下面的库函数得到。

SDIO_GetCommandResponse(); //卡返回接收到的命令

SDIO_GetResponse(SDIO_RESP1); //卡返回的 argument field 内容

数据写入,读取。请看下面的写数据时序图,在软件上,我们要处理的只是读忙。另外,我们的实验中用的是 Micro SD 卡,有 4 条数据线,默认的时候 SDIO 采用 1 条数据线的传输方式,更改为 4 条数据线模式要通过向卡发送命令来更改。

                                

卡的种类

STM32 的 SDIO 支持 SD 存储卡,SD I/O 卡 ,MMC 卡。

其中 SDI/O 卡与 SD 存储卡是有区别的,SDI/O 卡实际上就是利用 SDIO 接口的一些模块,插入 SD 的插槽中,扩展设备的功能,如:SDI/O wifi, SDI/Ocmos 相机等。而 SD 存储卡就是我们平时常见的单纯用于存储数据的卡。

                                        

下面以SD卡读写测试为例:

Main函数入口

int main(void)
{
	NVIC_Configuration();
	RS232_Init(9600);
	printf( " 开始初始化\r\n " );	
	if( SD_OK == ( Status = SD_Init() ) )     
		printf( "初始化成功 \r\n " );		
	else
		printf("初始化失败 \r\n" );			  	
	    
 	printf( " \r\n CardType is :%d ", SDCardInfo.CardType );
	printf( " \r\n CardCapacity is :%d ", SDCardInfo.CardCapacity );
	printf( " \r\n CardBlockSize is :%d ", SDCardInfo.CardBlockSize );
	printf( " \r\n RCA is :%d ", SDCardInfo.RCA);
	printf( " \r\n ManufacturerID is :%d \r\n", SDCardInfo.SD_cid.ManufacturerID );

	SD_EraseTest();	   

	SD_SingleBlockTest();  

 	SD_MultiBlockTest();  	

  while (1)
  {}
}

用 NVIC_Configuration() 初始化好 SDIO 的中断;用 RS232_Init(9600); 配置好用于返回调试信息的串口, SD_Init() 开始

进行 SDIO 的初始化;最后分别用 SD_EraseTest()、SD_SingleBlockTest()、SD_MultiBlockTest()

进行擦除,单数据块读写,多数据块读写测试。

SD_Init()  SD初始化 卡处于传输数据状态

SD_Error SD_Init(void)
{
  SD_Error errorstatus = SD_OK;
  GPIO_Configuration();
  SDIO_DeInit();  
  errorstatus = SD_PowerON(); 
  if (errorstatus != SD_OK)
  {
    return(errorstatus);	
  }
  errorstatus = SD_InitializeCards(); 
  if (errorstatus != SD_OK)	 
  {
    return(errorstatus);
  }
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_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);
  if (errorstatus == SD_OK)
  {
      errorstatus = SD_GetCardInfo(&SDCardInfo);
  }
  if (errorstatus == SD_OK)
  {
    errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));	  
  }

  if (errorstatus == SD_OK)
  {
    errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);	
  }  

  return(errorstatus);
}

用 GPIO_Configuration() 进行 SDIO 的端口底层配置

分别调用了 SD_PowerON()和 SD_InitializeCards()函数,这两个函数共同实现了上面提到的卡检测、识别流程。

调用 SDIO_Init(&SDIO_InitStructure) 库函数配置 SDIO 的时钟,数据线宽度,硬件流(在读写数据的时候,开启硬件流是和很必要的,可以减少出错)。

调用 SD_GetCardInfo(&SDCardInfo) 获取 sd 卡的 CSD 寄存器中的内容,在main 函数里输出到串口的数据就是这个时候从卡读取得到的。

调用 SD_SelectDeselect() 选定后面即将要操作的卡。

调用 SD_EnableWideBusOperation(SDIO_BusWide_4b) 开启 4bit 数据线模式。

如果 SD_Init() 函数能够执行完整个流程,并且返回值是 SD_OK 的话则说明初始

化成功,就可以开始进行擦除、读写的操作了。

分析SD_PowerON()的工作过程,大致能了解SDIO的接收和发送命令的过程。下面将会上传完整的工程到我的网盘,工程里附带详细的注释和完整的代码,有需要的自行下载。链接:https://pan.baidu.com/s/1ancN8BAqB_NkHuvwnKGWJg 提取码:9pjm 

有兴趣的可以加一下我新建的STM32技术交流群:956967059,一起交流STM32技术,有疑问大家可以一起帮忙解决。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章