裝置驅動移植
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