精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

SD nand 與 SD卡的SPI模式驅動

深圳市雷龍發展有限公司 ? 2023-05-10 17:45 ? 次閱讀

文章目錄

SD nand 與 SD卡的SPI模式驅動

1. 概述

2. SPI接口模式與SD接口模式區別

2.1 接口模式區別

2.2 硬件引腳

2.3 注意事項

3. SD接口協議

3.1 命令

3.1.1 命令格式

3.1.2 命令類型

3.2 響應

3.2.1 響應格式

4. SD nand(SD卡)結構描述

5. SD nand SPI通訊

5.1 SD nand SPI 通訊概述

5.2 SPI 時序

5.3 上電初始化及模式切換

5.3.1 初始化及模式切換流程說明

5.3.2 代碼實現

5.4 識別過程

5.4.1 識別流程說明

5.4.2 代碼實現

5.3 數據傳輸

5.3.1 數據寫入

5.3.2 數據讀取

5.3.3 代碼實現

6. 總結

1. 概述

首先簡單介紹下SD卡和SD nand:

  • SD卡,也稱之為內存卡,在早些年間的手機上出現過,主要用來存儲數據;
image.php?url=YD_cnt_75_01MFhjMGyZvq

?

  • SD nand,貼片式SD卡,使用起來和SD卡一致,不同的是采用,通常采用LGA-8封裝,尺寸為8mm x 6mm x 0.75mm,重點是采用貼片封裝,可以直接貼在板卡上,直接解決了SD卡固定問題,再也不用為SD卡的接觸穩定性操心!
image.php?url=YD_cnt_75_01MFhjniH7ds

?

  • SD nand 與 SD卡除了封裝上的區別,使用起來基本沒什么不一樣,因此下文中不再做區分,統一以SD nand作為描述。
  • SD nand 和 SD 卡、SPI Nor flash、 nand flash、eeprom一樣,都是嵌入式系統中常見的用來存儲數據所使用的存儲芯片,這幾種存儲芯片主要的區別在于存儲數據容量不一樣、操作的大小不一樣,價格不一樣,因此在實際產品設計中,往往需要根據自身產品的需求來選擇對應的存儲芯片。
  • SD nand存儲空間大小在上述存儲系列芯片中屬于偏大的,其存儲空間小到 1Gb(256MB) 起步,大到可以到32G,最小讀寫單元通常是 512 Byte,與SD卡一樣,均支持SD接口模式以及SPI接口模式(后文會詳細描述其區別)。
  • 關于采用SPI接口模式完成于SD nand和SD卡的通訊,網上也有相關資料,但描述均不是很清楚或完整,因此特整理此博客,以作記錄及分享。
  • 本博文以 CS 創世 CSNPGCR01-AOW 這顆IC為例,著重描述如何通過SPI接口完成SD nand(SD卡)的讀寫驅動。
  • 2. SPI接口模式與SD接口模式區別
  • 2.1 接口模式區別
  • SD nand同時支持SPI接口和SD接口,接下來主要從以下幾個維度分析二者的區別:
  • 硬件資源角度:
  • SD接口需要控制器具有SDIO外設硬件支持
  • SPI接口如果控制器具有SPI硬件外設那就最好了,沒有也可以使用軟件模式SPI
  • 傳輸效率:
  • SD接口支持四線同時傳輸
  • SPI只有MOSI一根總線
  • 且接口速度上SD接口速度通常要大于SPI接口,因此SD效率遠高于SPI接口
  • 控制難度:
  • SPI協議比較簡單,也是嵌入式開發中最常使用的協議之一,只有MISO和MOSI兩根數據總線,因此控制難度簡單;
  • SD協議相對SPI要復雜,且需要控制的引腳多,內部還存在狀態機,相比SPI較為復雜
  • 綜上分析,SD接口效率更高,但是需要芯片有對應外設支持,而SPI接口雖然效率比不上SD接口,但是控制起來簡單,且對芯片外設硬件依賴不高,對于低端的控制器,亦可使用軟件模式SPI來驅動SD nand。
  • 2.2 硬件引腳
  • SD nand以及SD 卡在SPI接口以及SD接口模式下,硬件引腳如下圖所示:
  • SD nand SPI接口及SD接口模式IO定義
image.php?url=YD_cnt_75_01MFhjmOOIeq

  • SD卡 SPI接口及SD接口模式IO定義
image.php?url=YD_cnt_75_01MFhjnAe1r5

  • 2.3 注意事項
  • 此外對于使用SPI接口需要注意的是,SPI接口只是定義了物理傳輸層,并沒有定義完整的數據傳輸協議,因此上層軟件還是需要遵循SD接口協議!
  • 3. SD接口協議
  • 在2.3中我們重點強調了,SPI接口只是定義了物理層,也即硬件鏈路層,關于協議層并沒有定義,寫一次依舊遵循SD接口協議,因此我們需要首先了解下SD總線協議的內容。
  • SD 總線協議由SD卡協議定義,是一個通用的標準協議。首先說明的是,SD總線協議不僅僅只適用于SD卡,還支持IO卡,MMC卡等等,而且對這些不同類型的設備均能做出區分的!有點像USB一樣牛逼!
