正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1)

2022年06月26日20:57:11 熱門 1305

1)實驗平台:正點原子開拓者FPGA 開發板

2)摘自《開拓者FPGA開發指南》關注官方微信號公眾號,獲取更多資料:正點原子

3)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/thread-13912-1-1.html

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

第三十九章 SD卡讀寫測試實驗

SD存儲卡是一種基於半導體快閃記憶器的新一代記憶設備。它具有體積小、傳輸速度快、

支持熱插拔等優點,在便攜式裝置領域得到了廣泛的應用,如數碼相機、多媒體播放器、筆記

本電腦等。本章我們將使用FPGA開發板學習如何對SD卡進行讀寫操作並進行SD卡的讀寫測試實

驗。

本章包括以下幾個部分:

39.1 SD卡簡介

39.2 實驗任務

39.3 硬件設計

39.4 程序設計

39.5 下載驗證

SD卡簡介

SD卡的英文全稱是Secure Digital Card,即安全數字卡(又叫安全數碼卡),是在MMC卡

(Multimedia Card,多媒體卡)的基礎上發展而來,主要增加了兩個特色:更高的安全性和

更快的讀寫速度。SD卡和MMC卡的長度和寬度都是32mm x 24mm,不同的是,SD卡的厚度為2.1mm,

而MMC卡的厚度為1.4mm,SD卡比MMC卡略厚,以容納更大容量的存貯單元,同時SD卡比MMC卡觸

點引腳要多,且在側面多了一個寫保護開關。SD卡與MMC卡保持着向上兼容,也就是說,MMC卡

可以被新的SD設備存取,兼容性則取決於應用軟件,但SD卡卻不可以被MMC設備存取。SD卡和

MMC卡可通過卡片上面的標註進行區分,如下圖左側圖片上面標註為「MultiMediaCard」字母

樣式的為MMC卡,右側圖片上面標註為「SD」字母樣式的為SD卡。

圖 39.1.1 MMC外觀圖(左)和SD卡外觀圖(右)

上圖中右側圖片的SD卡實際上為SDHC卡,SD卡從存儲容量上分為3個級別,分別為:SD卡、

SDHC卡(Secure Digital High Capacity,高容量安全數字卡)和SDXC卡(SD eXtended Capacity,

容量擴大化的安全存儲卡)。SD卡在MMC卡的基礎上發展而來,使用FAT12/FAT16文件系統,SD

卡採用SD1.0協議規範,該協議規定了SD卡的最大存儲容量為2GB;SDHC卡是大容量存儲SD卡,

使用FAT32文件系統,SDHC卡採用SD2.0協議規範,該協議規定了SDHC卡的存儲容量範圍為2GB

至32GB;SDXC卡是新提出的標準,不同於SD卡和SDHC卡使用的FAT文件系統,SDXC卡使用exFAT

文件系統,即擴展FAT文件系統。SDXC卡採用SD3.0協議規範,該協議規定了SDXC卡的存儲容量

範圍為32GB至2TB(2048GB),一般用於中高端單反相機和高清攝像機。

下表為不同類型的SD卡採用的協議規範、容量等級及支持的文件系統。

表 39.1.1 SD卡的類型、協議規範、容量等級及支持的文件系統

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

不同協議規範的SD卡有着不同速度等級的表示方法。在SD1.0協議規範中(現在用的較少),

使用「X」表示不同的速度等級;在SD2.0協議規範中,使用SpeedClass表示不同的速度等級;

SD3.0協議規範使用UHS(Ultra High Speed)表示不同的速度等級。SD2.0規範中對SD卡的速

度等級劃分為普通卡(Class2、Class4、Class6)和高速卡(Class10);SD3.0規範對SD卡的

速度等級劃分為UHS速度等級1和3。不同等級的讀寫速度和應用如下圖所示。

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.2 SD卡不同速度等級表示法

SD卡共有9個引腳線,可工作在SDIO模式或者SPI模式。在SDIO模式下,共用到CLK、CMD、

DAT[3:0]六根信號線;在SPI模式下,共用到CS(SDIO_DAT[3])、CLK(SDIO_CLK)、MISO

(SDIO_DAT[0])、MOSI(SDIO_CMD)四根信號線。

SD卡接口定義以及各引腳功能說明如圖 39.1.3

所示。

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.3 SD卡接口定義以及各引腳功能說明

市面上除標準SD卡外,還有MicroSD卡(原名TF卡),是一種極細小的快閃存儲器卡,是

由SanDisk(閃迪)公司發明,主要用於移動手機。MicroSD卡插入適配器(Adapter)可以轉換成SD卡,其操作時序和SD卡是一樣的。MicroSD卡接口定義以及各引腳功能說明如圖 39.1.4所

示。

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.4 MicroSD卡接口定義以及各引腳功能說明

標準SD卡2.0版本中,工作時鐘頻率可以達到50Mhz,在SDIO模式下採用4位數據位寬,理

