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






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