基于STM32的录音机设计(STM32F103+VS1053B)

简介: 基于STM32的录音机设计(STM32F103+VS1053B)

一、环境介绍

MCU:  STM32F103C8T6


开发软件:  Keil5


音频模块:  VS1053B


录音文件存储设备:  SD卡,采用SPI协议驱动


显示屏:  SPI接口的0.96寸OLED


代码风格:  采用寄存器编程,代码简洁、执行效率高、注释到位、移植方便。


项目完整源代码下载地址(下载即可编译运行测试):  https://download.csdn.net/download/xiaolong1126626497/19520781


二、功能介绍

这是基于STM32F103C8T6设计的录音机功能,支持的功能如下:


1.  按下按键1启动自动录音,默认为5秒录音一次,录音完毕自动保存在SD指定目录下。文件名称采用当前时间命名;音频文件格式采用WAV格式存储。


2.  按下按键2启动手动录音,按键按下之后开始录音,再次按下结束录音,录音完毕之后,文件也是一样的保存在SD卡里。


3. SD卡文件系统采用FAT32格式,STM32移植了FATFS开源文件系统对SD卡进行读写操作。


4. OLED显示屏用于显示当前录音机的状态:  空闲、录音、回放等状态。


5. 按下按键3,启动自动回放功能。自动扫描目录,按顺序播放录音文件。

image.png

image.png

技术介绍:


1.  SD卡采用SPI协议驱动,因为对速度没有很高要求,SPI协议已经完全满足;如果要更高的速度,可以采用SDIO协议。


2.  音频模块采用VS1053B,这个芯片支持IIS和SPI协议。我这里采用的是SPI协议驱动,SPI比较简单,代码也好移植,可以很方便的移植到其他单片机上运行。VS1053功能比较强大,支持录音、解码播放。


3.  文件系统采用的是FATFS文件系统,这个文件系统功能比较完善,使用免费,支持FAT16、FAT32等格式。底层也比较好适配移植。当前,除了FATFS以外,还有很多其他的嵌入式文件系统可以选择,移植都大同小异。


4. OLED显示屏是0.96寸的。采用SPI协议驱动,主要是显示一些状态,SPI刷屏比较快,这款OLED也支持IIC接口。


5. VS1053模块上没有喇叭设备,可以适应耳机或者音箱听回放的录音。



硬件与STM32的接线说明:


OLED显示屏:

D0----SCK-----PB14

D1----MOSI----PB13

RES—复位(低电平有效)—PB12

DC---数据和命令控制管脚—PB1

CS---片选引脚-----PA7


VS1053:

#define VS1053_DREQ     PAin(11)      //DREQ  数据请求

#define VS1053_RESET    PAout(12)   //RST   硬件复位--低电平有效

#define VS1053_XCS      PAout(13)      //XCS   片选--低电平有效

#define VS1053_XDCS     PAout(14)      //XDCS  用于数据片选、字节同步

#define VS1053_SCLK     PAout(15)

#define VS1053_OUTPUT   PBout(3)

#define VS1053_INPUT    PBin(4)


SD卡接口:

5V----5V

GND---GND

SPI1_MOSI---PA7

SPI1_MISO---PA6

SPI1_CS---PA4

SPI1_SCK--PA5


三、使用的相关硬件

STM32F103C8T6系统板:  

image.png

OLED显示屏:

image.png

VS1053:

image.png

SD卡卡槽:

image.png

四、操作说明

开发板有一个复位键和一个K0按键

image.png

程序下载:

image.png

程序支持三种模式:


因为开发板只有一个K0按键,所以三种模式都是通过一个按键进行切换的。


一个按键是通过按下的计数方式进行切换的,切换的顺序是自动录音模式、手动录音模式、回放模式。


(1)自动录音模式:按下一次按键后,进入自动录音模式,自动录音模式下,录音5秒自动退出,退出后自动启动播放状态,就是播放刚才5秒录制的音频,播放过程中按下按键可以退出播放状态。