論上可以達到200Mbps(50Mx4bit)的傳輸速率;在SPI模式下採用1位數據位寬,理論上可以

達到50Mbps的傳輸速率。因此SD卡在SDIO模式下的傳輸速率更快,同時其操作時序也更複雜。

對於使用SD卡讀取音樂文件和圖片來說,SPI模式下的傳輸速度已經能夠滿足我們的需求,因

此我們本章採用SD卡的SPI模式來對SD卡進行讀寫測試。

SD卡在正常讀寫操作之前,必須先對SD卡進行初始化,SD卡的初始化過程就是向SD中寫入

命令,使其工作在預期的工作模式。在對SD卡進行讀寫操作時同樣需要先發送寫命令和讀命令,

因此SD卡的命令格式是學習SD卡的重要內容。SD卡的命令格式由6個位元組組成,發送數據時高

位在前,SD卡的寫入命令格式如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.5 SD卡命令格式

Byte1:命令字的第一個位元組為命令號(如CMD0、CMD1等),格式為「0 1 x x x x x x」。

命令號的最高位始終為0,是命令號的起始位;次高位始終為1,是命令號的發送位;低6位為

具體的命令號(如CMD55,8』d55 = 8』b0011_0111,命令號為 0 1 1 1 0 1 1 1 = 0x77)。

Byte2~Byte5:命令參數,有些命令參數是保留位,沒有定義參數的內容,保留位應設置

為0。

Byte6:前7位為CRC(循環冗餘校驗)校驗位,最後一位為停止位0。SD卡在SPI模式下默

認不開啟CRC校驗,在SDIO模式下開啟CRC校驗。也就是說在SPI模式下,CRC校驗位必須要發,但是SD卡會在讀到CRC校驗位時自動忽略它,所以校驗位全部設置為1即可。需要注意的是,SD

卡上電默認是SDIO模式,在接收SD卡返回CMD0的響應命令時,拉低片選CS,進入SPI模式。所

以在發送CMD0命令的時候,SD卡處於SDIO模式,需要開啟CRC校驗。另外CMD8的CRC校驗是始終

啟用的,也需要啟用CRC校驗。除了這兩個命令,其它命令的CRC可以不用做校驗。

SD卡的命令分為標準命令(如CMD0)和應用相關命令(如ACMD41)。ACMD命令是特殊命令,

發送方法同標準命令一樣,但是在發送應用相關命令之前,必須先發送CMD55命令,告訴SD卡

接下來的命令是應用相關命令,而非標準命令。發送完命令後,SD卡會返迴響應命令的信息,

不同的CMD命令會有不同類型的返回值,常用的返回值有R1類型、R3類型和R7類型(R7類型是

CMD8命令專用)。SD卡的常用命令說明如下表(表 39.1.2)所示。

表 39.1.2 SD卡常用命令說明

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

SD卡返回類型R1數據格式如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.6 SD卡返回類型R1數據格式

由上圖可知,SD卡返回類型R1格式共返回1個位元組,最高位固定為0,其它位分別表示對應

狀態的標誌,高電平有效。

SD卡返回類型R3數據格式如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.7 SD卡返回類型R3數據格式

由上圖可知,SD卡返回類型R3格式共返回5個位元組,首先返回的第一個位元組為前面介紹的

R1的內容,其餘位元組為OCR(Operation Conditions Register,操作條件寄存器)寄存器的內

容。

SD卡返回類型R7數據格式如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.8 SD卡返回類型R7數據格式

由上圖可知,SD卡返回類型R7格式共返回5個位元組,首先返回的第一個位元組為前面介紹的

R1的內容,其餘位元組包含SD卡操作電壓信息和校驗位元組等內容。其中電壓範圍是一個比較重要

的參數,其具體內容如下所示:

Bit[11:8]:操作電壓反饋

0:未定義

1:2.7V~3.6V

2:低電壓

4:保留位

8:保留位

其它:未定義

SD卡在正常讀寫操作之前,必須先對SD卡進行初始化,使其工作在預期的工作模式。SD卡

1.0版本協議和2.0版本協議在初始化過程中有區別,只有SD2.0版本協議的SD卡才支持CMD8命

令,所以響應此命令的SD卡可以判斷為SD2.0版本協議的卡,否則為SD1.0版本協議的SD卡或者

MMC卡;對於CMD8無響應的情況,可以發送CMD55 + ACMD41命令,如果返回0,則表示SD1.0協

議版本卡初始化成功,如果返回錯誤,則確定為MMC卡;在確定為MMC卡後,繼續向卡發送CMD1

命令,如果返回0,則MMC卡初始化成功,否則判斷為錯誤卡。

由於市面上大多採用SD2.0版本協議的SD卡,接下來我們僅介紹SD2.0版本協議的初始化流

程,以下提到的SD卡均代表基於SD2.0版本協議的SDHC卡,其詳細初始化步驟如下:

