一:个人注重点
1.SDIO得驱动在标准库中已经有了。所以不建议所以构建驱动,毕竟官方驱动代码比个人严谨(大牛除外)
2.常规得移植,修改IO口,但是STM32使用SDIO已经固定在特定得GPIO端口,所以移植得工作又省了,直接添加官方得驱动c文件,加个头文件即可使用
3.一般使用SDIO为驱动SD卡,并使用fatfs文件系统,下面讲述添加SDIO官方驱动以及使用到Fatfs中。
二:实际操作流程
1.找到标准库STM32F10x_StdPeriph_Lib_V3.5.0,搜索找到stm32_eval_sdio_sd.c文件,此文件为官网驱动SDIO文件
2.添加stm32_eval_sdio_sd.c文件到自己工程,随后就是调用里面写好得API函数如:初始化Init,擦除Erase,读写Write/Read;可自行进行验证测试
3.移植到Fatfs中,将Fatfs控制函数添加上面得api函数到对应得位置,此处放diskio.c代码
/*-----------------------------------------------------------------------*/
/* 获取设备状态 */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* 物理编号 */
)
{
DSTATUS status = STA_NOINIT;
switch (pdrv)
{
case ATA: /* SD CARD */
status &= ~STA_NOINIT;
break;
case SPI_FLASH: /* SPI Flash */
break;
default:
status = STA_NOINIT;
}
return status;
}
/*-----------------------------------------------------------------------*/
/* 设备初始化 */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* 物理编号 */
)
{
DSTATUS status = STA_NOINIT;
switch (pdrv)
{
case ATA: /* SD CARD */
if(SD_Init()==SD_OK)
{
status &= ~STA_NOINIT;
}
else
{
status = STA_NOINIT;
}
break;
case SPI_FLASH: /* SPI Flash */
break;
default:
status = STA_NOINIT;
}
return status;
}
/*-----------------------------------------------------------------------*/
/* 读扇区:读取扇区内容到指定存储区 */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* 设备物理编号(0..) */
BYTE *buff, /* 数据缓存区 */
DWORD sector, /* 扇区首地址 */
UINT count /* 扇区个数(1..128) */
)
{
DRESULT status = RES_PARERR;
SD_Error SD_state = SD_OK;
switch (pdrv)
{
case ATA: /* SD CARD */
{
//判断数据存储地址是否按4字节对齐
//可以在这里先判断是否是4字节对齐的,如果不是对齐的,动态申请一块区域做存储,并做首地址对齐后做传输,传输结束后再复制到buf里面。
if((DWORD)buff&3)
{
DRESULT res = RES_OK;
//定义了数组作为存储空间
DWORD scratch[SD_BLOCKSIZE / 4];
while (count--)
{
res = disk_read(ATA,(void *)scratch, sector++, 1);
if (res != RES_OK)
{
break;
}
memcpy(buff, scratch, SD_BLOCKSIZE);
buff += SD_BLOCKSIZE;
}
return res;
}
//SD_state=SD_ReadMultiBlocks(buff,(uint64_t)sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
for ( UINT i = 0 ; i <count ; i++ )
{
SD_state=SD_ReadBlock((uint8_t *)(&buff[i<<9]), (sector +i)<< 9, SD_BLOCKSIZE);
}
if(SD_state==SD_OK)
{
/* Check if the Transfer is finished */
SD_state=SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if(SD_state!=SD_OK)
status = RES_PARERR;
else
status = RES_OK;
break;
case SPI_FLASH:
break;
default:
status = RES_PARERR;
}
return status;
}
}
/*-----------------------------------------------------------------------*/
/* 写扇区:见数据写入指定扇区空间上 */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (
BYTE pdrv, /* 设备物理编号(0..) */
const BYTE *buff, /* 欲写入数据的缓存区 */
DWORD sector, /* 扇区首地址 */
UINT count /* 扇区个数(1..128) */
)
{
DRESULT status = RES_PARERR;
SD_Error SD_state = SD_OK;
if (!count)
{
return RES_PARERR; /* Check parameter */
}
switch (pdrv)
{
case ATA: /* SD CARD */
if((DWORD)buff&3)
{
DRESULT res = RES_OK;
DWORD scratch[SD_BLOCKSIZE / 4];
while (count--)
{
memcpy( scratch,buff,SD_BLOCKSIZE);
res = disk_write(ATA,(void *)scratch, sector++, 1);
if (res != RES_OK)
{
break;
}
buff += SD_BLOCKSIZE;
}
return res;
}
//SD_state=SD_WriteMultiBlocks((uint8_t *)buff,(uint64_t)sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
for ( UINT i = 0 ; i <count ; i++ )
{
SD_state=SD_WriteBlock((uint8_t *)(&buff[i<<9]), (sector +i)<< 9, SD_BLOCKSIZE);
}
if(SD_state==SD_OK)
{
/* Check if the Transfer is finished */
SD_state=SD_WaitWriteOperation();
/* Wait until end of DMA transfer */
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if(SD_state!=SD_OK)
status = RES_PARERR;
else
status = RES_OK;
break;
case SPI_FLASH:
break;
default:
status = RES_PARERR;
}
return status;
}
#endif
/*-----------------------------------------------------------------------*/
/* 其他控制 */
/*-----------------------------------------------------------------------*/
#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* 物理编号 */
BYTE cmd, /* 控制指令 */
void *buff /* 写入或者读取数据地址指针 */
)
{
DRESULT status = RES_PARERR;
switch (pdrv)
{
case ATA: /* SD CARD */
switch (cmd)
{
// Get R/W sector size (WORD)
case GET_SECTOR_SIZE :
*(WORD * )buff = SD_BLOCKSIZE;
break;
// Get erase block size in unit of sector (DWORD)
case GET_BLOCK_SIZE :
*(DWORD * )buff = 1;
break;
case GET_SECTOR_COUNT:
*(DWORD * )buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize;
break;
case CTRL_SYNC :
break;
}
status = RES_OK;
break;
case SPI_FLASH:
break;
default:
status = RES_PARERR;
}
return status;
}
#endif
4.剩下就是在主函数中正常使用fatfs文件系统函数得api,挂载,打开,读写
rt_kprintf("\r\n****** 这是一个SD卡 文件系统实验 ******\r\n");
//在外部SD卡挂载文件系统,文件系统挂载时会对SDIO设备初始化
res_sd = f_mount(&fs,"0:",1);
/*----------------------- 格式化测试 ---------------------------*/
/* 如果没有文件系统就格式化创建创建文件系统 */
if(res_sd == FR_NO_FILESYSTEM)
{
rt_kprintf("》SD卡还没有文件系统,即将进行格式化...\r\n");
/* 格式化 */
res_sd=f_mkfs("0:",0,0);
if(res_sd == FR_OK)
{
rt_kprintf("》SD卡已成功格式化文件系统。\r\n");
/* 格式化后,先取消挂载 */
res_sd = f_mount(NULL,"0:",1);
/* 重新挂载 */
res_sd = f_mount(&fs,"0:",1);
}
else
{
rt_kprintf("《《格式化失败。》》\r\n");
while(1);
}
}
else if(res_sd!=FR_OK)
{
rt_kprintf("!!SD卡挂载文件系统失败。(%d)\r\n",res_sd);
rt_kprintf("!!可能原因:SD卡初始化不成功。\r\n");
while(1);
}
else
{
rt_kprintf("》文件系统挂载成功,可以进行读写测试\r\n");
}
/*----------------------- 文件系统测试:写测试 -----------------------------*/
/* 打开文件,如果文件不存在则创建它 */
rt_kprintf("\r\n****** 即将进行文件写入测试... ******\r\n");
res_sd = f_open(&fnew, "0:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
if ( res_sd == FR_OK )
{
rt_kprintf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
/* 将指定存储区内容写入到文件内 */
res_sd=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(res_sd==FR_OK)
{
rt_kprintf("》文件写入成功,写入字节数据:%d\n",fnum);
rt_kprintf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
}
else
{
rt_kprintf("!!文件写入失败:(%d)\n",res_sd);
}
/* 不再读写,关闭文件 */
f_close(&fnew);
}
else
{
rt_kprintf("!!打开/创建文件失败。\r\n");
}
/*------------------- 文件系统测试:读测试 ------------------------------------*/
rt_kprintf("****** 即将进行文件读取测试... ******\r\n");
res_sd = f_open(&fnew, "0:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);
if(res_sd == FR_OK)
{
rt_kprintf("》打开文件成功。\r\n");
res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(res_sd==FR_OK)
{
rt_kprintf("》文件读取成功,读到字节数据:%d\r\n",fnum);
rt_kprintf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);
}
else
{
rt_kprintf("!!文件读取失败:(%d)\n",res_sd);
}
}
else
{
rt_kprintf("!!打开文件失败。\r\n");
}
/* 不再读写,关闭文件 */
f_close(&fnew);
/* 不再使用文件系统,取消挂载文件系统 */
f_mount(NULL,"0:",1);
三:移植过程中遇到得问题
1.STM32F1系列目前只支持SD卡规范2.0,所以最大可使用得SD卡为32G,超过识别不了SD卡
2.需要判断是否4字节对齐,这是一个比较隐性得坑;因为官方SDIO驱动中使用DMA,而DMA寄存器中IDMABASE0 低两位是强制为0;因此读写之前先要判断指针地址是否按4字节对齐
3.在裸机中,可以直接使用连续读写多块扇区,但一旦移植到RT_Thread系统中时出现问题,直接死循环在sdio驱动中的某个死循环中。原因是连续读写速度过快,而F1系列主频才72M,带不动25M频率下的连续读写,后续修改为单块扇区读写后没有问题