日韩一区二区三区精品,欧美疯狂xxxxbbbb牲交,热99re久久免费视精品频,人妻互换 综合,欧美激情肉欲高潮视频

歷史上的今天

今天是:2025年07月19日(星期六)

2018年07月19日 | STM32 TFT學(xué)習(xí)筆記——SD卡讀寫

發(fā)布者:淺唱夢(mèng)幻 來源: eefocus關(guān)鍵字:STM32  TFT  SD卡讀寫 手機(jī)看文章 掃描二維碼
隨時(shí)隨地手機(jī)看文章

主機(jī)環(huán)境:Windows 7 SP1

開發(fā)環(huán)境:MDK5.14

目標(biāo)板:ST NUCLEO-F303RE

TFT型號(hào):2.4英寸,帶觸摸,SD卡,240*320分辨率,26萬色

驅(qū)動(dòng)IC:ILI9325

ST庫版本:STM32Cube_FW_F3_V1.1.0

SD卡:Kingston 16GB Micro SDHC Class 10


本TFT模塊是帶有SD卡插槽的,之前連線一直沒接,現(xiàn)在可以使用了,對(duì)于該TFT模塊來說一副圖片需要的空間為150K(240*320*2),如果圖片都存入FALSH空間肯定是存放不了多少圖片資源的,因此我們可以把圖片資源存入SD卡中等到需要時(shí)再從SD卡讀取來顯示,這樣只要SD卡空間夠大我們就可以顯示很多圖片資源了,因此來研究看SD卡的讀寫吧,這里是沒有為SD卡做文件系統(tǒng)的,那個(gè)是以后的事情了。

對(duì)于SD卡的操作跟LCD類似,EVAL的固件庫里面同樣有寫好了的SD庫函數(shù)stm32303e_eval_sd.c/h,如下


基于SD卡資料文檔配合庫函數(shù)就可以完成我們SD卡的操作了,由于NUCLEO-F303RE的SPI1接口中的MOSI腳被用于LED2的操作,因此這里使用IO口模擬SPI通信來對(duì)SD卡進(jìn)行操作,接口聲明如下


#define SPI_SDCARD_SCK_GPIO_CLK_ENABLE() __GPIOC_CLK_ENABLE()

#define SPI_SDCARD_MISO_GPIO_CLK_ENABLE() __GPIOC_CLK_ENABLE()

#define SPI_SDCARD_MOSI_GPIO_CLK_ENABLE() __GPIOC_CLK_ENABLE()

 

#define SPI_SDCARD_SCK_PIN                 GPIO_PIN_6

#define SPI_SDCARD_SCK_GPIO_PORT           GPIOC

#define SPI_SDCARD_MISO_PIN                 GPIO_PIN_8

#define SPI_SDCARD_MISO_GPIO_PORT           GPIOC

 

#define SPI_SDCARD_MOSI_PIN                 GPIO_PIN_5

#define SPI_SDCARD_MOSI_GPIO_PORT           GPIOC

 

 

#define SPI_SDCARD_nCS_PIN GPIO_PIN_9

#define SPI_SDCARD_nCS_GPIO_PORT GPIOC

 

#define SPI_SDCARD_nCS_Set_Low() (GPIOC->BRR = GPIO_PIN_9)

#define SPI_SDCARD_nCS_Set_High() (GPIOC->BSRRL = GPIO_PIN_9)

 

#define SPI_SDCARD_SCK_Set_Low() {__NOP();__NOP(); \

__NOP();__NOP(); \

GPIOC->BRR = GPIO_PIN_6; \

__NOP();__NOP(); \

__NOP();__NOP();}

 

#define SPI_SDCARD_SCK_Set_High() {__NOP();__NOP(); \

__NOP();__NOP(); \

GPIOC->BSRRL = GPIO_PIN_6; \

__NOP();__NOP();}

 

#define SPI_SDCARD_MOSI_Set_Low() (GPIOC->BRR = GPIO_PIN_5)

#define SPI_SDCARD_MOSI_Set_High() (GPIOC->BSRRL = GPIO_PIN_5)

 

#define SPI_SDCARD_MISO_Read() (GPIOC->IDR & GPIO_PIN_8)

SD卡是有9個(gè)引腳的,分配如下


當(dāng)使用SPI模式時(shí)我們只用到了4個(gè)引腳,CLK、CS、DataIn、DataOut


SD里面是有一個(gè)接口控制器的,如下圖


接口里面有7個(gè)寄存器CID、RCA、SCD、SCR、OCR、CSR(Card Status)、SSR(SD Status)。


但是這些寄存器在不同的模式下有些許不同,大體上是一致的,SPI模式下RCA寄存器是不支持的。上面5個(gè)寄存器的內(nèi)容有很多,也很重要,文檔提了一大坨,用到時(shí)再來看吧。

SD卡系統(tǒng)特性如下


時(shí)鐘限制在0~25MHz,電壓限制在2.0~3.6V,一般是使用3.3V供電電壓。在使用SPI通信時(shí)數(shù)據(jù)在CLK的上升沿鎖存,CLK空閑狀態(tài)是低電平,MOSI在空閑狀態(tài)下為1