(2)手动录音模式:第二次按下K0按键后,进入手动录音模式,手动录音模式下,可以长时间录音,如果要结束录音,按下K0按键即可结束;结束后自动启动播放状态,就是播放刚才录制的音频,播放过程中按下按键可以退出播放状态。


(3)回放模式:第三次按下K0按键后,进入回放模式,自动扫描wav目录,进行顺序播放音频文件。


   播放过程中可以按下K0按键退出回放模式。

每次录音后的文件是存放在SD卡根目录下的wav目录下。

每个状态都会在OLED显示屏上显示

也会同时通过串口打印到串口调试助手终端。


五、SD卡上存放的文件

SD卡上有两个目录:font目录和wav目录。

font目录下存放16x16字库文件。

wav目录下存放录音的音频文件。

image.png

image.png

image.png

六、部分源码

image.png

6.1 VS1053.c   这是VS1053的驱动代码

#include "vs1053b.h"  
/*
函数功能:移植接口--SPI时序读写一个字节
函数参数:data:要写入的数据
返 回 值:读到的数据
*/
u8 VS1053_SPI_ReadWriteByte(u8 tx_data)
{          
  u8 rx_data=0;        
  u8 i;
  for(i=0;i<8;i++)
  {
    VS1053_SCLK=0;  
    if(tx_data&0x80){VS1053_OUTPUT=1;}
    else {VS1053_OUTPUT=0;}
    tx_data<<=1;  
    VS1053_SCLK=1;
    rx_data<<=1;
    if(VS1053_INPUT)rx_data|=0x01;
  }
  return rx_data; 
}
/*
函数功能:初始化VS1053的IO口   
*/
void VS1053_Init(void)
{
   RCC->APB2ENR|=1<<0;
   AFIO->MAPR&=~(0x7<<24);  //释放PA13/14/15
   AFIO->MAPR|=0x4<<24;
   RCC->APB2ENR|=1<<2;
   RCC->APB2ENR|=1<<3;
   GPIOA->CRH&=0x00000FFF;
   GPIOA->CRH|=0x33338000;
   GPIOB->CRL&=0xFFF00FFF;
   GPIOB->CRL|=0x00083000;
   VS1053_SCLK=1;
   VS1053_XCS=1;
     VS1053_RESET=1;
} 
/*
函数功能:软复位VS10XX
*/
void VS1053_SoftReset(void)
{  
  u8 retry=0;            
  while(VS1053_DREQ==0);              //等待软件复位结束     
  VS1053_SPI_ReadWriteByte(0Xff);     //启动传输
  retry=0;
  while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 软件复位,新模式  
  {
    VS1053_WriteCmd(SPI_MODE,0x0804);   // 软件复位,新模式     
    DelayMs(2);//等待至少1.35ms 
    if(retry++>100)break;     
  } 
  while(VS1053_DREQ==0);//等待软件复位结束   
  retry=0;
  while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD 
  {
    VS1053_WriteCmd(SPI_CLOCKF,0X9800); //设置VS10XX的时钟,3倍频 ,1.5xADD
    if(retry++>100)break;       
  }  
  DelayMs(20);
} 
/*
函数 功 能:硬复位MP3
函数返回值:1:复位失败;0:复位成功 
*/
u8 VS1053_Reset(void)
{
  u8 retry=0;
  VS1053_RESET=0;
  DelayMs(20);
  VS1053_XDCS=1;//取消数据传输
  VS1053_XCS=1; //取消数据传输
  VS1053_RESET=1;    
  while(VS1053_DREQ==0&&retry<200)//等待DREQ为高
  {
    retry++;
    DelayUs(50);
  }
  DelayMs(20);  
  if(retry>=200)return 1;
  else return 0;           
}
/*
函数功能:向VS10XX写命令
函数参数:
        address:命令地址
        data   :命令数据
*/
void VS1053_WriteCmd(u8 address,u16 data)
{  
  while(VS1053_DREQ==0);  //等待空闲           
  VS1053_XDCS=1;   
  VS1053_XCS=0;    
  VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
  VS1053_SPI_ReadWriteByte(address);  //地址
  VS1053_SPI_ReadWriteByte(data>>8);  //发送高八位
  VS1053_SPI_ReadWriteByte(data);     //第八位
  VS1053_XCS=1;            
} 
/*
函数参数:向VS1053写数据
函数参数:data:要写入的数据
*/
void VS1053_WriteData(u8 data)
{
  VS1053_XDCS=0;   
  VS1053_SPI_ReadWriteByte(data);
  VS1053_XDCS=1;      
}
/*
函数功能:读VS1053的寄存器 
函数参数:address:寄存器地址
返回值:读到的值
*/
u16 VS1053_ReadReg(u8 address)
{ 
  u16 temp=0;     
  while(VS1053_DREQ==0);//非等待空闲状态     
  VS1053_XDCS=1;       
  VS1053_XCS=0;        
  VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//发送VS10XX的读命令
  VS1053_SPI_ReadWriteByte(address);        //地址
  temp=VS1053_SPI_ReadWriteByte(0xff);      //读取高字节
  temp=temp<<8;
  temp+=VS1053_SPI_ReadWriteByte(0xff);     //读取低字节
  VS1053_XCS=1;      
   return temp; 
}  
/*
函数功能:读取VS1053的RAM
函数参数:addr:RAM地址
返 回 值:读到的值
*/
u16 VS1053_ReadRAM(u16 addr) 
{ 
  u16 res;            
  VS1053_WriteCmd(SPI_WRAMADDR, addr); 
  res=VS1053_ReadReg(SPI_WRAM);  
  return res;
} 
/*
函数功能:写VS1053的RAM
函数参数:
        addr:RAM地址
        val:要写入的值 
*/
void VS1053_WriteRAM(u16 addr,u16 val) 
{           
  VS1053_WriteCmd(SPI_WRAMADDR,addr); //写RAM地址 
  while(VS1053_DREQ==0);              //等待空闲     
  VS1053_WriteCmd(SPI_WRAM,val);      //写RAM值 
} 
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
          1,本次数据未成功发送   
