STM32
直播中

王婷

7年用户 983经验值
私信 关注
[问答]

怎样采用SPI模式去读写SD卡呢

怎样采用SPI模式去读写SD卡呢?
如何利用库函数向SD卡中写入数据呢?有哪些步骤?

回帖(1)

徐丹

2021-12-14 11:49:43
1.硬件引脚介绍:
  本测试采用SPI模式读写SD卡,相关引脚配置如下:
  
  片选:SD_CS->PB13,对应SD卡的1脚,低电平有效
  时钟:SPI1_SCK->PA5,对应SD卡的5脚
  主入从出:MISO->PA6,对应SD卡的7脚
  主出从入:MOSI->PA7,对应SD卡的2脚
  
  2.初始化步骤:
  while(SD_Initialize())
  {
  //提示检查SD卡
  }

u8 SD_Initialize(void)
{
  u8 r1;      // 存放SD卡的返回值
  u16 retry;  // 用来进行超时计数
  u8 buf[4];  
        u16 i;
        SD_SPI_Init();                //初始化IO
        SD_SPI_SpeedLow();        //设置到低速模式
        for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲
        retry=20;
        do
        {
                r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
        }while((r1!=0X01) && retry--);
        SD_Type=0;//默认无卡
        if(r1==0X01)
        {
                if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
                {
                        for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);        //Get trailing return value of R7 resp
                        if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
                        {
                                retry=0XFFFE;
                                do
                                {
                                        SD_SendCmd(CMD55,0,0X01);        //发送CMD55
                                        r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
                                }while(r1&&retry--);
                                if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
                                {
                                        for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);//得到OCR值
                                        if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS
                                        else SD_Type=SD_TYPE_V2;   
                                }
                        }
                }else//SD V1.x/ MMC        V3
                {
                        SD_SendCmd(CMD55,0,0X01);                //发送CMD55
                        r1=SD_SendCmd(CMD41,0,0X01);        //发送CMD41
                        if(r1<=1)
                        {               
                                SD_Type=SD_TYPE_V1;
                                retry=0XFFFE;
                                do //等待退出IDLE模式
                                {
                                        SD_SendCmd(CMD55,0,0X01);        //发送CMD55
                                        r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
                                }while(r1&&retry--);
                        }else//MMC卡不支持CMD55+CMD41识别
                        {
                                SD_Type=SD_TYPE_MMC;//MMC V3
                                retry=0XFFFE;
                                do //等待退出IDLE模式
                                {                                                                                            
                                        r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
                                }while(r1&&retry--);  
                        }
                        if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
                }
        }
        SD_DisSelect();//取消片选
        SD_SPI_SpeedHigh();//高速
        if(SD_Type)return 0;
        else if(r1)return r1;           
        return 0xaa;//其他错误
}
如果返回值为正常类型,则跳出初始化循环,接下来介绍该函数中的SD_SPI_Init()函数及其内部调用的SPI_Init()函数
SD_SPI_Init()配置片选引脚PB13并拉低电平设置片选有效,然后调用SPI_Init()函数,代码略  
  SPI_Init()函数初始化单片机的SPI1外设,具体配置为PA5.6.7三个引脚的设置


//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card                                                          

SPI_InitTypeDef  SPI_InitStructure;

void SPI1_Init(void)
{
        GPIO_InitTypeDef GPIO_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_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //选择了串行时钟的稳态:时钟悬空高
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //数据捕获于第二个时钟沿
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
        SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
        SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

        SPI_Cmd(SPI1, ENABLE); //使能SPI外设
       
        SPI1_ReadWriteByte(0xff);//启动传输                 
}   
3.主函数部分:

int main(void)
{
        u32 total,free;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
        delay_init();                     //延时函数初始
        LED_Init();
        uart_init(19200);                 //串口初始化为19200
        exfuns_init();                //为fatfs相关变量申请内存                                                                
        mem_init();                        //初始化内存池       
        printf ("程序初始化结束:->rn");       
        if(SD_Initialize())                                        //检测SD卡
        {
                printf ("未检测到SD卡:->rn");
                while(SD_Initialize())
                {
                        printf("别看着了,滚去找原因,根本没插卡:rn");
                        LED1=!LED1;
                        delay_ms(500);
                }
                LED1=1;
        }                                                                          
        printf("SD卡正常加载:rn");
        printf("申请内存结果:%drn",(unsigned int)exfuns_init());//为fatfs相关变量申请内存,返回0成功                                 
          f_mount(fs[0],"0:",1);                                         //挂载SD卡
        exf_getfree((u8*)"0",&total,&free);//得到SD卡的总容量和剩余容量
        printf("SD卡的容量是:%drn",free);
        printf("开始扫描串口数据**************************rn");
        while(1)        //测试使用串口输入函数名是成功的
        {
        if(USART_RX_STA&0x8000)//不断检测串口接收完成?这里可以去串口中断服务函数中做一些if判断来限制串口的数据格式,数据大小
        {                       
                WriteData();        //串口接收完成开始写入SD卡,该函数是自己封装的,详细请看下面介绍的SD卡写入步骤
                USART_RX_STA=0;                //清零标志位
        }
        LED0=!LED0;//程序运行指示灯
        delay_ms(500);
        }
}


4.向SD卡中写入的步骤:(采用库函数)  
  由于里面的函数都是SD驱动函数封装起来的,所以要了解底层写入过程,还要参考SD卡的通讯时序等等,不在此赘述,以后会另做记录
  

void WriteData(void)                        //一旦在串口中断标志置位,则调用该函数,开始启动创建文件的步骤:
{
        //*********写入SD卡****************************
        mf_mount(0,1);        //注册工作区域
        mf_open(fileName,4);        //创建文件
        mf_close();
        mf_open(fileName,2);                                                                                //以可写方式打开文件
        mf_lseek(mf_size());                                                                                //将指针移动到文件末尾,以便追加数据
        mf_write(USART_RX_BUF,142);                                                                                        //追加写入串口发来的数据到SD卡
        mf_close();                                                                                                                        //关闭文件
        printf("WriteData()执行完毕,数据写入成功rn");
}
举报

更多回帖

×
20
完善资料,
赚取积分