SDCard的通訊協(xié)議分三塊Command、Response、Data-block,形式如下:


發(fā)送一個(gè)命令后SD卡的控制器回返回一個(gè)response,之后傳輸數(shù)據(jù)且?guī)в?a href="http://www.weightgang.cn/zhuanti/LS4GuD" style="color:#4595e6;" target="_blank">CRC校驗(yàn)碼。但是SPI模式下CRC校驗(yàn)?zāi)J(rèn)是禁止的。

SD卡上電初始化時(shí)序


上電時(shí)需要74個(gè)時(shí)鐘周期來穩(wěn)定。SD卡在上電后默認(rèn)是SD模式,要想使用SPI模式需要發(fā)送命令給SD卡的控制器來進(jìn)行模式切換


即上電之后需要發(fā)送CMD0命令來實(shí)現(xiàn)模式的切換,文中提到雖然SPI模式下CRC校驗(yàn)是禁止的,但是CMD0命令的發(fā)送還是要帶上正確的7位CRC校驗(yàn)值0x4A,畢竟此時(shí)還不是SPI模式,發(fā)送CMD0命令完畢后,SD卡會(huì)進(jìn)入SPI模式同時(shí)會(huì)返回一個(gè)R1 response(0x01)。SD卡是支持單block的讀寫和多blocks讀寫。之前已經(jīng)看到了單block的讀操作,現(xiàn)在看以下多block的讀操作:


多block讀和單block讀基本相似,唯一不同的是多block讀在最后需要發(fā)送一個(gè)Stop命令(CMD12),再來看以下寫操作


寫操作與讀操作類似,但是讀操作的頻率是大于寫操作的頻率,因?yàn)閿?shù)據(jù)一般都在SD卡中寫好了的。寫操作需要注意的是數(shù)據(jù)寫完之后MCU需要去發(fā)送SEND_STATUS命令CMD13來檢測數(shù)據(jù)寫入是否正確。此外還有讀取CID/CSD寄存器命令,SPI模式下與SD模式下讀取寄存器是不一樣的。


在SPI模式下讀取寄存器跟讀取block數(shù)據(jù)是一致的。

SPI通信還需要注意以下幾點(diǎn):


即所有操作完畢后,MCU仍需要提供8個(gè)clock給SD控制器以便其在MCU停止時(shí)鐘之前完成其操作。以上是SD卡在SPI模式下的操作流程,下面就說說其命令格式


命令共有6個(gè)字節(jié),且以MSB形式傳輸,第一個(gè)字節(jié)為Command后面會(huì)提到命令列表,bit[7:6]=01b固定,即Command只占用了6個(gè)字節(jié),即最多有64個(gè)命令;中間4個(gè)字節(jié)為命令參數(shù)(Command Argument)命令不需要參數(shù)時(shí)該值為0,最后一個(gè)字節(jié)為CRC值,bit[0]=1b固定,不使用CRC時(shí)寫1即可,前面提到使SD卡進(jìn)入SPI模式是發(fā)送CMD1其CRC值為0x4A(1001010b),再加上bit[0]=1即為10010101b,CMD1不需要參數(shù),因此具體發(fā)送的數(shù)據(jù)為0x40 0x00 0x00 0x00 0x00 0x95。文檔中亦給出了CRC的計(jì)算方式


由于SPI模式下CRC默認(rèn)禁止了就沒去仔細(xì)研究這個(gè),以后用到了再細(xì)看吧。SD卡的命令分為了10類:class0-class9

命令列表如下


由于命令列表比較長,意思一下就行了。用到的時(shí)候再查文檔就可以了。

來看以下SPI的回碼R1


R1是用于響應(yīng)Command(除了SEND_STATUS之外),最高位固定為0,response還有R1b、R2、R3,此外還有data response如下


data token如下


還有data err token等,這里就不細(xì)說了,不然太冗長了。。。,開始對(duì)比EVAL的庫函數(shù)以及文檔來編輯我們的SD卡操作,首先是SPI的驅(qū)動(dòng)函數(shù)

由于是模擬spi因此只需要初始化io口就夠了



/**********************************************************************

函數(shù):HAL_SPI_SDCARD_MspInit()

函數(shù)作用:sd卡spi資源初始化

參數(shù):無

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-02

說明: SD卡通訊接口為模擬SPI

**********************************************************************/

void HAL_SPI_SDCARD_MspInit(void)

