SD卡读写程序设计(四)
一、SD卡命令层程序设计
1、上次的程序已经写出了SD操作的用户层函数,比如函数
void SD_ReadBlock( u32 BlockIndex, u8* ReadBuf ){
u16 i;
u8 TmpByte="0xFF";
SD_CmdReadSingle( BlockIndex ); //发出读单块命令17
while ( TmpByte == 0xFF ) //等待块读令牌0xFE或错误信号
{ TmpByte = SPI_GetByte( );
}
if ( TmpByte !=0xFE )return; //如果数据错误,直接返回
for ( i="0"; i<BlockLen; i++ )
ReadBuf = SPI_GetByte();
TmpByte = SPI_GetByte( );
TmpByte = SPI_GetByte( ); //不进行CRC校验
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
SPI_PutByte( 0xFF ); //纯粹只是为了延时。
}
2、本次的主要任务就是实现像SD_CmdReadSingle( BlockIndex ); //发出读单块命令17
这样的命令。根据SD卡的SPI总线通信协议,首先写了下面的代码。
//ReplyType对应返回值类型
void SD_SendCmd( u8 Command, u8* Param, u8 ReplyType, u8* Reply ){
s32 i,ReplyLen;
u8 TmpByte;
SPI_PutByte( Command ); //发送命令
for ( i="3"; i>=0; i-- )
SPI_PutByte ( Param ); //发送参数,四个字节,高字节在前
SPI_PutByte ( 0x95 ); //发送CRC校验码,0x95用于复位的校验,其它随意
ReplyLen =0;
switch ( ReplyType ) //根据返回类型,决定接受几个字节
{ case R1: ReplyLen="1"; break;
case R1B: ReplyLen="1"; break;
case R2: ReplyLen="2"; break;
case R3: ReplyLen="5"; break;
default: SPI_PutByte ( 0xFF );
break;
}
TmpByte=0x80;
while ( (TmpByte&0x80) != 0 )
TmpByte = SPI_GetByte( ); //最高位为1时,不是正确的返回值
for ( i= ReplyLen-1; i>=0; i-- )
{ Reply = TmpByte; //先收到高字节
TmpByte = SPI_GetByte( );
}
}
3、其他的一些相关操作都通过发送命令来实现,如复位、发读单块命令等,程序如下:
void SD_Reset( void ){
u8 ParamByte[4],Reply;
SD_PackParam ( ParamByte, 0 );
SD_SendCmd ( CmdReset, ParamByte, CmdReset_R, &Reply );
} //发复位命令
void SD_CmdReadSingle( u32 BlockIndex ){
u8 ParamByte[4],Reply;
SD_PackParam ( ParamByte, (u32)BlockIndex );
SD_SendCmd ( CmdReadSingle, ParamByte, CmdReadSingle_R, &Reply );
}//发送读单块命令,其中CmdReadSingle和CmdReadSingle_R是宏定义,分别对应命令代码和返回类型。
二、SPI操作程序设计
1、下面就剩下SPI的操作和配置了。
2、SPI的读写操作,由库函数完成。
u8 SPI_GetByte( void ){
while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) == RESET );
return ( SPI_I2S_ReceiveData( SPI1 ) );
}
void SPI_PutByte( u8 SendByte ){
while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) == RESET );
SPI_I2S_SendData( SPI1, SendByte ) ;
}
刚开始还出了一些错,首先stm<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />32f10x_conf.h中没有定义SPI,然后,V2.02的库与李宁这个书上的还不同,查看了一下头文件才发现。
3、开始进行配置
(1)使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
(2)配置IO引脚:
void SPI_Config(void){ //进行SPI的初始化配置
// u8 TmpByte;
GPIO_InitTypeDef GPIO_InitStructure; //进行GPIO端口设置的数据结构
SPI_InitTypeDef SPI_InitStructure;
//第五脚用于SCK,第六脚用于SD的输出SPI的输入,第七脚是SD的输入口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 |GPIO_Pin_7 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; /* SD的片选引脚 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工通信
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ; //空闲时高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //第二个时钟数据有效
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //空闲时高电平
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //进行四分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB; //先发送低位
SPI_InitStructure.SPI_CRCPolynomial = 7 ;
SPI_Init(SPI1, &SPI_InitStructure); /* 根据上面结构的参数对SPI1进行初始化 */
SPI_Cmd(SPI1, ENABLE); /* 使能SPI1 */
// SPI_I2S_SendData( SPI1, 0xaa );
// TmpByte=SPI_I2S_ReceiveData( SPI1 );
}
简单地对SPI测试了一下,可以使用。
对整个工程编译,下载,SD读卡程序陷入死循环。明天再调试。
用户1593293 2009-12-3 10:24