u8 SD_SendCommand(u8 cmd,u32 arg, u8 crc)
{
u8 r1;
unsigned int Retry = 0;
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);//提高兼容性,如果沒有這里,有些SD卡可能不支持
SD_CS_ENABLE();//片選端置低,選中SD卡
/*發送命令序列*/
SPI_ReadWriteByte(cmd | 0x40);
SPI_ReadWriteByte((u8)(arg>>24));//參數[31..24]
SPI_ReadWriteByte((u8)(arg>>16));//參數[23..16]
SPI_ReadWriteByte((u8)(arg>>8)); //參數[15..8]
SPI_ReadWriteByte((u8)arg); //參數[7..0]
SPI_ReadWriteByte(crc);
//等待響應,或超時退出
while((r1 = SPI_ReadWriteByte(0xff)==0xff))
{
Retry++;
if(Retry>800)break;//根據實驗測得,最好重試次數多點
}
//關閉片選
SD_CS_DISABLE();
//在總線上額外增加8個時鐘,讓SD卡完成剩下的工作
SPI_ReadWriteByte(0xff);
//返回狀態值
return r1;
}
/************************************************************************
*SD卡初始化函數
*延時等待SD卡上電完成
*給至少74個脈沖讓SD卡自己初始化完成
*持續發送發送CMD0接收0x01(可以不接受)SD回應進入Idle(空閑)狀態
*
************************************************************************/
u8 SD_Init(void)
{
u16 i; //用來循環計數
u8 r1; //存放SD卡的返回值
u16 retry; //用來進行超時計數
u8 buff[6];
SPI_ControlLine(); //SPI的配置初始化
SPI_SetSpeed(SPI_SPEED_LOW);
SD_CS_ENABLE();
//純延時,等待SD卡上電完成
for(i=0;i<0xf00;i++);
//先產生至少74個脈沖,讓SD卡自己初始化完成
for(i=0;i<10;i++)
{
SPI_ReadWriteByte(0xFF); //80clks
}
//-------------------SD卡復位到idle開始-------------------
//循環連續發送CMD0,直到SD卡返回0x01,進入IDLE狀態
//超時則直接退出
retry = 0;
do
{
//發送CMD0,讓SD卡進入IDLE狀態
ri = SD_SendCommand(CMD0,0,0x95);
retry++;
}while((r1 != 0x01)&& (retry<200));
//跳出循環后,檢查原因: 初始化成功?or重試超時?
if(retry==200) return 1;//超時返回1
//--------------SD卡復位到idle結束----------
//獲取卡片的SD版本信息
r1 = SD_SendCommand_NoDeassert(CMD8,0x1aa,0x87);
//如果卡片版本信息是V1.0版本的,即r1=0x05,則進行以下初始化
if(r1==0x05)
{
//設置卡類型為SDV1.0,如果后面檢測為MMC卡,再修改為MMC
SD_Type = SD_TYPE_V1;
//如果是V1.0卡,CMD8指令后沒有后續數據
//片選置高,結束本次命令
SD_CS_DISABLE();
//多發8個clk,讓SD結束后續操作
SPI_ReadWriteByte(0xff);
//----------------SD卡、MMC卡初始化開始------------------
//發卡初始化指令CMD55+ACMD41
//如果有應答,說明是SD卡,且初始化完成
//沒有回應,說明是MMC卡,額外進行相應初始化
retry = 0;
do
{
//先發CMD55,應返回0x01,否則出錯
r1 = SD_SendCommand(CMD55,0,0);
if(r1 !=0x01)
return r1;
//得到正確響應后,發ACMD41,應得到返回值0x00,佛則重試400次
r1 = SD_SendCommand(ACMD41,0,0);
retry++;
}while((r1!=0x00)&&(retry<400));
//判斷是超時還是得到正確回應
// 若有回應:是SD卡:沒有回應:是MMC卡
//---------------MMC卡額外初始化操作開始-------------
if(retry==400)
{
retry =0;
//發送MMC卡初始化命令(沒有測試)
do
{
r1=SD_SendCommand(CMD1,0,0);
retry++;
}while(r1!=0x00)&&(retry<400);
if(retry==400)return 1;//MMC卡初始化超時
//寫入卡類型
SD_Type=SD_TYPE_MMC;
}
//----------MMC卡額外初始化操作結束---------------
//設置SPI為高速模式
SPI_SetSpeed(SPI_SPEED_HIGH);
SPI_ReaadWriteByte(0xff);
//禁止CRC校驗
r1=SD_SendCommand(CMD59,0,0x95);
if(r1!=0x00)return r1;//命令錯誤,返回r1
//-------------SD卡、MMC卡初始化結束-------------
}//SD卡為V1.0版本的初始化結束
//下面是V2.0卡的初始化
//其中需要讀取OCR數據,判斷是SD2.0還是SD2.0HC
else if(r1==0x01)
{
//v2.0的卡,CMD8命令后會傳回4字節的數據,要跳過在結束本命令
buff[0]=SPI_ReadWriteByte(0xff);//shoule be 0x00
buff[1]=SPI_ReadWriteByte(0xff);//shoule be 0x00
buff[2]=SPI_ReadWriteByte(0xff);//shoule be 0x11
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);//the next 8 clocks
//判斷該卡是否支持2.7-3.6的電壓范圍
//if(buff[2]==0x01&&buff[3]==0xaa)//如不判斷,讓其支持的卡更多
//{
retry = 0;
//發卡初始化指令CMD55+ACMD41
do
{
r1=SD_SendCommand(CMD55,0,0);
if(r1!=0x01)return r1;
r1=SD_SendCommand(ACMD41,0x40000000,1);
if(retry>200)return r1;//超時則返回r1狀態
}while(r1!=0);
//初始化指令發送完成,接下來獲取OCR信息
//----------鑒別SD2.0卡版本開始------------
r1=SD_SendCommand_NoDeassert(CMD58,0,0);
if(r1!=0x00)return r1;//如果命令沒有返回正確應答,直接退出返回應答
//讀OCR指令發出后,緊接著是4字節的OCR信息
buff[0]=SPI_ReadWriteByte(0xff);
buff[1]=SPI_ReadWriteByte(0xff);
buff[2]=SPI_ReadWriteByte(0xff);
buff[3]=SPI_ReadWriteByte(0xff);
//OCR接收完成,片選置高
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);
//檢查接收到的OCR中的bit30位(CCS),確定其為SD2還是SDHC
//如果CCS=1:SDHC CCS=0: SD2.0
if(buff[0]&0x40)SD_Type = SD_TYPE_V2HC;//檢查CCS
else SD_Type=SD_TYPE_V2;
//------------------鑒別SD2.0卡版本結束------------------
//設置SPI為高速模式
SPI_SetSpeed(1);
}
return r1
}
/******************************************************************
*********************SPI模式GPIO端口設置***************************
**************PA5=SCK、PA6=MISO、PA7=MOSI、PA4=CS******************
******************************************************************/
void SPI_ControlLine(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC ,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
/*configuration SPI1 pins:,SCK,MISO and MOSI*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //MISO應該要初始化為上拉輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*configration PA4 Pin: CS Pin*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**************************************************************************
***********************SPI通信模式初始化***********************************
***********************設置高速或低速模式**********************************
**************************************************************************/
void SPI_SetSpeed(u8 SpeedSet)
{
/* Initialize the SPI1 according to the SPI_InitStructure members */
SPI_InitTypeDef SPI_InitStructure;
if(SpeedSet==SPI_SPEED_HIGH)//高速
{
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DatSize = SPI_DatSize_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 = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/*SPI1 enable*/
SPI_Cmd(SPI1,ENABLE);
}
else//低速
{
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DatSize = SPI_DatSize_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_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1,&SPI_InitStructure);
/*SPI1 enable*/
SPI_Cmd(SPI1,ENABLE);
}
}
/************************************************************
**************************發送命令***************************
************************************************************/
u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg,u8 crc)
{
unsigned char r1;
unsigned int Retry = 0;
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);//提高兼容性,如果沒有這里,有些SD卡可能不支持
SD_CS_ENABLE();//片選端置低,選中SD卡
/*發送命令序列*/
SPI_ReadWriteByte(cmd | 0x40);
SPI_ReadWriteByte((u8)(arg>>24));//參數[31..24]
SPI_ReadWriteByte((u8)(arg>>24));//參數[23..16]
SPI_ReadWriteByte((u8)(arg>>24));//參數[7..0]
SPI_ReadWriteByte(crc);
//等待響應,或超時退出
while((r1 = SPI_ReadWriteByte(0xff))==0xff)
{
Retry++;
if(Retry>600)break;//根據實驗測得,最好重試次數多點
}
//返回響應值
return r1;
}
/**************************************************************
************************向SD卡寫一個塊*************************
**************************************************************/
u8 SD_WriteSingleBlock(u32 sector,const u8 *data)
{
u8 r1;
u16 i;
u16 retry;
//設置為高速模式
SPI_SetSpeed(SPI_SPEED_LOW);
//如果不是SDHC,給定的是sector地址,將其轉換成byte地址
if(SD_Type!=SD_TYPE_V2HC)
{
sector = sector<<9;//512*sector即物理扇區的邊界對齊地址
}
r1 = SD_SendCommand(CMD24,sector,0x00);
if(r1 !=0x00)
{
return r1;//應答不正確,直接返回
}
//開始準備數據傳輸
SD_CS_ENABLE();
//先放3個空數據,等待SD卡準備好
SPI_ReadWriteByte(0xff);
SPI_ReadWriteByte(0xff);
SPI_ReadWriteByte(0xff);
//放起始令牌0xfe
SPI_ReadWriteByte(0xfe);
//發一個sector的數據
for(i=0;i<512;i++)
{
SPI_ReadWriteByte(*data++);
}
//發2個Byte的dummy CRC
SPI_ReadWriteByte(0xff);
SPI_ReadWriteByte(0xff);
//等待SD卡應答
r1 = SPI_ReadWriteByte(0xff);
if((r1&0x1f)!=0x05)
{
SD_CS_DISABLE();
return r1;
}
//等待操作完成
retry = 0;
while(!SPI_ReadWriteByte(0xff))//卡自編程時,數據線被拉低
{
retry++;
if(retry>65534) //如果長時間寫入沒有完成,報錯退出
{
SD_CS_DISABLE();
return 1; //寫入超時返回1
}
}
//寫入完成,片選置1
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);
return 0;
}
/********************************************************
******************從SD中讀取一個塊***********************
********************************************************/
u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
{
u8 r1;
//設置為高速模式
SPI_SetSpeed(SPI_SPEED_LOW);
if(SD_Type!=SD_TYPE_V2HC)
{
sector = sector<<9;//512*sector即物理扇區的邊界對齊地址
}
//如果不是SDHC,將sector地址轉成byte地址
//sector = sector<<9;
r1 = SD_SendCommand(CMD17,sector,1);//讀命令
if(r1 !=0x00)return r1;
r1 = SD_ReceiveData(buffer,512,EELEASE) ;
if(r1 != 0)
return r1; //讀數據出錯!
else
return 0;
}
/************************************************************
************************接收數據*****************************
************************************************************/
u8 SD_ReceiveData(u8 *data,u16 len,u8 release)
{
u16 retry;
u8 r1;
//啟動一次傳輸
SD_CSENABLE();
//等待SD卡發回數據起始令牌0xfe
retry = 0;
do
{
r1 = SPI_ReadWriteByte(0xff);
retry++;
if(retry>4000) //4000次等待后沒有應答,退出報錯(根據實驗測試,此處最好多試幾次)
{
SD_CS_DISABLE();
return 1;
}
}while(r1 != 0xfe);
//開始接收數據
while(len--)
{
*data = SPI_ReadWriteByte(0xff);
data++;
}
//下面是2個偽CRC(dummy CRC)
SPI_ReadWriteByte(0xff);
SPI_ReadWriteByte(0xff);
//按需釋放總線,將CS置高
if(release == RELEASE)
{
//傳輸結束
SD_CS_DISABLE();
SPI_ReadWriteByte(0xff);
}
return 0;
}
void SPI_ReadWriteByte(u8 xxx)
{
}
void SPI_ReadWriteByte(u8 xxx)
{
}
void USART_Configuration(void)
{
}
寫寫看看400多行,尚有海量代碼沒有公布(還沒寫出或移植。
第一步:實現SD卡的初始化利用usart與筆記本通信。
第二步:在SD卡初始化實現的基礎上對SD卡進行讀寫操作。
第三步:移植FAT文件系統管理圖像,聲音以及視頻文件(猜想或有技術上的錯誤)。
第四步:ucgui的使用以及TFT用戶界面的設計。
當然第三步與第四步相當復雜所以必須有必勝的信心以及優秀的體力與異常集中的精神。
第一步:實現SD卡的初始化利用usart與筆記本通信。
第二步:在SD卡初始化實現的基礎上對SD卡進行讀寫操作。
第三步:移植FAT文件系統管理圖像,聲音以及視頻文件(猜想或有技術上的錯誤)。
第四步:ucgui的使用以及TFT用戶界面的設計。
當然第三步與第四步相當復雜所以必須有必勝的信心以及優秀的體力與異常集中的精神。