image.php?url=YD_cnt_75_01MFhjlde53u

  • 我們首先來了解下SD總線協議中的命令及響應。

3.1 命令

  • 命令由主機發出,分為廣播命令和尋址命令
  • 廣播命令是針對與SD主機連接的所有設備發出的
  • 尋址命令是指定某個地址的設備進行命令傳輸
  • 3.1.1 命令格式
  • 命令由48bit位(6字節)組成,格式如下:
image.php?url=YD_cnt_75_01MFhjkVHPOh

  • 起始位:1bit 固定為0
  • 傳輸位:1bit 主要用于區分傳輸方向,1代表主機發送給從機的命令,0代表從機響應的主機命令
  • 命令號:6bit 命令號索引,總共能表示2^6=64個命令
  • 命令參數:32bit 命令所包含的參數信息
  • CRC7:7bit CRC校驗位,用于保證數據傳輸的正確性,生成器多項式為:G(x) = x^7 + x^3 + 1
  • 3.1.2 命令類型
  • 命令主要有4種類型:
  • bc:無響應廣播命令
  • bcr:有響應廣播命令
  • ac:尋址命令,發送到選定卡,DAT線沒有數據傳輸
  • adtc:尋址數據傳輸命令,發送到選定的卡,且DAT線有數據傳輸
  • 在SD總線協議中,經常見到的CMDx,代表的就是命令號,后面的x代表命令索引,在3.1.1中命令格式組成中描述了命令號總共占6bit,所以CMDx的范圍是CMD0 - CMD63,CMD后面的數字代表的就是命令號command index的值。
  • 對于SD這么復雜的協議,64種命令類型通常還不能涵蓋所有類型的數據,因此SD協會在制定此協議的時候將命令繼續細化,分了兩種類型的命令:CMD和ACMD,CMD代表常規命令,ACMD代表特定應用的命令,ACMD通常為制造商特定使用的。
  • 那么SD協議又是如何區分CMD和ACMD命令的呢?
  • 在發送ACMD命令之前必須發送特定的CMD命令(APP_CMD)表明接下來的一幀命令是ACMD命令,在SD協議種規定此特定命令名稱叫APP_CMD,也就是CMD55。
  • 需要注意的是,CMD命令類型這么多,但實際上并沒有都使用,針對SD nand(SD卡)的命令也就那么幾條(注意SD模式命令的響應和SPI模式命令的響應有些許不同,SD模式請自行查閱手冊)
image.php?url=YD_cnt_75_01MFhjjjKI6a

?

image.php?url=YD_cnt_75_01MFhjjAmhQ6

image.php?url=YD_cnt_75_01MFhjiUI9O1

?

image.php?url=YD_cnt_75_01MFhjhsdEQ6

編輯

?

image.php?url=YD_cnt_75_01MFhjfd6uUs

編輯

?

  • 上圖中,命令序號對應3.1.1節命令格式中的命令號 command index,參數對應3.1.1節命令格式中的命令參數argument。
  • 3.2 響應
  • 針對需要響應的命令(bcr),SD nand(SD卡)在接收到命令之后會做出響應,根據命令的不同,響應的類型也不相同,其中命令中已規定哪個命令需要響應,并且返回什么類型的響應。
  • 響應總共分為7中類型,分別是R1~R7,需要注意的是,SD nand(SD卡)沒有R4、R5類型的響應。
  • 響應的數據長度也并非完全一樣,響應根據內容長度分為短響應和長響應,短響應長度為48bit(6Byte),長響應長度為136bit(17Byte),其中只有R2屬于長響應,其他均屬于短響應。
  • 3.2.1 響應格式
image.php?url=YD_cnt_75_01MFhjf0vYF7

編輯

?

image.php?url=YD_cnt_75_01MFhje9Wirm

編輯

?

image.php?url=YD_cnt_75_01MFhjcdRCYi

編輯

?

image.php?url=YD_cnt_75_01MFhjgLiq1g

編輯

?

  • 其中重點講下R1響應,在上圖中我們可以看到R1返回的內容為卡的狀態,關于卡狀態的描述如下,每個bit均代表著對應的含義,如下圖中所示:
image.php?url=YD_cnt_75_01MFhjcGiODJ

編輯

?

image.php?url=YD_cnt_75_01MFhjb5uwGs

編輯

?

  • 上圖是SD nand的內部結構,與SD卡完全類似,主要有五個部分組成,這里就不細述了,不然此篇文章會過于臃長,關于這塊大家可以上網查找,需要重點注意的是內部有7個寄存器,主要用來對卡片進行配置和讀取卡片有關的信息,描述如下,其中SD接口有些命令就指定了讀取哪個寄存器的內容!
image.php?url=YD_cnt_75_01MFhjZeyW7u

編輯

?

