?
一、環境介紹
開發軟件: Keil5
音頻模塊: VS1053B
錄音文件存儲設備: SD卡,采用SPI協議驅動
代碼風格: 采用寄存器編程,代碼簡潔、執行效率高、注釋到位、移植方便。
二、功能介紹
這是基于STM32F103C8T6設計的錄音機功能,支持的功能如下:
1. 按下按鍵1啟動自動錄音,默認為5秒錄音一次,錄音完畢自動保存在SD指定目錄下。文件名稱采用當前時間命名;音頻文件格式采用WAV格式存儲。
2. 按下按鍵2啟動手動錄音,按鍵按下之后開始錄音,再次按下結束錄音,錄音完畢之后,文件也是一樣的保存在SD卡里。
3. SD卡文件系統采用FAT32格式,STM32移植了FATFS開源文件系統對SD卡進行讀寫操作。
4. OLED顯示屏用于顯示當前錄音機的狀態: 空閑、錄音、回放等狀態。
5. 按下按鍵3,啟動自動回放功能。自動掃描目錄,按順序播放錄音文件。
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoKAMN4_AAYoNdrzfqo115.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoSAFev2ABNmSNyXYU0481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.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系統板:
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoSAQk5rAADd9S_9bhE374.png)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
OLED顯示屏:
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoWAJBImAAJJ17h_8hE725.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
VS1053:
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoaADJjmAAXlWUkhk9w132.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
SD卡卡槽:
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoaAIHloAARQz1O9h0Q496.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
四、操作說明
開發板有一個復位鍵和一個K0按鍵。
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoeAJGTHAAPFtjHvCqk333.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
程序下載:
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoeASrEAAAF5KjceUDs160.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
程序支持三種模式:
因為開發板只有一個K0按鍵,所以三種模式都是通過一個按鍵進行切換的。
一個按鍵是通過按下的計數方式進行切換的,切換的順序是自動錄音模式、手動錄音模式、回放模式。
(1)自動錄音模式:按下一次按鍵后,進入自動錄音模式,自動錄音模式下,錄音5秒自動退出,退出后自動啟動播放狀態,就是播放剛才5秒錄制的音頻,播放過程中按下按鍵可以退出播放狀態。
(2)手動錄音模式:第二次按下K0按鍵后,進入手動錄音模式,手動錄音模式下,可以長時間錄音,如果要結束錄音,按下K0按鍵即可結束;結束后自動啟動播放狀態,就是播放剛才錄制的音頻,播放過程中按下按鍵可以退出播放狀態。
(3)回放模式:第三次按下K0按鍵后,進入回放模式,自動掃描wav目錄,進行順序播放音頻文件。
播放過程中可以按下K0按鍵退出回放模式。
每次錄音后的文件是存放在SD卡根目錄下的wav目錄下。
每個狀態都會在OLED顯示屏上顯示
也會同時通過串口打印到串口調試助手終端。
五、SD卡上存放的文件
SD卡上有兩個目錄:font目錄和wav目錄。
font目錄下存放16x16字庫文件。
wav目錄下存放錄音的音頻文件。
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoeAEWcQAAA9Bqu12eI054.png)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoeARalKAAAuyIpW2Ng252.png)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
![](https://file.elecfans.com//web2/M00/32/F7/poYBAGIcWoiAE4J9AABjuqK46Fc241.png)
?
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
六、部分源碼
![](https://file.elecfans.com//web2/M00/32/FE/pYYBAGIcWoiAZnMPAAYESM9BiIw414.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9sb25nMTEyNjYyNjQ5Nw==,size_16,color_FFFFFF,t_70)
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.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;iriff.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
}
;)>
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
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;//
}
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](http://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
-
接口
+關注
關注
33文章
8692瀏覽量
151922 -
STM32
+關注
關注
2272文章
10924瀏覽量
357584 -
SPI
+關注
關注
17文章
1722瀏覽量
92129 -
錄音機
+關注
關注
3文章
79瀏覽量
38531 -
keil5
+關注
關注
6文章
45瀏覽量
20733
發布評論請先 登錄
相關推薦
STM32F407錄音機實現的相關資料推薦
錄音機兼用于對講通話電路圖
![<b class='flag-5'>錄音機</b>兼用于對講通話電路圖](https://file1.elecfans.com//web2/M00/A5/AE/wKgZomUMOZOAPpGIAAKKL327fuk072.jpg)
(網盤)關于SDRAM和錄音機等FPGA視頻
![(網盤)關于SDRAM和<b class='flag-5'>錄音機</b>等FPGA視頻](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論