主機(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卡由于我沒有所以就沒測試過。。。
上一篇:基于STM32的SDIO用4位總線24MHZDMA模式操作SHDC卡
下一篇:STM32CubeMx + SD Card + FatFs 讀寫SD卡死等問題
推薦閱讀
史海拾趣
設(shè)計(jì)資源 培訓(xùn) 開發(fā)板 精華推薦
- 神經(jīng)形態(tài)芯片可能是革新機(jī)器人實(shí)時(shí)電機(jī)控制的未來
- 從三個(gè)方面理解ARM嵌入式系統(tǒng)
- 自動(dòng)報(bào)警 基于MCU的家庭防盜報(bào)警系統(tǒng)的設(shè)計(jì)
- 存儲(chǔ)控制器及其訪問外設(shè)的原理
- 基于51系列單片機(jī)的智能照明控制系統(tǒng)設(shè)計(jì)方案
- 基于STM32的四旋翼飛行器控制系統(tǒng)
- 單片機(jī)應(yīng)用編程技巧解析
- 基于89C52的教室智能節(jié)能照明系統(tǒng)設(shè)計(jì)
- 一種新型的雨量光照傳感器的設(shè)計(jì)
- 直播已結(jié)束|如何通過【TI MSPM0 固件示例】及【常用電機(jī)驅(qū)動(dòng)器方案和拓?fù)洹浚?0分鐘內(nèi)旋轉(zhuǎn)電機(jī)
- 快來訪問泰克高速串行通信專題 配置您的專屬解決方案 贏好禮!
- 有獎(jiǎng)活動(dòng)“庖丁”解智能睡眠監(jiān)測儀,一波“水軍”來圍觀
- ADI有獎(jiǎng)下載活動(dòng)之20:基于NDIR和PID的ADI氣體探測器解決方案和新產(chǎn)品
- 【已結(jié)束】RIGOL直播|示波器、可編程直流電源應(yīng)用分享
- 村田在線課堂:健康篇
- 快來應(yīng)援吧!投票選出你最愛的TI培訓(xùn)課程
- 折疊led顯示屏有望成為高端市場新寵兒
- 蘋果Siri和百度度秘的霸主之爭
- 安富利躋身2018年Gartner高科技供應(yīng)鏈25強(qiáng)
- 馬斯克等科技大佬聯(lián)名承諾不發(fā)展致命性人工智能武器系統(tǒng)
- 華為公布手機(jī)銷量 發(fā)貨量破億
- 國巨集團(tuán)欲并購勝麗,打造CIS全領(lǐng)域供應(yīng)商
- 高功率&高能量密度 尼吉康小型鋰離子可充電電池上市
- 華為將在今年打造自己的HMS生態(tài)系統(tǒng),取代Google GMS
- 基于STM8單片機(jī)的蜂鳴器和弦聲音的設(shè)計(jì)
- STM8學(xué)習(xí)筆記---利用UID碼實(shí)現(xiàn)開機(jī)產(chǎn)生隨機(jī)數(shù)
- 報(bào)名參會(huì)三重禮 | 西門子車用半導(dǎo)體全棧解決方案直播——助力車企形成研發(fā)閉環(huán)!
- 餓死啦,工程師開飯了!來塊電路板,你要哪款
- ST 首款650MHZ 雙核處理器發(fā)布
- 關(guān)于MSP430F4的ADC采集問題
- 通過編程控制攝像頭云臺(tái)運(yùn)動(dòng),該怎么做呢
- GPRS遠(yuǎn)程抄表系統(tǒng)問題
- 關(guān)于進(jìn)程中止的問題,求教!
- 為什么我的1602液晶屏寫程序進(jìn)去沒反應(yīng)
- powerpc860 在vxworks下 和FPGA的以太網(wǎng)通信問題?
- 關(guān)于電池的充電過程曲線