5. SD nand SPI通訊

  • 主要參考資料:官方文檔《Part_1_Pjysical_Layer_Specification_Ver2.0.0pdf》建議大家有時間的話也可以讀一讀,還是有收獲的,如果沒時間的話也可以先參考本博文
  • 5.1 SD nand SPI 通訊概述
  • SD nand SPI通訊接口完成驅動主要可以分為三大部分:
  • 上電初始化以及模式切換
  • SD nand(SD卡)識別
  • 數據傳輸兩大步
  • 在以上三大部分中,每個部分均有命令傳輸,從3.1.1中我們可以知道發送給SD nand的命令為48bit,也就是8字節,那么SPI模式下與SD nand通訊,發送命令其實就是采用SPI總線往SD nand傳輸8個字節的數據,大家把握這這個思路去理解下文的通訊過程也就簡單多了。
  • 需要注意的是:
  • SD nand或SD卡上電默認均為SD模式,需要對齊完成初始化以及模式切換后才能切換到SPI模式。
  • SD 模式,所有命令默認開啟CRC校驗,因此沒有切換到SPI模式之前,所有命令都必須攜帶正確的CRC校驗值
  • 進入SPI模式后,默認關閉CRC校驗,此時CRC校驗字段默認填充1即可,當然也可以通過命令配置打開SPI模式的CRC校驗
  • 5.2 SPI 時序
  • 在開始進行通訊讀寫前,我們先來看下SPI時序,使用SPI完成于SD nand(SD卡)的通訊與我們平常使用SPI與其他設備通訊會有一點點小小的區別,主要在于往SD nand寫了數據之后,回復不是馬上的,以及在必要的數據之間需要增加間隔,我們挑幾個重點看下,在實際開發中有需要注意的在后文對應處有描述,不用過于擔心。
  • 1.主機發送命令給卡,卡響應,注意圖中的NCR,NCR最小不是0,因此主機發送了命令之后,SD nand不是馬上就響應的
image.php?url=YD_cnt_75_01MFhjZ73w92

編輯

?

  • 2.卡連續響應兩個指令之間需要有間隔,如圖中的NRC
image.php?url=YD_cnt_75_01MFhjYACU75

編輯

?

  • 5.3 上電初始化及模式切換
  • 5.3.1 初始化及模式切換流程說明
  • 首先配置控制器SPI外設
  • SD nand(SD卡)電源應該在250ms內到大VCC,這是硬件電路要求
  • 同時保持CS引腳為高電平狀態,CLK時鐘引腳至少發送74個時鐘給SD nand已啟動SD nand
  • 之后SD nand進入空閑狀態,發送CMD0命令至SD卡切換進入SPI模式
  • 注意務必保證CMD0是第一包命令
  • SD卡選擇了對應的模式之后不可切換,如果需要重新切換,需要重新上電
image.php?url=YD_cnt_75_01MFhjXgiVaW

編輯

?

  • 5.3.2 代碼實現
  • 1.SPI外設配置代碼如下:
  • #ifndef __BSP_SPI_H__
  • #define __BSP_SPI_H__
  • #include "stm32f10x.h"
  • #define PIN_HIGH 1
  • #define PIN_LOW 0
  • int sd_spi_config(void);
  • void set_sd_spi_cs_pin(uint8_t state);
  • #endif /* __BSP_SPI_H__ */

#include "./spi/bsp_spi.h"

/**

* @brief spi gpio configuration

*

* @note CLK:PA5 MISO:PA6 MOSI:PA7 CS:PA8

*

*/

static void _spi_gpio_init(void)

{

GPIO_InitTypeDef GPIO_InitStructure = {0};

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

/* Configure SD_SPI pins: SCK */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure SD_SPI pins: MOSI */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure SD_SPI pins: MISO */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/*!< Configure SD_SPI_CS_PIN pin: SD Card CS pin */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

}

/**

* @brief configer spi1 peripher.

*

* @note Data rising edge acquisition.

*/

static void _spi_config(void)

{

SPI_InitTypeDef SPI_InitStructure = {0};

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

/*!< SD_SPI Config */

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 0;

SPI_Init(SPI1, &SPI_InitStructure);

SPI_Cmd(SPI1, ENABLE);

}

int sd_spi_config(void)

{

_spi_gpio_init();

_spi_config();

return 0;

}

void set_sd_spi_cs_pin(uint8_t state)

{

if (state)

GPIO_SetBits(GPIOA, GPIO_Pin_8);

else

GPIO_ResetBits(GPIOA, GPIO_Pin_8);

}

2.SD初始化代碼如下,set_sd_to_idle_state 函數向SD nand發送CMD0指令,同時由于發送CMD0時,SD nand還處于SD模式,因此手動計算CRC結果為0x95并發送,發送完CMD0之后等待SD nand的R1響應,并根據響應內容,知道SD nand操作完成。

#ifndef __SD_SPI_DRV_H__

#define __SD_SPI_DRV_H__

#include "stm32f10x.h"

/**

* @brief Commands: CMDxx = CMD-number | 0x40

*/

#define SD_CMD_GO_IDLE_STATE 0 /*!< CMD0 = 0x40 */

#define SD_CMD_SEND_OP_COND 1 /*!< CMD1 = 0x41 */

#define SD_CMD_SEND_IF_COND 8 /*!< CMD8 = 0x48 */

#define SD_CMD_SEND_CSD 9 /*!< CMD9 = 0x49 */

#define SD_CMD_SEND_CID 10 /*!< CMD10 = 0x4A */

#define SD_CMD_STOP_TRANSMISSION 12 /*!< CMD12 = 0x4C */

