⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spi_mmc.c

📁 基于SPI方式的MMC卡的FAT代码,可修改用于SD卡的管理.
💻 C
字号:
/**********************************************************************************************
**
**	Copyright(c) Semitek
**
**	模 块 名:spi_mmc.c
**
**	模块功能:SPI和MMC的接口模块
**
**	修改日期:2007年4月5日
**
**********************************************************************************************/
#include <LPC213X.H>
#include "spi_mmc.h"
#include "string.h"

BYTE MMCWRData[MMC_DATA_SIZE];//写数据缓冲区
BYTE MMCRDData[MMC_DATA_SIZE];//读数据缓冲区
BYTE MMCCmd[MMC_CMD_SIZE];//MMC操作命令
BYTE MMCStatus = 0;//MMC读写状态




/************************** SPI Init **********************************************
**
**	函数名称:spi_init()
**
**	函数功能:初始化SPI
**
**	输入参数:无
**
**	输出参数:无
**
**	函数说明:无
**
**********************************************************************************/
void SPI_Init( void )
{
	DWORD portConfig;
	BYTE i, Dummy;

	SSPCR1 = 0x00; //设置SPI为主机模式

	portConfig = PINSEL1;
	PINSEL1 = portConfig | 0x00A8;//设置引脚功能寄存器
	IODIR0 = SPI_SEL; //SSEL 设置为输出
	IOSET0 = SPI_SEL; //SSEL 置高
      
	VPBDIV = 0x02;// 设置 PCLK = CCLK /2

	SSPCR0 = 0x0707;// 设置数据位为8为,帧格式位SPI, CPOL = 0, CPHA = 0, SCR = 15
   
	SSPCPSR = 0x2;// 设置时钟预分频寄存器(SSPCPSR)

	SSPCR1 = 0x02;// 设备作为主机,使能SSP,处于正常工作模式

	for ( i = 0; i < 8; i++ ) 
	{
		Dummy = SSPDR; // 清除接收寄存器 
	}
	return;
}




/************************** SPI Send ********************************************
**
**	函数名称:SPI_Send()
**
**	函数功能:SPI发送Length字节的数据
**
**	输入参数:*buf--待发数据,Length--待发数据的字节数
**
**	输出参数:无
**
**	函数说明:无
**
**********************************************************************************/
void SPI_Send( BYTE *buf, DWORD Length )
{
	BYTE Dummy;

	if ( Length == 0 ) return;
   
	while ( Length != 0 )
	{
		while ( !(SSPSR & 0x02) );//等到TNF置位说明TxFIFO为空,说明可以开始发送

		SSPDR = *buf;

		while ( !(SSPSR & 0x04) );//等到忙状态为被清0
		Dummy = SSPDR; //清除RxFIFO数据
		Length--;
		buf++;
	}
	return;
}



/************************** SPI Receive *****************************************
**
**	函数名称:SPI_Receive()
**
**	函数功能:SPI接收Length字节的数据
**
**	输入参数:*buf--接收缓冲器,Length--接收数据的字节数
**
**	输出参数:无
**
**	函数说明:无
**
**********************************************************************************/
void SPI_Receive( BYTE *buf, DWORD Length )
{
	DWORD i;

	for ( i = 0; i < Length; i++ )
	{
		*buf = SPI_ReceiveByte();
		buf++;
	}
	return;
}





/************************** SPI ReceiveByte *************************************
**
**	函数名称:SPI_ReceiveByte()
**
**	函数功能:SPI接收1字节的数据
**
**	输入参数:无
**
**	输出参数:接收到的数据
**
**	函数说明:无
**
**********************************************************************************/
BYTE SPI_ReceiveByte( void )
{
	BYTE data;

	SSPDR = 0xFF;//向寄存器中写入0xFF来生成时钟,然后读数据

	while ( SSPSR & 0x10 );//等到忙状态为被清0
	data = SSPDR;
	return ( data );
}