1、 SD卡完成上電後,主機FPGA先對從機SD卡發送至少74個以上的同步時鐘,在上電同

步期間,片選CS引腳和MOSI引腳必須為高電平(MOSI引腳除發送命令或數據外,其

余時刻都為高電平);

2、 拉低片選CS引腳,發送命令CMD0(0x40)複位SD卡,命令發送完成後等待SD卡返迴響應數據;

3、 SD卡返迴響應數據後,先等待8個時鐘周期再拉高片選CS信號,此時判斷返回的響應

數據。如果返回的數據為複位完成信號0x01,在接收返回信息期間片選CS為低電平,

此時SD卡進入SPI模式,並開始進行下一步,如果返回的值為其它值,則重新執行第

2步;

4、 拉低片選CS引腳,發送命令CMD8(0x48)查詢SD卡的版本號,只有SD2.0版本的卡才

支持此命令,命令發送完成後等待SD卡返迴響應數據;

5、 SD卡返迴響應數據後,先等待8個時鐘周期再拉高片選CS信號,此時判斷返回的響應

數據。如果返回的電壓範圍為4』b0001即2.7V~3.6V,說明此SD卡為2.0版本,進行下一

步,否則重新執行第4步;

6、 拉低片選CS引腳,發送命令CMD55(0x77)告訴SD卡下一次發送的命令是應用相關

命令,命令發送完成後等待SD卡返迴響應數據;

7、 SD卡返迴響應數據後,先等待8個時鐘周期再拉高片選CS信號,此時判斷返回的響應

數據。如果返回的數據為空閑信號0x01,開始進行下一步,否則重新執行第6步。

8、 拉低片選CS引腳,發送命令ACMD41(0x69)查詢SD卡是否初始化完成,命令發送完

成後等待SD卡返迴響應數據;

9、 SD卡返迴響應數據後,先等待8個時鐘周期再拉高片選CS信號,此時判斷返回的響應

數據。如果返回的數據為0x00,此時初始化完成,否則重新執行第6步。

SD卡上電及複位命令時序如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.9 SD卡上電及複位命令時序圖

至此,SD卡完成了複位以及初始化操作,進入到SPI模式的讀寫操作。需要注意的是:SD

卡在初始化的時候,SPI_CLK的時鐘頻率不能超過400KHz,在初始化完成之後,再將SPI_CLK的

時鐘頻率切換至SD卡的最大時鐘頻率。儘管目前市面上的很多SD卡支持以較快的時鐘頻率進行

初始化,為了能夠兼容更多的SD卡,在SD卡初始化的時候時鐘頻率不能超過400KHz。

SD卡讀寫一次的數據量必須為512位元組的整數倍,即對SD卡讀寫操作的最少數據量為512個

位元組。我們可以通過命令CMD16來配置單次讀寫操作的數據長度,以使每次讀寫的數據量為

(n*512)個位元組(n≥1),本次SD卡的讀寫操作使用SD卡默認配置,即單次讀寫操作的數據

量為512個位元組。

SD卡初始化完成後,即可對SD卡進行讀寫測試,SD卡的讀寫測試是先向SD卡中寫入數據,

再從SD卡中讀出數據,並驗證數據的正確性。SD卡的寫操作時序圖如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.10 SD卡寫操作時序圖

SD卡的寫操作流程如下:

1、 拉低片選CS引腳,發送命令CMD24(0x58)讀取單個數據塊,命令發送完成後等待

SD卡返迴響應數據;

2、 SD卡返回正確響應數據0x00後,等待至少8個時鐘周期,開始發送數據頭0xfe;

3、 發送完數據頭0xfe後,接下來開始發送512個位元組的數據;

4、 數據發送完成後,發送2個位元組的CRC校驗數據。由於SPI模式下不對數據進行CRC校

驗,直接發送兩個位元組的0xff即可;

5、 校驗數據發送完成後,等待SD卡響應;

6、 SD卡返迴響應數據後會進入寫忙狀態(MISO引腳為低電平),即此時不允許其它操

作。當檢測到MISO引腳為高電平時,SD卡此時退出寫忙狀態;

7、 拉高CS引腳,等待8個時鐘周期後允許進行其它操作。

SD卡的讀操作時序圖如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.11 SD卡讀操作時序圖

SD卡的讀操作流程如下:

1、 拉低片選CS引腳,發送命令CMD17(0x51)讀取單個數據塊,命令發送完成後等待

SD卡返迴響應數據;

2、 SD卡返回正確響應數據0x00後,準備開始解析SD卡返回的數據頭0xfe;

3、 解析到數據頭0xfe後,接下來接收SD卡返回的512個位元組的數據;

4、 數據解析完成後,接下來接收兩個位元組的CRC校驗值。由於SPI模式下不對數據進行

CRC校驗,可直接忽略這兩個位元組;

5、 校驗數據接收完成後,等待8個時鐘周期;

6、 拉高片選CS引腳,等待8個時鐘周期後允許進行其它操作。