#define SD_CMD_SEND_STATUS 13 /*!< CMD13 = 0x4D */

#define SD_CMD_SET_BLOCKLEN 16 /*!< CMD16 = 0x50 */

#define SD_CMD_READ_SINGLE_BLOCK 17 /*!< CMD17 = 0x51 */

#define SD_CMD_READ_MULT_BLOCK 18 /*!< CMD18 = 0x52 */

#define SD_CMD_SET_BLOCK_COUNT 23 /*!< CMD23 = 0x57 */

#define SD_CMD_WRITE_SINGLE_BLOCK 24 /*!< CMD24 = 0x58 */

#define SD_CMD_WRITE_MULT_BLOCK 25 /*!< CMD25 = 0x59 */

#define SD_CMD_PROG_CSD 27 /*!< CMD27 = 0x5B */

#define SD_CMD_SET_WRITE_PROT 28 /*!< CMD28 = 0x5C */

#define SD_CMD_CLR_WRITE_PROT 29 /*!< CMD29 = 0x5D */

#define SD_CMD_SEND_WRITE_PROT 30 /*!< CMD30 = 0x5E */

#define SD_CMD_SD_ERASE_GRP_START 32 /*!< CMD32 = 0x60 */

#define SD_CMD_SD_ERASE_GRP_END 33 /*!< CMD33 = 0x61 */

#define SD_CMD_UNTAG_SECTOR 34 /*!< CMD34 = 0x62 */

#define SD_CMD_ERASE_GRP_START 35 /*!< CMD35 = 0x63 */

#define SD_CMD_ERASE_GRP_END 36 /*!< CMD36 = 0x64 */

#define SD_CMD_UNTAG_ERASE_GROUP 37 /*!< CMD37 = 0x65 */

#define SD_CMD_ERASE 38 /*!< CMD38 = 0x66 */

#define SD_CMD_READ_OCR 58 /*!< CMD58 */

#define SD_CMD_APP_CMD 55 /*!< CMD55 返回0x01*/

#define SD_ACMD_SD_SEND_OP_COND 41 /*!< ACMD41 返回0x00*/

typedef enum {

/**

* @brief SD reponses and error flags

*/

SD_RESPONSE_NO_ERROR = (0x00),

SD_IN_IDLE_STATE = (0x01),

SD_ERASE_RESET = (0x02),

SD_ILLEGAL_COMMAND = (0x04),

SD_COM_CRC_ERROR = (0x08),

SD_ERASE_SEQUENCE_ERROR = (0x10),

SD_ADDRESS_ERROR = (0x20),

SD_PARAMETER_ERROR = (0x40),

SD_RESPONSE_FAILURE = (0xFF),

/**

* @brief Data response error

*/

SD_DATA_OK = (0x05),

SD_DATA_CRC_ERROR = (0x0B),

SD_DATA_WRITE_ERROR = (0x0D),

SD_DATA_OTHER_ERROR = (0xFF)

} SD_ERROR;

//SD卡的類型

#define SD_TYPE_NOT_SD 0 //非SD卡

#define SD_TYPE_V1 1 //V1.0的卡

#define SD_TYPE_V2 2 //SDSC

#define SD_TYPE_V2HC 4 //SDHC

extern uint8_t SD_Type;

void sd_power_on(void);

SD_ERROR set_sd_to_idle_state(void);

SD_ERROR get_sd_card_type(void);

#endif /* __SD_SPI_DRV_H__ */

#include "./sd_nand/sd_spi_drv.h"

#include "./spi/bsp_spi.h"

#define SD_SPI SPI1

#define SD_DUMMY_BYTE 0xFF

uint8_t SD_Type = 0;

static uint8_t _spi_read_write_byte(uint8_t data)

{

while(SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_TXE) == RESET);

SPI_I2S_SendData(SD_SPI, data);

while(SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_RXNE) == RESET);

return SPI_I2S_ReceiveData(SD_SPI);

}

static void sd_send_cmd(uint8_t cmd, uint32_t arg, uint8_t crc)

{

uint8_t data[6] = {0};

/* command bit7 is always 1, bit6 is always 0, see SD manual. */

data[0] &= ~(0x80);

data[0] = cmd | 0x40;

data[1] = (uint8_t)(arg >> 24);

data[2] = (uint8_t)(arg >> 16);

data[3] = (uint8_t)(arg >> 8);

data[4] = (uint8_t)(arg);

data[5] = crc;

for (int i = 0; i < 6; i ++)

_spi_read_write_byte(data[i]);

}

static uint8_t sd_read_response(uint8_t response)

{

uint32_t repeat = 0xfff;

while (repeat --) {

if (_spi_read_write_byte(SD_DUMMY_BYTE) == response)

break;

}

if (repeat)

return SD_RESPONSE_NO_ERROR;

else

return SD_RESPONSE_FAILURE;

}

void sd_power_on(void)

{

set_sd_spi_cs_pin(PIN_HIGH);

uint32_t i = 0;

for (i = 0; i <= 9; i++) {

_spi_read_write_byte(SD_DUMMY_BYTE);

}

}

SD_ERROR set_sd_to_idle_state(void)

