13_EMMC编程

2022-05-05 18:27:12 浏览数 (1)

第十三章 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。

0 人点赞