{

GPIO_InitTypeDef  GPIO_InitStruct;

SPI_SDCARD_SCK_GPIO_CLK_ENABLE();

SPI_SDCARD_MISO_GPIO_CLK_ENABLE();

SPI_SDCARD_MOSI_GPIO_CLK_ENABLE();

SPI_SDCARD_nCS_GPIO_CLK_ENABLE();

 

GPIO_InitStruct.Pin  = SPI_SDCARD_SCK_PIN;

GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull      = GPIO_PULLUP;

GPIO_InitStruct.Speed     = GPIO_SPEED_HIGH;

HAL_GPIO_Init(SPI_SDCARD_SCK_GPIO_PORT, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin  = SPI_SDCARD_MOSI_PIN;

HAL_GPIO_Init(SPI_SDCARD_MOSI_GPIO_PORT, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin  = SPI_SDCARD_nCS_PIN;

HAL_GPIO_Init(SPI_SDCARD_nCS_GPIO_PORT, &GPIO_InitStruct);

 

GPIO_InitStruct.Pin  = SPI_SDCARD_MISO_PIN;

GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;

HAL_GPIO_Init(SPI_SDCARD_MISO_GPIO_PORT, &GPIO_InitStruct);

}


其讀、寫、初始化操作如下


/**********************************************************************

函數(shù):SPI_SDCARD_Init()

函數(shù)作用:sd卡spi初始化

參數(shù):無

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-02

說明: SD卡通訊接口為模擬SPI

**********************************************************************/

void SPI_SDCARD_Init(void)

{

HAL_SPI_SDCARD_MspInit();

SPI_SDCARD_nCS_Set_High();

SPI_SDCARD_SCK_Set_Low();

SPI_SDCARD_MOSI_Set_High();

}

/**********************************************************************

函數(shù):SPI_SDCARD_Write()

函數(shù)作用:SPI SDCARD發(fā)送一個(gè)字節(jié)數(shù)據(jù)

參數(shù):

uint8_t value----------------------------待發(fā)送的字節(jié)數(shù)據(jù)

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-07-31

說明: 上升沿鎖存數(shù)據(jù),MSB

**********************************************************************/

void SPI_SDCARD_Write(uint8_t value)

{

uint8_t i = 0;

for(i = 0; i < 8; i++)

{

SPI_SDCARD_SCK_Set_Low();

if(value&0x80)

{

SPI_SDCARD_MOSI_Set_High();

}

else

{

SPI_SDCARD_MOSI_Set_Low();

}

value<<=1;

SPI_SDCARD_SCK_Set_High();//鎖存數(shù)據(jù)

}

SPI_SDCARD_MOSI_Set_High();//釋放數(shù)據(jù)總線

}

/**********************************************************************

函數(shù):SPI_SDCARD_Read()

函數(shù)作用:SPI SDCARD接收一個(gè)字節(jié)數(shù)據(jù)

參數(shù):無

返回值:讀取的字節(jié)內(nèi)容

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-02

說明: 上升沿鎖存數(shù)據(jù),MSB

**********************************************************************/

uint8_t SPI_SDCARD_Read(void)

{

uint8_t i = 0,value = 0;

for(i = 0; i < 8; i++)

{

SPI_SDCARD_SCK_Set_Low();

SPI_SDCARD_SCK_Set_High();//鎖存數(shù)據(jù)

value<<=1;

if(SPI_SDCARD_MISO_Read())

{

value|=0x01;

}

}

return value;

}

spi驅(qū)動(dòng)搞完后就是我們的SDCARD操作了,首先是初始化



這里有個(gè)提示當(dāng)SD卡處于IDLE模式時(shí)只有CMD1、ACMD41、CDM59、CDM58命令是有效的,MCU需持續(xù)的發(fā)送CMD1命令,直到SD卡退出IDLE模式

拷貝EVAL庫函數(shù)中的命令列表以及response碼如下



#define SDCARD_DUMMY_BYTE 0xFF

 

/**

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

  */

#define SDCARD_CMD_GO_IDLE_STATE           0   /* CMD0 = 0x40 */

#define SDCARD_CMD_SEND_OP_COND           1   /* CMD1 = 0x41 */

#define SDCARD_CMD_SEND_CSD               9   /* CMD9 = 0x49 */

#define SDCARD_CMD_SEND_CID               10  /* CMD10 = 0x4A */

#define SDCARD_CMD_STOP_TRANSMISSION       12  /* CMD12 = 0x4C */

#define SDCARD_CMD_SEND_STATUS             13  /* CMD13 = 0x4D */

#define SDCARD_CMD_SET_BLOCKLEN           16  /* CMD16 = 0x50 */

#define SDCARD_CMD_READ_SINGLE_BLOCK       17  /* CMD17 = 0x51 */

#define SDCARD_CMD_READ_MULT_BLOCK         18  /* CMD18 = 0x52 */

#define SDCARD_CMD_SET_BLOCK_COUNT         23  /* CMD23 = 0x57 */

#define SDCARD_CMD_WRITE_SINGLE_BLOCK     24  /* CMD24 = 0x58 */

#define SDCARD_CMD_WRITE_MULT_BLOCK       25  /* CMD25 = 0x59 */

#define SDCARD_CMD_PROG_CSD               27  /* CMD27 = 0x5B */

#define SDCARD_CMD_SET_WRITE_PROT         28  /* CMD28 = 0x5C */

#define SDCARD_CMD_CLR_WRITE_PROT         29  /* CMD29 = 0x5D */

#define SDCARD_CMD_SEND_WRITE_PROT         30  /* CMD30 = 0x5E */

#define SDCARD_CMD_SDCARD_ERASE_GRP_START   32  /* CMD32 = 0x60 */

#define SDCARD_CMD_SDCARD_ERASE_GRP_END     33  /* CMD33 = 0x61 */

#define SDCARD_CMD_UNTAG_SECTOR           34  /* CMD34 = 0x62 */

#define SDCARD_CMD_ERASE_GRP_START         35  /* CMD35 = 0x63 */

#define SDCARD_CMD_ERASE_GRP_END           36  /* CMD36 = 0x64 */

#define SDCARD_CMD_UNTAG_ERASE_GROUP       37  /* CMD37 = 0x65 */

#define SDCARD_CMD_ERASE                   38  /* CMD38 = 0x66 */

 

 

typedef enum

{

/**

  * @brief  SD reponses and error flags

  */

SDCARD_RESPONSE_NO_ERROR      = (0x00),

SDCARD_IN_IDLE_STATE          = (0x01),

SDCARD_ERASE_RESET            = (0x02),

SDCARD_ILLEGAL_COMMAND        = (0x04),

SDCARD_COM_CRC_ERROR          = (0x08),

SDCARD_ERASE_SEQUENCE_ERROR   = (0x10),

SDCARD_ADDRESS_ERROR          = (0x20),

SDCARD_PARAMETER_ERROR        = (0x40),

SDCARD_RESPONSE_FAILURE       = (0xFF),

 

/**

* @brief  Data response error

*/

SDCARD_DATA_OK                = (0x05),

SDCARD_DATA_CRC_ERROR         = (0x0B),

SDCARD_DATA_WRITE_ERROR       = (0x0D),

SDCARD_DATA_OTHER_ERROR       = (0xFF)

}SDCARD_Info;


這些都是我們后面要用到的,初始化SD卡就要發(fā)送命令,且要獲取響應(yīng)碼,因此編輯SDCARD_WriteCmd函數(shù)先


/**********************************************************************

函數(shù):SDCARD_WriteCmd()

函數(shù)作用:發(fā)送SD卡命令

參數(shù):

uint8_t cmd---------------------------------------發(fā)送的命令

uint32_t args-----------------------------------------命令參數(shù)

uint8_t crc_value--------------------------------------CRC校驗(yàn)碼

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-02

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

void SDCARD_WriteCmd(uint8_t cmd, uint32_t args, uint8_t crc_value)

{

uint8_t i = 0x00;

uint8_t frame[6];

 

/* Prepare Frame to send */

frame[0] = (cmd | 0x40);   /* Construct byte 1 */

frame[1] = (uint8_t)(args >> 24); /* Construct byte 2 */

frame[2] = (uint8_t)(args >> 16); /* Construct byte 3 */

frame[3] = (uint8_t)(args >> 8);   /* Construct byte 4 */

frame[4] = (uint8_t)(args);   /* Construct byte 5 */

frame[5] = (crc_value); /* Construct byte 6 */

 

/* Send Frame */

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

{

SPI_SDCARD_Write(frame[i]); /* Send the Cmd bytes */

}

}

/**********************************************************************

函數(shù):SDCARD_WaitResponse()

函數(shù)作用:獲取SD卡回碼

參數(shù):

uint8_t response-----------------------------------指定的回碼

返回值:0:成功-1:失敗

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-02

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

int8_t SDCARD_WaitResponse(uint8_t response)

{

uint16_t time_out = 0xFFFF;

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

while ((SPI_SDCARD_Read() != response) && time_out)

{

time_out--;

}

 

if (time_out == 0)

{

/* After time out */

return (int8_t)-1;

}