*/ 
u8 VS1053_SendMusicData(u8* buf)
{
  u8 n;
  if(VS1053_DREQ!=0)  //送数据给VS10XX
  {          
    VS1053_XDCS=0;  
    for(n=0;n<32;n++)
    {
      VS1053_SPI_ReadWriteByte(buf[n]);       
    }
    VS1053_XDCS=1;               
  }else return 1;
  return 0;//成功发送了
}
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
          1,本次数据未成功发送   
*/ 
void VS1053_SendMusicByte(u8 data)
{
  u8 n;
  while(VS1053_DREQ==0){}      
  VS1053_XDCS=0;  
  VS1053_SPI_ReadWriteByte(data);       
  VS1053_XDCS=1;               
}
/*
函数功能:设定VS1053播放的音量
函数参数:volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
    u16 volt=0;             //暂存音量值
    volt=254-volx;          //取反一下,得到最大值,表示最大的表示 
    volt<<=8;
    volt+=254-volx;         //得到音量设置后大小
    VS1053_WriteCmd(SPI_VOL,volt);//设音量 
}
/*--------------------------------------录音功能-----------------------------------------------------*/
/*
函数功能:vs10xx装载patch
函数参数:
        patch:patch首地址
        len  :patch长度
*/
void VS1053_LoadPatch(u16 *patch,u16 len) 
{
  u16 i; 
  u16 addr, n, val;              
  for(i=0;i<len;) 
  { 
    addr = patch[i++]; 
    n    = patch[i++]; 
    if(n & 0x8000U) //RLE run, replicate n samples 
    { 
      n  &= 0x7FFF; 
      val = patch[i++]; 
      while(n--)VS1053_WriteCmd(addr, val);  
    }
    else //copy run, copy n sample 
    { 
      while(n--) 
      { 
        val = patch[i++]; 
        VS1053_WriteCmd(addr, val); 
      } 
    } 
  }   
}
/*
函数参数:初始化WAV头
*/
void VS1053_RecoderWavInit(__WaveHeader* wavhead) //初始化WAV头        
{
  wavhead->riff.ChunkID=0X46464952; //"RIFF"
  wavhead->riff.ChunkSize=0;        //还未确定,最后需要计算
  wavhead->riff.Format=0X45564157;  //"WAVE"
  wavhead->fmt.ChunkID=0X20746D66;  //"fmt "
  wavhead->fmt.ChunkSize=16;        //大小为16个字节
  wavhead->fmt.AudioFormat=0X01;    //0X01,表示PCM;0X01,表示IMA ADPCM
  wavhead->fmt.NumOfChannels=1;     //单声道
  wavhead->fmt.SampleRate=8000;     //8Khz采样率 采样速率
  wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16位,即2个字节
  wavhead->fmt.BlockAlign=2;        //块大小,2个字节为一个块
  wavhead->fmt.BitsPerSample=16;    //16位PCM
  wavhead->data.ChunkID=0X61746164; //"data"
  wavhead->data.ChunkSize=0;        //数据大小,还需要计算  
}
//VS1053的WAV录音有bug,这个plugin可以修正这个问题                   
const u16 VS1053_WavPlugin[40]=/* Compressed plugin */ 
{ 
    0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /* 0 */ 
    0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /* 8 */ 
    0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /* 10 */ 
    0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /* 18 */ 
    0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,  
}; 
/*
函数功能:激活PCM 录音模式
函数参数:
        agc:0,自动增益
        1024相当于1倍
        512相当于0.5倍
        最大值65535=64倍      
*/
void VS1053_RecoderInit(u16 agc)
{
  //如果是IMA ADPCM,采样率计算公式如下:
  //采样率=CLKI/256*d; 
  //假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(2*12288000)/256*6=16Khz
  //如果是线性PCM,采样率直接就写采样值 
  VS1053_WriteCmd(SPI_BASS,0x0000);    
  VS1053_WriteCmd(SPI_AICTRL0,8000);  //设置采样率,设置为8Khz
  VS1053_WriteCmd(SPI_AICTRL1,agc);   //设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍 
  VS1053_WriteCmd(SPI_AICTRL2,0);     //设置增益最大值,0,代表最大值65536=64X
  VS1053_WriteCmd(SPI_AICTRL3,6);     //左通道(MIC单声道输入)
  VS1053_WriteCmd(SPI_CLOCKF,0X2000); //设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288Mhz
  VS1053_WriteCmd(SPI_MODE,0x1804);   //MIC,录音激活    
  DelayMs(5);         //等待至少1.35ms 
  VS1053_LoadPatch((u16*)VS1053_WavPlugin,40);//VS1053的WAV录音需要patch
}

