STM32 SPI方式读写SD卡

        前段时间在51上模拟SPI实现了对SD卡的读取,效果还算不错,最近将其移植到STM32上,不过使用硬件SPI和使用软件SPI还是有差别的。

        代码如下:

void User_SPIInit(void)
{
 GPIO_InitTypeDef GPIO_InitStructure; 
 SPI_InitTypeDef SPI_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1,ENABLE);  //使能时钟
 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_3;
 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_High;   //这里要注意,一定要配置为上升沿数据有效,因为SD卡为上升沿数据有效
 
 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);
 SPI_Cmd(SPI1,ENABLE); 
}

SPI初始化以后就可以写SPI读写函数了,以下两个函数参照了网上的资料,出处找不到了,但是这两个函数帮了我大忙,再次感谢提供资料的无名者

void SD_WriteByte(unsigned char data)
{
 uint16_t temp;

 temp=data;
 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE )==RESET);
   SPI_I2S_SendData(SPI1,temp);
 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE )==RESET);
   SPI_I2S_ReceiveData(SPI1);
}

 

unsigned char SD_ReadByte(void)
{
 unsigned char temp;
 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE )==RESET);
   SPI_I2S_SendData(SPI1,0xFF);
 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE )==RESET);
   temp=SPI_I2S_ReceiveData(SPI1);
 return temp;
}

有了上面两个函数,问题就好解决了,下面实现发送SD命令函数

unsigned char SD_SendCmd(unsigned char *Cmd)  //Cmd为unsigned char Cmd[6]数组,存放SD固定6字节命令
{
 unsigned char i,temp;
 
 temp=0xFF;   //赋予一个初值
 for(i=0;i<6;i++)
    SD_WriteByte(CMD[i]);   //发送6字节命令
 do
 {
   temp=SD_ReadByte();  //一直读SD的应答字节,其实应答字节数量不定,这里简化只收取第一个应答字节,赋temp为0xFF主要因为发现所有应答字节序列的第一个字节不  

                                         //会是0xFF
 }while(temp==0xFF);

  return temp; 
}

发送命令函数完成后下面就该是SD_Init()函数了

unsigned char SD_Init(void)
{
 unsigned char i,temp;
 for(i=0;i<200;i++)          //SD卡要求复位前至少发送74个clock,这里我发了很多,足够多
   SD_WriteByte(0xFF);

 temp=0x00;
 CMD[0]=0x40;   //发送复位命令CMD0,CMD[1]-CMD[4]初始化为0,这里不用再写了,因为CMD0不需要参数
 CMD[5]=0x95;
 do
 {
  temp=SD_SendCmd(CMD);  
  
 }while(temp!=0x01);     //不断发送CMD0,直到返回0x01,即SD卡的Idle状态(我设置的无论何时SD卡CSS始终为低电平)

 temp=0x03;  //发送指令CMD55和指令ACMD41
 CMD[5]=0xFF;
 do
 {
   CMD[0]=0x77;  //CMD55
   temp=SD_SendCmd(CMD);
   CMD[0]=0x69;  //ACMD41
   temp=SD_SendCmd(CMD);
 }while(temp!=0x00);     //循环发送CMD55和ACMD41,直到SD卡返回0x00,即初始化完成且进入到SPI模式,注意在整个所有的过程中,SD卡的CSS时钟为低电平
 
 return temp;  //当然,返回0x00则SD卡初始化成功
}

既然SD卡初始化成功,下面就好说了,下面实现读取一个512字节的块和写入512字节的块

void  SD_Read_SigleBlock(unsigned long addr,unsigned char *ptr)   //addr为4字节地址,这里必须为512的整数倍,ptr为大于512字节的接受缓冲区指针,必须为byte
{
 unsigned char temp;
 unsigned int i=0;
 temp=0xFF;
 CMD[0]=0x51;
 CMD[4]=addr;
 addr=addr>>8;
 CMD[3]=addr;
 addr=addr>>8;
 CMD[2]=addr;
 addr=addr>>8;
 CMD[1]=addr;
 CMD[5]=0xFF;
 do
 {
   temp=SD_SendCmd(CMD);
 }while(temp!=0x00);         //直到返回读取单块命令的正确应答字节,即返回0x00,说明命令发送成功,发送成功后就要读取SD发送的数据了
 do
 {
   temp=SD_ReadByte();
 }while(temp!=0xFE&&temp!=0xFC);    //读取SD卡发送的数据,不断的读取,直到读到SD发送的数据开始信号,即0xFE或0xFC,再往下就是512字节的正式数据
  for(i=0;i<512;i++)
    ptr[i]=SD_ReadByte();       //读取512字节的正式数据
 temp=SD_ReadByte();        //下面还要读取两个字节的CRC校验数据,SD的SPI模式下除了CMD0的CRC有效外,其他CRC校验都无效
 temp=SD_ReadByte();
}
void SD_Write_SigleBlock(unsigned long addr,unsigned char *ptr)  //参数addr为写入数据的地址,必须为512整数倍;ptr为512字节的发送缓冲区指针,必须为byte
{
  unsigned char temp;
 unsigned int i=0;
 temp=0xFF;
 CMD[0]=0x58;
 CMD[4]=addr;
 addr=addr>>8;
 CMD[3]=addr;
 addr=addr>>8;
 CMD[2]=addr;
 addr=addr>>8;
 CMD[1]=addr;
 CMD[5]=0xFF;
 do
 {
   temp=SD_SendCmd(CMD);
 }while(temp!=0x00);   //循环发送写单块命令,直到返回正确应答信号0x00
 
 
  SD_WriteByte(0xFE);   //给SD卡发送正式数据的开始字节信号0xFE或0xFC,这里我选取0xFE
 for(i=0;i<512;i++)
   SD_WriteByte(ptr[i]);   //给SD卡发送要写的512字节的正式数据
 SD_WriteByte(0xFF);   //发送两字节的CRC校验数据,虽说没有用,但形式上还是要发送的
 SD_WriteByte(0xFF);
}