{

uint32_t repeat = 0xfff;

set_sd_spi_cs_pin(PIN_LOW);

sd_send_cmd(SD_CMD_GO_IDLE_STATE, 0, 0x95);

if (sd_read_response(SD_IN_IDLE_STATE)) //查詢卡是否處于空閑狀態

return SD_RESPONSE_FAILURE;

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE); //釋放卡

if (repeat == 0)

return SD_RESPONSE_FAILURE;

else

return SD_RESPONSE_NO_ERROR;

}

5.4 識別過程

SD nand的識別過程頗為復雜,需要參考下圖所示狀態機。

其復雜的原因是,隨著科技的發展,SD卡也迭代了好幾輪,但是協議需要兼容所有版本的卡,因此看上去會復雜很多。

我們采用的SD nand 型號為 CSNPGCR01-AOW,為V2.0.0的卡,且容量為1Gb,因此整體識別路線為中間那條線路。

5.4.1 識別流程說明

V2.0卡識別流程:

1.SD nand上電首先完成初始化,并發送CMD0配置為SPI模式

2.之后發送CMD8命令,讀取R7響應,判斷SD nand的版本

如果響應值為0x01則判斷為V2.0的卡(此時是這個)

如果響應值非0x01則需要進一步判斷時V1.0的卡還是MMC卡

3.發送循環指令CMD55+ACMD41,(CMD55用來表示后面的CMD41為ACMD命令),讀取R1響應,直到響應0x00表示SD 2.0卡初始化完成

4.發送CMD58命令,讀取R3響應,R3中包含OCR寄存器的值,OCR寄存器的第31位(bit30)描述了此卡類型是否為SDHC類型,根據此位判斷此卡屬于標準容量卡還是高容量卡

V1.0卡識別流程:

1.SD nand上電首先完成初始化,并發送CMD0配置為SPI模式

2.之后發送CMD8命令判斷SD nand的版本

如果響應值為0x01則判斷為V2.0的卡

如果響應值非0x01則需要進一步判斷時V1.0的卡還是MMC卡(此時是這個)

3.發送CMD58命令,并判斷響應值R3,如果沒有返回則不是SD V1.0的卡

4.發送ACMD41(argument為置0),并判斷R1響應值,直到卡空閑

image.php?url=YD_cnt_75_01MFhjTyjNWv

?

image.php?url=YD_cnt_75_01MFhjTYyl6Y

?

關于CMD8指令,此處重點說明:

CMD8命令的參數中主要包含兩個部分,Voltage Supplied(VHS)和check pattern,發送CMD8時,VHS參數應設置為主機支持的電壓范圍,我們的控制器通常是3.3V,因此此處設置為0001b; check pattern可以設置為任意值,當SD nand(SD卡)接收到此CMD8指令之后會返回R7響應,如果SD nand支持此電壓等級,SD nand會回顯 VHS 和check pattern的內容在R7中,如果SD nand不支持此電壓等級,SD nand將不會返回,并始終保持在空閑狀態。

5.4.2 代碼實現

SD nand識別代碼如下:

SD_ERROR get_sd_card_type(void)

{

uint32_t i = 0;

uint32_t count = 0xFFF;

uint8_t R7R3_Resp[4];

uint8_t R1_Resp;

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE);

set_sd_spi_cs_pin(PIN_LOW);

sd_send_cmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87);

/*!< Check if response is got or a timeout is happen */

while (( (R1_Resp = _spi_read_write_byte(SD_DUMMY_BYTE)) == 0xFF) && count) {

count--;

}

if (count == 0) {

/*!< After time out */

return 1;

}

//響應 = 0x05 非V2.0的卡

if(R1_Resp == (SD_IN_IDLE_STATE|SD_ILLEGAL_COMMAND)) {

/*----------Activates the card initialization process-----------*/

count = 0xfff;

do {

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE);

set_sd_spi_cs_pin(PIN_LOW);

/*!< 發送CMD1完成V1 版本卡的初始化 */

sd_send_cmd(SD_CMD_SEND_OP_COND, 0, 0xFF);

/*!< Wait for no error Response (R1 Format) equal to 0x00 */

if (sd_read_response(SD_RESPONSE_NO_ERROR))

break;

} while (count --);

if (count == 0) {

return 2;

}

SD_Type = SD_TYPE_V1;

//不處理MMC卡

//初始化正常

} else if (R1_Resp == 0x01) { //響應 0x01 V2.0的卡

/*!< 讀取CMD8 的R7響應 */

for (i = 0; i < 4; i++) {

R7R3_Resp[i] = _spi_read_write_byte(SD_DUMMY_BYTE);

}

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE);

set_sd_spi_cs_pin(PIN_LOW);

