STM32 SDIO学习笔记

STM32 同时被 2 个专栏收录
3 篇文章 0 订阅
1 篇文章 0 订阅

一:个人注重点

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频率下的连续读写,后续修改为单块扇区读写后没有问题

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 1024 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值