以上都完成后,我们就可以操作SD卡了

unsigned char data[512];

unsigned int i=0;

void mian()
{
  for(i=0;i<512;i++)
    data[i]=0xFF;

  SD_Write_SigleBlock(0x00000000,data);
  for(i=0;i<512;i++)
    data[i]=0;

  SD_Read_SigleBlock(0x00000000,data);
   //在这里检查data里的内容是否都为0xFF即可,如果为0xFF,说明一切成功,否则,要检查了
    while(1);
}

http://www.voidcn.com/article/p-orspydgx-bkz.html

  • 4
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在STM32F4系列中,使用SPI和DMASD是相对较简单的操作。 首先,我们需要配置SPI和DMA。对于SPI,我们需要设置SPI的工作模式、数据大小、极性和相位等参数。我们还需要配置DMA来传输数据。DMA可以在SPISD之间进行数据传输,提高速度和效率。 然后,我们需要初始化SD。这包括发送一系列的命令和参数来配置SD,使其能够与MCU进行通信。这些命令可以通过SPI进行传输,并且可以使用DMA进行数据传输。 接下来,我们可以使用SPI和DMA来SD。通过发送命令和地址来指定要的数据块。通过SPI和DMA进行数据传输,以取或入数据。 同时要注意,对于取数据,我们需要等待SD的响应,并确保数据正确接收到MCU。对于入数据,我们也需要等待SD的响应,并检查是否成功入。 最后,在使用完SD后,我们需要进行关闭和释放相关资源的操作。这包括关闭SPI和DMA的功能,并确保SD处于适当的状态。 综上所述,通过配置SPI和DMA,初始化SD并使用SPI和DMA进行数据传输,我们可以在STM32F4上实现SD操作。这种方法能够提高效率和速度,并简化了操作过程。 ### 回答2: STM32F4 SPI+DMA方式SD的操作步骤如下: 1. 初始化SPI和DMA:首先需要初始化SPI和DMA模块,设置SPI相关参数,如数据位数、时钟分频等。同时,配置DMA的通道和相关参数,以便实现SPI数据的直接传输。 2. 初始化SD:根据SD规范,使用SPI发送命令和参数来初始化SD。初始化过程中,需要发送CMD0命令以及CMD8命令等,以及在响应中获取SD的OCR、CSD等信息。 3. SD:使用SPI+DMA方式进行SD操作。对于操作,先发送CMD17命令来指定要取的块地址,然后启动DMA传输,将从SD取的数据保存到目标内存中。对于操作,先发送CMD24命令来指定要入的块地址,然后启动DMA传输,将数据从源内存传输到SD。 4. 数据检验与校验:在操作完成后,需要进行数据的检验与校验。对于操作,可以使用CRC校验码进行数据的完整性验证;对于操作,可以使用CRC校验码来确保入的数据正确。 5. 错误处理与重试:在操作中,可能会遇到SPI和DMA传输错误、SD响应错误等情况。在这种情况下,可以根据具体的错误类型进行相应的处理和重试操作,例如重新初始化SPI和DMA,重新发送命令等。 总结:通过SPI+DMA方式进行SD操作,可以提高数据传输的效率和速度,并且能够充分利用STM32F4的硬件资源。在实际操作过程中,需要注意配置SPI和DMA的相关参数,正确发送SD的命令和参数,以及处理可能出现的错误情况。 ### 回答3: STM32F4系列微控制器支持SPI(串行外设接口)和DMA(直接内存访问)功能,可以实现对SD操作。 首先,配置SPI硬件资源。选择一个可用的SPI外设和对应的引脚,并配置SPI的时钟频率、数据位、极性、相位等参数。 其次,配置DMA传输。选择一个可用的DMA通道,并设置传输方向、数据宽度、传输数量等参数,以使得DMA能够自动地在SPI和内存之间传输数据。 然后,初始化SD。通过发送SD命令和接收响应来识别和初始化SD,包括设置SPI的工作模式、速度、起始块地址等。 接下来,进行数据传输操作。如果要SD上的数据,首先发送数据命令和相应的地址;然后通过DMA启动数据传输,将SD中的数据取到指定的内存地址;最后等待DMA传输完成,并检查传输数据的正确性。 如果要入数据到SD,首先发送数据命令和相应的地址;然后通过DMA启动数据传输,将指定内存地址的数据入到SD中;最后等待DMA传输完成,并检查入数据的正确性。 最后,进行数据处理和错误处理。对于取操作,可以对传输的数据进行解析和处理,以满足应用的需求。对于入操作,可以检查入数据是否正确,并处理入数据过程中可能出现的错误,如超时、电压不稳定等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值