在前面介紹的SD卡讀寫操作中,使用的是SD卡的SPI模式,即採用SPI協議進行讀寫操作。

SPI和IIC都是芯片上常用的通信協議,SPI相比於IIC具有更高的通信速率,但同時佔用更多的

引腳線,接下來我們了解一下SPI的協議及傳輸時序。

SPI(Serial Peripheral interface)是由摩托羅拉公司定義的一種串行外圍設備接口,

是一種高速、全雙工、同步的通信總線,只需要四根信號線即可,節約引腳,同時有利於PCB

的布局。正是出於這種簡單易用的特性,現在越來越多的芯片集成了SPI通信協議,如FLASH、

AD轉換器等。

SPI的通信原理比較簡單,它以主從方式工作,通常有一個主設備(此處指FPGA)和一個或多個從設備(此處指SD卡)。SPI通信需要四根線,分別為SPI_CS、SPI_CLK、SPI_MOSI和

SPI_MISO。其中SPI_CS、SPI_CLK和SPI_MOSI由主機輸出給從機,而SPI_MISO由從機輸出給主

機。SPI_CS用於控制芯片是否被選中,也就是說只有片選信號有效時(對於SD卡來說是低電平

有效),對芯片的操作才有效;SPI_CLK是由主機產生的同步時鐘,用於同步數據;SPI_MOSI

和SPI_MISO是主機發送和接收的數據腳。

一般而言,SPI通信有4種不同的模式,不同的從設備在出廠時被廠家配置為其中一種模式,

模式是不允許用戶修改的。主設備和從設備必須在同一模式下進行通信,否則數據會接收錯誤。

SPI的通信模式是由CPOL(時鐘極性)和CPHA(時鐘相位)來決定的,四種通信模式如下:

模式0:CPOL = 0,CPHA = 0;

模式1:CPOL = 0,CPHA = 1;

模式2:CPOL = 1,CPHA = 0;

模式3:CPOL = 1,CPHA = 1。

CPOL控制着SPI_CLK的時鐘極性,時鐘極性變化如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.1.12 SPI_CLK時鐘極性

由上圖可知,當CPOL = 1時,SPI_CLK在空閑時為高電平,發起通信後的第一個時鐘沿為

下降沿;CPOL = 0時,SPI時鐘信號SPI_CLK空閑時為低電平,發起通信後的第一個時鐘沿為上

升沿。

CPHA用於控制數據與時鐘的對齊模式,其不同模式下的時序圖如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.1.13 不同CPHA模式下的時序圖

由上圖可知,當CPHA = 1時,時鐘的第一個變化沿(上升沿或者下降沿)數據開始改變,

那麼也就意味着時鐘的第2個變化沿(與第一個變化沿相反)鎖存數據;當CPHA = 0時,數據

在時鐘的第一個變化沿之前就已經改變,並且保持穩定,也就意味着在時鐘的第一個變化沿鎖

存數據。

對於SD卡的SPI模式而言,採用的SPI的通信模式為模式3,即CPOL = 1,CPHA = 1,在SD

卡2.0版本協議中,SPI_CLK時鐘頻率可達50Mhz。

以上是SD卡簡介部分的全部內容,在這裡還需要補充下FAT文件系統的知識。如果對SD卡

的讀寫測試像EEPROM一樣僅僅是寫數據,讀數據並驗證數據的正確性的話,是不需要FAT文件

系統的。而SD卡經常被用來在Windows操作系統上存取數據,必須使用Windows操作系統支持的

FAT文件系統才能在電腦上正常使用。

FAT(File Allocation Table,文件分配表)是Windows操作系統所使用的一種文件系統,

它的發展過程經歷了FAT12、FAT16、FAT32三個階段。FAT文件系統用「簇」作為數據單元,一

個「簇」由一組連續的扇區組成,而一個扇區由512個位元組組成。簇所包含的扇區數必須是2的

整數次冪,其扇區個數最大為64,即32KB(512Byte * 64 = 32KB)。所有的簇從2開始進行編

號,每個簇都有一個自己的地址編號,用戶文件和目錄都存儲在簇中。

FAT文件系統的基本結構依次為:分區引導記錄、文件分配表(FAT表1和FAT表2)、根目

錄和數據區。分區引導記錄:分區引導記錄區通常佔用分區的第一個扇區,共512個位元組。包含四部分

內容:BIOS參數記錄塊BPB(BIOS Parameter Block)、磁盤標誌記錄表、分區引導記錄代碼

區和結束標誌0x55AA。

文件分配表(FAT表1和FAT表2):文件在磁盤上以簇為單位存儲,但是同一個文件的數據

並不一定完整地存放在磁盤的一個連續的區域內,往往會分成若干簇,FAT表就是記錄文件存

儲中簇與簇之間連接的信息,這就是文件的鏈式存儲。對於FAT16文件系統來說,每個簇用16Bit