else

{

/* Right response got */

return 0;

}

}


使用這兩個(gè)函數(shù)就可以實(shí)現(xiàn)我們的sd卡初始化了,如下


/**********************************************************************

函數(shù):SDCARD_Init()

函數(shù)作用:SD卡初始化

參數(shù):無

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-03

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

void SDCARD_Init(void)

{

uint8_t i = 0;

SPI_SDCARD_nCS_Set_High();

 

//發(fā)送80 個(gè)clks

for(i = 0; i < 10; i++)

{

SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);

}

 

SPI_SDCARD_nCS_Set_Low();

SDCARD_WriteCmd(SDCARD_CMD_GO_IDLE_STATE,0x00000000,0x95);

 

if(0 != SDCARD_WaitResponse(SDCARD_IN_IDLE_STATE))

{

//進(jìn)入IDLE模式失敗

printf("response err!\r\n");

}

SPI_SDCARD_nCS_Set_High();

SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);

printf("response success!\r\n");

SPI_SDCARD_nCS_Set_Low();

 

do

{

SDCARD_WriteCmd(SDCARD_CMD_SEND_OP_COND,0x00000000,0xFF);

}while(0 != SDCARD_WaitResponse(SDCARD_RESPONSE_NO_ERROR));

printf("sdcard init over!\r\n");

}


下載代碼到NUCLEO_F303RE目標(biāo)板運(yùn)行,發(fā)現(xiàn)只輸出了“response success”,SD卡是進(jìn)入了IDLE模式但是退不出來,理想很豐滿,現(xiàn)實(shí)很骨感。。。試過改動(dòng)一些效果依然,可能是文檔不對(duì)頭吧,買TFT模塊提供的SD卡文檔是SanDisk Secure Digital Card product Manual Version2.2還是2004年的,而我的卡是Kingston的,但是找Kingston文檔找不到,就找SD規(guī)范組織的一個(gè)文檔SD Specifications part1 Physical Layer Simplified Specification Version 4.10這個(gè)就比較新了是2013年的,這個(gè)文檔就比較全面了,且提出了SDHC、SDXC,SDHC跟SDSC會(huì)有些許不同這個(gè)看文檔就曉得了。在初始化時(shí)有了不同的流程,之前我們?cè)贗DLE模式下不停的發(fā)送CMD1來使其退出IDLE模式,行不通。現(xiàn)在來看一下新文檔中有關(guān)SD卡初始化的流程


不再是單純的發(fā)送CMD1了而是多出了CMD8命令操作,在IDLE模式下發(fā)送CMD8命令,該命令是帶有參數(shù)的用來確認(rèn)SD卡的接口操作條件,如果該命令發(fā)送完畢后返回illegal command,表明其是Ver1.X規(guī)范的SD卡或者不是SD卡,如果該命令響應(yīng)正確表明該SD卡是符合Ver2.X規(guī)范就可以執(zhí)行其他操作了CMD58、ACMD41。官方文檔中不建議發(fā)送CMD1給SD卡了,因?yàn)樵撁詈茈y讓Host分辨出所接的是SD卡還是MMC卡。文檔中提到CMD8命令的CRC是一直使能的,因此CMD8和CMD1一樣,都要帶有正確的CRC值,現(xiàn)在來看一下CMD8的命令詳解


該命令參數(shù)中Arg[11:0]為有效位,但重要的是VHS域,來確定電壓提供范圍,這里當(dāng)然選擇1了——2.7-3.6V,Check pattern為任意值,谷歌一下很多人寫的是0xAA,所以這里就寫一樣的值吧,CRC=0x87,有關(guān)CRC的計(jì)算可以去網(wǎng)上下載計(jì)算工具,有很多,不然手動(dòng)計(jì)算很費(fèi)力的。CMD8的響應(yīng)碼為R7,5個(gè)字節(jié),如下

現(xiàn)在重寫一下SDCARD_Init函數(shù)來看看其返回值

/**********************************************************************

函數(shù):SDCARD_Init()

函數(shù)作用:SD卡初始化

參數(shù):無

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-05

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

void SDCARD_Init(void)

{

uint8_t i = 0;

SPI_SDCARD_nCS_Set_High();

 

//發(fā)送80 個(gè)clks

for(i = 0; i < 10; i++)

{

SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);

}

 

SPI_SDCARD_nCS_Set_Low();

SDCARD_WriteCmd(SDCARD_CMD_GO_IDLE_STATE,0x00000000,0x95);

 

if(0 != SDCARD_WaitResponse(SDCARD_IN_IDLE_STATE))

{

//進(jìn)入IDLE模式失敗

printf("response err!\r\n");

}

SPI_SDCARD_nCS_Set_High();

SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);

printf("response success!\r\n");

SPI_SDCARD_nCS_Set_Low();

 

SDCARD_WriteCmd(SDCARD_CMD_SEND_IF_COND,0x1AA,0x87);//verify SD Card interface operating condition

while(1)

{

printf("ack:%02X\r\n",SPI_SDCARD_Read());

i++;

if(i >= 255)

{

break;

}

}

 

printf("sdcard init over!\r\n");

}

下載到目標(biāo)板查看串口輸出如下:


將結(jié)果跟R7對(duì)比,0xFF是無效值不用去管它,R7=0x01 0x00 0x00 0x01 0xAA共5個(gè)字節(jié),第一個(gè)字節(jié)為R1為IDLE模式,command version=0x00,voltage accepted=0x01,check pattern=0xAA,可以看到返回結(jié)果跟文檔匹配。說明SD卡是支持CMD8命令同時(shí)可以在該電壓下工作,而且check pattern驗(yàn)證正確。啊,看到了一絲曙光那。。。

接下來對(duì)比前面的SPI模式初始化流程圖該發(fā)送CMD58(READ OCR)命令,流程圖中該框框是虛框即該步驟是可以省略的。CMD58命令格式如下:


其響應(yīng)碼為R3,R3的格式如下:


R3回碼由R1+OCR寄存器組成。

編輯SDCARD_Init函數(shù)增加讀取OCR寄存器代碼如下



SDCARD_WriteCmd (SDCARD_CMD_READ_OCR,0x00000000,SDCARD_NOCRC_BYTE);//read ocr register

 

while(1)

{

printf("acc:%02X\r\n",SPI_SDCARD_Read ());

i++;

if(i >= 250)

{

break;

}

}

查看返回值,如下:


在SPI模式下讀取寄存器是跟讀取block數(shù)據(jù)是一致的,返回值是一個(gè)字節(jié)響應(yīng)碼和幾個(gè)字節(jié)的data block,OCR寄存器的詳細(xì)內(nèi)容如下:


OCR寄存器是32bit共4個(gè)字節(jié),對(duì)比我們串口收到的回碼:0x01 0x00 0xFF 0x80 0x00,對(duì)比OCR寄存器可以發(fā)現(xiàn)OCR[23:15]=111111111b。UHS-II Card Status=0(只有UHS-I Card支持此位),CCS=0,busy=0(上電流程未完成),此時(shí)發(fā)送CMD58命令只能確認(rèn)其電壓范圍無法得知SD卡類型,等SD卡上電初始化完成之后再發(fā)送該命令就可以得知SD卡類型了通過CCS位來判斷。CCS=0:SD卡為SDSC,CCS=1:SD卡為SDHC/SDXC。CCS位只在busy=1時(shí)有效。因此在上電初始化時(shí)我們就省略這一步,直接操作下一步ACMD41命令。ACMD41命令是用來啟動(dòng)初始化流程并用來檢測SD卡的初始化流程是否完成,文檔中提到CMD8是一定要優(yōu)先于ACMD41命令發(fā)送的,SD卡接收CMD8命令后(如果有效)會(huì)擴(kuò)展CMD58和ACMD41指令。

啟動(dòng)初始化時(shí)用戶需要不斷的發(fā)送ACMD41命令給SD卡直到初始化完成即R1=0x00.ACMD41看名字就知道和CMD41不一樣,ACMD41命令是application specific command,在發(fā)送該命令之前需要發(fā)送CMD55命令(不帶參數(shù))來高速SD卡的控制器下一條命令是application specific command。

ACMD41命令格式如下:


其是帶有參數(shù)的,有效參數(shù)位為HCS,詳細(xì)信息如下


SPI模式下和SD模式下ACMD41的返回不一樣,在SPI模式下我們只需要將HCS位置1即可,表明支持高容量SD卡,再次修改SDCARD_Init函數(shù)


while(1)

{

SDCARD_WriteCmd (SDCARD_CMD_APP,0x00000000,SDCARD_NOCRC_BYTE);

SDCARD_GetResponse ();

SDCARD_WriteCmd (SDCARD_ACMD_SEND_OP_COND,0x40000000,SDCARD_NOCRC_BYTE);

while(1)

{

ack = SDCARD_GetResponse ();

printf("acc:%02X\r\n",ack);

i++;

if( i >= 5 || ack == 0x00)

{

break;

}

}

if(ack == 0x00)

{

break;

}

}

查看其返回結(jié)果,如下:


已經(jīng)初始化完畢,現(xiàn)在我們可以執(zhí)行CMD58命令來檢測SD卡類型,結(jié)果如下:


可以看到此時(shí)OCR寄存器中CCS位和busy位都是1,即初始化完成且SD卡類型為SDHC/SDXC。整合一下代碼,目前初始化的完整代碼如下

/**********************************************************************
函數(shù):SDCARD_Init()
函數(shù)作用:SD卡初始化
參數(shù):無
返回值:無
上一版本:無
當(dāng)前版本:1.0
作者:anobodykey
最后修改時(shí)間:2015-08-05
說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作
**********************************************************************/
void SDCARD_Init(void)
{
	uint8_t i = 0;
	uint8_t ack = 0;
	SPI_SDCARD_nCS_Set_High();
 
	//發(fā)送80 個(gè)clks
	for(i = 0; i < 10; i++)
	{
		SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);
	}
 
	SPI_SDCARD_nCS_Set_Low();
	SDCARD_WriteCmd(SDCARD_CMD_GO_IDLE_STATE,0x00000000,0x95);
 
	if(0 != SDCARD_WaitResponse(SDCARD_IN_IDLE_STATE))
	{
		//進(jìn)入IDLE模式失敗
		printf("response err!\r\n");
	}
	
	SPI_SDCARD_nCS_Set_High();
	SPI_SDCARD_Write(SDCARD_DUMMY_BYTE);
	printf("response success!\r\n");
	SPI_SDCARD_nCS_Set_Low();
 
	SDCARD_WriteCmd(SDCARD_CMD_SEND_IF_COND,0x1AA,0x87);//verify SD Card interface operating condition
	ack = SDCARD_GetResponse ();
	if(SDCARD_IN_IDLE_STATE != ack)
	{
		printf("get R1 err!\r\n");
		return;
	}
	ack = SDCARD_GetResponse ();
	ack = SDCARD_GetResponse ();
	ack = SDCARD_GetResponse ();
	if(0x01 != ack)
	{
		printf("can't accept the voltage!\r\n");
		return;
	}
	ack = SDCARD_GetResponse ();
	if(0xAA != ack)
	{
		printf("check pattern err!\r\n");
		return;
	}
	
	while(1)
	{
		SDCARD_WriteCmd (SDCARD_CMD_APP,0x00000000,SDCARD_NOCRC_BYTE);
		SDCARD_GetResponse ();
		SDCARD_WriteCmd (SDCARD_ACMD_SEND_OP_COND,0x40000000,SDCARD_NOCRC_BYTE);
		while(1)
		{
			ack = SDCARD_GetResponse ();
			i++;
			if( i >= 5 || ack == 0x00)
			{
				break;
			}
		}
		if(ack == 0x00)
		{
			break;
		}
	}
	
	SDCARD_WriteCmd (SDCARD_CMD_READ_OCR,0x00000000,SDCARD_NOCRC_BYTE);//read ocr register
	ack = SDCARD_GetResponse ();
	ack = SDCARD_GetResponse ();
	SDCARD_GetResponse ();
	SDCARD_GetResponse ();
	SDCARD_GetResponse ();
	
	SPI_SDCARD_nCS_Set_High ();
	SDCARD_WriteDummy ();
 
	if(0xC0 == ack)
	{
		printf("sdhc/sdxc init over!\r\n");
	}
	
}
初始化結(jié)果如下:
終于搞定。。。SD卡信息比較大,這個(gè)代碼也是弄了三四天了吧大概,初始化完成之后剩下的就是讀寫操作了。這里需要注意的是SDSC卡的block size是可以設(shè)定的,而sdhc/sdxc卡的block size是固定512字節(jié)的不可修改。讀寫操作又可以參考EVAL庫函數(shù)里面的讀寫函數(shù),先看一下讀寫命令格式

