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

歷史上的今天

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

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

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

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

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

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

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

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

ST庫(kù)版本:STM32Cube_FW_F3_V1.1.0

SD卡:Kingston 16GB Micro SDHC Class 10


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

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


基于SD卡資料文檔配合庫(kù)函數(shù)就可以完成我們SD卡的操作了,由于NUCLEO-F303RE的SPI1接口中的MOSI腳被用于LED2的操作,因此這里使用IO口模擬SPI通信來(lái)對(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í)再來(lái)看吧。

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://m.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í)鐘周期來(lái)穩(wěn)定。SD卡在上電后默認(rèn)是SD模式,要想使用SPI模式需要發(fā)送命令給SD卡的控制器來(lái)進(jìn)行模式切換


即上電之后需要發(fā)送CMD0命令來(lái)實(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的讀寫(xiě)和多blocks讀寫(xiě)。之前已經(jīng)看到了單block的讀操作,現(xiàn)在看以下多block的讀操作:


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


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


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

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


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


命令共有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í)寫(xiě)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)禁止了就沒(méi)去仔細(xì)研究這個(gè),以后用到了再細(xì)看吧。SD卡的命令分為了10類:class0-class9

命令列表如下


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

來(lái)看以下SPI的回碼R1


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


data token如下


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

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



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

函數(shù):HAL_SPI_SDCARD_MspInit()

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

參數(shù):無(wú)

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

說(shuō)明: 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);

}


其讀、寫(xiě)、初始化操作如下


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

函數(shù):SPI_SDCARD_Init()

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

參數(shù):無(wú)

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

說(shuō)明: 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ù)

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

說(shuō)明: 上升沿鎖存數(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ù):無(wú)

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

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

說(shuō)明: 上升沿鎖存數(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庫(kù)函數(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)碼

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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:失敗

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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ù):無(wú)

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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模式但是退不出來(lái),理想很豐滿,現(xiàn)實(shí)很骨感。。。試過(guò)改動(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來(lái)使其退出IDLE模式,行不通。現(xiàn)在來(lái)看一下新文檔中有關(guān)SD卡初始化的流程


不再是單純的發(fā)送CMD1了而是多出了CMD8命令操作,在IDLE模式下發(fā)送CMD8命令,該命令是帶有參數(shù)的用來(lái)確認(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)在來(lái)看一下CMD8的命令詳解


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

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

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

函數(shù):SDCARD_Init()

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

參數(shù):無(wú)

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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

接下來(lái)對(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)其電壓范圍無(wú)法得知SD卡類型,等SD卡上電初始化完成之后再發(fā)送該命令就可以得知SD卡類型了通過(guò)CCS位來(lái)判斷。CCS=0:SD卡為SDSC,CCS=1:SD卡為SDHC/SDXC。CCS位只在busy=1時(shí)有效。因此在上電初始化時(shí)我們就省略這一步,直接操作下一步ACMD41命令。ACMD41命令是用來(lái)啟動(dòng)初始化流程并用來(lái)檢測(cè)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ù))來(lái)高速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命令來(lái)檢測(cè)SD卡類型,結(jié)果如下:


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

/**********************************************************************
函數(shù):SDCARD_Init()
函數(shù)作用:SD卡初始化
參數(shù):無(wú)
返回值:無(wú)
上一版本:無(wú)
當(dāng)前版本:1.0
作者:anobodykey
最后修改時(shí)間:2015-08-05
說(shuō)明: 需使用SPI模式對(duì)SD卡進(jìn)行讀寫(xiě)操作
**********************************************************************/
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è)代碼也是弄了三四天了吧大概,初始化完成之后剩下的就是讀寫(xiě)操作了。這里需要注意的是SDSC卡的block size是可以設(shè)定的,而sdhc/sdxc卡的block size是固定512字節(jié)的不可修改。讀寫(xiě)操作又可以參考EVAL庫(kù)函數(shù)里面的讀寫(xiě)函數(shù),先看一下讀寫(xiě)命令格式

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

對(duì)于SDSC來(lái)說(shuō)地址單元是字節(jié),對(duì)于SDHC/SDXC來(lái)說(shuō)地址單元是block即512字節(jié),(想想也就知道了只有32bit,按字節(jié)只能尋址4GB空間),發(fā)送完讀寫(xiě)命令后除了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ǔ)地址

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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指令來(lái)確定,默認(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卡通過(guò)讀卡器查到PC機(jī)上然后使用winhex軟件來(lái)查看其數(shù)據(jù)或者通過(guò)后面的寫(xiě)block函數(shù)來(lái)對(duì)比。讀block函數(shù)搞定后,寫(xiě)block就easy了,如下


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

函數(shù):SDCARD_WriteBlocks()

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

參數(shù):

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

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

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

返回值:無(wú)

上一版本:無(wú)

當(dāng)前版本:1.0

作者:anobodykey

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

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

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

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]);

}

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


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


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

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

