第十三章 EMMC编程(有误)
参考资料
https://linux.codingbelief.com/zh/storage/flash_memory/emmc/
资料光盘: 00_UserManual参考资料EMMC编程JESD84-B50-1eMMCStandard.pdf
1.1 EMMC介绍
1.1.1 EMMC简介
eMMC (Embedded Multi Media Card)是MMC协会订立的嵌入式存储器标准规格,主要针对手机、数码相机、平板电脑等产品。eMMC主要是为了简化手机内存储器的设计,节省电路板的面积。eMMC是将NAND Flash、控制器和MMC标准接口封装到一块芯片上,如下图所示。
1.1.2 EMMC硬件连接
如下图所示,CPU和eMMC设备通过CLK、CMD、DATA[0-7]和RST信号脚互联。接口信号描述如下。
CLK:MMC总线的时钟线,由主机提供的信号。当时钟有效时才能传输命令和数据。在SDR模式下,时钟的上升沿为数据的采样时间。在DDR模式下,时钟的上下沿均进行数据的采样。不同的总线模式时钟的速率是不一样的。
CMD:双向传输的命令线,用于主机和设备的数据通信。当主机发送命令之后,设备会给主机应答,应答信号通过CMD线返回到主机。
RST:复位信号线。
DATA[0…7]:eMMC的双向数据总线,用于主机和设备之间的数据通信。DATA线是时分复用的,要么数据从主机传输到eMMC设备,要么从eMMC设备传输到主机。当用户上电或者复位的时候,仅能用DATA0传输数据。同时,用户根据自己的需要配置数据总线的位数。当用户选择4位时,eMMC设备配置DATA1-3的内部上拉,如果用户选择的是8位,那么同理会配置DATA1-7的上拉。
1.1.3 eMMC总线速度
eMMC5.0协议规定了5种总线数据模式如下表所示。兼容MMC模式、高速SDR模式和高速DDR模式可以兼容协议4.5版本之前的协议。
模式名 | 数据速率模式 | IO电压 | 总线位宽 | 时钟速率 | 最大数据传输速率 |
---|---|---|---|---|---|
兼容MMC模式 | SDR | 3/1.8/1.2V | 1、4、8bit | 0~26MHz | 26MHz |
高速SDR模式 | SDR | 3/1.8/1.2V | 1、4、8bit | 0~52MHz | 52MHz |
高速DDR模式 | DDR | 3/1.8/12V | 4、8bit | 0~52MHz | 104MHz |
HS200模式 | SDR | 1.8/12V | 4、8bit | 0~200MHz | 200MHz |
HS400模式 | DDR | 1.8/12V | 8bit | 0~200MHz | 400MHz |
1.1.4 eMMC总线协议
eMMC总线可以挂载一个主设备和多个eMMC设备。总线上的所有通讯都由主机发起,主机一次只能与一个eMMC设备通讯。
系统在上电后,主机会给所有eMMC设备分配地址。当主机需要和某一个eMMC设备通讯时,会先根据RCA选中该eMMC设备,只有被选中的 eMMC设备才会应答主机的命令。
eMMC的通信是由单个或多个块组成的。它们分别是命令、应答、数据块,如下图所示。
读取eMMC数据的数据流如下图所示。如果主机发送的是读单个块的命令,那么eMMC设备只会发送一个块的数据,并且不用发送停止命令。
写入eMMC数据过程如下图所示。
如下图所示通信中除了带数据的命令,也有不带数据和不带应答的命令。
1.1.5 命令(CMD)介绍
1.1.5.1 (1) 命令类型
CMD有4中类型,分别为:不带应答的广播命令(br);带有应答的广播命令(bcr);点对点无数据传输(ac);点对点有数据传输(adtc)。
1.1.5.2 (2) 命令格式
如下图所示CMD是一个由起始位、传输位、命令索引、命令参数、CRC和结束位组成,一共有48位。
起始位 | 传输位 | 命令索引 | 命令参数 | CRC | 结束位 | |
---|---|---|---|---|---|---|
位置 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
值 | “0” | “1” | x | x | x | “1” |
起始位固定为0。传输位表示数据传输的方向,1代表主机到eMMC设备。命令索引用占用了6个位,取值范围为0~63。命令参数是根据每个命令的功能要求提供相应的参数值。CRC7是起始位、传输位、命令索引、命令参数的内容的校验值。结束位固定为1。
1.1.5.3 (3) 命令分类
eMMC将56个命令分成如下表的12个class,每个class代表一类功能,包含所有命令的一个子集。设备支持哪些class,可以通过CSD寄存器的CSD[95:84]来查询,每一个位代表一个class,当某一位为1时代表支持该位对应class。
命令类别 | 描述 | 备注 |
---|---|---|
class 0 | basic | 基本命令 |
class 1 | Obsolete | 废弃 |
class 2 | block read | 块数据读相关命令,包括设置块长度、读取单块、读取多块 |
class 3 | obsolete | 废弃 |
class 4 | block write | 块数据写相关命令 |
class 5 | erase | 擦除操作 |
class 6 | write protection | 设置写保护 |
class 7 | Lock device | 锁或者解锁设备 |
class 8 | Application-specific | 特定应用命令 |
class 9 | I/O mode | 写寄存器,设置系统进入中断模式 |
class 10 | security protocols | 连续传输数据块 |
class 11 | reserved | 预留 |
1.1.6 应答(Response)介绍
所有应答都是eMMC设备接收到主机的命令后在CMD信号线上发送,而应答的内容取决于应答的类型。如下图所示应答格式由起始位、传输位、数据位、CRC位和结束位组成。
eMMC协议中有6种应答类型,分别是R1、R1b、R2、R3、R4和R5。
- R1和R1b应答数据结构:
R1应答总长为48bit,其中[45:40]代表应答的命令索引,[39:8]表示设备的状态以及错误标记位。R1b的结构是在R1的基础上增加了一个Busy信号。
起始位 | 传输位 | 命令索引 | 设备状态 | CRC | 结束位 | |
---|---|---|---|---|---|---|
位置 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
值 | “0” | “0” | x | x | CRC | “1” |
设备状态的含义如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MT82r3X3-1642060301308)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/13_EMMC_Program_image009.png)]
- R2应答数据结构:
R2应答主要返回设备CID或CSD寄存器的内容,CID寄存器分别对应CMD2和CMD10,CSD寄存器则是对应CMD9。
起始位 | 传输位 | 检查位 | 数据 | CRC | 结束位 | |
---|---|---|---|---|---|---|
位置 | 135 | 134 | [133:128] | [127:8] | [7:1] | 0 |
位宽 | 1 | 1 | 6 | 120 | 7 | 1 |
值 | “0” | “0” | “111111” | x | CRC | “1” |
- R3应答数据结构:
R3应答主要返回设备ORC寄存器的内容,只有下发CMD1时,设备才回复R3应答。
起始位 | 传输位 | 检查位 | ORC寄存器 | 检查位 | 结束位 | |
---|---|---|---|---|---|---|
位置 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
值 | “0” | “0” | “111111” | x | “1111111” | “1” |
- R4应答数据结构:
R4应答主要用于写入和读出某个寄存器其中一个字节的数据。只有主机下发CMD39时,设备应答R4。
起始位 | 传输位 | 命令39 | 参数 | CRC | 结束位 | |
---|---|---|---|---|---|---|
位置 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
值 | “0” | “0” | “100111” | X | “1111111” | “1” |
参数对应内容如下表所示。
[39:8]参数 | 相对地址 | 状态 | 寄存器地址 | 寄存器内容 |
---|---|---|---|---|
位宽 | 16 | 1 | 7 | 8 |
- R5应答数据结构:
R5作为中断请求应答,其应答结构如下。
起始位 | 传输位 | 命令39 | 参数 | CRC | 结束位 | |
---|---|---|---|---|---|---|
位置 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
值 | “0” | “0” | “100111” | X | “1111111” | “1” |
参数对应内容如下表所示。
[39:8]参数 | 相对地址 | 中断数据 |
---|---|---|
位宽 | 16 | 16 |
1.1.7 eMMC工作模式
如下表所示,EMMC有5种工作模式:开机模式、识别模式、中断模式、数据传输模式和无效模式。
模式 | 描述 |
---|---|
开机模式(Boot mode) | 上电后,eMMC设备若收到带有0xF0F0F0F0参数的CMD0时,如果eMMC设备支持开机模式则进入开始模式,否则进入识别模式。 |
识别模式(Card identification) | 上电后,开机模式结束或者不支持开机模式,eMMC设备就会进入测模式,并等待主机下发CMD3设置相对地址。 |
中断模式(Interrupt mode) | 在此模式中不能进行数据传输操作,只能发出中断服务请求。 |
数据传输模式(Data transfer mode) | 当主机为eMMC设备配置完RCA后,就会进入数据传输模式。 |
无效模式(Inactive mode) | 当eMMC设备电压不符规定时就会进入此模式,也可以通过下发CMD1命令使eMMC设备进入无效模式 |
eMMC设备运行时,根据不同模式跳转到不同的工作状态,如下表所示。
设备状态 | 操作模式 | 总线模式 |
---|---|---|
Inactive State | 非活动模式 | 开漏 |
Pre-Idle State | 启动模式 | |
Pre-Boot State | ||
Idle State | 设备识别模式 | |
Ready State | ||
Identification State | ||
Stand-by State | 数据传输模式 | 上下拉 |
Sleep State | ||
Transfer State | ||
Bus-Test State | ||
Sending-data State | ||
Programming State | ||
Disconnect State | ||
Boot State | 启动模式 | |
Wait-IRQ State | 中断模式 | 开漏 |
1.1.8 eMMC寄存器
如下表所示,总共有6种。它们可以得到设备的相关内容以及设置工作时的控制对象,在读写数据前的步骤操作相对应的寄存器实现。因此协议中明确定义所用寄存器的含义。
名称 | 大小(Byte) | 描述 |
---|---|---|
CID | 16 | 设备识别寄存器 |
RCA | 2 | 相关设备地址 |
DSR | 2 | 驱动等级寄存器 |
CSD | 16 | 存储设备相关信息 |
OCR | 4 | 操作状态寄存器 |
EXT_CSD | 512 | 扩展设备寄存器 |
1.1.9 eMMC初始化过程
eMMC设备的正常工作,必须完成初始化过程。在初始化过程中,主机会对EMMC设备进行识别,确定设备工作的电压范围和使用模式,向设备分配相对地址(RCA,一个系统可以支持多个eMMC设备)。设备初始化过程如下图所示。
电源上电后,eMMC设备进入空闲(Idle State)。虽然进入了空闲状态,但是设备的上电复位过程不一定完成,这时需要读取OCR的Busy位来判断设备的上电复位过程是否完成。
在空闲状态中只有CMD1和CMD58是合法命令。主机通过发送CMD1,读取eMMC设备的OCR寄存器,并进行电压匹配和通过BUSY位判断复位是否完成。当BUSY为1时,代表eMMC设备初始化完毕进入Ready State。
在Ready State中,主机下发CMD2命令后会收到CID寄存器的值,此时eMMC设备进入Identification State。接着主机发送CMD3命令,为eMMC设备配置RCA。配置完成后eMMC设备进入standby state。此时初始化过程结束。
1.1.10 数据传输模式
在数据传输模式下可实现对EMMC设备进行读写,擦除,总线测试等操作。此模式工作状态图如下所示。
1.2 IMX6ULL EMMC寄存器介绍
1.2.1 IMX6ULL USDHC模块介绍
IMX6ULL共2个独立的USDHC,主要特型如下:
1) 兼容MMC系统规范4.2/4.3/4.4/4.41/4.5版本;
2) 时钟频率最高可达208MHz;
3) 支持1位/4位/8位的SD、SDIO和MMC模式;
4) 在SDR(单数据速率)模式下使用4条并行数据线对SDIO卡进行高达832Mbps的数据传输;
5) 在DDR(双数据速率)模式下使用4条并行数据线对SDXC卡进行高达400Mbps的数据传输;
6) 在SDXC卡模式下使用4条并行数据线对SDXC卡进行高达832Mbps的数据传输;
7) 在SDXC卡模式下使用4条并行数据传输高达400Mbps的数据传输(双数据传输)支持单块/多块读写,支持1~4096字节的块大小,支持写操作的写保护开关,支持同步和异步中止,支持块间隙数据传输期间的暂停,支持SDIO读取等待和暂停恢复操作,支持自动CMD12 对于多块传输,主机可以在数据传输进行时启动非数据传输命令。
以上只是列举了部分的特性,详细的特征描述可以查看芯片手册《Chapter 58 Ultra Secured Digital Host Controller (uSDHC)》。
1.2.2 IMX6ULL USDHC寄存器介绍
IMX6ULL有两路USDHC,每一路各有29个寄存器,只要学会一路寄存器的使用,即可掌握另一路的使用。
USDHC的寄存器如下图所示,从图中可得知两路USDHC的寄存器各分配到两段连续的地址空间中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZmmpDQb-1642060301313)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/13_EMMC_Program_image014.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mWkQs1p8-1642060301314)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/13_EMMC_Program_image015.png)]
接下来介绍我们实验要用到的寄存器:
1.2.2.1 (1)uSDHC2_BLK_ATT
uSDHC2_BLK_ATT寄存器为块的读写服务。BLKSIZE[12:0]用于设置读写块的大小,最大可以设置为4096字节,如果设置为0时表示没有数据传输。BLKCNT用于配置读写块的数量,最大可配置为65535个块。当在多个块读写时,每读写完一个块BLKCNT自动减一直到0停止。
1.2.2.2 (2)uSDHC2_CMD_ARG
uSDHC2_CMD_ARG是命令的参数寄存器,保存要下发命令的参数。
1.2.2.3(3)uSDHC2_CMD_XFR_TYP
uSDHC2_CMD_XFR_TYP是命令传输类型寄存器。用于配置传输的命令编号、命令类型、数据传输、应答类型和校验使能等。
1.2.2.4 (4)uSDHC2_CMD_RSP0-3
uSDHC2_CMD_RSP0-3是32位的命令应答寄存器。如下表所示系统根据应答类型,将eMMC设备应答的数据分别保存在相应的命令应答寄存器中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdiYf827-1642060301317)(第十三章 EMMC编程.assets/13_EMMC_Program_image019.png)]
1.2.2.5(5)uSDHC2_DATA_BUFF_ACC_PORT
uSDHC2_DATA_BUFF_ACC_PORT是一个32位的数据缓存接口,这个寄存器是内部数据缓存器的一个读写口。通过这个数据缓存接口,就可以读取到数据缓存的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMw1jTsb-1642060301317)(第十三章 EMMC编程.assets/13_EMMC_Program_image020.png)]
1.2.2.6(6)uSDHC2_PRES_SETATE
uSDHC2_PRES_SETATE是USDHC的状态寄存器。状态寄存器体现USDHC的命令传输、数据读写、时钟等的状态。我们主要关注CIHB、CDIHB、SDSTB、BREN、BWEN位。具体使用方法编程阶段会一一介绍。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KXeuREj-1642060301318)(第十三章 EMMC编程.assets/13_EMMC_Program_image021.png)]
1.2.2.7 (7)uSDHC2_PROT_CTRL
uSDHC2_PROT_CTRL是端口控制寄存器,主要是控制数据位宽、大小端配置等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P70Wt5Z9-1642060301318)(第十三章 EMMC编程.assets/13_EMMC_Program_image022.png)]
1.2.2.8 (8)uSDHC2_SYS_CTRL
uSDHC2_SYS_CTRL是系统控制寄存器,用于软件复位、时钟分频配置和超时时间等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbap7RNU-1642060301318)(第十三章 EMMC编程.assets/13_EMMC_Program_image023.png)]
1.2.2.9(9)uSDHC2_INT_STATUS
uSDHC2_INT_STATUS是中断状态寄存器,命令传输完成、读写完成和传输错误都会在中断体现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-upreBSQ9-1642060301319)(第十三章 EMMC编程.assets/13_EMMC_Program_image024.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncPEFo33-1642060301319)(第十三章 EMMC编程.assets/13_EMMC_Program_image025.png)]
1.2.2.10(10)uSDHC2_INT_SIGNAL_EN
通过配置这个寄存器使能中断的状态。将这个寄存器某位置为1时则uSDHC2_INT_STATUS对应的状态使能。反则屏蔽该状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amiGDL1I-1642060301319)(第十三章 EMMC编程.assets/13_EMMC_Program_image026.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5OzW9hhu-1642060301319)(第十三章 EMMC编程.assets/13_EMMC_Program_image027.png)]
1.2.2.11 (11)uSDHC2_WTMK_LVL
这个是读写水位寄存器。当缓存器的数据大于WR_WML或者RD_WML时,中断状态寄存器中的读写状态置1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cTuypDS-1642060301320)(第十三章 EMMC编程.assets/13_EMMC_Program_image028.png)]
1.2.2.12 (12)uSDHC2_MIX_CTRL
uSDHC2_MIX_CTRL是混合控制寄存器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J24ZRv8s-1642060301320)(第十三章 EMMC编程.assets/13_EMMC_Program_image029.png)]
1.3 EMMC编程
1.3.1 eMMC引脚配置
前两节介绍了eMMC的协议和IMX6ULL USDHC相关寄存器,接下来开始讲解EMMC驱动设计。编写驱动首先要配置引脚。为了正确配置引脚我们需要了解的是硬件的连接方式和相关引脚的配置寄存器。
(1)确定USDHC引脚
从下图可知,eMMC设备的引脚连接到IMX6-NAND的ALE、nRE、nWE和DATA0-7引脚上。查看IMX6UUL数据手册可知所使用的是USDHC2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QB4JHTW-1642060301320)(第十三章 EMMC编程.assets/13_EMMC_Program_image030.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5hrHiPCE-1642060301321)(第十三章 EMMC编程.assets/13_EMMC_Program_image031.png)]
(2)USDHC2相关引脚寄存器配置
1.3.1.1 步骤1:配置USDHC2相关引脚的复用功能
在手册《Chapter4 External Signals and Pin Multiplexing》中,可以找到USDHC2引脚所对应的复用模式,如下表所示。从表中可知USDHC2的相关引脚的复用模式应设置为ALT1模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ybc7Gj6Q-1642060301321)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/13_EMMC_Program_image032.png)]
- 配置GPIO4_IO10(USDHC2_RESET_B)复用功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDviR0hi-1642060301321)(第十三章 EMMC编程.assets/13_EMMC_Program_image033.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Rcy1OPL-1642060301321)(第十三章 EMMC编程.assets/13_EMMC_Program_image034.png)]
由上图可知IOMUXC_SW_MUX_CTL_PAD_NAND_ALE[MUX_MODE]应设为0x01。
- 配置GPIO4_IO00(USDHC2_CLK)复用功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2rfAjEK-1642060301321)(第十三章 EMMC编程.assets/13_EMMC_Program_image035.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b45rPH8k-1642060301322)(第十三章 EMMC编程.assets/13_EMMC_Program_image036.png)]
由上图可知IOMUXC_SW_MUX_CTL_PAD_NAND_RE_B[MUX_MODE]应设为0x01。
- 配置GPIO4_IO01(USDHC2_CMD)复用功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LE5K0x5F-1642060301322)(第十三章 EMMC编程.assets/13_EMMC_Program_image037.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S1qsHfu2-1642060301322)(第十三章 EMMC编程.assets/13_EMMC_Program_image038.png)]
由上图可知IOMUXC_SW_MUX_CTL_PAD_NAND_WE_B[MUX_MODE]应设为0x01。
- 配置USDHC2_ DATA0-7的复用功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6AZOSO9M-1642060301322)(第十三章 EMMC编程.assets/13_EMMC_Program_image039.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8WRetLCs-1642060301323)(第十三章 EMMC编程.assets/13_EMMC_Program_image040.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3yAzoM4b-1642060301323)(第十三章 EMMC编程.assets/13_EMMC_Program_image041.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eLMFGVuM-1642060301323)(第十三章 EMMC编程.assets/13_EMMC_Program_image042.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmmlLNcM-1642060301323)(第十三章 EMMC编程.assets/13_EMMC_Program_image043.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2SQeSJZn-1642060301324)(第十三章 EMMC编程.assets/13_EMMC_Program_image044.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JDrdvtY4-1642060301324)(第十三章 EMMC编程.assets/13_EMMC_Program_image045.png)]
由于DATA0-7都是ALT1所以IOMUXC_SW_MUX_CTL_PAD_NAND_DATA0、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA1、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA2、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA3IOMUXC_SW_MUX_CTL_PAD_NAND_DATA4、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA5、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA6、IOMUXC_SW_MUX_CTL_PAD_NAND_DATA7均配置为0x01。
具体代码如下:
代码语言:javascript复制61 // 配置引脚复用功能
62 IOMUXC_SW_MUX_CTL_PAD_NAND_RE_B = (volatile unsigned int *)(0x20E0178);
63 IOMUXC_SW_MUX_CTL_PAD_NAND_WE_B = (volatile unsigned int *)(0x20E017C);
64 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA0 = (volatile unsigned int *)(0x20E0180);
65 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA1 = (volatile unsigned int *)(0x20E0184);
66 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA2 = (volatile unsigned int *)(0x20E0188);
67 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA3 = (volatile unsigned int *)(0x20E018C);
68 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA4 = (volatile unsigned int *)(0x20E0190);
69 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA5 = (volatile unsigned int *)(0x20E0194);
70 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA6 = (volatile unsigned int *)(0x20E0198);
71 IOMUXC_SW_MUX_CTL_PAD_NAND_DATA7 = (volatile unsigned int *)(0x20E019C);
72 IOMUXC_SW_MUX_CTL_PAD_NAND_ALE = (volatile unsigned int *)(0x20E01A0);
73
74 *IOMUXC_SW_MUX_CTL_PAD_NAND_RE_B = 0x1U;
75 *IOMUXC_SW_MUX_CTL_PAD_NAND_WE_B = 0x1U;
76 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA0 = 0x1U;
77 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA1 = 0x1U;
78 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA2 = 0x1U;
79 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA3 = 0x1U;
80 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA4 = 0x1U;
81 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA5 = 0x1U;
82 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA6 = 0x1U;
83 *IOMUXC_SW_MUX_CTL_PAD_NAND_DATA7 = 0x1U;
84 *IOMUXC_SW_MUX_CTL_PAD_NAND_ALE = 0x1U;
1.3.1.2 步骤2:设置USDHC2相关引脚的输入源:
如下图IMX6中的功能模块(如:UART、USDHC)可能存在1个或多个可配置引脚,所以在配置完GPIO模式后还要设置输入源选择寄存器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9pbZtuPd-1642060301324)(第十三章 EMMC编程.assets/13_EMMC_Program_image046.png)]
- USDHC2_CLK输入源寄存器设置:
由下图可知IOMUXC_USDHC2_CLK_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i07myik8-1642060301324)(第十三章 EMMC编程.assets/13_EMMC_Program_image047.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MFlkUFee-1642060301325)(第十三章 EMMC编程.assets/13_EMMC_Program_image048.png)]
- USDHC2_CMD输入源寄存器设置:
由下图可知IOMUXC_USDHC2_CMD_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ITJM1X1G-1642060301325)(第十三章 EMMC编程.assets/13_EMMC_Program_image049.png)]
- USDHC2_DATA0输入源寄存器设置:
由下图可知IOMUXC_USDHC2_DATA0_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAuBnEei-1642060301325)(第十三章 EMMC编程.assets/13_EMMC_Program_image050.png)]
- USDHC2_DATA1输入源寄存器设置:
由下图可知IOMUXC_USDHC2_DATA1_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfqhMe3A-1642060301325)(第十三章 EMMC编程.assets/13_EMMC_Program_image051.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6KLqjGe-1642060301325)(第十三章 EMMC编程.assets/13_EMMC_Program_image052.png)]
- USDHC2_DATA2输入源寄存器设置:
由下图可知IOMUXC_USDHC2_DATA2_SELECT_INPUT [DAISY]应设为0x1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w3y1tGWd-1642060301326)(第十三章 EMMC编程.assets/13_EMMC_Program_image053.png)]
- USDHC2_DATA3输入源寄存器设置:
由下图可知IOMUXC_USDHC2_DATA3_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kdER2YfM-1642060301326)(第十三章 EMMC编程.assets/13_EMMC_Program_image054.png)]
- USDHC2_DATA4-7输入源寄存器设置:
USDHC2_DATA4-7的DAISY都0x2,所以IOMUXC_USDHC2_DATA4_SELECT_INPUT [DAISY]、IOMUXC_USDHC2_DATA5_SELECT_INPUT [DAISY]、IOMUXC_USDHC2_DATA6_SELECT_INPUT [DAISY]、IOMUXC_USDHC2_DATA7_SELECT_INPUT [DAISY]应设为0x2。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ak6n7zEW-1642060301326)(第十三章 EMMC编程.assets/13_EMMC_Program_image055.png)]
代码如下:
代码语言:javascript复制86 // 配置引脚的输入源
87 IOMUXC_USDHC2_CLK_SELECT_INPUT = (volatile unsigned int *)(0x020E0670);
88 IOMUXC_USDHC2_CMD_SELECT_INPUT = (volatile unsigned int *)(0x020E0678);
89 IOMUXC_USDHC2_DATA0_SELECT_INPUT = (volatile unsigned int *)(0x020E067C);
90 IOMUXC_USDHC2_DATA1_SELECT_INPUT = (volatile unsigned int *)(0x020E0680);
91 IOMUXC_USDHC2_DATA2_SELECT_INPUT = (volatile unsigned int *)(0x020E0684);
92 IOMUXC_USDHC2_DATA3_SELECT_INPUT = (volatile unsigned int *)(0x020E0688);
93 IOMUXC_USDHC2_DATA4_SELECT_INPUT = (volatile unsigned int *)(0x020E068C);
94 IOMUXC_USDHC2_DATA5_SELECT_INPUT = (volatile unsigned int *)(0x020E0690);
95 IOMUXC_USDHC2_DATA6_SELECT_INPUT = (volatile unsigned int *)(0x020E0694);
96 IOMUXC_USDHC2_DATA7_SELECT_INPUT = (volatile unsigned int *)(0x020E0698);
97
98 *IOMUXC_USDHC2_CLK_SELECT_INPUT = 0x2U;
99 *IOMUXC_USDHC2_CMD_SELECT_INPUT = 0x2U;
100 *IOMUXC_USDHC2_DATA0_SELECT_INPUT = 0x2U;
101 *IOMUXC_USDHC2_DATA1_SELECT_INPUT = 0x2U;
102 *IOMUXC_USDHC2_DATA2_SELECT_INPUT = 0x1U;
103 *IOMUXC_USDHC2_DATA3_SELECT_INPUT = 0x2U;
104 *IOMUXC_USDHC2_DATA4_SELECT_INPUT = 0x1U;
105 *IOMUXC_USDHC2_DATA5_SELECT_INPUT = 0x1U;
106 *IOMUXC_USDHC2_DATA6_SELECT_INPUT = 0x1U;
107 *IOMUXC_USDHC2_DATA7_SELECT_INPUT = 0x1U;
1.3.1.3 步骤3:设置USDHC2相关引脚的输入输出参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKBZV50F-1642060301326)(第十三章 EMMC编程.assets/13_EMMC_Program_image056.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PtUKdCkP-1642060301327)(第十三章 EMMC编程.assets/13_EMMC_Program_image057.png)]
相关引脚的输入输出参数配置如上图红圈所示,所有引脚配置值为0x17059。
相关代码如下:
代码语言:javascript复制109 // 配置引脚的输入输出参数
110 IOMUXC_SW_PAD_CTL_PAD_NAND_RE_B = (volatile unsigned int *)(0x020E0404U);
111 IOMUXC_SW_PAD_CTL_PAD_NAND_WE_B = (volatile unsigned int *)(0x020E0408U);
112 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA00 = (volatile unsigned int *)(0x020E040CU);
113 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA01 = (volatile unsigned int *)(0x020E0410U);
114 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA02 = (volatile unsigned int *)(0x020E0414U);
115 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA03 = (volatile unsigned int *)(0x020E0418U);
116 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA04 = (volatile unsigned int *)(0x020E041CU);
117 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA05 = (volatile unsigned int *)(0x020E0420U);
118 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA06 = (volatile unsigned int *)(0x020E0424U);
119 IOMUXC_SW_PAD_CTL_PAD_NAND_DATA07 = (volatile unsigned int *)(0x020E0428U);
120
121 *IOMUXC_SW_PAD_CTL_PAD_NAND_RE_B = 0x17059;
122 *IOMUXC_SW_PAD_CTL_PAD_NAND_WE_B = 0x17059;
123 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA00 = 0x17059;
124 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA01 = 0x17059;
125 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA02 = 0x17059;
126 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA03 = 0x17059;
127 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA04 = 0x17059;
128 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA05 = 0x17059;
129 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA06 = 0x17059;
130 *IOMUXC_SW_PAD_CTL_PAD_NAND_DATA07 = 0x17059;
1.3.2 时钟配置
配置完引脚后,我们还需要设置时钟源。为了正确设置USDHC2的时钟需要解决两个问题。第一是EMMC设备支持什么时钟速率,第二USDHC2的时钟是从哪里来。
1.3.2.1 步骤1:获得eMMC设备的时钟速率。
查看核心板所用的eMMC设备手册《MTFC4GACAJC》得知,MTFC4GACAJC 支持HS200、HS400、SDR和DDR模式,最高时钟频率为52MHz。那么上电时MTFC4GACAJC 支持的时钟频率是多少呢?在MTFC4GACAJC手册的EXCSD表中HS_TIMING得知其始化值为00h。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mWWMATyq-1642060301327)(第十三章 EMMC编程.assets/13_EMMC_Program_image058.png)]
这个值代表什么意思呢?我们通过《eMMC5.0Spec_JESD84-B50》得知,HS_TIMING高字节代表驱动能力,低字节代表时钟速率。HS_TIMING为0时,代表时钟速率为兼容模式,即时钟速率范围为0~26MHz。本实验我们选择USDHC2时钟速率为400KHz。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQJrBUra-1642060301327)(第十三章 EMMC编程.assets/13_EMMC_Program_image059.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yp3ovN8w-1642060301327)(第十三章 EMMC编程.assets/13_EMMC_Program_image060.png)]
11.3.2.2 步骤2:USDHC2的时钟配置
在芯片应用手册《Chapter 18 Clock Controller Module》的时钟树图中可以看到USDHC2时钟源是PLL2的PFD0或PFD2。同时我们也可以看出,使能USDHC2时钟所要配置的寄存器为CSCDR1[USDHC2_POPF]、CSCMR1[USDHC2_CLK_SEL]和CCGR6[CG2]。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8p7oxp9p-1642060301328)(第十三章 EMMC编程.assets/13_EMMC_Program_image061.png)]
- 配置CSCMR1[USDHC2_CLK_SEL]:
如下图,USDHC2_CLK_SEL为0时时钟源是PLL2_PFD2,否则是PLL2_PFD0。本实验选择PLL2_PFD2,所以CSCMR1[USDHC2_CLK_SEL]使用默认值即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MlnCP61T-1642060301328)(第十三章 EMMC编程.assets/13_EMMC_Program_image062.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBztYbaF-1642060301328)(第十三章 EMMC编程.assets/13_EMMC_Program_image063.png)]
- 配置CSCDR1[USDHC2_POPF]:
如下图USDHC2_POPF的值代表分频系数,范围为1~8。本实验采用2分频所以CSCDR1[USDHC2_POPF]选择默认值0x1即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9SW1lBm2-1642060301328)(第十三章 EMMC编程.assets/13_EMMC_Program_image064.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mPdQvwvx-1642060301329)(第十三章 EMMC编程.assets/13_EMMC_Program_image065.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJxRuC0A-1642060301329)(第十三章 EMMC编程.assets/13_EMMC_Program_image066.png)]
- 配置CCGR6[CG2]:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-np4Kext9-1642060301329)(第十三章 EMMC编程.assets/13_EMMC_Program_image067.png)]
由上图CCM_CCGR6寄存器可知,CCM_CCGR6[CG2] 的默认值为11,11表示该模块时钟使能(参考《4.3.2 CCM用于设置是否向GPIO模块提供时钟》),所以这里采用初始值即可。
由时钟树图我们可知道此时USDHC2_CLK_ROOT = PLL2_PDF2 / 2。通过查询手册得知PLL2_PDF2初始值396MHz。USDHC2_CLK_ROOT的频率则为198MHz。
为了实现400KHz的时钟频率,我们还要配置USDHC2_SYS_CTRL寄存器中的SDCLKFS[15:8]和DVS[7:4],
- 配置USDHC2_SYS_CTRL:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-80G5ZA14-1642060301329)(第十三章 EMMC编程.assets/13_EMMC_Program_image068.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QzDyJM70-1642060301330)(第十三章 EMMC编程.assets/13_EMMC_Program_image069.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDkfkWrA-1642060301330)(第十三章 EMMC编程.assets/13_EMMC_Program_image070.png)]
SDCLKFS是uSDHC时钟预分频系数,当在SDR模式时,分频值为1、2、4、8、16、32、64、128、256。当在
DDR模式时,分频值为SDR模式时的一倍。
DVS是uSDHC时钟分频系数,范围从1到16。
USDHC2_CLK的速率公式存在两种它们分别为:
SDR模式: USDHC2_CLK = USDHC2_CLK_ROOT / (SDCLKFS * DVS)
DDR模式: USDHC2_CLK = USDHC2_CLK_ROOT / (SDCLKFS * 2* DVS)
本实验我们的uSDHC_CLK设定为400KHz,eMMC上电后工作在SDR模式中,经过计算,选择SDCLKFS设置为40h,DVS设置为3。通过公式计算实际uSDHC_CLK频率为396KHz。
相关代码如下:
代码语言:javascript复制192 /* 设置时钟为400KHz. */
193 //Single Data Rate mode,默认使用PLL2_PFD2 = 396Mhz, usdhc2_clk_root = 396Mhz / 2 = 198Mhz
194 //usdhc2_clk = usdhc2_clk_root / (prescaler * divisor)
195 //SDCLKFS = 0x40 = 128分频
196 //DVS = 0x03 = 4分频
197 //usdhc2_clk = 198Mhz/(128*4) = 0.387Mhz
198 usdhc2_reg->SYS_CTRL &= ~((0xFF00) | (0xF0));
199 usdhc2_reg->SYS_CTRL |= (0x0040U << 8) | (0x0003U << 4);
200
201 /* 等待时钟稳定 */
202 while (!(usdhc2_reg->PRES_STATE & 0x8U))
203 {
204
205 }
第198行代码:将SYS_CTRL的SDCLKFS和DVS位置0。
第199行代码:将SYS_CTRL的SDCLKFS和DVS位分别设置为0x40和0x03。
第202行代码:设置完时钟后,需要检验PRES_STATE寄存器的SDSTB位,如果该位为1代表时钟稳定可以往下操作,否则继续等待。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rhEWtPeo-1642060301330)(第十三章 EMMC编程.assets/13_EMMC_Program_image071.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RAuBwbFv-1642060301330)(第十三章 EMMC编程.assets/13_EMMC_Program_image072.png)]
详细代码参考01_emmc目录下的代码。
1.3.3 USDHC2软复位
引脚和时钟配置好后,接下来的工作是初始化USDHC2。
通过设置USDHC2_SYS_CTRL的RSTA位使EMMC控制器复位,复位完成时RSTA位硬件清零。通过下图可知RSTA的掩码为0x1000000。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SLPvWr0R-1642060301331)(第十三章 EMMC编程.assets/13_EMMC_Program_image073.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVJvC5tD-1642060301331)(第十三章 EMMC编程.assets/13_EMMC_Program_image074.png)]
相关代码如下,详细代码参考01_emmc目录下的mmc.c。
代码语言:javascript复制209 /**********************************************************************
210 * 函数名称: USDHC_Reset
211 * 功能描述: usdhc复位
212 * 输入参数: unsigned int mask :
213 * unsigned int timeout :超时时间
214 * 输出参数: unsigned char
215 * 返 回 值: 0为复位成功,1为复位失败。
216 * 修改日期 版本号 修改人 修改内容
217 * -----------------------------------------------
218 * 2020/02/28 V1.0 LJZ 创建
219 ***********************************************************************/
220 static unsigned char USDHC_Reset(unsigned int mask, unsigned int timeout)
221 {
222 usdhc2_reg->SYS_CTRL |= mask;
223
224 while ((usdhc2_reg->SYS_CTRL & mask) != 0U)
225 {
226 if (timeout == 0U)
227 {
228 break;
229 }
230 timeout--;
231 }
232
233 return ((!timeout) ? 0 : 1);
234 }
133
134 /**********************************************************************
135 * 函数名称: USDHC_Init
136 * 功能描述: usdhc初始化
137 * 输入参数: 无
138 * 输出参数: 无
139 * 返 回 值: 无
140 * 修改日期 版本号 修改人 修改内容
141 * -----------------------------------------------
142 * 2020/02/28 V1.0 LJZ 创建
143 ***********************************************************************/
144 void USDHC_Init(void)
145 {
146 CCM_CCGR6 = (volatile unsigned int *)(0x20C4080);
147
148 Emmc_PinConfig();
149
150 /* 使能USDHC时钟 */
151 *CCM_CCGR6 = ((*CCM_CCGR6) & ~(3U << 0x04)) | (((unsigned int)3U) << 0x04);
152
153 /* 复位 USDHC */
154 if( USDHC_Reset(0x1000000U, 100U) == 1 )
155 {
156 /* 复位成功 */
157 printf("Reset Successnr");
158 }
159 else
160 {
161 /* 复位失败 */
162 printf("Reset falsenr");
163 return;
164 }
165 .
166 .
167 .
207 }
1.3.4 初始化eMMC
1.3.4.1 步骤0:编写发送CMD函数
发送命令我们要做哪些事呢?首先要告诉uSDHC2的命令号、命令类型、应答类型和命令参数等。然后就要判断uSDHC2的状态信息,如:命令发送完成、错误标记等。那么我们就要对uSDHC2_PRES_STATE、uSDHC2_MIX_CTRL和uSDHC2_CMD_XFR_TYP寄存器进行操作。
为了方便函数调用,我们将命令号、命令类型和应答数据和标志位的掩码描述为一个结构体,如下代码所示。
代码语言:javascript复制47 typedef enum
48 {
49 ResponseTypeNone = 0U,
50 ResponseTypeR1 = 1U,
51 ResponseTypeR1b = 2U,
52 ResponseTypeR2 = 3U,
53 ResponseTypeR3 = 4U,
54 ResponseTypeR4 = 5U,
55 ResponseTypeR5 = 6U,
56 ResponseTypeR5b = 7U,
57 ResponseTypeR6 = 8U,
58 ResponseTypeR7 = 9U
59 }USDHC_Respones_Type;
60
61 typedef struct
62 {
63 unsigned int index;
64 unsigned int arg;
65 unsigned int response[4];
66 USDHC_Respones_Type response_type;
67 unsigned int mix_ctrl;
68 unsigned int xfr_typ;
69 }USDHC_Command;
USDHC_Respones_Type是用枚举来描述应答的类型。USDHC_Command结构体中index是命令的编号,arg是命令的参数,response数据是应答的数据,response_type是应答的类型,mix_ctrl是设置MIX_CTRL寄存器的值,xfr_typ_mark是设置CMD_XFR_TYP寄存器的值。
函数的参数已经准备好了,现在开始编写发送命令的函数。在发送CMD之前要判断命令和数据操作是否可以执行。这就要检查uSDHC2_PRES_STATE的状态寄存器的CIHB和CDIHB位。如下图所示当CIHB和CDIHB为1时代表不能发送命令和数据,直到这两个位为0才可以发送CMD。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wcFufax-1642060301331)(第十三章 EMMC编程.assets/13_EMMC_Program_image075.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEl9FVV6-1642060301331)(第十三章 EMMC编程.assets/13_EMMC_Program_image076.png)]
代码如下:
代码语言:javascript复制248 /* 等待命令发送完成 */
249 // CIHB掩码:0x01
250 while( usdhc2_reg->PRES_STATE & 0x01U )
251 {
252 }
253
254 /* 等待数据完成 */
255 // CDIHB掩码:0x02
256 while( usdhc2_reg->PRES_STATE & 0x02U )
257 {
258 }
第248和258行代码分别是对CIHB(0x1U)和CDIHB(0x2U)位进行判断,如果这两个位为1继续等待,直到CIHB和CDIHB位为0退出循环。
确认可以发送CMD时,根据命令的类型配置uSDHC2_MIX_CTRL的DDR_EN(DDR模式)、DTDSEL(数据传输方向,1为读操作,0为写操作)、BCEN(块计数使能)和MSBSEL(单块或多块操作,0为单个块操作,1为多个块操作)位。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njHsSRuh-1642060301332)(第十三章 EMMC编程.assets/13_EMMC_Program_image077.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUlMgdk7-1642060301332)(第十三章 EMMC编程.assets/13_EMMC_Program_image078.png)]
代码如下:
代码语言:javascript复制262 /* 配置传输方式 */
263 // BCEN掩码: 0x02U
264 // DDR_EN掩码:0x08U
265 // DTDSEL掩码:0x10U
266 // MSBSEL掩码:0x20U
267 usdhc2_reg->MIX_CTRL &= ~(0x20U | 0x2U | 0x10U | 0x8U );
268 usdhc2_reg->MIX_CTRL |= ((command->mix_ctrl) & (0x20U | 0x2U | 0x10U | 0x8U ));
DDR_EN掩码为0x8U,DTDSEL的掩码为0x10,BCEN的掩码为0x2U,MSBSEL的掩码为0x20U。
第267行代码:将uSDHC2_MIX_CTRL的DDR_EN、DTDSEL、BCEN、MSBSEL设置为0。
第268行代码:将uSDHC2_MIX_CTRL设置为mix_ctrl的值。
配置完传输方式后,将命令参数写入uSDHC2_CMD_ARG寄存器。
代码语言:javascript复制270 usdhc2_reg->CMD_ARG = command->arg;
最后根据命令的要求配置uSDHC2_CMD_XFR_TYP寄存器的命令索引、应答类型、命令CRC校验使能、命令检查使能和数据传输使能。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-64EiP2oF-1642060301332)(第十三章 EMMC编程.assets/13_EMMC_Program_image079.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qR59Vbe-1642060301332)(第十三章 EMMC编程.assets/13_EMMC_Program_image080.png)]
代码如下:
代码语言:javascript复制272 /* 配置命令参数 */
273 // CDMINX掩码:0x3F000000U
274 // DPSEL掩码:0x200000U
275 // CICEN掩码:0x100000U
276 // CCCEN掩码:0x80000U
277 // PSPTYP[1:0]掩码:0x30000U
278 usdhc2_reg->CMD_XFR_TYP &= ~(0x3F000000U | 0x200000U | 0x100000U | 0x80000U | 0x30000U);
279
280 usdhc2_reg->CMD_XFR_TYP = (((command->index << 24U) & 0x3F000000U) |
281 ((command->xfr_typ) &(0x3F000000U | 0x200000U | 0x100000U | 0x80000U | 0x30000U)));
CMDINX、DPSEL、CMDTYP、CICEN、CCCEN和RSPTYP对应掩码值分别为0x3F000000U、0x200000U、0x100000U、0x80000U和0x30000U。
第278行代码:将CMD_XFR_TYP寄存器的CMDINX、DPSEL、CMDTYP、CICEN、CCCEN和RSPTYP位清零。
第280行代码:设置CMD_XFR_TYP寄存器命令编号和设置相应的标记位。
CMD_XFR_TYP配置完成后uSDHC2就会开始传输命令。此时可以通过查询uSDHC2_INT_STATUS的CC位判断传输是否完成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUnb2gsR-1642060301332)(第十三章 EMMC编程.assets/13_EMMC_Program_image081.png)]
具体代码如下:
代码语言:javascript复制282 /* 等待命令发送 */
283 // CC位掩码:0x01
284 while( !(usdhc2_reg->INT_STATUS & 0x1U ) )
285 {
286 }
287
288 /* 清中断标记位 */
289 // CC位掩码:0x01
290 usdhc2_reg->INT_STATUS &= 0x01U ;
第284-286行代码:检查INT_STATUS寄存器的CC位是否为1,如果为1则进行后续操作,否则继续等待直到命令发送完毕。
第290行代码:清除INT_STATUS寄存器的CC位。
最后根据应答类型读取应答寄存器的值。IMX6将应答数据 的CRC部分去掉,其余部分保存到 uSDHC2_CMD_RSP0-3寄存器中。如下表所示,除R2应答是120bit,其他的应答都是32位并存储在uSDHC2_CMD_RSP0或uSDHC2_CMD_RSP3。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZaYKGOR-1642060301333)(第十三章 EMMC编程.assets/13_EMMC_Program_image082.png)]
代码如下:
代码语言:javascript复制295 /**********************************************************************
296 * 函数名称: USDHC_ReadResponse
297 * 功能描述: 读取Response
298 * 输入参数: USDHC_Command* command :USDHC命令结构体
299 * 输出参数: 无
300 * 返 回 值: 无
301 * 修改日期 版本号 修改人 修改内容
302 * -----------------------------------------------
303 * 2020/02/28 V1.0 LJZ 创建
304 ***********************************************************************/
305 void USDHC_ReadResponse(USDHC_Command *command)
306 {
307 unsigned int i;
308
309 if( command->response_type != ResponseTypeNone )
310 {
311 command->response[0] = usdhc2_reg->CMD_RSP0;
312
313 if( command->response_type == ResponseTypeR2 )
314 {
315 command->response[1] = usdhc2_reg->CMD_RSP1;
316 command->response[2] = usdhc2_reg->CMD_RSP2;
317 command->response[3] = usdhc2_reg->CMD_RSP3;
318
319 i = 4;
320
321 do
322 {
323 command->response[i - 1] <<= 8;
324 if (i > 1)
325 {
326 command->response[i - 1] |= ((command->response[i - 2] & 0xFF000000U) >> 24U);
327 }
328 } while (i--);
329 }
330 }
331 }
第313-329代码:为了后续处理方便应答类型为R2时我们将uSDHC2_CMD_RSP0-3的数据左以7位。
1.3.4.2 步骤1:eMMC初始化
发送命令的函数已经准备好了,现在我们编写程序发送第一个命令CMD0。在编程序之前先了解CMD0的信息。从下表可知,CMD0是无应答广播命令,根据参数的不同eMMC进入不同的状态。
类型 | 参数 | 应答 |
---|---|---|
无应答广播 | 0x00000000 | 无 |
无应答广播 | 0xF0F0F0F0 | 无 |
- | 0xFFFFFFFA | 无 |
本步骤中需要eMMC进入IDLE状态进行复位,所以参数的值为0x00000000。(这里应该怎么说)MIX_CTRL的值应为0x0。CMD_XFR_TYP也设置为0。应答类型为无应答。代码如下:
代码语言:javascript复制16 command.index = 0;
17 command.arg = 0;
18 command.mix_ctrl = 0;
19 command.xfr_typ = 0;
20 command.response_type = ResponseTypeNone;
21
22 //发送CMD0
23 USDHC_SendCommand(&command);
根据13.1.9的状态图,此时需要发送CMD1读取eMMC的OCR寄存器,通过第31位判断eMMC是否复位完毕。CMD1的描述如下:
类型 | 参数 | 应答类型 |
---|---|---|
有应答广播 | 0 | R3 |
相关代码如下:
代码语言:javascript复制25 for( i = 0; i < 10; i )
26 {
27 //发送CMD1
28 //设置响应类型 R3
29 //答复长度为48bit,RSPTYP[1:0] = 0b10 = 2
30 command.index = 1;
31 command.arg = 0;
32 command.mix_ctrl = 0;
33 command.xfr_typ = 2 << 16;
34 command.response_type = ResponseTypeR3;
35
36 USDHC_SendCommand(&command);
37
38 // 等待EMMC复位完成
39 // 在OCR中第31为表示EMMC设备是否准备好处理数据,1为准备好,0为忙
40 if ( command.response[0] & (1U << 31U) )
41 {
42 printf("MMC is Ready ok nr");
43 break;
44 }
45 else
46 {
47 printf("MMC is Busy.nr");
48 }
49 }
1.3.4.3 参考章节《4-1.4编译程序》编译程序
进入 cd 01_emmc 源码目录
1.3.4.4 参考章节《4-1.4映像文件烧写、运行》烧写、运行程序
此时观察串口现象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xemyxjpj-1642060301333)(第十三章 EMMC编程.assets/13_EMMC_Program_image083.png)]
1.3.4.5 步骤2:读取CID信息
eMMC初始化完成后进入Ready状态。在Ready状态下需要发送CMD2命令,使eMMC进入Identification状态。如下表所示CMD2是有应答广播命令,参数可以是任意值,应答类型为R2。
类型 | 参数 | 应答类型 |
---|---|---|
有应答广播 | 任意值 | R2 |
本步骤中参数设置为0,应答类型设置为ResponseTypeR2。CMD_XFR_TYP的命令CRC检测使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为1,对应的值为1<<16|1<<19。代码如下:
代码语言:javascript复制57 //发送CMD2
58 //设置响应类型 R2
59 //答复长度为136bit,RSPTYP[1:0] = 0b01 = 1
60 command.index = 2;
61 command.arg = 0;
62 command.mix_ctrl = 0;
63 command.xfr_typ = 1 << 16 | 1 << 19;
64 command.response_type = ResponseTypeR2;
65
66 USDHC_SendCommand(&command);
CMD2发送成功后,主机会收到CID寄存器的值。这里打印CID寄存器的ManufacturerID、CBX、ApplicationID和Product name信息,代码如下:
代码语言:javascript复制68 printf("ManufacturerID: 0x%x nr", (unsigned char)((command.response[3U] &
0xFF000000U) >> 24U));
69 printf("CBX: 0x%x nr", (unsigned char)((command.response[3U] & 0x00FF0000U) >> 16U));
70 printf("ApplicationID: 0x%x nr", (unsigned char)((command.response[3U] & 0x0000FF00U) >> 8U));
71 printf("Product version: 0x%x nr", (unsigned char)((command.response[1U] & 0xFF000000U) >> 24U));
详细代码参考02_emmc目录中的代码。接着我们上机验证,实验结果如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5fRP3Qog-1642060301333)(第十三章 EMMC编程.assets/13_EMMC_Program_image084.png)]
1.3.4.6 步骤3:配置RCA
成功进入识别模式后,向eMMC发送CMD3配置RCA,使eMMC进入Standby状态。如下表所示,CMD3参数高16位是RCA,低16位为任意值,应答类型为R1。
类型 | 参数 | 应答类型 |
---|---|---|
点对点无数据传输 | [31:16]RCA,[15:0]任意值 | R1 |
实验中参数的RCA设为1,低16位为0,即赋值为1<<16即可,应答类型设置为ResponseTypeR1。CMD_XFR_TYP的命令编号检测使能位CICEN设置为1,CRC检测使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为2,CMD_XFR_TYP赋值为2<<16|1<<19|1<<20。代码如下:
代码语言:javascript复制73 // Send CMD3 设置设备相对地址
74 command.index = 3;
75 command.arg = 1 << 16;
76 command.mix_ctrl = 0;
77 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20;
78 command.response_type = ResponseTypeR1;
命令发送成功后会收到类型为R1的应答。从《13.1.6应答(Response)介绍》中可知,它是关于设备状态的信息,通过检查对应的位,判断设备处于什么状态和出现哪些错误。这里我们检查CRC错误COM_CRC_ERROR和非法命令ILLEGAL_COMMAND着两个错误位即可。代码如下:
代码语言:javascript复制82 // COM_CRC_ERROR:(1U << 23U)
83 // ILLEGAL_COMMAND:(1U << 22U)
84 if (!(command.response[0] & ((1U << 23U) | (1U << 22U))))
85 {
86 printf("Emmc in Stand-by Statenr");
87 }
88 else
89 {
90 printf("SDMMC_R1 Errornr");
91 return 0;
92 }
1.3.4.7 步骤4:读取CSD和EXCSD
在Standby状态下,通过发送CMD9命令读取CSD信息,CMD9格式如下表所示。
类型 | 参数 | 应答类型 |
---|---|---|
点对点无数据传输 | [31:16]RCA,[15:0]任意值 | R2 |
CMD9参数高16位是RCA,低16位为任意值,应答类型为R2。本实验RCA设为1。应答类型设置为ResponseTypeR2。CMD_XFR_TYP的CRC检测使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为1, CMD_XFR_TYP的值应为1<<16|1<<19。命令发送成功后会返回CSD信息,这里我们只显示CSD结构版本信息。具体代码如下:
代码语言:javascript复制94 // Send CMD9 读取CSD信息
95 command.index = 9;
96 command.arg = 1 << 16;
97 command.mix_ctrl = 0;
98 command.xfr_typ = 1 << 16 | 1 << 19;
99 command.response_type = ResponseTypeR2;
100
101 USDHC_SendCommand(&command);
102
103 printf("CSD structure: %x nr", (unsigned char)((command.response[3U] & 0xC0000000U) >> 30U));
因为EXCSD有512字节组成,所以需要通过数据总线传输。在Standby状态下还不能传输数据,需要要跳转到transfer状态下。此时根据《13-1.2 数据传输模式》的状态图可以得知,需要CMD7切换到transfer状态。CMD7的描述如下表所示。
类型 | 参数 | 应答类型 |
---|---|---|
点对点无数据传输 | [31:16]RCA,[15:0]任意值 | R1/R1b |
CMD7参数高16位是RCA,低16位为任意值,应答类型为R1或R1b。这里参数的RCA设为1。应答类型设置为ResponseTypeR1。CMD_XFR_TYP的命令编号检测使能位CICEN设置为1,CRC检测使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为2,CMD_XFR_TYP赋值为2<<16|1<<19|1<<20。代码如下:
代码语言:javascript复制105 // Send CMD7 进入tranfaer状态
106 command.index = 7;
107 command.arg = 1 << 16;
108 command.mix_ctrl = 0;
109 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20;
110 command.response_type = ResponseTypeR1;
111
112 USDHC_SendCommand(&command);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KWTK9HSQ-1642060301333)(第十三章 EMMC编程.assets/13_EMMC_Program_image085.png)]
代码如下:
代码语言:javascript复制114 if (!(command.response[0] & ((1U << 23U) | (1U << 22U))))
115 {
116 printf("Emmc in Transfer Statenr");
117 }
118 else
119 {
120 printf("SDMMC_R1 Errornr");
121 return 0;
122 }
第114行代码:查询设备状态的第22位和23位,如果这两位为0则表示CMD7操作成功。
进入Transfer状态后,发送CMD8读取EXCSD。CMD8信息如下表所示。
类型 | 参数 | 应答类型 |
---|---|---|
点对点有数据传输 | 任意值 | R1 |
CMD8参数设为0。应答类型设置为ResponseTypeR1。CMD_XFR_TYP的命令编号检测使能位CICEN设置为1,CRC检测使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为2,CMD_XFR_TYP赋值为2<<16|1<<19|1<<20。MIX_CTRL寄存器的数据传输方向DTDSEL位设置位1,其掩码值应为1<<4。相应代码在下面第385到387行代码中。
代码语言:javascript复制366 /**********************************************************************
367 * 函数名称: MMC_ReadExCsd
368 * 功能描述: 读ECsd
369 * 输入参数: 无
370 * 输出参数: 无
371 * 返 回 值: 无
372 * 修改日期 版本号 修改人 修改内容
373 * -----------------------------------------------
374 * 2020/02/28 V1.0 LJZ 创建
375 ***********************************************************************/
376 void MMC_ReadExCsd(void)
377 {
378 USDHC_Command command;
379 unsigned char data[512];
380
381 command.index = 8;
382 command.arg = 0;
383 //
384 //
385 command.mix_ctrl = 1 << 4 | 0x1 << 1;
386 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20 | 1 << 21;
387 command.response_type = ResponseTypeR1;
388
389 usdhc2_reg->BLK_ATT = 512 | 1 << 16;
390
391 USDHC_SendCommand(&command);
392
393 USDHC_ReadWordData((unsigned int*)data);
394
395 printf("Card type: 0x%x nr", data[196U]);
396 printf("DataBusWidth: 0x%xnr", data[183U]);
397 printf("CSD structure: %xnr", data[194U]);
398 printf("Extended CSD revision: %xnr", data[192U]);
399 }
第389行代码:设置BLK_ATT寄存器,将每块数据设置为512字节,块计数为1。
第393行代码:调用USDHC_ReadWordData读取缓存中的数据,此函数接下里介绍。
第395-398行代码:打印CSD信息。
下面讲解USDHC_ReadWordData的函数,相关代码如下:
代码语言:javascript复制332 /**********************************************************************
333 * 函数名称: USDHC_ReadWordData
334 * 功能描述: 读取数据
335 * 输入参数: unsigned int* data :数据存储地址
336 * 输出参数: 无
337 * 返 回 值: 无
338 * 修改日期 版本号 修改人 修改内容
339 * -----------------------------------------------
340 * 2020/02/28 V1.0 LJZ 创建
341 ***********************************************************************/
342 void USDHC_ReadWordData(unsigned int* data)
343 {
344 unsigned int i;
345
346 printf("Waitting Read buffer readynr");
347 //
348 //
349 // BRR的掩码:0x1<<5
350 while (!( usdhc2_reg->INT_STATUS & (1 << 5) ))
351 {
352
353 }
354
355 printf("Read buffer has readiednr");
356 usdhc2_reg->INT_STATUS &= (1 << 5);
357
358 for(i=0; i<512/4; i )
359 {
360 data[i] = usdhc2_reg->DATA_BUFF_ACC_PORT;
361 }
362
363 printf("Read buffer completenr");
364 }
第350行代码,检测INT_STATUS寄存器的BRR位是否为1,判断缓存是否可读。
第358-361行代码,读取512节的数据。
最后打印EXCSD的Card type、DataBusWidth、CSD Structure和EXCsd Version的信息验证程序的正确性。代码如下:
代码语言:javascript复制395 printf("Card type: 0x%x nr", data[196U]);
396 printf("DataBusWidth: 0x%xnr", data[183U]);
397 printf("CSD structure: %xnr", data[194U]);
398 printf("Extended CSD revision: %xnr", data[192U]);
接着我们参考《4.4.4 编译程序》与《3.4 映像文件烧写、运行》,上机验证。结果如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxxV5QAq-1642060301334)(第十三章 EMMC编程.assets/13_EMMC_Program_image086.png)]
1.3.4.8 步骤5:配置位宽
接下来我们要将eMMC的数据位宽设置为4bit。相关代码如下:
代码语言:javascript复制402 /**********************************************************************
403 * 函数名称: MMC_SetBusWidth
404 * 功能描述: 设置数据总线宽度
405 * 输入参数: 无
406 * 输出参数: 无
407 * 返 回 值: 无
408 * 修改日期 版本号 修改人 修改内容
409 * -----------------------------------------------
410 * 2020/02/28 V1.0 LJZ 创建
411 ***********************************************************************/
412 void MMC_SetBusWidth(void)
413 {
414 USDHC_Command command;
415
416 command.index = 6;
417 command.arg = (3<<24) | (183<<16) | (1<<8) | (0<<0);
418 command.mix_ctrl = 0;
419 command.xfr_typ = 3 << 16 | 1 << 19;
420 command.response_type = ResponseTypeR1b;
421
422 USDHC_SendCommand(&command);
423
424 while(1)
425 {
426 command.index = 13;
427 command.arg = 1U << 16;
428 command.mix_ctrl = 0;
429 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20;
430 command.response_type = ResponseTypeR1;
431
432 USDHC_SendCommand(&command);
433
434 //
435 if ( command.response[0] & (1U << 8U) )
436 {
437 printf("SetBusWidth Complete. nr");
438 break;
439 }
440 else
441 {
442 printf("Emmc is Busy. nr");
443 }
444 }
445 }
CMD6描述如下表所示。
类型 | 参数 | 应答类型 |
---|---|---|
点对点无数据传输 | [31:26] 0, [25:24] Access, [23:16] Index, [15:8] Value, [7:3] Set to 0, [2:0] Cmd Set | R1b |
CMD6参数中第26到31位为0。Access是代表什么呢?查阅《eMMC5.0Spec_JESD84-B50》手册得到下图。当Access为3时,写EXCSD某一个字节,所以这里Access赋值为3。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COXTyMLI-1642060301334)(第十三章 EMMC编程.assets/13_EMMC_Program_image087-1641905989040.png)]
Index是指EXCSD的字节编号,总线位宽在EXCSD的第183字节中,所以Index设置为183。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJAT81aR-1642060301334)(第十三章 EMMC编程.assets/13_EMMC_Program_image088.png)]
在《eMMC5.0Spec_JESD84-B50》中查到BUS_WIDTH数值对应的位宽模式如下图所示。现在我们设置是SDR的4bit数据总线,所以Value应设置为1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1i2ETygW-1642060301334)(第十三章 EMMC编程.assets/13_EMMC_Program_image089.png)]
CMD6参数应赋值为(3<<24) | (183<<16) | (1<<8) | (0<<0)。应答类型设置为ResponseTypeR1。CMD_XFR_TYP寄存器的命令编号检测使能位CICEN设置为1,CRC校验使能位设为1,应答类型RSPTYP[1:0]设置为2,对应掩码值应为2<<16|1<<19|1<<20。相应代码在第416至420行中。
在Transfer状态中,只能通过CMD13来读取ORC值,并通过ORC值的第31位判断eMMC是否处理完成。相应的代码在424至444行。
修改eMMC数据总线位宽后,也需要设置IMX6 USDHC2的数据总线位宽。相关代码如下:
代码语言:javascript复制447 /**********************************************************************
448 * 函数名称: USDHC_SetBusWidth
449 * 功能描述: 设置USDHC的数据总线宽度
450 * 输入参数: 无
451 * 输出参数: 无
452 * 返 回 值: 无
453 * 修改日期 版本号 修改人 修改内容
454 * -----------------------------------------------
455 * 2020/02/28 V1.0 LJZ 创建
456 ***********************************************************************/
457 void USDHC_SetBusWidth(void)
458 {
459 usdhc2_reg->PROT_CTRL &= ~(6U << 1);
460 usdhc2_reg->PROT_CTRL |= 1U<<1;
461 }
我们通过USDHC2_PROT_CTRL寄存器的DTW[1:0]修改USDHC2的数据总线位宽。如下图所示,DTW[1:0]应设置为0x1。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjQBErb1-1642060301335)(第十三章 EMMC编程.assets/13_EMMC_Program_image090.png)]
第459行代码:将USDHC2_PROT_CTRL寄存器的DTW[1:0]清0。
第460行代码:将USDHC2_PROT_CTRL寄存器的DTW[1:0]赋值为0x01。
详细代码请参考03_emmc目录下的main.c、mmc.c和mmc.h。
1.3.5 读写实验
接下来开始读写实验,在读写操作之前要设置Block的大小。设置Block的大小使用CMD16来完成。CMD16描述如下:
类型 | 参数 | 应答类型 |
---|---|---|
点对点无数据传输 | [31:26] Block长度 | R1 |
设置Block的函数代码如下:
代码语言:javascript复制463 /**********************************************************************
464 * 函数名称: MMC_SetBlockSize
465 * 功能描述: 设置MMC的Block大小
466 * 输入参数: 无
467 * 输出参数: 无
468 * 返 回 值: 无
469 * 修改日期 版本号 修改人 修改内容
470 * -----------------------------------------------
471 * 2020/02/28 V1.0 LJZ 创建
472 ***********************************************************************/
473 void MMC_SetBlockSize(void)
474 {
475 USDHC_Command command;
476
477 command.index = 16;
478 command.arg = 512;
479 command.mix_ctrl = 0;
480 command.xfr_typ = 2 << 16 | 1 << 19;
481 command.response_type = ResponseTypeR1;
482
483 USDHC_SendCommand(&command);
484
485 //COM_CRC_ERROR:1U << 23U
486 //ILLEGAL_COMMAND:1U << 22U
487 if (!(command.response[0] & ((1U << 23U) | (1U << 22U))))
488 {
489 printf("Emmc Set Blocksize Oknr");
490 }
491 else
492 {
493 printf("Emmc Set Blocksize Errornr");
494 }
495 }
第477-481行代码: 设置CMD16的参数为512,CMD_XFR_TYP寄存器的CRC校验使能位CCCEN设置为1,应答类型RSPTYP[1:0]设置为2,对应掩码值应为2<<16|1<<19。
第487行代码:检查R1应答中的CRC错误位COM_CRC_ERROR和非法命令ILLEGAL_COMMAND错误位。
读数据的函数在读EXCSD的时候已经编写完成,这里添加一个写数据的函数,相关代码如下:
代码语言:javascript复制497 /**********************************************************************
498 * 函数名称: MMC_WriteBlock
499 * 功能描述: 写一个BLOCK数据到EMMC
500 * 输入参数: unsigned int address: 地址
501 * const unsigned int* data: 数据存储地址
502 * 输出参数: 无
503 * 返 回 值: 无
504 * 修改日期 版本号 修改人 修改内容
505 * -----------------------------------------------
506 * 2020/02/28 V1.0 LJZ 创建
507 ***********************************************************************/
508 void MMC_WriteBlock(unsigned int address, const unsigned int* data)
509 {
510 USDHC_Command command;
511 unsigned int i;
512
513 command.index = 24;
514 command.arg = address;
515 command.mix_ctrl = 0;
516 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20 | 1 << 21;
517 command.response_type = ResponseTypeR1;
518
519 usdhc2_reg->BLK_ATT = 512 | 1 << 16;
520
521 USDHC_SendCommand(&command);
522
523 //COM_CRC_ERROR:1U << 23U
524 //ILLEGAL_COMMAND:1U << 22U
525 if (!(command.response[0] & ((1U << 23U) | (1U << 22U))))
526 {
527 printf("CMD24 Successnr");
528
529 for(i=0; i<512/4; i )
530 {
531 usdhc2_reg->DATA_BUFF_ACC_PORT = data[i];
532 }
533 }
534 else
535 {
536 printf("CMD24 Errornr");
537 }
538 }
第513-517行代码:CMD24参数设为地址值。应答类型设置为ResponseTypeR1。CMD_XFR_TYP寄存器的命令编号检测使能位CICEN设置为1,CRC校验使能位设为1,数据传输选择位DPSEL为1,应答类型RSPTYP[1:0]设置为2,CMD_XFR_TYP寄存器值应为2 << 16 | 1<<19 |1<<20 | 1<<21。
这里还要添加一个读Block数据函数,其代码为:
代码语言:javascript复制540 /**********************************************************************
541 * 函数名称: MMC_ReadBlock
542 * 功能描述: 读一个BLOCK数据
543 * 输入参数: unsigned int address: 地址
544 * unsigned int* data: 数据存储地址
545 * 输出参数: 无
546 * 返 回 值: 无
547 * 修改日期 版本号 修改人 修改内容
548 * -----------------------------------------------
549 * 2020/02/28 V1.0 LJZ 创建
550 ***********************************************************************/
551
552 void MMC_ReadBlock(unsigned int address, unsigned int* data)
553 {
554 USDHC_Command command;
555 unsigned int i;
556
557 command.index = 17;
558 command.arg = address;
559
560
561 command.mix_ctrl = 0x1 << 4 | 0x1 << 1;
562 command.xfr_typ = 2 << 16 | 1 << 19 | 1 << 20 | 1 << 21;
563 command.response_type = ResponseTypeR1;
564
565 usdhc2_reg->BLK_ATT = 512 | 1 << 16;
566
567 USDHC_SendCommand(&command);
568
569 // COM_CRC_ERROR:1U << 23U
570 //ILLEGAL_COMMAND:1U << 22U
571 if (!(command.response[0] & ((1U << 23U) | (1U << 22U))))
572 {
573 printf("CMD17 Successnr");
574 }
575 else
576 {
577 printf("CMD17 Errornr");
578 }
579
580 USDHC_ReadWordData(data);
581 }
第561-563行代码:CMD17参数设为地址值。应答类型设置为ResponseTypeR1。CMD_XFR_TYP寄存器的命令编号检测使能位CICEN设置为1,CRC校验使能位设为1,数据传输选择位DPSEL为1,应答类型RSPTYP[1:0]设置为2,CMD_XFR_TYP寄存器值应为2 << 16 | 1<<19 |1<<20 | 1<<21。MIX_CTRL寄存器的数据传输方向DTDSEL位设为1。
在主函数上分别调用这几个函数,相关代码如下:
代码语言:javascript复制152 //设置EMMC的Block大小
153 MMC_SetBlockSize();
154
155 for(i=0; i<512/4; i )
156 {
157 WriteData[i] = i;
158 }
159 //写一个Block数据
160 MMC_WriteBlock(0,WriteData);
161 //读数据
162 MMC_ReadBlock(0,ReadData);
163
164 for(i=0; i<512/4; i )
165 {
166 printf("Data[%d]: %dnr", i, ReadData[i]);
167 }
1.3.6 参考章节《4-1.4编译程序》编译程序
进入 cd 04_emmc 源码目录进行编译。
1.3.7 参考章节《4-1.4映像文件烧写、运行》烧写、运行程序
此时查看串口输出信息
详细代码请参考04_emmc目录下的main.c、mmc.c和mmc.h。