可見參數(shù)為數(shù)據(jù)地址,這里SDSC和SDHC/SDXC又不同了,

對(duì)于SDSC來說地址單元是字節(jié),對(duì)于SDHC/SDXC來說地址單元是block即512字節(jié),(想想也就知道了只有32bit,按字節(jié)只能尋址4GB空間),發(fā)送完讀寫命令后除了R1回碼外還有一個(gè)start block tokens and stop tran token。如下

我們的讀block函數(shù)如下

/**********************************************************************

函數(shù):SDCARD_ReadBlocks()

函數(shù)作用:讀取SD卡塊數(shù)據(jù)

參數(shù):

uint32_t blockStartAddr---------------------------塊的起始地址

uint8_t blockNumbers--------------------------------讀取的塊數(shù)

uint8_t *context---------------------------------數(shù)據(jù)存儲(chǔ)地址

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-05

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

int8_t SDCARD_ReadBlocks(uint32_t blockStartAddr, uint8_t blockNumbers, uint8_t *context)

{

uint32_t offset = 0;

uint16_t i = 0;

while(blockNumbers--)

{

SDCARD_WriteDummy ();

SDCARD_WriteCmd (SDCARD_CMD_READ_SINGLE_BLOCK,blockStartAddr+offset,SDCARD_NOCRC_BYTE);

 

if(0 != SDCARD_WaitResponse (SDCARD_RESPONSE_NO_ERROR))

{

return (int8_t)-1;

}

if(0 != SDCARD_WaitResponse (SDCARD_START_DATA_SINGLE_BLOCK_READ))

{

return (int8_t)-1;

}

for(i = 0; i < SDCARD_BLOCK_SIZE; i++)

{

*context = SPI_SDCARD_Read ();

context++;

}

offset += SDCARD_BLOCK_OFFSET;

/* get CRC bytes (not really needed by us, but required by SD) */