來表示文件分配表,而對於FAT32文件系統,使用32Bit來表示文件分配表,這是兩者之間的最

重要區別。

根目錄:根目錄是文件或者目錄的首簇號。在FAT32文件系統中,不再對根目錄的位置做

硬性規定,可以存儲在分區內可尋址的任意簇內。不過通常根目錄是最早建立的(格式化就生

成了)目錄表,所以我們看到的情況基本上都是根目錄首簇緊鄰FAT2,占簇區順序上的第1個簇

(即2號簇)。

數據區:數據區緊跟在根目錄後面,是文件等數據存放的地方,佔用大部分的磁盤空間。

實驗任務

本節實驗任務是使用FPGA開發板向SD卡指定的扇區地址中寫入512個位元組的數據,寫完後

將數據讀出,並驗證數據是否正確。

硬件設計

我們的開拓者FPGA開發板上有一個SD卡插槽,用於插入SD卡,其原理圖如圖 39.3.1所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.3.1 SD卡接口原理圖

由上圖可知,在SD卡的SPI模式下,只用到了SDIO_D3(SPI_CS)、SDIO_CMD(SPI_MOSI)、

SDIO_SCK(SPI_SCK)和SDIO_D0(SPI_MISO)引腳,而其它兩個引腳是在SD卡的SDIO模式下用

到的。

本實驗中,各端口信號的管腳分配如下表所示。

表 39.3.1 SD卡讀寫測試實驗管腳分配

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

程序設計

通過前面介紹的SD卡初始化、寫操作以及讀操作可知,SD卡的這個三個操作是相互獨立且

不能同時進行的,因此我們可以將SD卡的初始化、寫操作以及讀操作分別劃分為三個獨立的模

塊,最後將這三個模塊例化在SD卡的控制器模塊中,便於在其它工程項目中使用。圖 39.4.1

是本章實驗的系統框圖,PLL時鐘模塊(PLL)為各個模塊提供驅動時鐘,SD卡測試數據產生模

塊產生測試數據寫入SD卡,寫完後從SD卡中讀出數據,最終讀寫測試結果由LED顯示模塊通過

控制LED燈的顯示狀態來指示。

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.4.1 SD卡讀寫測試系統框圖

頂層模塊的原理圖如下圖所示:

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞


圖 39.4.2 頂層模塊原理圖

由上圖可知,SD卡測試數據產生模塊產生的開始寫入信號(wr_start_en)及數據(wr_data)

連接至SD卡控制器模塊,數據寫完後輸出開始讀出信號(rd_start_en)即可從SD卡控制器中

讀出數據(rd_data),數據測試的結果error_flag連接至LED顯示模塊,完成各模塊之間的數

據交互。

FPGA頂層模塊(top_sd_rw)例化了以下四個模塊:PLL時鐘模塊(pll_clk)、SD卡測試

數據產生模塊(data_gen)、SD卡控制器模塊(sd_ctrl_top)和LED顯示模塊(led_alarm)。

頂層模塊(top_sd_rw):頂層模塊完成了對其它四個模塊的例化,SD卡測試數據產生模

塊產生的開始寫入信號及數據連接至SD卡控制器模塊,數據寫完後從SD卡控制器中讀出數據,

並驗證數據的正確性,將驗證的結果連接至LED顯示模塊。

PLL時鐘模塊(pll_clk):PLL時鐘模塊通過調用鎖相環(PLL)IP核來實現,總共輸出兩

個時鐘,頻率都是50Mhz,但兩個時鐘相位相差180度。我們知道,SD卡的SPI通信模式為CPOL=1,

CPHA=1;即SPI_CLK在空閑時為高電平,數據發送是在時鐘的第一個邊沿,也就是SPI_CLK由高

電平到低電平的跳變,所以數據採集是在上升沿,數據發送是在下降沿。為了在程序代碼中統

一使用上升沿,我們使用兩個相位相差180度的時鐘來對SD卡進行操作。

SD卡測試數據產生模塊(data_gen):SD卡測試數據產生模塊產生的開始寫入信號和數據

寫入SD卡控制器模塊中,數據寫完後從SD卡控制器中讀出數據,並驗證數據的正確性,將驗證

的結果發送給LED顯示模塊。

SD卡控制器模塊(sd_ctrl_top):SD卡控制器模塊例化了SD卡初始化模塊(sd_init)、

SD卡寫數據模塊(sd_write)和SD卡讀數據模塊(sd_read)。SD卡初始化模塊完成對SD卡的

上電初始化操作;SD卡寫數據模塊完成對SD卡的寫操作;SD卡讀數據模塊完成對SD卡的讀操作。

由於這三個模塊都操作了SD卡的引腳信號,且這三個模塊在同一時間內不會同時操作,所以此

模塊實現了對其它三個模塊的例化以及選擇SD卡的引腳連接至其中某一個模塊。

LED顯示模塊(led_alarm):LED顯示模塊將SD卡測試數據產生模塊輸出的驗證結果值,

