设备驱动移植
CAM:以内容进行寻址的存储器,是一种特殊的存储阵列RAM。它的主要工作机制就是同时将一个
输入数据项与存储在CAM中的所有数据项自动进行比较,判别该输入数据项与CAM中存储的数据项是否
相匹配,并输出该数据项对应的匹配信息。
USB:通用串行总线,数据传输率高、易扩展、支持即插即用和热插拔,usb1.1包含全速和低速;usb2.0增加了高速方式,半双工。usb3.0为全双工。
**以太网接口:**由MAC(以太网媒体接入控制器)和PHY(物理接口收发器)组成。MAC和PHY之间采用MII(媒体独立接口)连接,它是IEEE-802.3定义的以太网行业标准,包括1个 数据接口与MAC和PHY之间的1个管理接口。
PCI和PCI-E:一种局部总线;PCI-E(PCI Express)是Intel公司提出的新一代的总线接口,PCI Express采用了目前业内流行的点对 点串行连接,比起PCI以及更早的计算机总线的共享并行架构,每个设备都有自己的专用连接,采用串行 方式传输数据,不需要向整个总线请求带宽,并可以把数据传输率提高到一个很高的频率,达到PCI所不能提供的高带宽。
**EMMC:**eMMC就是NAND Flash、闪存控制芯片和标准接口封装的集合,它把NAND和控制芯片直
接封装在一起成为一个多芯片封装(MCP)芯片
Linux内核
PCB:进程的有效信息放在进程控制块的数据结构中,为task_struct
每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体.
内核线程的函数:pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
bootrom:嵌入在处理器芯片内的一小块掩模ROM或写保护闪存.它包含处理器在上电或复位时执行的第一个代码,bootrom会去引导bootloader;init程序被调用,init程序再派生其他进程,派生出来的进程再派生其他进程
switch case x…y这样的语法,区间[x,y]中的数都会满足这个case的条件
特殊属性声明: 声明后添加“_attribute_((ATTRIBUTE))”ATTRIBUTE为属性说
明,如果存在多个属性,则以逗号分隔。
noreturn属性作用于函数,表示该函数从不返回。这会让编译器优化代码,并消除不必要的警告信
息
format属性也用于函数,表示该函数使用printf、scanf或strftime风格的参数,指定format属性可以让编 译器根据格式串检查参数类型。
unused属性作用于函数和变量,表示该函数或变量可能不会用到,这个属性可以避免编译器产生警告
信息。
aligned属性用于变量、结构体或联合体,指定变量、结构体或联合体的对齐方式,以字节为单位
packed属性作用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结构
体或联合体类型时表示该类型使用最小的内存。
交叉编译工具链
arm-linux-gnueabihf-gcc(后续工具省略前缀)、strip、gcc、 objdump、ld、gprof、nm、readelf、addr2line
strip:可以删除可执行文件中的符号表和调试信息等来实 现缩减程序体积的目的。
gprof:在编译过程中在函数入口处插入计数器以收集每个函数的被调用情况和被 调用次数,检查程序计数器并在分析时找出与程序计数器对应的函数来统计函数占用的时间。
objdump是 反汇编工具。
nm则用于显示关于对象文件、可执行文件以及对象文件库里的符号信息。
Linux文件系统与设备文件
应用程序和VFS之间的接口是系统调用,而VFS与文件系统以及设备文件之间的接口是file_operations
结构体成员函数。应用程序---->系统调用------>vfs---->file_operations结构体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xO0JKaS-1618219314339)(C:\Users\59966\AppData\Roaming\Typora\typora-user-images\image-20210401164908477.png)]
Linux设备驱动:
device_driver驱动 device 设备 bus_type总线 通过match()来匹配,匹配成功,xxx_driver的probe()就被执行(xxx是总线名, 如platform、pci、i2c、spi、usb等)
globalman设备驱动
open里面里面定义数据,在其他的write,read,ioctl里面访问数据:
是_IOC_NONE(无数据传输)、 _IOC_READ(读)、_IOC_WRITE(写)和_IOC_READ|_IOC_WRITE(双向)
RCU锁机制:读-复制-更新,写执行时,先复制一个资源的副本,等待一个时机去替换,等所有CPU都停止对资源的访问
rcu_read_lock()和rcu_read_unlock()
同步RCU synchronize_rcu();
O_NONBLOCK阻塞和非阻塞
异步IO AIO
aio_read()异步读操作和aio_write()异步写操作,aio_error()函数被用来确定请求的状态。
aio_suspend()阻塞调用进程,知道异步请求完成,lio_listio()函数可用于同时发起多个传输。
中断
上半部登记中断,下半部处理
内存管理单元
TLB:转换旁路缓存。TLB是MMU的核心部件,它缓存少量的 虚拟地址与物理地址的转换关系,是转换表的Cache,因此也经常被称为“快表”。
总线地址是从设备角度看到的内存地址,物理地址是CPU MMU控制器外围角度看到的内存地址
container_of()和file->private_data找到对应的实例
网络设备驱动
上到下四层:网络协议层、网络设备接口层、设备驱动功能层、网络设备与媒介层
1)网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP,还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立
于具体的设备。
2)网络设备接口层向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device, 该结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件的设 备驱动功能层的结构。
3)设备驱动功能层的各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件 完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收 操作。
4)网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网 络适配器被设备驱动功能层中的函数在物理上驱动。对于Linux系统而言,网络设备和媒介都可以是虚拟 的。
嵌入式网络硬件分为:MAC和PHY
内部的 MAC 外设会通过 MII 或者 RMII 接口来连接外部的 PHY 芯片,MII/RMII 接口用来
传输网络数据。另外主控需要配置或读取 PHY 芯片,也就是读写 PHY 的内部寄存器,所以还
需要一个控制接口,叫做 MIDO,MDIO 很类似 IIC,也是两根线,一根数据线叫做 MDIO,一
根时钟线叫做 MDC。
MII接口:介质独立接口,以太网标准接口,MII接口用于以太网MAC连接PHY芯片,一共有16根线
**RMII接口:**精简的介质独立接口。只需要7根线
MDIO接口:管理数据输入输出接口,一根MDIO数据线,一根MDC时钟线。一根 MDC 时钟线。驱动程序可以通过 MDIO 和 MDC 这两根线访问 PHY 芯片的任意一个寄存器。MDIO 接口支持多达 32 个 PHY
**RJ45接口:**插入网线的叫做 RJ45 座
IMX6ULL ENET接口:
MAC层支持双工、半双工局域网。
PHY芯片
PHY 芯片寄存器地址空间为 5 位,地址 0~31 共 32 个寄存器,
内核的网络设备框架
net_device 网络设备结构体,表示一个具体的网络设备,初始化结构体
重要成员对象:name 名字;mem_end 共享内存结束地址;mem_start 共享内存开始地址 ; base_addr 网络设备IO地址; irq 网络设备的中断号; dev_list 是全局网络设备列表;napi_list 网络设备napi的列表入口; unreg_list 注销网络设备的列表入口; close_lsit 关闭网络设备的列表入口;
netdev_ops 网络设备的操作函数集,包含了一系列的的网络设备操作回调函数,类似于字符设备的file_operations;
ethtool_ops 是网络管理工具相关函数集,用户空间网络管理工具会调用此结构体中的相关函数获取网卡状态或者配置网卡。
header_ops 是头部的相关操作函数集,比如创建、解析、缓冲等。
flags 是网络接口标志,标志类型定义在 include/uapi/linux/if.h 文件中。if_port 指定接口的端口类型;dma 是网络设备所使用的 DMA 通道;mtu 是网络最大传输单元,为 1500;type 用于指定 ARP 模块的类型,last_rx 是最后接收的数据包时间戳;
phydev是对应的 PHY 设备。****_rx 是接收队列;_tx 是发送队列。num_rx_queues 是接收队列数量,在调用 register_netdev 注册网络设备的时候会分配指定数量的接收队列。real_num_rx_queues 是当前活动的队列数量
num_tx_queues 是发送队列数量,通过 alloc_netdev_mq 函数分配指定数量的发送。real_num_tx_queues 是当前有效的发送队列数量。
基本步骤
1、申请netdevcie一个网络设备 alloc_netdev(sizeof_priv, name**,** name_assign_type**,** setup**)**
sizeof_priv:**私有数据块大小。**name:**设备名字。**setup:**回调函数,初始化设备的设备后调用此函数。**txqs:**分配的发送队列数量。**rxqs:分配的接收队列数量。**返回值:**如果申请成功的话就返回申请到的 net_device 指针,失败的话就返回 NULL。
申请一个以太网设备 alloc_etherdev(sizeof_priv)
*xx_setup(struct net_device dev) 对net_device的初步初始化
*2、删除net_device设备 void free_netdev(struct net_device dev)
*3、向内核注册net_device int register_netdev(struct net_device dev) 0 注册成功,负值 注册失败
net_device_ops结构体
ndo_uninit 函数,卸载网络设备的时候此函数会执行。
ndo_open 函数,打开网络设备的时候此函数会执行,网络驱动程序需要实现此函数,非常重要!
ndo_stop 函数,关闭网络设备的时候此函数会执行,网络驱动程序也需要实现此函数。
*int (*ndo_open)(struct net_device dev);
int (*ndo_stop)(struct net_device dev);*
**网络数据发送函数 ** *static inline int dev_queue_xmit(struct sk_buff skb) 0 -1
驱动人员编写**netdev_tx_t (*ndo_start_xmit) (struct sk_buff skb, struct net_device dev);
sk_buff结构体
next 和 prev 分别指向下一个和前一个 sk_buff,构成一个双向链表。sk 表示当前 sk_buff 所属的 Socket。dev 表示当前 sk_buff 从哪个设备接收到或者发出的。cb 为控制缓冲区,不管哪个层都可以自由使用此缓冲区,用于放置私有数据。len 为实际的数据长度,包括主缓冲区中数据长度和分片中的数据长度。data_len为数据长度,只计算分片中数据的长度。head 指向缓冲区的头部,data 指向实际数据的头部。data 和 tail 指向实际数据的头部和尾部,head 和 end 指向缓冲区的头部和尾部。
分配sk_buff
**static inline struct sk_buff netdev_alloc_skb(struct net_device dev,unsigned int length)
释放SK_BUFF *void dev_kfree_skb (struct sk_buff skb)
尾部**扩展数据区:****unsigned char skb_put(struct sk_buff skb, unsigned int len) **返回值:**扩展出来的那一段数据区首地址。
头部扩展:**unsigned char skb_push(struct sk_buff skb, unsigned int len)
删除数据区:**unsigned char skb_pull(struct sk_buff skb, unsigned int len)
static inline void skb_reserve(struct sk_buff *skb, int len)
网络 NAPI 处理机制
不全部采用中断来读取网络数据,而是采用中断来唤醒数据接收服务程序,在接收服务程序中采用 POLL 的方法来轮询处理数据。
1、初始化NAPI: void netif_napi_add(struct net_device *dev, struct napi_struct *napi,int (*poll)(struct napi_struct *, int), int weight) dev:每个 NAPI 必须关联一个网络设备,此参数指定 NAPI 要关联的网络设备。
napi:要初始化的 NAPI 实例。poll:**NAPI 所使用的轮询函数,非常重要,一般在此轮询函数中完成网络数据接收的工作。**weight:NAPI 默认权重(weight),一般为 NAPI_POLL_WEIGHT。
2、删除NAPI void netif_napi_del(struct napi_struct *napi)
3、使能NAPI inline void napi_enable(struct napi_struct *n)
4、关闭NSPI void napi_disable(struct napi_struct *n)
5、检查是否可以调度和调度NAPI void napi_schedule**(**struct napi_struct *n)
网络设备驱动设备树
**必要属性:**compatible 一般是“fsl,-fec”, reg:SOC 网络外设寄存器地址范围。interrupts:网络中断。phy-mode:网络所使用的 PHY 接口模式,是MII 还是 RMII。
**&**fec1 {
pinctrl**-names = “default”;**
pinctrl**-**0 **= <&**pinctrl_enet1 &pinctrl_enet1_reset>;
phy**-mode = “rmii”;**
phy**-**handle **= <&**ethphy0>;
phy**-reset-**gpios **= <&gpio5 7 GPIO_ACTIVE_LOW>;
phy**-reset-**duration = <200>;
status = “okay”; };
1、驱动实验:参考Linux内核的main.c里面的peobe函数drivers\net\ethernet\freescale\fec_main.c
2、MDIO总线注册
管理PHY芯片的,分为MDIO和MDC两根线,Linux内核专门为MDIO准备了一根总线为MDIO总线,
mii_bus结构体表示MDIO总线
结构体的read和write函数就是读写PHY芯片的操作函数,of_mdiobus_register 函数有两个主要的功能,一个是通过 mdiobus_register函数向 Linux 内核,另一个就是通过 of_mdiobus_register_phy 函数向内核注册 PHY。
TSO:全称是 TCP Segmentation Offload,利用网卡对大数据包进行自动分段处理,降低 CPU
负载。
GSO:全称是 Generic Segmentation Offload,在发送数据之前先检查一下网卡是否支持 TSO,
如果支持的话就让网卡分段,不过不支持的话就由协议栈进行分段处理,分段处理完成以后再
交给网卡去发送。
Linux内核PHY子系统
使用**struct phy_device {}**表示PHY设备
注册phy_device实例 *int phy_device_register(struct phy_device phy) 0 -1
struct phy_device get_phy_device**(struct mii_bus bus,* int addr**,bool is_c45)** 获取PHY设备
PHY驱动 phy_driver
1、注册驱动:phy_driver_register(struct phy_driver *new_driver)
2、卸载PHY驱动:void phy_driver_unregister(struct phy_driver *drv)
内核通用PHY驱动drivers/net/phy/phy_device.c
块设备
块设备:以块为单位进行读写访问,块是Linux虚拟文件系统基本的数据传输单位;字符设备是以字节为单位进行数据传输的,不需要缓冲
块设备结构上是可以进行随机访问的,对于设备的读写都是按块进行的,块设备使用缓冲区来暂存数据,等条件合适将一次性将缓冲区数据写入到块设备中。
字符设备是顺序的数据流设备,字符设备是按照字节进行读写访问的。字符设备不需要缓
冲区,对于字符设备的访问都是实时的,而且也不需要按照固定的块大小进行访问。
块设备驱动框架
struct block_device结构体表示块设备,重要成员gendisk磁盘设备
1、注册块设备
int register_blkdev(unsigned int major, const char *name)
major:主设备号。
name:块设备名字。
**返回值:**如果参数 major 在 1~255 之间的话表示自定义主设备号,那么返回 0 表示注册成
功,如果返回负值的话表示注册失败。如果 major 为 0 的话表示由系统自动分配主设备号,那
么返回值就是系统分配的主设备号(1~255),如果返回负值那就表示注册失败。
2、注销块设备
void unregister_blkdev(unsigned int major, const char *name) major:要注销的块设备主设备号。
name:要注销的块设备名字。
gendisk结构体
Linux内核使用gendisk表示一个磁盘设备,这是一个结构体 struct gendisk {
1、申请gendisk struct gendisk *alloc_disk(int minors)
minors:**次设备号数量,也就是 gendisk 对应的分区数量。**返回值:成功:返回申请到的 gendisk,失败:NULL。
2、删除gendisk void del_gendisk(struct gendisk *gp)
3、添加到内核 void add_disk(struct gendisk *disk)
4、设置 gendisk 容量 void set_capacity(struct gendisk *disk, sector_t size)
disk:要设置容量的 gendisk。
size:磁盘容量大小,注意这里是扇区数量。块设备中最小的可寻址单元是扇区,一个扇区一般是 512 字节,有些设备的物理扇区可能不是 512 字节。不管物理扇区是多少,内核和块设备驱动之间的扇区都是 512 字节。所以 set_capacity 函数设置的大小就是块设备实际容量除以512 字节得到的扇区数量。比如一个 2MB 的磁盘,其扇区数量就是(210241024)/512=4096。
5、调整gendisk引用计数
truct kobject *get_disk(struct gendisk *disk) get_disk 是增加 gendisk 的引用计数
void put_disk(struct gendisk *disk) put_disk 是减少 gendisk 的引用计数
block_device_operations结构体 块设备操作集
1、请求队列 request_queue
①、初始化请求队列 **request_queue *blk_init_queue(request_fn_proc rfn, spinlock_t lock)
rfn:请求处理函数指针,每个 request_queue 都要有一个请求处理函数,请求处理函数request_fn_proc 原型如下:void (request_fn_proc) (struct request_queue *q) 请求队列会用到 I/O 调度器
请求处理函数需要驱动编写人员自行实现。
**lock:**自旋锁指针,需要驱动编写人员定义一个自旋锁,然后传递进来。 NULL
②、删除请求队列 void blk_cleanup_queue(struct request_queue *q)
blk_init_queue会被分配一个IO调度器
request_queue 申请函数 blk_alloc_queue ------->
*struct request_queue blk_alloc_queue(gfp_t gfp_mask) gfp_mask:内存分配掩码,一般为 GFP_KERNEL。
blk_alloc_queue 函数申请到的请求队列绑定一个“制造请求”函数
**void blk_queue_make_request(struct request_queue q, make_request_fn mfn)
q:需要绑定的请求队列,也就是 blk_alloc_queue 申请到的请求队列。
mfn:需要绑定的“制造”请求函数,函数原型如下:
void (make_request_fn) (struct request_queue *q, struct bio *bio) “制造请求”函数需要驱动编写人员实现。
blk_alloc_queue 和 blk_queue_make_request 是搭配在一起使用的,用于非机械的
存储设备、无需 I/O 调度器,比如 EMMC、SD 卡
2、请求request request是一个结构体,数据存储在bio的成员变量中
①、从request_queue获取请求 request *blk_peek_request(struct request_queue *q)
②、开启请求 void blk_start_request(struct request *req)
③、处理请求 struct request ***blk_fetch_request(**struct request_queue *q) 可以替换①②两步
④、其他api函数 __blk_end_request_cur(q,0)检查是不是最后一个请求
3、bio结构
每个 request 里面里面会有多个 bio,bio 保存着最终要读写的数据、地址等信息。根据IO调度构造成新的bio结构,加入到request_queue中,也可能被合并到request_queue中
struct bio{}重要成员 bvec_iter描述了要操作的设备扇区信息 bio_vec 描述了就是“page,offset,len”组合page 指定了所在的物理页,offset 表示所处页的偏移地址,len 就是数据长度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xxmxEhsD-1618219314342)(C:\Users\59966\AppData\Roaming\Typora\typora-user-images\image-20210406151542109.png)]
①、遍历请求中的bio
#define __rq_for_each_bio(_bio, rq) \
if ((rq->bio)) \
for (_bio = (rq)->bio; _bio; _bio = _bio->bi_next)
②、遍历bio中的段
#define bio_for_each_segment(bvl, bio, iter) \
__bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)
③、通知 bio 处理结束 bvoid bio_endio(struct bio *bio, int error)
kmalloc()、kzalloc()、vmalloc() 的区别是:
- kzalloc 是强制清零的 kmalloc 操作;(以下描述不区分 kmalloc 和 kzalloc)
- kmalloc 分配的内存大小有限制(128KB),而 vmalloc 没有限制;
- kmalloc 可以保证分配的内存物理地址是连续的,但是 vmalloc 不能保证;
- kmalloc 分配内存的过程可以是原子过程(使用 GFP_ATOMIC),而 vmalloc 分配内存时则可能产生阻塞;
- kmalloc 分配内存的开销小,因此 kmalloc 比 vmalloc 要快;
格式化磁盘
mkfs.vfat /dev/xxx 格式化成vfat格式
mount /dev/xxx /tmp 挂载到tmp目录下使用
MMC块设备驱动
**区块层:**block.c;一个块设备驱动程序时需要的 block_device_operations 结构体变量的定义,其中有 open/release/request 函数的实现,而 queue.c 则是对内核提供的请求队列的封装
**核心层:**核心层封装了 MMC/SD 卡的命令,例如存储卡的识别,设置,读写。例如不管什么卡都应该有一些识别,设置,和读写的命令,这些流程都是必须要有的,只是具体对于不同的卡会有一些各自特有的操作。 Core.c 文件是由 sd.c 、 mmc.c 两个文件支撑的, core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别由 sd.c 和 sd_ops.c 、 mmc.c 和 mmc_ops.c 来完成。
**主机控制层:**主机控制器则是依赖于不同的平台的,例如 s3c2410 的卡控制器和 atmel 的卡控制器必定是不一样的,所以要针对不同的控制器来实现。以 s3cmci.c 为例,它首先要进行一些设置,例如中断函数注册,全能控制器等等。然后它会向 core 层注册一个主机( host ),用结构 mmc_host_ops 描述,这样核心层就可以拿着这个 host 来操作 s3c24xx 的卡控制器了,而具体是 s3c24xx 的卡控制器还是 atmel 的卡控制器, core 层是不用知道的。
重要数据结构:
struct mmc_host 描述卡控制器
struct mmc_card 描述卡
struct mmc_driver描述mmc驱动
struct mmc_host_ops 用来描述卡控制器操作集,用于从主机控制器层向 core 层注册操作函数,从而将 core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数。
probe函数
mmc_alloc_host申请一个mmc_host
检测是哪种卡mmc_rescan
mmc_add_host添加一个mmc_host
xx_irq_cd中断机制来判断卡是否插入
queue_delayed_work函数
- 定义workqueue要做的delayed工作:struct delayed_work mdelayed_work;
- 定义workqueue: struct workqueue_struct *mworkqueue;
- 初始化workqueue:INIT_DELAYED_WORK(mworkqueue, mdelayed_work);
- 创建线程queue并加以名字:mworkqueue = create_singlethread_workqueue(“myqueue”);
- 运行queue:queue_delayed_work(mworkqueue, mdelayed_work, delay_time);
读数据都是在mmc_blk_issue_rq完成
MMC通讯协议
两种工作模式:支持MMC和SPI
eMMC系统有三个模块
eMMC设备
主机
eMMC控制器(eMMC HOST)
emmc设备里面包含了FLASH的存储单元和它的控制单元,主机可以通过eMMC HOST来访问设备,eMMC协议规定了接口的功能和设备控制器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CexADRz9-1618219314345)(C:\Users\59966\AppData\Roaming\Typora\typora-user-images\image-20210407172942097.png)]
协议规定的引脚:
**CLK:**设备的输入时钟,由主机提供信号,在时钟到来的时候才可以发送和接受命令,一个时钟周期可以出书一位的信号,在DDR模式下,可以在一个时钟周期传输2位的信号。CLK的频率在0到最大的频率之间。
**CMD:**它是双向传输的命令线,用于主机和设备的数据通信。它有开漏和推挽两种模式,分别用来应对初始化和应对快速的命令传输。它能够双向传输,当主机发送命令之后,设备会给主机应答,通过CMD线可以返回给主机。
**RESET:**单向的复位信号线,由主机发送的。
**DAT0~DAT7:**eMMC的双向数据总线,用于主机和设备的数据通信。它工作在应对快速的命令传输的推挽模式。DAT线在某一时刻只能支持单向传输,只能被设备或eMMC HOST一方控制。默认下,当用户上电或者复位的时候,仅能用DAT0一根线传输数据。用户可以自己配置想要使用的DAT线的数量,也可以选择4根或者8根。当用户选择4根时,eMMC设备会断DAT1-3的内部上拉,如果用户选择的是8根,那么同理会断开DAT1-7的上拉。
**Data Strobe:**这是数据的锁存线,它主要用于eMMC5.0提出的HS400模式下,设备要锁存输出信号。
eMMC5.0协议是向之前版本的协议兼容,在速度方面是兼容低速度模式。
eMMC5.0协议规定了5种总线数据模式,前三种速度模式可以兼容协议4.5版本之前的协议,后面两种可以支持HS200和HS400模式
eMMC设备的内部寄存器(协议规定)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSMsiqkR-1618219314347)(C:\Users\59966\AppData\Roaming\Typora\typora-user-images\image-20210407174035851.png)]
图中寄存器对应
设备识别寄存器:128位寄存器,存放设备信息
相对地址寄存器:16bits的寄存器,一旦设备被识别到,设备会被主机指定相对的地址,存在这个寄存器中,传输数据的时候,主机利用这些信息选择设备,规定这个寄存器默认的值为0X0001。如果地址变成0X0000,那么只有发送CMD7指令才可让设备处于Stand-by State才可用。
**驱动等级寄存器:**这是一个16位的寄存器,规定设备的等级,设备通过主机进行设置来增强设备的性能,
Host 与 eMMC Device 之间的通信都是由 Host 以一个 Command 开始发起的,eMMC Device 在完成 Command 所指定的任务后,则返回一个 Response。
如果 Host 发送的是 Single Block Read 的 Command,那么 eMMC Device 只会发送一个 Block 的数据
如果 Host 发送的是 Multiple Block Read 的 Command,那么 eMMC Device 会持续发送数据,直到 Host 主动发送 Stop Command。
如果 Host 发送的是 Single Block Write Command,那么 eMMC Device 只会将后续第一个 Block 的数据写入的存储器中。
如果 Host 发送的是 Multiple Block Write Command,那么 eMMC Device 会持续地将接收到的数据写入到存储器中,直到 Host 主动发送 Stop Command。
eMMC Device 在接收到一个 Block 的数据后,会进行 CRC 校验,然后将校验结果通过 CRC Token 发送给 Host。发送完 CRC Token 后,如果 CRC 校验成功,eMMC Device 会将数据写入到内部存储器时,此时 DAT0 信号会拉低,作为 Busy 信号。Host 会持续检测 DAT0 信号,直到为高电平时,才会接着发送下一个 Block 的数据。如果 CRC 校验失败,那么 eMMC Device 不会进行数据写入,此次传输后续的数据都会被忽略。
从 eMMC Device 读写数据都是按 Block 的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S56koPoy-1618219314349)(C:\Users\59966\AppData\Roaming\Typora\typora-user-images\image-20210408100759826.png)]
文章参考:https://www.pianshen.com/article/4472438159/
文章参考:https://blog.csdn.net/luopingfeng/article/details/42844611