SPI_SDCARD_Read ();

SPI_SDCARD_Read ();

}

SDCARD_WriteDummy ();

 

return 0;

}


需要注意的是SDCARD_BLOCK_OFFSET在SDHC/SDXC中就不是512而是1了,如果sd卡類型是SDSC的話SDCARD_BLOCK_OFFSET就是可變的,由CMD16指令來確定,默認(rèn)的話一般是512,


#define SDCARD_DUMMY_BYTE 0xFF

#define SDCARD_NOCRC_BYTE 0xFF

#define SDCARD_BLOCK_SIZE 0x200

#define SDCARD_BLOCK_OFFSET 1

在主函數(shù)調(diào)用該函數(shù),



SPI_SDCARD_nCS_Set_Low ();

if(0 != SDCARD_ReadBlocks (0x00,1,arr))

{

printf("read err!\r\n");

}

SPI_SDCARD_nCS_Set_High ();

for(i = 0; i < 512; i++)

{

if(0 == i%16)

{

printf("\r\n");

}

printf("%02X ",arr[i]);

}

結(jié)果如下:


一看到最后的55 AA就曉得這個(gè)讀block函數(shù)是成功的,這個(gè)對(duì)比可以將SD卡通過讀卡器查到PC機(jī)上然后使用winhex軟件來查看其數(shù)據(jù)或者通過后面的寫block函數(shù)來對(duì)比。讀block函數(shù)搞定后,寫block就easy了,如下