通過控制LED燈的顯示狀態來指示。

頂層模塊的代碼如下:

1 module top_sd_rw(

2 input sys_clk , //系統時鐘

3 input sys_rst_n , //系統複位,低電平有效

4

5 //SD卡接口

6 input sd_miso , //SD卡SPI串行輸入數據信號

7 output sd_clk , //SD卡SPI時鐘信號

8 output sd_cs , //SD卡SPI片選信號

9 output sd_mosi , //SD卡SPI串行輸出數據信號

10 //LED

11 output [3:0] led //LED燈

12 );

13

14 //wire define

15 wire clk_ref ;

16 wire clk_ref_180deg ;

17 wire rst_n ;

18 wire locked ;

19

20 wire wr_start_en ; //開始寫SD卡數據信號

21 wire [31:0] wr_sec_addr ; //寫數據扇區地址

22 wire [15:0] wr_data ; //寫數據

23 wire rd_start_en ; //開始寫SD卡數據信號

24 wire [31:0] rd_sec_addr ; //讀數據扇區地址

25 wire error_flag ; //SD卡讀寫錯誤的標誌

26

27 wire wr_busy ; //寫數據忙信號

28 wire wr_req ; //寫數據請求信號

29 wire rd_busy ; //讀忙信號

30 wire rd_val_en ; //數據讀取有效使能信號

31 wire [15:0] rd_val_data ; //讀數據

32 wire sd_init_done ; //SD卡初始化完成信號

33

34 //*****************************************************

35 //** main code

36 //*****************************************************

37

38 assign rst_n = sys_rst_n & locked;

39

40 //鎖相環