6.2   SD.c  这是SD卡的驱动代码

#include "sdcard.h"        
static u8  SD_Type=0;  //存放SD卡的类型
/*
函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{    
    u16 cnt=0;         
    while((SPI1->SR&1<<1)==0)    //等待发送区空--等待发送缓冲为空 
    {
      cnt++;
      if(cnt>=65530)return 0;     //超时退出  u16=2个字节
    } 
    SPI1->DR=DataTx;                //发送一个byte 
    cnt=0;
    while((SPI1->SR&1<<0)==0)     //等待接收完一个byte   
    {
      cnt++;
      if(cnt>=65530)return 0;    //超时退出
    }                   
    return SPI1->DR;              //返回收到的数据
}
/*
函数功能:底层SD卡接口初始化
SPI1接口---SD卡接线原理
5V----5V
GND---GND
SPI1_MOSI---PA7
SPI1_MISO---PA6
SPI1_CS---PA4
SPI1_SCK--PA5
*/
void SDCardSpiInit(void)
{
  /*1. 开启时钟*/
  RCC->APB2ENR|=1<<2;       //使能PORTA时钟
  /*2. 配置GPIO口模式*/
  GPIOA->CRL&=0x0000FFFF;
  GPIOA->CRL|=0xB8B30000;
  /*3. 上拉*/
  GPIOA->ODR|=1<<4;
    /*SPI1基本配置*/
  RCC->APB2ENR|=1<<12;    //开启SPI1时钟
  RCC->APB2RSTR|=1<<12;
  RCC->APB2RSTR&=~(1<<12);
  SPI1->CR1=0X0;    //清空寄存器
  SPI1->CR1|=0<<15; //选择“双线双向”模式
  SPI1->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;
  SPI1->CR1|=0<<10; //全双工(发送和接收);
  SPI1->CR1|=1<<9;  //启用软件从设备管理
  SPI1->CR1|=1<<8;  //NSS
  SPI1->CR1|=0<<7;  //帧格式,先发送高位
  SPI1->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。
  SPI1->CR1|=1<<2;  //配置为主设备
  SPI1->CR1|=1<<1;  //空闲状态时, SCK保持高电平。
  SPI1->CR1|=1<<0;  //数据采样从第二个时钟边沿开始。
  SPI1->CR1|=1<<6;  //开启SPI设备。
}
/*
函数功能:取消选择,释放SPI总线
*/
void SDCardCancelCS(void)
{
  SDCARD_CS=1;
  SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
}
/*
函数 功 能:选择sd卡,并且等待卡准备OK
函数返回值:0,成功;1,失败;
*/
u8 SDCardSelectCS(void)
{
  SDCARD_CS=0;
  if(SDCardWaitBusy()==0)return 0;//等待成功
  SDCardCancelCS();
  return 1;//等待失败
}
/*
函数 功 能:等待卡准备好
函数返回值:0,准备好了;其他,错误代码
*/
u8 SDCardWaitBusy(void)
{
  u32 t=0;
  do
  {
    if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
    t++;      
  }while(t<0xFFFFFF);//等待 
  return 1;
}
/*
函数功能:等待SD卡回应
函数参数:
          Response:要得到的回应值
返 回 值:
          0,成功得到了该回应值
          其他,得到回应值失败
*/
u8 SDCardGetAck(u8 Response)
{
  u16 Count=0xFFFF;//等待次数                 
  while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应     
  if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回应失败   
  else return SDCard_RESPONSE_NO_ERROR;//正确回应
}
/*
函数功能:从sd卡读取一个数据包的内容
函数参数:
        buf:数据缓存区
        len:要读取的数据长度.
返回值:
      0,成功;其他,失败; 
*/
u8 SDCardRecvData(u8*buf,u16 len)
{           
  if(SDCardGetAck(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
        *buf=SDCardReadWriteOneByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SDCardReadWriteOneByte(0xFF);
    SDCardReadWriteOneByte(0xFF);                                 
    return 0;//读取成功
}
/*
函数功能:向sd卡写入一个数据包的内容 512字节
函数参数:
          buf 数据缓存区
          cmd 指令
返 回 值:0表示成功;其他值表示失败;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{ 
  u16 t;          
  if(SDCardWaitBusy())return 1;  //等待准备失效
  SDCardReadWriteOneByte(cmd);
  if(cmd!=0XFD)//不是结束指令
  {
    for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间
      SDCardReadWriteOneByte(0xFF); //忽略crc
      SDCardReadWriteOneByte(0xFF);
      t=SDCardReadWriteOneByte(0xFF); //接收响应
    if((t&0x1F)!=0x05)return 2;   //响应错误                                  
  }                                             
    return 0;//写入成功
}
/*
函数功能:向SD卡发送一个命令
函数参数:
        u8 cmd   命令 
        u32 arg  命令参数
        u8 crc   crc校验值 
返回值:SD卡返回的响应
*/                          
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
  u8 r1;  
  u8 Retry=0; 
  SDCardCancelCS();               //取消上次片选
  if(SDCardSelectCS())return 0XFF;//片选失效 
  //发送数据
  SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令
  SDCardReadWriteOneByte(arg >> 24);
  SDCardReadWriteOneByte(arg >> 16);
  SDCardReadWriteOneByte(arg >> 8);
  SDCardReadWriteOneByte(arg);    
  SDCardReadWriteOneByte(crc); 
  if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
  Retry=0X1F;
  do
  {
    r1=SDCardReadWriteOneByte(0xFF);
  }while((r1&0X80) && Retry--);   //等待响应,或超时退出
   return r1; //返回状态值
} 
/*
函数功能:获取SD卡的CID信息,包括制造商信息
函数参数:u8 *cid_data(存放CID的内存,至少16Byte)    
返 回 值:
          0:成功,1:错误       
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
    u8 r1;     
    //发SDCard_CMD10命令,读CID
    r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
    if(r1==0x00)
    {
      r1=SDCardRecvData(cid_data,16);//接收16个字节的数据  
    }
  SDCardCancelCS();//取消片选
  if(r1)return 1;
  else return 0;
} 
/*
函数说明:
          获取SD卡的CSD信息,包括容量和速度信息
函数参数:
          u8 *cid_data(存放CID的内存,至少16Byte)     
返 回 值:
          0:成功,1:错误 
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
  u8 r1;   
  r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //发SDCard_CMD9命令,读CSD
  if(r1==0)
  {
    r1=SDCardRecvData(csd_data, 16);//接收16个字节的数据 
  }
  SDCardCancelCS();//取消片选
  if(r1)return 1;
  else return 0;
}  
/*
函数功能:获取SD卡的总扇区数(扇区数)   
返 回 值:
        0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节)
说   明:
        每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过.  
*/
u32 GetSDCardSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u16 csize;                
    if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0; //取CSD信息,如果期间出错,返回0
    if((csd[0]&0xC0)==0x40)  //SDHC卡,按照下面方式计算
    { 
      csize = csd[9] + ((u16)csd[8] << 8) + 1;
      Capacity = (u32)csize << 10;//得到扇区数        
    }
    return Capacity;
}
/*
函数功能: 初始化SD卡
返 回 值: 非0表示初始化失败!
*/
u8 SDCardDeviceInit(void)
{
  u8 r1;      // 存放SD卡的返回值
  u16 retry;  // 用来进行超时计数
  u8 buf[4];  
  u16 i;
  SDCardSpiInit();    //初始化底层IO口
  for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //发送最少74个脉冲
  retry=20;
  do
  {
    r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进入IDLE状态 闲置
  }while((r1!=0X01) && retry--);
  SD_Type=0;   //默认无卡
  if(r1==0X01)
  {
    if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1)  //SD V2.0
    {
      for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);
      if(buf[2]==0X01&&buf[3]==0XAA)    //卡是否支持2.7~3.6V
      {
        retry=0XFFFE;
        do
        {
          SendSDCardCmd(SDCard_CMD55,0,0X01);     //发送SDCard_CMD55
          r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//发送SDCard_CMD41
        }while(r1&&retry--);
        if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
        {
          for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
          if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC;    //检查CCS
          else SD_Type=SDCard_TYPE_V2;   
        }
      }
    }
  }
  SDCardCancelCS();       //取消片选
  if(SD_Type)return 0;  //初始化成功返回0
  else if(r1)return r1; //返回值错误值     
  return 0xaa;          //其他错误
}
/*
函数功能:读SD卡
函数参数:
        buf:数据缓存区
        sector:扇区
        cnt:扇区数
返回值:
        0,ok;其他,失败.
说  明:
        SD卡一个扇区大小512字节
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
  u8 r1;
  if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地址
  if(cnt==1)
  {
    r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读命令
    if(r1==0)                         //指令发送成功
    {
      r1=SDCardRecvData(buf,512);     //接收512个字节     
    }
  }else
  {
    r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//连续读命令
    do
    {
      r1=SDCardRecvData(buf,512);//接收512个字节  
      buf+=512;  
    }while(--cnt && r1==0);   
    SendSDCardCmd(SDCard_CMD12,0,0X01); //发送停止命令
  }   
  SDCardCancelCS();//取消片选
  return r1;//
}
/*
函数功能:向SD卡写数据
函数参数:
        buf:数据缓存区
        sector:起始扇区
        cnt:扇区数
返回值:
        0,ok;其他,失败.
说  明:
        SD卡一个扇区大小512字节
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
  u8 r1;
  if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地址
  if(cnt==1)
  {
    r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读命令
    if(r1==0)//指令发送成功
    {
      r1=SDCardSendData(buf,0xFE);//写512个字节    
    }
  }
  else
  {
    if(SD_Type!=SDCard_TYPE_MMC)
    {
      SendSDCardCmd(SDCard_CMD55,0,0X01); 
      SendSDCardCmd(SDCard_CMD23,cnt,0X01);//发送指令 
    }
    r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//连续读命令
    if(r1==0)
    {
      do
      {
        r1=SDCardSendData(buf,0xFC);//接收512个字节   
        buf+=512;  
      }while(--cnt && r1==0);
      r1=SDCardSendData(0,0xFD);//接收512个字节 
    }
  }   
  SDCardCancelCS();//取消片选
  return r1;//
} 
目录
相关文章
|
2月前
|
传感器 芯片 内存技术
STM32F103标准外设库——认识STM32(一)
STM32F103标准外设库——认识STM32(一)
109 0
STM32F103标准外设库——认识STM32(一)
|
传感器 数据采集 监控
上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享
在网上收集了接近上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享(涵盖了大部分的毕设课设题目),学习单片机的最好教程,也可以作为帮助大家在做电子课设毕设时有利的帮助,可以从以下百度网盘下载(按照编号下载)。
1829 0
上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享
|
传感器 网络协议 物联网
基于STM32设计的智能家居系统(采用ESP8266+OneNet云平台)
基于STM32设计的智能家居系统(采用ESP8266+OneNet云平台)
1202 0
基于STM32设计的智能家居系统(采用ESP8266+OneNet云平台)
|
存储 JSON 移动开发
基于STM32+ESP8266的奥运会奖牌榜设计之经典
基于STM32+ESP8266的奥运会奖牌榜设计之经典
193 0
基于STM32+ESP8266的奥运会奖牌榜设计之经典
|
传感器 数据采集 编解码
基于STM32设计的环境检测设备
设计以STM32微控制器为平台,采用DHT11温湿度传感器、烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135对室内温湿度和危险气体进行采集。通过wifi无线网络将数据传送给微控制器,STM32微控制器处理数据后,由自带oled液晶屏显示。当室内温度达到预警值或有危险气体时,系统将会自动警报并将警报信息通过wifi网络传输给客户手机。且每隔一段时间会通过wifi自动发送监测信息到手机,从而实现对室内环境的监测及报警功能。
682 0
|
算法 芯片
基于STM32设计的计算器(实现基本运算)
当前文章介绍的是STM32+LCD触摸屏设计的一个触摸计算器功能,实现基本的加减乘除,二进制转换显示等功能。LCD屏使用的是3.5寸带触摸屏的显示屏,方便操作屏幕,MCU采用STM32F103ZET6。设计的这个计算器用到的硬件不多,主要是LCD屏和触摸屏,用到了一个W25Q64存储芯片。
379 0
|
传感器 算法
基于STM32设计的健康检测设备(测温心率计步)
本文介绍的项目是基于STM32设计的健康检测设备,支持体温测量,心率检测,支持运动计步(采用MPU6050陀螺仪实现),支持WIFI传输数据到手机APP打印显示。
322 0
|
传感器 数据采集 前端开发
基于STM32设计的数字电子秤
当前项目是采用采用STM32+称重模块+OLED实现了简单的电子秤项目,称重模块上采用24位的ADC芯片,精度较高。实现了称重,校准、去皮等功能。
645 0
|
物联网 数据安全/隐私保护 芯片
基于STM32设计的校园一卡通项目
本文介绍通过STM32 微控制器+RFID RC522设计的一个**校园一卡通消费充值机的项目**,可以模拟实现充值、消费、修改密码、挂失、登录、查询.......等操作。
405 0
|
传感器 算法 芯片
基于STM32设计的指针式电子钟与日历
这是基于STM32设计的一个指针式电子钟+万年历小项目,采用3.5寸的LCD屏显示时钟,日历、温度、天气,支持触摸屏调整设置时间,设置闹钟,查看日历等等。整体项目主要是技术点就是LCD屏的图形绘制。比如: 时钟的时针绘制、分针、秒针、表盘、日历绘制等等。
636 0