一. 硬件平台
硬件以ch32V103 开发板为平台,记录了tf卡驱动的实验过程。
SD采用SPI模式时的引脚连接关系
硬件系统设计图如下所示:
SPI模式的TF卡引脚连接关系图如下。
序号 | TF卡引脚 | CPU引脚 |
1 | CS | PA3 |
2 | SCK | PA5 |
3 | MISO | PA6 |
4 | MOSI | PA7 |
5 | SD_IN | PA8 |
这里需要特别提醒,CS引脚默认是不焊接的,如果需要测试必须先短接后PA3引脚才能生效。在这里绕了好几天。
软件驱动1) IO口配置
这里默认配置SPI的CS引脚为软件控制。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_SCK; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_MISO; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init( GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_MOSI; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_NSS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_SDCMD; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init( GPIOA, &GPIO_InitStructure);
复制代码2) SPI设备配置
SPI设备采用SPI1,全双工2线方式,软件控制CS信号。
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //SPI_CPOL_High SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init( SPI1, &SPI_InitStructure ); SPI_Cmd( SPI1, ENABLE );
复制代码3) TF卡驱动
向SD卡写入一个CMD或者ACMD指令的过程是这样的: 首先使CS为低电平,SD卡使能;其次在SD卡的Din写入指令;写入指令后还要附加8个填充时钟,是SD卡完成内部操作;之后在SD卡的Dout上接受回应;回应接受完毕使CS为低电平,再附加8个填充时钟。
注意:在SD卡的Din没有数据写入时,应使Din保持高电平。
l SD卡复位:
1.拉高CS,发送至少74个clk周期来使SD卡达到正常工作电压和进行同步
2.选低CS,发送CMD0,需要收到回应0x01表示成功进入idle状态
3.拉高CS,发送8个时钟
实现代码:
for(i=0;i<10;i++) SPI_ReadWrite_Byte(0XFF);//发送至少74个脉冲 retry=20; do { r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态 }while((r1!=0X01) && retry--);
复制代码l SD卡初始化:
复位成功后,SD卡就进入了SPI模式,接着应该进行初始化。发送CMD55+ACMD41完成初始化。
时序图如下:
程序代码实现:
SD_SendCmd(CMD55,0,0X01); //发送CMD55
r1=SD_SendCmd(CMD41,0,0X01); //发送CMD41
l SDK卡读操作
操作流程:
1.发送CMD18读,收到0x00表示成功
2.连续读直到读到开始字节0xFE
3.读512字节
4.读两个CRC字节
5.如果还想读下一扇区,重复2-4
6.发送CMD12来停止读多块操作
时序图如下:
程序实现:
r1=SD_SendCmd(CMD18,sector,0X01); do { r1=SD_RecvData(buf,512); buf+=512; }while(--cnt && r1==0); SD_SendCmd(CMD12,0,0X01)
复制代码l SDK卡写操作
操作走流程:
1.发送CMD24(写单块),CMD25(写多块)收到0x00表示成功
2.发送若干时钟
3.发送写单块开始字节0xFE,写多块开始字节0xFC
4.发送512个字节数据
5.发送2字节CRC(可以均为0xff)
6.连续读直到读到XXX00101表示数据写入成功
7.继续读进行忙检测(读到0x00表示SD卡正忙),当读到0xff表示写操作完成
8.如果想写下一扇区重复2-7步骤
9.发送写多块停止字节0xFD来停止写操作
10.进行忙检测直到读到0xFF
时序图:
程序代码实现:
r1=SD_SendCmd(CMD25,sector,0X01); if(r1==0) { do { r1=SD_SendBlock(buf,0xFC); buf+=512; }while(--cnt && r1==0); r1=SD_SendBlock(0,0xFD); }
复制代码成功的完成SD卡初始化,并且读到了SD卡容量。