/**********************************************************************

函數(shù):SDCARD_WriteBlocks()

函數(shù)作用:向SD卡寫入塊數(shù)據(jù)

參數(shù):

uint32_t blockStartAddr---------------------------塊的起始地址

uint8_t blockNumbers--------------------------------寫入的塊數(shù)

uint8_t *context---------------------------------數(shù)據(jù)存儲(chǔ)地址

返回值:無

上一版本:無

當(dāng)前版本:1.0

作者:anobodykey

最后修改時(shí)間:2015-08-05

說明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫操作

**********************************************************************/

int8_t SDCARD_WriteBlocks(uint32_t blockStartAddr, uint8_t blockNumbers, uint8_t *context)

{

uint16_t i = 0;

uint32_t offset = 0;

uint8_t ack = 0;

while(blockNumbers--)

{

SDCARD_WriteDummy ();

SDCARD_WriteCmd (SDCARD_CMD_WRITE_SINGLE_BLOCK,blockStartAddr+offset,SDCARD_NOCRC_BYTE);

if(0 != SDCARD_WaitResponse (SDCARD_RESPONSE_NO_ERROR))

{

return (int8_t)-1;

}

/* Send the data token to signify the start of the data */

    SPI_SDCARD_Write (SDCARD_START_DATA_SINGLE_BLOCK_WRITE);

 

for(i = 0; i < SDCARD_BLOCK_SIZE; i++)

{

SPI_SDCARD_Write (*context);

context++;

}

offset+=SDCARD_BLOCK_OFFSET;

    /* get CRC bytes (not really needed by us, but required by SD) */

SPI_SDCARD_Read ();

SPI_SDCARD_Read ();

 

ack = SDCARD_GetResponse ();//get data response

if(SDCARD_DATA_OK != ack)

{

return (int8_t)-1;

}

}

SDCARD_WriteDummy ();

return 0;

}