41 pll_clk u_pll_clk(

42 .areset (1'b0 ),

43 .inclk0 (sys_clk ),

44 .c0 (clk_ref ),

45 .c1 (clk_ref_180deg),

46 .locked (locked )

47 );

48

49 //產生SD卡測試數據

50 data_gen u_data_gen(

51 .clk (clk_ref),

52 .rst_n (rst_n),

53 .sd_init_done (sd_init_done),

54 .wr_busy (wr_busy),

55 .wr_req (wr_req),

56 .wr_start_en (wr_start_en),

57 .wr_sec_addr (wr_sec_addr),

58 .wr_data (wr_data),

59 .rd_val_en (rd_val_en),

60 .rd_val_data (rd_val_data),

61 .rd_start_en (rd_start_en),

62 .rd_sec_addr (rd_sec_addr),

63 .error_flag (error_flag)

64 );

65

66 //SD卡頂層控制模塊

67 sd_ctrl_top u_sd_ctrl_top(

68 .clk_ref (clk_ref),

69 .clk_ref_180deg (clk_ref_180deg),

70 .rst_n (rst_n),

71 //SD卡接口

72 .sd_miso (sd_miso),

73 .sd_clk (sd_clk),

74 .sd_cs (sd_cs),

75 .sd_mosi (sd_mosi),

76 //用戶寫SD卡接口

77 .wr_start_en (wr_start_en),

78 .wr_sec_addr (wr_sec_addr),

79 .wr_data (wr_data),

80 .wr_busy (wr_busy),

81 .wr_req (wr_req),

82 //用戶讀SD卡接口

83 .rd_start_en (rd_start_en),

84 .rd_sec_addr (rd_sec_addr),

85 .rd_busy (rd_busy),

86 .rd_val_en (rd_val_en),

87 .rd_val_data (rd_val_data),

88

89 .sd_init_done (sd_init_done)

90 );

91

92 //led警示

93 led_alarm #(

94 .L_TIME (25'd25_000_000)

95 )

96 u_led_alarm(

97 .clk (clk_ref),

98 .rst_n (rst_n),

99 .led (led),

100 .error_flag (error_flag)

101 );

102

103 endmodule

SD卡控制器模塊輸出的sd_init_done(SD卡初始化完成信號)連接至SD卡測試數據產生模

塊,只有在SD卡初始化完成之後(sd_init_done為高電平),才能對SD卡進行讀寫測試。SD卡

控制器模塊將SD卡的初始化以及讀寫操作封裝成方便用戶調用的接口,SD卡測試數據產生模塊

只需對SD卡控制器模塊的用戶接口進行操作即可完成對SD卡的讀寫操作。

在代碼的第94行定義了一個參數(L_TIME),用於在讀寫測試錯誤時控制LED閃爍的時間,

其單位是1個時鐘周期。因為輸入的時鐘頻率為50Mhz,周期為20ns,所以20 * 25'd25_000_000

= 500ms,因此LED燈在讀寫錯誤時每500ms閃爍一次。

SD卡控制器模塊的代碼如下:

1 module sd_ctrl_top(

2 input clk_ref , //時鐘信號

3 input clk_ref_180deg, //時鐘信號,與clk_ref相位相差180度

4 input rst_n , //複位信號,低電平有效

5 //SD卡接口

6 input sd_miso , //SD卡SPI串行輸入數據信號

7 output sd_clk , //SD卡SPI時鐘信號

8 output reg sd_cs , //SD卡SPI片選信號

9 output reg sd_mosi , //SD卡SPI串行輸出數據信號

10 //用戶寫SD卡接口

11 input wr_start_en , //開始寫SD卡數據信號

12 input [31:0] wr_sec_addr , //寫數據扇區地址

13 input [15:0] wr_data , //寫數據

14 output wr_busy , //寫數據忙信號

15 output wr_req , //寫數據請求信號

16 //用戶讀SD卡接口

17 input rd_start_en , //開始讀SD卡數據信號

18 input [31:0] rd_sec_addr , //讀數據扇區地址

19 output rd_busy , //讀數據忙信號

20 output rd_val_en , //讀數據有效信號

21 output [15:0] rd_val_data , //讀數據

22

23 output sd_init_done //SD卡初始化完成信號

24 );

25

26 //wire define

27 wire init_sd_clk ; //初始化SD卡時的低速時鐘

28 wire init_sd_cs ; //初始化模塊SD片選信號

29 wire init_sd_mosi ; //初始化模塊SD數據輸出信號

30 wire wr_sd_cs ; //寫數據模塊SD片選信號

31 wire wr_sd_mosi ; //寫數據模塊SD數據輸出信號

32 wire rd_sd_cs ; //讀數據模塊SD片選信號

33 wire rd_sd_mosi ; //讀數據模塊SD數據輸出信號

34

35 //*****************************************************

36 //** main code

37 //*****************************************************

38

39 //SD卡的SPI_CLK

40 assign sd_clk = (sd_init_done==1'b0) ? init_sd_clk : clk_ref_180deg;

41

42 //SD卡接口信號選擇

43 always @(*) begin

44 //SD卡初始化完成之前,端口信號和初始化模塊信號相連

45 if(sd_init_done == 1'b0) begin

46 sd_cs = init_sd_cs;

47 sd_mosi = init_sd_mosi;

48 end

49 else if(wr_busy) begin

50 sd_cs = wr_sd_cs;

51 sd_mosi = wr_sd_mosi;

52 end

53 else if(rd_busy) begin

54 sd_cs = rd_sd_cs;

55 sd_mosi = rd_sd_mosi;

56 end

57 else begin

58 sd_cs = 1'b1;

59 sd_mosi = 1'b1;

60 end

61 end

62

63 //SD卡初始化

64 sd_init u_sd_init(

65 .clk_ref (clk_ref),

66 .rst_n (rst_n),

67

68 .sd_miso (sd_miso),

69 .sd_clk (init_sd_clk),

70 .sd_cs (init_sd_cs),

71 .sd_mosi (init_sd_mosi),

72

73 .sd_init_done (sd_init_done)

74 );

75

76 //SD卡寫數據

77 sd_write u_sd_write(

78 .clk_ref (clk_ref),

79 .clk_ref_180deg (clk_ref_180deg),

80 .rst_n (rst_n),

81

82 .sd_miso (sd_miso),

83 .sd_cs (wr_sd_cs),

84 .sd_mosi (wr_sd_mosi),

85 //SD卡初始化完成之後響應寫操作

86 .wr_start_en (wr_start_en & sd_init_done),

87 .wr_sec_addr (wr_sec_addr),

88 .wr_data (wr_data),

89 .wr_busy (wr_busy),

90 .wr_req (wr_req)

91 );

92

93 //SD卡讀數據

94 sd_read u_sd_read(

95 .clk_ref (clk_ref),

96 .clk_ref_180deg (clk_ref_180deg),

97 .rst_n (rst_n),

98

99 .sd_miso (sd_miso),

100 .sd_cs (rd_sd_cs),

101 .sd_mosi (rd_sd_mosi),

102 //SD卡初始化完成之後響應讀操作

103 .rd_start_en (rd_start_en & sd_init_done),

104 .rd_sec_addr (rd_sec_addr),

105 .rd_busy (rd_busy),

106 .rd_val_en (rd_val_en),

107 .rd_val_data (rd_val_data)

108 );

109

110 endmodule

SD卡控制器模塊例化了SD卡初始化模塊(sd_init)、SD卡寫數據模塊(sd_write)和SD

卡讀數據模塊(sd_read)。由於這三個模塊都驅動着SD卡的引腳,因此在代碼的第42行開始

的always塊中,用於選擇哪一個模塊連接至SD卡的引腳。在代碼的第40行,init_sd_clk用於

初始化SD卡時提供較慢的時鐘,在SD卡初始化完成之後,再將較快的時鐘clk_ref_180deg賦值

給sd_clk。sd_clk從上電之後,是一直都有時鐘的,而我們在前面說過SPI_CLK的時鐘在空閑

時為高電平或者低電平。事實上,為了簡化設計,sd_clk在空閑時提供時鐘也是可以的,其是

否有效主要由片選信號來控制。

在這裡主要介紹下SD卡控制器模塊的使用方法。當外部需要對SD卡進行讀寫操作時,首先

要判斷sd_init_done(SD卡初始化完成)信號,該信號拉高之後才能對SD卡進行讀寫操作;在

對SD卡進行寫操作時,只需給出wr_start_en(開始寫SD卡數據信號)和wr_sec_addr(寫數據

扇區地址),此時SD卡控制器模塊會拉高wr_busy信號,開始對SD卡發起寫入命令;在命令發

起成功後SD卡控制器模塊會輸出wr_req(寫數據請求)信號,此時我們給出wr_data(寫數據)

即可將數據寫入SD卡中;待所有數據寫入完成後,wr_busy信號拉低,即可再次發起讀寫操作。

SD卡的讀操作是給出rd_start_en(rd_start_en)和rd_sec_addr(讀數據扇區地址),此時

SD卡控制器會拉高rd_busy(讀數據忙)信號,開始對SD卡發起讀出命令;在命令發起成功後

SD卡控制器模塊會輸出rd_val_en(讀數據有效)信號和rd_val_data(讀數據),待所有數據

讀完之後,拉低rd_busy信號。需要注意的是,SD卡單次寫入和讀出的數據量為512個位元組,因

為接口封裝為16位數據,所以單次讀寫操作會有256個16位數據。

SD卡初始化模塊完成對SD卡的上電初始化操作,我們在SD卡的簡介部分已經詳細的介紹了

SD卡的初始化流程,我們只需要按照SD卡的初始化步驟即可完成SD卡的初始化。由SD卡的初始

化流程可知,其步驟非常適合狀態機編寫,其狀態跳轉圖如圖 39.4.3所示。

正點原子開拓者FPGA開發板資料連載第39章SD卡讀寫測試實驗(1) - 天天要聞

圖 39.4.3 SD卡初始化狀態跳轉圖

由上圖可知,我們把SD卡初始化過程定義為7個狀態,分別為st_idle(初始狀態)、

st_send_cmd0(發送軟件複位命令)、st_wait_cmd0(等待SD卡響應)、st_send_cmd8(發送

CMD8命令)、st_send_cmd55(發送CMD55命令)、st_send_acmd41(發送ACMD41命令)以及

st_init_done(SD卡初始化完成)。因為SD卡的初始化只需要上電後執行一次,所以在初始化

完成之後,狀態機一直處於st_init_done狀態。

SD卡初始化模塊的部分代碼如下所示:

112 //接收sd卡返回的響應數據

113 //在div_clk_180deg(sd_clk)的上升沿鎖存數據

114 always @(posedge div_clk_180deg or negedge rst_n) begin

115 if(!rst_n) begin

116 res_en <= 1'b0;

117 res_data <= 48'd0;

118 res_flag <= 1'b0;

119 res_bit_cnt <= 6'd0;

120 end

121 else begin

122 //sd_miso = 0 開始接收響應數據

123 if(sd_miso == 1'b0 && res_flag == 1'b0) begin

124 res_flag <= 1'b1;

125 res_data <= {res_data[46:0],sd_miso};

126 res_bit_cnt <= res_bit_cnt + 6'd1;

127 res_en <= 1'b0;

128 end

129 else if(res_flag) begin

130 //R1返回1個位元組,R3 R7返回5個位元組

131 //在這裡統一按照6個位元組來接收,多出的1個位元組為NOP(8個時鐘周期的延時)

132 res_data <= {res_data[46:0],sd_miso};

133 res_bit_cnt <= res_bit_cnt + 6'd1;

134 if(res_bit_cnt == 6'd47) begin

135 res_flag <= 1'b0;

136 res_bit_cnt <= 6'd0;

137 res_en <= 1'b1;

138 end

139 end

140 else

141 res_en <= 1'b0;

142 end

143 end

熱門分類資訊推薦

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO - 天天要聞

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO雖然說《愛情公寓》這部劇在劇情上充滿了爭議,但是一定程度上,這部劇也是很多人的回憶,是伴隨了一代人的青春回憶,而且劇中的很多角色都成為了經典,他們的口頭禪也一直被拿來玩兒梗。
Lisa榕做主持多年沒紅,被陳赫拉進愛情公寓爆紅,如今怎樣了 - 天天要聞

Lisa榕做主持多年沒紅,被陳赫拉進愛情公寓爆紅,如今怎樣了

談到《愛情公寓》這部火爆一時的歡樂喜劇,大家肯定都不陌生。不知道大家是否還記得《愛情公寓》中那個把曾小賢治得服服帖帖的女上司Lisa榕,現實中的她名叫榕榕,和劇中的形象也判若兩人。1981年出生在遼寧瀋陽的榕榕,畢業於上海戲劇學院,後來成為了上海東方傳媒集團有限公司的一名主持人。