推薦閱讀

原本想收割整個(gè)行業(yè),不料反遭行業(yè)內(nèi)玩家“群毆”。?日前,就海信等公司針對(duì)廣州廣晟數(shù)碼技術(shù)有限公司(以下簡(jiǎn)稱廣晟公司)持有的“用于對(duì)音頻信號(hào)進(jìn)行解碼的方法和設(shè)備 ”(專利號(hào):2008100034623)發(fā)明專利發(fā)起的無(wú)效宣告請(qǐng)求,國(guó)家知識(shí)產(chǎn)權(quán)局專利復(fù)審委員會(huì)(以下簡(jiǎn)稱專利復(fù)審委員會(huì))作出了“宣告專利部分無(wú)效”的審查決定書(shū)。?截止目前,廣晟公司...
首先,任何外設(shè)都需要時(shí)鐘,51單片機(jī),stm32,430等等,因?yàn)榧拇嫫魇怯蒁觸發(fā)器組成的,往觸發(fā)器里面寫(xiě)東西,前提條件是有時(shí)鐘輸入。51單片機(jī)不需要配置時(shí)鐘,是因?yàn)橐粋€(gè)時(shí)鐘開(kāi)了之后所有的功能都可以用了,而這個(gè)時(shí)鐘是默認(rèn)開(kāi)啟的,比如有一個(gè)水庫(kù),水庫(kù)有很多個(gè)門,這些門默認(rèn)是開(kāi)啟的,所以每個(gè)門都會(huì)出水,我們需要哪個(gè)門的水的時(shí)候可以直接用,但是也...
1、串口的模式有3種,一是查詢模式,二是中斷模式,三是DMA模式HAL_UART_Transmit();串口輪詢模式發(fā)送,使用超時(shí)管理機(jī)制HAL_UART_Receive();串口輪詢模式接收,使用超時(shí)管理機(jī)制HAL_UART_Transmit_IT();串口中斷模式發(fā)送HAL_UART_Receive_IT();串口中斷模式接收HAL_UART_Transmit_DMA();串口DMA模式發(fā)送HAL_UART_Transmit_DMA();串口DMA模式接收2、阻塞傳...
一、IIC模塊介紹目前市場(chǎng)上很多單片機(jī)都已經(jīng)具有硬件IIC總線控制單元,這類單片機(jī)在工作時(shí),IIC總線狀態(tài)由硬件監(jiān)測(cè),無(wú)需用戶介入,操作方便。IIC總線是雙線、雙向的串行總線,是與其它芯片交換數(shù)據(jù)的有效手段。XEP100單片機(jī)的IIC模塊的功能框圖如下圖所示。IIC總線由一個(gè)雙向的時(shí)鐘線SCL和一個(gè)雙向的數(shù)據(jù)線SDA組成。該實(shí)驗(yàn)通過(guò)IIC總線對(duì)片外EEPROM進(jìn)行通...

史海拾趣

問(wèn)答坊 | AI 解惑

關(guān)于信號(hào)輸出對(duì)電路造成影響的問(wèn)題!

我現(xiàn)在遇到一個(gè)奇怪的問(wèn)題,如果我將電路中的某個(gè)信號(hào)賦值給一個(gè)輸出管腳的話,那么整個(gè)電路的邏輯都不對(duì)了。(如果不輸出的話,通過(guò)其它管腳觀察時(shí)邏輯是正確的) 大家有沒(méi)有碰到過(guò)這種情況?是怎么回事?請(qǐng)高手指點(diǎn)! 注:我是在quartus8.1中用 ...…

查看全部問(wèn)答∨

128M Nand flash如何尋址?

CPU為32位ARM芯片 Nand flash芯片為三星K9F1G08X0M (X8),有效存儲(chǔ)128M + 4M OOB 地址分配如下面所示:             IO0      IO1    IO2       ...…

查看全部問(wèn)答∨

收購(gòu):現(xiàn)需要收購(gòu) SmartARM2400 的包裝盒若干個(gè),以及裝書(shū)的那個(gè)硬包裝盒若干個(gè),謝謝!