在主函數(shù)調(diào)用該函數(shù),


for(i = 0; i < 512; i++)

{

wri[i] = i;

}

SPI_SDCARD_nCS_Set_Low ();

if(0 != SDCARD_WriteBlocks (0x01,1,wri))

{

printf("write err!\r\n");

}

SPI_SDCARD_nCS_Set_High ();

SPI_SDCARD_nCS_Set_Low ();

if(0 != SDCARD_ReadBlocks (0x01,1,arr))

{

printf("read err!\r\n");

}

SPI_SDCARD_nCS_Set_High ();

for(i = 0; i < 512; i++)

{

if(0 == i%16)

{

printf("\r\n");

}

printf("%02X ",arr[i]);

}

不僅檢測了寫函數(shù)同時(shí)還檢測了讀函數(shù),測試結(jié)果如下:


好了,SD卡的基本讀寫也已經(jīng)完了,剩下的都是完善代碼了,三四天的感悟:多看文檔尤其是最新的文檔!以上代碼只針對(duì)SDHC卡,SDSC卡和SDXC卡由于我沒有所以就沒測試過。。。


關(guān)鍵字:STM32  TFT  SD卡讀寫 引用地址:STM32 TFT學(xué)習(xí)筆記——SD卡讀寫

上一篇:基于STM32的SDIO用4位總線24MHZDMA模式操作SHDC卡
下一篇:STM32CubeMx + SD Card + FatFs 讀寫SD卡死等問題

推薦閱讀

說起機(jī)器人大家可能想到的是電影里面的機(jī)器人,還是身邊的工業(yè)機(jī)器人,或者是google旗下的Atlas?但是你知道柔性機(jī)器人嗎? 柔性機(jī)器人目前分為工業(yè)和生物兩大類,主要是為了應(yīng)對(duì)制造業(yè)和醫(yī)療行業(yè)的需求,有意思的是柔性機(jī)器人在不同的領(lǐng)域定義也不完全相同。 從制造業(yè)的角度來講,柔性機(jī)器人是指運(yùn)用的六軸以上的工業(yè)機(jī)器人。從生物學(xué)角度來講,柔性...
據(jù)外媒報(bào)道,瑞典電動(dòng)汽車(EV)制造商國能(NEVS)與自動(dòng)駕駛汽車初創(chuàng)公司AutoX達(dá)成了獨(dú)家戰(zhàn)略合作伙伴關(guān)系,雙方將把AutoX的自動(dòng)駕駛汽車技術(shù)集成至國能下一代汽車架構(gòu)中。并購合作,自動(dòng)駕駛,國能AutoX,國能機(jī)器人出租車,AutoX機(jī)器人出租車,國能歐洲機(jī)器人出租車(圖片來源:國能電動(dòng)汽車瑞典有限公司官網(wǎng))蓋世汽車訊 據(jù)外媒報(bào)道,瑞典電動(dòng)汽車(...
UART共有三種操作方式,輪詢方式、中斷方式以及DMA方式。芯片:STM32F103C8T6應(yīng)用管腳:輸出:PA0、PA1USART1配置界面添加中斷配置添加DMA配置代碼應(yīng)用1.實(shí)現(xiàn)printf函數(shù)/* USER CODE BEGIN 0 */#ifdef __GNUC__/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to &#39;Yes&#39;) calls __io_p...
學(xué)習(xí)單片機(jī)很重要的一步就是為單片機(jī)燒寫程序,燒寫程序是初學(xué)者體驗(yàn)單片機(jī)并快速入門的第一步,51單片機(jī)由于比較簡單,并且為了節(jié)省學(xué)習(xí)成本,一般很少使用調(diào)試器,而是直接用單片機(jī)的串口燒寫程序。下圖是我使用的開發(fā)板,這個(gè)開發(fā)板上已經(jīng)集成了USB轉(zhuǎn)串口芯片。要為單片機(jī)燒寫程序首先要安裝USB轉(zhuǎn)串口芯片的驅(qū)動(dòng),不同的芯片的驅(qū)動(dòng)程序也不一樣,我這里...

史海拾趣

小廣播
設(shè)計(jì)資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機(jī)文章

 
EEWorld訂閱號(hào)

 
EEWorld服務(wù)號(hào)

 
汽車開發(fā)圈

 
機(jī)器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號(hào) 京ICP備10001474號(hào)-1 電信業(yè)務(wù)審批[2006]字第258號(hào)函 京公網(wǎng)安備 11010802033920號(hào) Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved