查看:
580
|
回复:
0
|
SD NAND 的 SDIO在STM32上的应用详解(下篇)
|
|
发表于2022-12-08 17:30:47
|
只看该作者
1#
电梯直达
七.SDIO外设结构体其实前面关于SDIO寄存器的讲解已经比较详细了,这里再借助于关于SDIO结构体再进行总结一遍。 标准库函数对 SDIO 外设建立了三个初始化结构体,分别为 SDIO 初始化结构体SDIO_InitTypeDef、SDIO 命令初始化结构体 SDIO_CmdInitTypeDef 和 SDIO 数据初始化结 构体 SDIO_DataInitTypeDef。这些结构体成员用于设置 SDIO 工作环境参数,并由 SDIO 相应初始化配置函数或功能函数调用,这些参数将会被写入到 SDIO 相应的寄存器,达到配置 SDIO 工作环境的目的。 至于为什么需要一个命令结构体与数据结构体,就是为了方便我们配置SDIO关于寄存器位,因为发送命令或者数据需要很多参数配置。1.SDIO初始化结构体SDIO 初始化结构体用于配置 SDIO 基本工作环境,比如时钟分频、时钟沿、数据宽度等等。它被 SDIO_Init 函数使用。 1) SDIO_ClockEdge:主时钟 SDIOCLK 产生 CLK 引脚时钟有效沿选择,可选上升沿或下降沿。 2) SDIO_ClockBypass:时钟分频旁路使用,可选使能或禁用,如果使能旁路,SDIOCLK (72MHZ )直接驱动 CLK 线输出时钟(不满足最高25HZ的要求),如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分频 SDIOCLK,然后输出到 CLK 线。一般选择禁用时钟分频旁路。 3) SDIO_ClockPowerSave:节能模式选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能节能模式,CLK 线只有在总线激活时才有时钟输出;如果禁用节能模式,始终使能 CLK 线输出时钟。 4) SDIO_BusWide:数据线宽度选择,可选 1 位数据总线、4 位数据总线或 8 为数据总线,系统默认使用 1 位数据总线,操作 SD 卡时在数据传输模式下一般选择 4 位数据总线。它设定 SDIO_CLKCR 寄存器的 WIDBUS 位的值。 5) SDIO_HardwareFlowControl:硬件流控制选择,可选使能或禁用,它设定SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 发送上溢和下溢错误。 6) SDIO_ClockDiv:时钟分频系数,它设定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,设置 SDIOCLK 与 CLK 线输出时钟分频系数: CLK 线时钟频率=SDIOCLK/([CLKDIV+2])。 2.SDIO命令初始化结构体 1) SDIO_Argument:作为命令的一部分发送到卡的命令参数,它设定 SDIO 参数寄存器(SDIO_ARG)的值。 (2) SDIO_CmdIndex:命令号选择,它设定 SDIO 命令寄存器(SDIO_CMD)的 CMDINDEX位的值。 (3) SDIO_Response:响应类型,SDIO 定义两个响应类型:长响应和短响应。根据命令号选择对应的响应类型。SDIO 定义了四个 32 位的 SDIO 响应寄存器(SDIO_RESPx,x=1…4),短响应只用SDIO_RESP1,长响应使用4个(SDIO_RESPx,x=1…4)。 1)命令响应寄存器 2)SDIO响应寄存器1~4 4) SDIO_Wait:等待类型选择,有三种状态可选,一种是无等待状态,超时检测功能启动,一种是等待中断,另外一种是等待传输完成。 5) SDIO_CPSM:命令路径状态机控制,可选使能或禁用 CPSM。它设定 SDIO_CMD 寄存器的 CPSMEN 位的值 只要我们使能的了命令状态机,则下面发送命令和接收响应的过程中的状态转换就不用我们管了 当我们要发送命令,我们只需要配置这个命令初始化结构体的成员,然后调用下图这个函数,则我们配置的参数写入对应的寄存器位中。 3.SDIO数据初始化结构体 1) SDIO_DataTimeOut:设置数据传输以卡总线时钟周期表示的超时周期,它设定 SDIO数据定时器寄存器(SDIO_DTIMER)的值。在 DPSM 进入 Wait_R 或繁忙状态后开始递减,直到 0 还处于以上两种状态则将超时状态标志置 1(详情前面的数据通道小节)。 2) SDIO_DataLength:设置传输数据长度。 3) SDIO_DataBlockSize:设置数据块大小,有多种尺寸可选,不同命令要求的数据块可能不同。 4) SDIO_TransferDir:数据传输方向,可选从主机到卡的写操作,或从卡到主机的读操作。 5) SDIO_TransferMode:数据传输模式,可选数据块或数据流模式。对于 SD 卡操作使用数据块类型。 6) SDIO_DPSM:数据路径状态机控制,可选使能或禁用 DPSM。它设定 SDIO_DCTRL寄存器的 DTEN 位的值。要实现数据传输都必须使能 SDIO_DPSM。 与命令一样使能了数据路径状态机,就不用高那么多麻烦的状态转换了八.SD卡读写测试实验我们平时使用的SD 卡都是已经包含有文件系统的,一般不会使用本实验的操作方式读写 SD 卡,但是对学习SD卡的驱动原理非常重要!!! 本实验是进行 SD卡最底层的数据读写操作,直接使用 SDIO 对 SD 卡进行读写,会损坏 SD 卡的文件系统,导致数据丢失,所以做这个实验之前需要备份SD卡数据。 主要是学习SD卡的卡识别过程,以及数据传输工过程,其实就是完全依照前面的两个流程图来实现代码的。 卡识别模式流程图 数据传输流程图 1.硬件设计原理图: 实物图: 我这里用的是CS创世的贴片式SD卡,也称之为SD NAND , 内部存储单元架构为SLC,适合存代码。直接上板时相比于拔插式SD卡在抗震和抗PIN氧化方面更有优势,对于缩小整板体积也有一定帮助。 详情请参考:雷龙官网 2.代码讲解先看主函数: SD_Terst函数: 我们主要讲解的就是SD卡的初始化 SD_Init()函数: /** * 函数名:SD_Init * 描述:初始化SD卡,使卡处于就绪状态(准备传输数据) * 输入:无 * 输出:-SD_Error SD卡错误代码 * 成功时则为 SD_OK * 调用:外部调用 */SD_Error SD_Init(void){ /*重置SD_Error状态*/ SD_Error errorstatus = SD_OK; NVIC_Configuration(); /* SDIO 外设底层引脚初始化 */ GPIO_Configuration(); /*对SDIO的所有寄存器进行复位*/ SDIO_DeInit(); /*上电并进行卡识别流程,确认卡的操作电压*/ errorstatus = SD_PowerON(); /*如果上电,识别不成功,返回“响应超时”错误 */ if (errorstatus != SD_OK) { /*!< CMD Response TimeOut (wait for CMDSENT flag) */ return(errorstatus); } /*卡识别成功,进行卡初始化*/ errorstatus = SD_InitializeCards(); if (errorstatus != SD_OK) //失败返回 { /*!< CMD Response TimeOut (wait for CMDSENT flag) */ return(errorstatus); } /* 配置SDIO外设 * 上电识别,卡初始化都完成后,进入数据传输模式,提高读写速度 */ /* SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */ SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; /*上升沿采集数据 */ SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising; /* Bypass模式使能的话,SDIO_CK不经过SDIO_ClockDiv分频 */ SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable; /* 若开启此功能,在总线空闲时关闭sd_clk时钟 */ SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; /* 暂时配置成1bit模式 */ SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; /* 硬件流,若开启,在FIFO不能进行发送和接收数据时,数据传输暂停 */ SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable; SDIO_Init(&SDIO_InitStructure); if (errorstatus == SD_OK) { /* 用来读取csd/cid寄存器 */ errorstatus = SD_GetCardInfo(&SDCardInfo); } if (errorstatus == SD_OK) { /* 通过cmd7,rca选择要操作的卡 */ errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16)); } if (errorstatus == SD_OK) { /* 最后为了提高读写,开启4bits模式 */ errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b); } return(errorstatus);} 接下来逐段代码来分析一下: errorstatus其实是一个SD_Error类型的枚举变量,SD_Error 是 |
|