if(R7R3_Resp[2]==0x01 && R7R3_Resp[3]==0xAA) { //判斷該卡是否支持2.7-3.6V電壓

count = 200; //支持電壓范圍,可以操作

do { //發卡初始化指令CMD55+ACMD41

sd_send_cmd(SD_CMD_APP_CMD, 0, 0xFF); //CMD55,以強調下面的是ACMD命令

if (sd_read_response(SD_RESPONSE_NO_ERROR)) // SD_IN_IDLE_STATE

return 3; //超時返回

sd_send_cmd(SD_ACMD_SD_SEND_OP_COND, 0x40000000, 0xFF); //ACMD41命令帶HCS檢查位

if (sd_read_response(SD_RESPONSE_NO_ERROR))

break;

}while(count--);

if(count == 0)

return 4; //重試次數超時

//初始化指令完成,讀取OCR信息,CMD58

//-----------鑒別SDSC SDHC卡類型開始-----------

count = 200;

do {

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE);

set_sd_spi_cs_pin(PIN_LOW);

sd_send_cmd(SD_CMD_READ_OCR, 0, 0xFF);

if (!sd_read_response(SD_RESPONSE_NO_ERROR))

break;

} while (count--);

if(count == 0)

return 5; //重試次數超時

//響應正常,讀取R3響應

/*!< 讀取CMD58的R3響應 */

for (i = 0; i < 4; i++) {

R7R3_Resp[i] = _spi_read_write_byte(SD_DUMMY_BYTE);

}

//檢查接收到OCR中的bit30(CCS)

//CCS = 0:SDSC CCS = 1:SDHC

if(R7R3_Resp[0]&0x40) { //檢查CCS標志 {

SD_Type = SD_TYPE_V2HC;

} else {

SD_Type = SD_TYPE_V2;

}

//-----------鑒別SDSC SDHC版本卡的流程結束-----------

}

}

set_sd_spi_cs_pin(PIN_HIGH);

_spi_read_write_byte(SD_DUMMY_BYTE);

//初始化正常返回

return SD_RESPONSE_NO_ERROR;

}

5.3 數據傳輸

在完成卡識別之后,便進入了數據傳輸過程,在輸出傳輸過程內即可完成數據的讀寫操作。

SD NAND單個塊為512字節,擦除、讀寫都是以塊為單位進行的,而且SD NAND可以直接寫入,不需要先擦除才能寫入!!!牛逼Plus吧!哈哈!

5.3.1 數據寫入

數據分為單塊寫入和多塊寫入,多塊寫入可循環執行多塊寫入實現。單個塊寫入使用CMD24,多個塊寫入使用CMD25,注意此處,SD nand的操作與SD卡可能會有所不一樣,在對應位置有詳細描述。

單塊寫入步驟如下:

1.發送CMD24,讀取響應值R1,判斷卡無錯誤

2.發送寫開始指令 0xFE(SD協議中未找到此描述,此應該是SD nand所特有)

3.依次傳輸寫入數據

4.發送兩個字節的CRC校驗,由于SPI默認沒有開啟CRC,因此填充為0xFFFF

5.讀取卡的狀態判斷是否有誤,結束

image.php?url=YD_cnt_75_01MFhjQYiAAo

?

5.3.2 數據讀取

數據讀取也分為單塊讀取和多塊讀取,多塊讀取可采用循環執行單塊讀取邏輯實現。

單塊數據讀取步驟如下:

  1. 1.發送CMD17,讀取響應值R1,判斷有無錯誤
  2. 2.等待SD nand發送數據輸出開始標志 0xFE
  3. 3.依次讀取數據
  4. 4.多讀取兩位CRC值,結束
image.php?url=YD_cnt_75_01MFhjT1xbbl

編輯