/************************** MMC Init **********************************************
**
**	函数名称:mmc_init()
**
**	函数功能:初始化MMC卡为SPI模式并设置数据块大小为512bytes
**
**	输入参数:无
**
**	输出参数:0或者错误代码
**
**	函数说明:返回0则初始化成功
**
**********************************************************************************/
int mmc_init()
{
	DWORD i;
	
	for(i=0;i<MMC_DATA_SIZE;i++)//将写数据缓冲区全部设置为0xFF
	{
		MMCWRData[i] = 0xFF;
	}
	MMCStatus = 0;

	IOSET0 = SPI_SEL; //SPI引脚置位
   
	for(i=0; i<10; i++)  //发送80个脉冲使MMC卡为SPI模式
	{
		MMCRDData[i] = 0xFF;
	}
   
	SPI_Send( MMCRDData, 10 );
	IOCLR0 = SPI_SEL; //SPI引脚清零
   
	//发送CMD0命令复位MMC卡
	MMCCmd[0] = 0x40;
	MMCCmd[1] = 0x00;
	MMCCmd[2] = 0x00;
	MMCCmd[3] = 0x00;
	MMCCmd[4] = 0x00;
	MMCCmd[5] = 0x95;
	SPI_Send( MMCCmd, MMC_CMD_SIZE );

	//如果为1说明在等待MMC响应0x01的时候超时
	if( mmc_response(0x01) == 1 ) {
		MMCStatus = IDLE_STATE_TIMEOUT;
		IOSET0 = SPI_SEL;//SPI引脚置位
		return MMCStatus;
	}

	IOSET0 = SPI_SEL; //SPI引脚置位
	SPI_ReceiveByte();
	IOCLR0 = SPI_SEL; //SPI引脚清零

	i = MAX_TIMEOUT;//一直发送命令直到返回响应数据为0x00或者超时
	do
	{
		//发送CMD1命令让MMC卡调处IDLE状态
		MMCCmd[0] = 0x41;
		MMCCmd[1] = 0x00;
		MMCCmd[2] = 0x00;
		MMCCmd[3] = 0x00;
		MMCCmd[4] = 0x00;
		MMCCmd[5] = 0xFF;//在SPI模式下,效验数据可以随便设置
		SPI_Send( MMCCmd, MMC_CMD_SIZE );
		i--;
	} while ( (mmc_response(0x00) != 0) && (i>0) );
   
	//如果为1说明在等待MMC响应0x00的时候超时
	if ( i == 0 ) {
		MMCStatus = OP_COND_TIMEOUT;
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}

	IOSET0 = SPI_SEL;//SPI引脚置位
	SPI_ReceiveByte();
	IOCLR0 = SPI_SEL;//SPI引脚清零

	//发送命令16来设置数据块的长度
	MMCCmd[0] = 0x50;
	MMCCmd[1] = 0x00;//此处的4字节代表数据块的长度,如:00 00 00 10 set to 16 bytes,00 00 02 00 set to 512 bytes
	MMCCmd[2] = 0x00;
	MMCCmd[3] = 0x02;
	MMCCmd[4] = 0x00;
	MMCCmd[5] = 0xFF;
	SPI_Send( MMCCmd, MMC_CMD_SIZE );
   
	if( (mmc_response(0x00))==1 ) {
		MMCStatus = SET_BLOCKLEN_TIMEOUT;
		IOSET0 = SPI_SEL;//SPI引脚置位
		return MMCStatus;
	}
   
	IOSET0 = SPI_SEL;//SPI引脚置位
	SPI_ReceiveByte();
	return 0;
}