發(fā)現(xiàn) SmartARM 2400 的包裝盒裝東西非常合適,想弄幾個(gè), SmartARM套件中的那個(gè)裝書(shū)的硬紙盒也非常不錯(cuò),用來(lái)整理書(shū)架是非常不錯(cuò)的選擇,非常想多弄幾個(gè)。 可以和我郵件聯(lián)系,  rampc@sian.com 不要太貴哦,我明天再把我想要的 ...…

查看全部問(wèn)答∨

信息產(chǎn)業(yè)部—硬件設(shè)計(jì)工程師培訓(xùn)火爆報(bào)名中!?。。?!

全國(guó)硬件工程師實(shí)訓(xùn)基地系信息產(chǎn)業(yè)部電子教育與考試中心指定的硬件工程師獨(dú)家教育機(jī)構(gòu),有著多年的IT業(yè)培訓(xùn)經(jīng)驗(yàn),與諸多IT企業(yè)有著密切聯(lián)系,經(jīng)過(guò)兩年多的精心策劃特推出PCB設(shè)計(jì)工程師,單片機(jī)設(shè)計(jì)工程師,嵌入式系統(tǒng)工程師職業(yè)培訓(xùn),詳情請(qǐng)百度搜索\"單 ...…

查看全部問(wèn)答∨

W77E58看門狗的問(wèn)題

我最近使用W77E58,使用了看門狗復(fù)位,原先使用正常,但在其他與看門狗無(wú)關(guān)的地方作了一些程序修改,之后看門狗工作就不正常了,正常喂狗時(shí)沒(méi)什么問(wèn)題,但我一停止喂狗后,程序會(huì)停下來(lái),只不過(guò)它沒(méi)有從頭開(kāi)始.就死在那里了,不知道為什么,哪位幫幫我.我的看 ...…

查看全部問(wèn)答∨

是否可以做個(gè)這個(gè)Stellaris® Robotic

官方網(wǎng)站有資料,采用的是9B92,有興趣的可以去看看 [ 本帖最后由 chenzhufly 于 2011-3-5 17:25 編輯 ]…

查看全部問(wèn)答∨

一個(gè)有關(guān)UCOSII的問(wèn)題

我在使用uC/OSII的時(shí)候遇到一些問(wèn)題:使用ICCAVR專業(yè)版6.31編譯的時(shí)候出現(xiàn)如下的信息:C:\\icc\\bin\\imakew -f MainController.mak    iccavr -o MainController -LC:\\icc\\lib\\ -g -uc ...…

查看全部問(wèn)答∨

智能吸塵器(能自動(dòng)尋找充電座充電)

本帖最后由 jameswangsynnex 于 2015-3-3 20:01 編輯 現(xiàn)在看到很多智能吸塵器(如:KV8,Deepoo等)都帶有自動(dòng)充電功能。 現(xiàn)在想詢問(wèn)一下,是怎樣讓吸塵器自動(dòng)找到充電座,然后準(zhǔn)確對(duì)接的,對(duì)這個(gè)問(wèn)題很是困惑,沒(méi)有一個(gè)好的解決方案。 ...…

查看全部問(wèn)答∨

用430做的控制板過(guò)不了群脈沖測(cè)試

用MSP430G2553做了一個(gè)控制板,一個(gè)TM1640芯片用作顯示,兩個(gè)595用作顯示和繼電器控制,但就是過(guò)不了群脈沖測(cè)試,群脈沖一打上去,顯示全亂了,觸摸按鍵也不靈光了,一不打,就好了,我不知道問(wèn)題出在哪?附件為原理圖?!?

查看全部問(wèn)答∨

請(qǐng)教07stack關(guān)于地址矛盾的問(wèn)題

不知道有沒(méi)人通過(guò)Zigbee 07stack的證書(shū)?想請(qǐng)教關(guān)于07stack關(guān)于地址矛盾的解決。之前我們的做法是一旦發(fā)生這個(gè)不管它是怎樣的狀況, 所有的地址矛盾的終端都重新派一個(gè)新的地址。但是最近發(fā)現(xiàn)要那證書(shū)的時(shí)候,完全被他們測(cè)試搞糊涂了, 而且07協(xié)議 ...…

查看全部問(wèn)答∨
小廣播
設(shè)計(jì)資源 培訓(xùn) 開(kāi)發(fā)板 精華推薦

最新單片機(jī)文章

 
EEWorld訂閱號(hào)

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

 
汽車開(kāi)發(fā)圈

 
機(jī)器人開(kāi)發(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