?

  1. 5.3.3 代碼實現
  2. #define SD_START_DATA_SINGLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Single Block Read */
  3. #define SD_START_DATA_MULTIPLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Multiple Block Read */
  4. #define SD_START_DATA_SINGLE_BLOCK_WRITE 0xFE /*!< Data token start byte, Start Single Block Write */
  5. #define SD_START_DATA_MULTIPLE_BLOCK_WRITE 0xFD /*!< Data token start byte, Start Multiple Block Write */
  6. #define SD_STOP_DATA_MULTIPLE_BLOCK_WRITE 0xFD /*!< Data toke stop byte, Stop Multiple Block Write */
  7. SD_ERROR sd_write_block(uint8_t* pbuf, uint64_t addr, uint16_t size)
  8. {
  9. uint32_t i = 0;
  10. SD_ERROR ret = SD_RESPONSE_FAILURE;
  11. //SDHC卡塊大小固定為512,且寫命令中的地址的單位是sector
  12. if (SD_Type == SD_TYPE_V2HC) {
  13. size = 512;
  14. addr /= 512;
  15. }
  16. /*!< SD chip select low */
  17. set_sd_spi_cs_pin(PIN_LOW);
  18. /*!< Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write multiple block */
  19. sd_send_cmd(SD_CMD_WRITE_SINGLE_BLOCK, addr, 0xFF);
  20. /*!< Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
  21. if (!sd_read_response(SD_RESPONSE_NO_ERROR)) {
  22. /*!< Send a dummy byte */
  23. _spi_read_write_byte(SD_DUMMY_BYTE);
  24. /*!< Send the data token to signify the start of the data */
  25. _spi_read_write_byte(SD_START_DATA_SINGLE_BLOCK_WRITE);
  26. /*!< Write the block data to SD : write count data by block */
  27. for (i = 0; i < size; i++) {
  28. /*!< Send the pointed byte */
  29. _spi_read_write_byte(*pbuf);
  30. /*!< Point to the next location where the byte read will be saved */
  31. pbuf++;
  32. }
  33. /*!< Put CRC bytes (not really needed by us, but required by SD) */
  34. _spi_read_write_byte(SD_DUMMY_BYTE);
  35. _spi_read_write_byte(SD_DUMMY_BYTE);
  36. /*!< Read data response */
  37. if (sd_get_data_response() == SD_DATA_OK) {
  38. ret = SD_RESPONSE_NO_ERROR;
  39. }
  40. }
  41. /*!< SD chip select high */
  42. set_sd_spi_cs_pin(PIN_HIGH);
  43. /*!< Send dummy byte: 8 Clock pulses of delay */
  44. _spi_read_write_byte(SD_DUMMY_BYTE);
  45. /*!< Returns the reponse */
  46. return ret;
  47. }
  48. SD_ERROR sd_read_block(uint8_t* pbuf, uint64_t addr, uint16_t size)
  49. {
  50. uint32_t i = 0;
  51. SD_ERROR ret = SD_RESPONSE_FAILURE;
  52. //SDHC卡塊大小固定為512,且讀命令中的地址的單位是sector
  53. if (SD_Type == SD_TYPE_V2HC) {
  54. size = 512;
  55. addr /= 512;
  56. }
  57. /*!< SD chip select low */
  58. set_sd_spi_cs_pin(PIN_LOW);
  59. /*!< Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */
  60. sd_send_cmd(SD_CMD_READ_SINGLE_BLOCK, addr, 0xFF);
  61. /*!< Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */
  62. if (!sd_read_response(SD_RESPONSE_NO_ERROR)) {
  63. /*!< Now look for the data token to signify the start of the data */
  64. if (!sd_read_response(SD_START_DATA_SINGLE_BLOCK_READ)) {
  65. /*!< Read the SD block data : read NumByteToRead data */
  66. for (i = 0; i < size; i++) {
  67. /*!< Save the received data */
  68. *pbuf = _spi_read_write_byte(SD_DUMMY_BYTE);
  69. /*!< Point to the next location where the byte read will be saved */
  70. pbuf++;
  71. }
  72. /*!< Get CRC bytes (not really needed by us, but required by SD) */
  73. _spi_read_write_byte(SD_DUMMY_BYTE);
  74. _spi_read_write_byte(SD_DUMMY_BYTE);
  75. /*!< Set response value to success */
  76. ret = SD_RESPONSE_NO_ERROR;
  77. }
  78. }
  79. /*!< SD chip select high */
  80. set_sd_spi_cs_pin(PIN_HIGH);
  81. /*!< Send dummy byte: 8 Clock pulses of delay */
  82. _spi_read_write_byte(SD_DUMMY_BYTE);
  83. /*!< Returns the reponse */
  84. return ret;
  85. }此外,為了驗證以上代碼正常運行,編寫簡單測試程序進行測試,代碼如下:
  86. int main(void)
  87. {
  88. USART1_Config();
  89. LED_GPIO_Config();
  90. sd_spi_config();
  91. printf("sd card test!\n");
  92. sd_init();
  93. uint8_t tx_data[512] = {0};
  94. uint8_t rx_data[512] = {0};
  95. for (i = 0; i < 512; i ++)
  96. tx_data[i] = 512-i;
  97. sd_write_block(tx_data, 0, sizeof(tx_data));
  98. sd_read_block(rx_data, 0, sizeof(rx_data));
  99. for (i = 0; i < 512; i ++) {
  100. if (tx_data[i] != rx_data[i])
  101. break;
  102. printf("%d ", rx_data[i]);
  103. }
  104. if (i == 512) {
  105. printf("sd card 讀寫測試成功\n");
  106. } else {
  107. printf("sd card 讀寫測試失敗, i:%d\n", i);
  108. }
  109. }代碼運行如下,測試通過:
image.php?url=YD_cnt_75_01MFhjRNh8qL

  1. 6. 總結
  2. 綜上,便是關于使用SPI接口驅動SD nand的全部說明了,確實花費了不少時間整理說明,關于SD nand的驅動玩法還有很多,比如采用SD接口驅動,移植文件系統,導入日志系統等等,后續有機會有時間我也會繼續做整理分享。
  3. 希望本篇博文能幫助到你對于如何使用SPI實現SD nand的驅動也有大致清晰的了解。
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 閃存
    +關注

    關注

    16

    文章

    1777

    瀏覽量

    114809
  • FlaSh
    +關注

    關注

    10

    文章

    1621

    瀏覽量

    147747
  • SD卡
    +關注

    關注

    2

    文章

    560

    瀏覽量

    63798
  • 存儲芯片
    +關注

    關注

    11

    文章

    886

    瀏覽量

    43088