/************************** MMC Write Block ****************************************
**
**	函数名称:mmc_write_block()
**
**	函数功能:向MMC卡中写入1个扇区(512bytes)的数据
**
**	输入参数:扇区号,待写数据
**
**	输出参数:0或者错误代码
**
**	函数说明:返回0则写入成功
**
**********************************************************************************/
int mmc_write_block(DWORD block_number,BYTE *buffer)
{
	BYTE Status;
   
	IOCLR0 = SPI_SEL;//SPI引脚清零

	block_number <<= 9;//将扇区号乘以512转换为地址
   
	//发送命令24向MMC中写入一个扇区的数据
	MMCCmd[0] = 0x58;
	MMCCmd[1] = (block_number & 0xFF000000) >> 24;//扇区地址
	MMCCmd[2] = (block_number & 0x00FF0000) >> 16;
	MMCCmd[3] = (block_number & 0x0000FF00) >> 8;
	MMCCmd[4] = (block_number & 0x000000FF);
	MMCCmd[5] = 0xFF;
	SPI_Send(MMCCmd, MMC_CMD_SIZE );
   
	if((mmc_response(0x00))==1) {
		MMCStatus = WRITE_BLOCK_TIMEOUT;
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}

	MMCCmd[0] = 0xFE;//发送数据的开始位,通常为0
	SPI_Send( MMCCmd, 1 );
   
	SPI_Send(buffer,MMC_DATA_SIZE);//发送数据

	//发送效验和,当最后的效验和被发送的时候,响应马上就会返回,检查返回的数据是否为0xX5,
	MMCCmd[0] = 0xFF;
	MMCCmd[1] = 0xFF;
	SPI_Send( MMCCmd, 2);
	Status = SPI_ReceiveByte();
	if ( (Status & 0x0F) != 0x05 ) {
		MMCStatus = WRITE_BLOCK_FAIL;
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}

	//等到写数据结束
	if(mmc_wait_for_write_finish()==1) {
		MMCStatus = WRITE_BLOCK_FAIL;
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}
	IOSET0 = SPI_SEL;
	SPI_ReceiveByte();
	return 0;
}


/************************** MMC Read Block ****************************************
**
**	函数名称:mmc_Read_block()
**
**	函数功能:从MMC卡中读出1个扇区(512bytes)的数据
**
**	输入参数:扇区号
**
**	输出参数:0或者错误代码
**
**	函数说明:返回0则读取成功
**
**********************************************************************************/
int mmc_read_block(DWORD block_number)
{
	WORD Checksum;

	IOCLR0 = SPI_SEL;

	block_number <<= 9;//将扇区号乘以512转换为地址
   
	//发送命令17从MMC中读出一个扇区的数据
	MMCCmd[0] = 0x51;
	MMCCmd[1] = (block_number & 0xFF000000) >> 24;
	MMCCmd[2] = (block_number & 0x00FF0000) >> 16;
	MMCCmd[3] = (block_number & 0x0000FF00) >> 8;
	MMCCmd[4] = (block_number & 0x000000FF);
	MMCCmd[5] = 0xFF;
	SPI_Send(MMCCmd, MMC_CMD_SIZE );

	if((mmc_response(0x00))==1) {
		MMCStatus = READ_BLOCK_TIMEOUT;
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}

	//等待数据令牌
	if((mmc_response(0xFE))==1) {
		MMCStatus = READ_BLOCK_DATA_TOKEN_MISSING; 
		IOSET0 = SPI_SEL;
		return MMCStatus;
	}
	SPI_Receive( MMCRDData, MMC_DATA_SIZE );//读一扇区的数据

	//进行CRC效验,但不是必须
	Checksum = SPI_ReceiveByte();
	Checksum = Checksum << 0x08 | SPI_ReceiveByte();
	IOSET0 = SPI_SEL;
	SPI_ReceiveByte();
	return 0;
}



/************************** MMC Response ****************************************
**
**	函数名称:mmc_response()
**
**	函数功能:不断的读MMC直到得到所需数据或者超时退出
**
**	输入参数:所需的响应数据
**
**	输出参数:读写是否成功
**
**	函数说明:返回0则读取成功
**
**********************************************************************************/
int mmc_response( unsigned char response)
{
	DWORD count = 0xFFF;
	while( (SPI_ReceiveByte() != response) && count ) 
	{
		count--;
	}
	if ( count == 0 )
		return 1; //由于超时而失败
	else
		return 0; //在超时之前得到数据,成功响应
}



/************************** MMC Wait for Write finish ***************************
**
**	函数名称:mmc_wait_for_write_finish()
**
**	函数功能:不断的读MMC直到得到非0数据标志写入完成或者超时退出
**
**	输入参数:暂无
**
**	输出参数:读写是否成功
**
**	函数说明:返回0则读取成功
**
**********************************************************************************/
int mmc_wait_for_write_finish( void )
{
	DWORD count = 0xFFFF; 

	BYTE result = 0;
   
	while( (result == 0) && count ) 
	{
		result = SPI_ReceiveByte();
		count--;
	}
	if ( count == 0 )
		return 1; //由于超时而失败
	else
		return 0; //在超时之前得到数据
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -