s5pv210-裸機之SD卡

    

    sd卡共支持三種傳輸模式:spi模式、1sd模式、4sd模式。所有的sd卡都必須支持較老的spi/mmc模式,這個模式支持慢速的四線spi接口,使很多微控制器都可以通過spi或模擬spi接口來讀寫sd卡。由於S5PV210具有sd總線控制器,並且兼容sd2.0sd卡,因此此處只分析4sd模式、sd2.0sd1.0版本的sd卡驅動實現,sd2.0以上版本sd卡、MMC卡、spi方式讀寫sd卡在本文不適用。

1、S5PV210的HSMMC主機控制器初始化

// 配製IO引腳爲SDIO功能
	GPG0CON = 0x2222222;

	// 禁止上下拉
	GPG0PUD = 0;
//	GPG0PUD = 0x2AAAA;
 
	// 配製時鐘源:channel 0 clock src = SCLKEPLL = 96M
	CLK_SRC4 = (CLK_SRC4 & (~(0xf<<0))) | (0x7<<0);
	
	// 時鐘分頻:channel 0 clock = SCLKEPLL/2 = 48M
	CLK_DIV4 = (CLK_DIV4 & (~(0xf<<0))) | (0x1<<0);
	
	// software reset for all
	SWRST0 = 1;

	timeout = 1000;
	temp = SWRST0;
	
	while (SWRST0 & (1<<0))
	{
		if (timeout == 0)
		{
			return -1;		// reset timeout
		}
		timeout--;
		delay(US * 10);
	}

	// 設置sd卡時鐘在100k~400k,sd卡在識別階段必須用慢速時鐘進行訪問
	s5pHsmmcSetClock(400000);	// 400k
首先,配製控器制的時鐘源,然後重啓控制器,最後把SD卡時鐘設置爲400K


2、向SD卡發送命令函數

	// 命令發送時,需檢查命令線是否已被使用,否則需要等待正在發送的命令發送完才能發送這個命令
	for (i = 0; i < 0x1000000; i++)
	{
		if (!(PRNSTS0 & (1 << 0)))
		{
			break;
		}
	}

	if (i == 0x1000000)
	{
		Debug("CMD line time out, PRNSTS: %04x\r\n", PRNSTS0);
		return -1; // 命令超時
	}

	// 如果命令回覆會帶忙信號(如R1b回覆),則需檢查數據線是否已被使用
	// 若是,則等待數據線空閒,帶忙回覆命令發送後,sd卡會拉低DAT[0]線表明sd卡正忙,數據線不可用
	if (response == CMD_RESP_R1B)	 // R1b回覆通過DAT0反饋忙信號
	{
		for (i = 0; i < 0x10000000; i++)
		{
			if (!(PRNSTS0 & (1 << 1)))
			{
				break;
			}		
		}
		if (i == 0x10000000)
		{
			Debug("Data line time out, PRNSTS: %04x\r\n", PRNSTS0);			
			return -2;
		}		
	}

	ARGUMENT0 = arg;		// 把命令參數寫入ARGUMENT這個寄存器中

	value = (cmd << 8);		// command index 在CMDREG中設置命令值[13:8]
	
	// CMD12可終止傳輸
	if (cmd == 0x12)
	{
		value |= (0x3 << 6); // command type
	}

	// 設置是否需使用data線,如塊讀,塊寫等命令發送後
	// 會緊接着在data線上傳輸數據,其它不需傳輸數據的命令不要設置使用data線CMDREG[5]
	if (data)
	{
		value |= (1 << 5); // 需使用DAT線作爲傳輸等
	}	

	// 設置sd卡的回覆類型,絕大部分命令在sd卡正確響應後,都會對主機進行回覆(R1-R7,R1b)
	// 每個命令對應的回覆類型請參考sd卡規範,回覆類型長度可能爲136或48
	// 回覆中是否包含CRC或命令值的反饋,如果包含,則告訴主控制器檢查回覆中相應的CRC或命令值反饋是否正確以確定傳輸正確
	// CMDREG設置好後,主控制器就會發送命令並接收設定長度的回覆並根據設定檢查CRC,命令值反饋是否正確(若回覆中包含CRC或命令值反饋的話)
	switch (response)
	{
		case CMD_RESP_NONE:
			value |= (0 << 4) | (0 << 3) | 0x0; // 沒有回覆,不檢查命令及CRC
			break;
		case CMD_RESP_R1:
		case CMD_RESP_R5:
		case CMD_RESP_R6:
		case CMD_RESP_R7:		
			value |= (1 << 4) | (1 << 3) | 0x2; // 檢查回覆中的命令,CRC
			break;
		case CMD_RESP_R2:
			value |= (0 << 4) | (1 << 3) | 0x1; // 回覆長度爲136位,包含CRC
			break;
		case CMD_RESP_R3:
		case CMD_RESP_R4:
			value |= (0 << 4) | (0 << 3) | 0x2; // 回覆長度48位,不包含命令及CRC
			break;
		case CMD_RESP_R1B:
			value |= (1 << 4) | (1 << 3) | 0x3; // 回覆帶忙信號,會佔用Data[0]線
			break;
		default:
			break;	
	}
	
	CMDREG0 = value;	
	
	errorState = s5pHsmmcWaitForCommandDone();
	if (errorState)
	{
		Debug("Command = %d\r\n", cmd);
	}
	
	return errorState; // 命令發送出錯
首先,檢查命令線是否被佔用,如果被佔用了,則需要等待其空閒,然後,判斷是否需要使用數據線,最後判斷命令的回覆型式。


3、SD卡初始化

s5pHsmmcIssueCommand(CMD0, 0, 0, CMD_RESP_NONE);
發送CMD0命令,復位SD卡


s5pHsmmcIssueCommand(CMD8, 0x1aa, 0, CMD_RESP_R7)
發送CMD8來檢查卡是否支持主機電壓(2.7v~3.3v),沒有回覆,說明是MMC/sd v1.x/not card


s5pHsmmcIssueCommand(CMD55, 0, 0, CMD_RESP_R1);
s5pHsmmcIssueCommand(CMD41, 0, 0, CMD_RESP_R3)
進一步發送ACMD41(CMD55+CMD41),參數HCS位爲0(非高容量卡)


如果CMD8命令接收到卡回覆信號,說明爲sd2.0版本卡

s5pHsmmcIssueCommand(CMD55, 0, 0, CMD_RESP_R1);
s5pHsmmcIssueCommand(CMD41, OCR, 0, CMD_RESP_R3); 
進一步發送ACMD41(CMD55+CMD41),判斷SD卡是sd2.0高容量sdhc卡或者是sd2.0標準容量卡

s5pHsmmcIssueCommand(CMD3, 0, 0, CMD_RESP_R6);
發送CMD3請求卡發佈一個16位新的相對地址(RCA)


s5pHsmmcIssueCommand(CMD16, 512, 0, CMD_RESP_R1)
通過CMD16設置塊長度爲512字節

4、讀SD卡

// 根據sd主機控制器標準,按順序寫入主機控制器相應的寄存器		
BLKSIZE0 = (7 << 12) | (512 << 0);	// 最大DMA緩存大小,block爲512字節			
BLKCNT0  = readBlock;			// 寫入這次讀block數目

// 設置命令寄存器,單塊讀CMD17,R1回覆
CMDREG0 = (CMD17 << 8) | (1 << 5) | (1 << 4) | (1 << 3) | 0x2;

// 設置命令寄存器,多塊讀CMD18,R1回覆	
CMDREG0 = (CMD18 << 8) | (1 << 5) | (1 << 4) | (1 << 3) | 0x2;			

for (j = 0; j < 512/4; j++)
{
	*(unsigned int *)pBuffer = BDATA0;
	pBuffer += 4;
}

首先,確定要讀的塊數目,然後設置是讀單塊還是讀多塊,最後從數據寄存器讀出數據。


5、寫SD卡

BLKSIZE0 = (7 << 12) | (512 << 0);	// 最大DMA緩存大小,block爲512字節		
BLKCNT0  = writeBlock;			// 寫入block數目	

// 設置命令寄存器,單塊寫CMD24,R1回覆
CMDREG0 = (CMD24 << 8) | (1 << 5) | (1 << 4) | (1 << 3) | 0x2;			

// 設置命令寄存器,多塊寫CMD25,R1回覆
CMDREG0 = (CMD25 << 8) | (1 << 5) | (1 << 4) | (1 << 3) | 0x2;					

for (j = 0; j < 512/4; j++)
{
	BDATA0 = *(unsigned int *)pBuffer;
	pBuffer += 4;
}
首先,確定寫入的塊數,然後設置是寫單塊還是多塊,最後把數據寫入寄存器。

6、

sd卡驅動的編寫必須參考sd2.0規範,此處只根據sd2.0規範講解幾個重要的過程或概念,這些過程具體的實現請參考sd驅動模塊中相應的函數實現。


程序完整代碼已上傳

http://download.csdn.net/download/wzs250969969/10122049






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章