收藏 人收藏

    評論

    相關推薦

    Arduino程序:實現SD NAND(貼片sd)的讀寫功能

      單片機上傳程序的時候,有時候感覺它的rom和 ram有時直接限制了他的使用,之前使用eeprom,和sd模塊. []()   然后最近看到了出的SD NAND 就是下面這個
    發表于 11-07 17:45

    瀚海微SD NANDSD 協議(40)SPI模式CMD8的操作

    )和b)的AND。 “不匹配”是其他情況。 a) vhs中只有1位設置為“1”。 b)支持主機電源電壓。 響應 響應令牌有幾種類型。和SD模式一樣,所有的都先傳輸MSB。 在SPI
    的頭像 發表于 10-14 10:00 ?222次閱讀
    瀚海微<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>之<b class='flag-5'>SD</b> 協議(40)<b class='flag-5'>SPI</b><b class='flag-5'>模式</b>CMD8的操作

    瀚海微SD NANDSD 協議(39)SPI模式 Transaction Packets

    SPI命令分為幾類,如下圖所示。每個類都支持一組卡片函數。 ASD存儲將在兩種通信模式下支持同一組可選命令類(CSD寄存器中只有一個命令類表)。 但是,在SD存儲
    的頭像 發表于 10-12 11:11 ?251次閱讀
    瀚海微<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>之<b class='flag-5'>SD</b> 協議(39)<b class='flag-5'>SPI</b><b class='flag-5'>模式</b> Transaction Packets

    瀚海微SD NANDSD 協議(37)SPI總線保護和讀寫

    總線傳輸保護 總線上傳輸的每條SD命令都受到CRC位的保護。在SPI模式下,SD存儲提供了C
    的頭像 發表于 10-09 10:29 ?283次閱讀
    瀚海微<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>之<b class='flag-5'>SD</b> 協議(37)<b class='flag-5'>SPI</b>總線保護和讀寫

    瀚海微SD NANDSD 協議(36)SPI模式

    簡介 SPI模式由基于flash的SD存儲提供的輔助通信協議組成。 這種模式SD存儲
    的頭像 發表于 10-08 10:13 ?242次閱讀
    瀚海微<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>之<b class='flag-5'>SD</b> 協議(36)<b class='flag-5'>SPI</b><b class='flag-5'>模式</b>

    SD NAND SPI模式:如何實現低功耗運行

    最近,收到客戶反饋,使用我們SD NAND過程中,使用SPI模式,對SD完成操作后,SD沒有進入
    的頭像 發表于 09-02 11:06 ?382次閱讀
    <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b> <b class='flag-5'>SPI</b><b class='flag-5'>模式</b>:如何實現低功耗運行

    貼片式SD功能介紹【MK SD NAND

    技術與傳統SD不同,SD NAND使用貼裝式封裝,允許直接焊接至電子設備的PCB上,提供一種內置的存儲功能。
    的頭像 發表于 07-05 17:03 ?704次閱讀
    貼片式<b class='flag-5'>SD</b><b class='flag-5'>卡</b>功能介紹【MK <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>】

    SD NANDSPI NAND和eMMC的區別對比分析

    SPI NANDSD NAND和eMMC是三種不同類型的嵌入式存儲技術,它們各自具有獨特的特點和應用場景。以下是這三種存儲技術的主要區別:
    的頭像 發表于 07-02 11:31 ?1099次閱讀
    <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>、<b class='flag-5'>SPI</b> <b class='flag-5'>NAND</b>和eMMC的區別對比分析

    Verilog:【8】基于FPGA實現SD NAND FLASH的SPI協議讀寫

    NAND的兩種使用模式,分別為SD MODE 以及 SPI MODE。他們所對應的引腳定義,如下圖所示: []()   對于兩種模式的切換
    發表于 06-21 17:58

    SD、MicroSDSD NAND的性能與應用對比

    在當前豐富多元的存儲解決方案領域,SD、MicroSD以及SD NAND憑借其各自的獨特優勢和特定的使用情景,贏得了市場的廣泛認可。每種
    的頭像 發表于 06-14 15:48 ?764次閱讀
    <b class='flag-5'>SD</b><b class='flag-5'>卡</b>、MicroSD<b class='flag-5'>卡</b>和<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>的性能與應用對比

    SD NANDSPI NAND的區別

    SD NANDSPI NAND各有優缺點,適用于不同的應用場景。SD NAND提供更高的讀寫速
    的頭像 發表于 06-04 14:26 ?1792次閱讀

    SD NAND 簡介

    SD NAND是一種創新的存儲芯片,可直接貼片,又名貼片式TF、貼片式T、貼片式SD、貼片
    的頭像 發表于 05-29 16:34 ?1116次閱讀
    <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b> 簡介

    什么是SD NAND存儲芯片? SD NAND與TF的區別

    什么是SD NAND?它俗稱貼片式T,貼片式TF,貼片式SD,貼片式內存
    的頭像 發表于 01-06 14:35 ?1666次閱讀
    什么是<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>存儲芯片? <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>與TF<b class='flag-5'>卡</b>的區別

    什么是SD NAND存儲芯片?

    標準驅動代碼,省去了驅動代碼編程環節。支持TF啟動的SOC都可以用SD NAND,提供STM32參考例程及原廠技術支持,主流容量:128M
    發表于 01-05 17:54

    ESP32應用教程— SD NAND(記錄飛控LOG)

    SD 接口)。    本文選擇的是 CSNP32GCR01-AOW 芯片。   不用編寫驅動程序,自帶壞塊管理的 NAND Flash(貼片式 TF ),尺寸小巧,簡單易用,兼容性
    發表于 11-30 18:16