毕业设计 单片机音乐播放器设计 - 物联网 嵌入式 Stm32
文章目錄
- 0 前言
- 1 簡介
- 2 主要器件
- 3 實現(xiàn)效果
- 4 設計原理
- 5 部分核心代碼
- 6 最后
0 前言
🔥 這兩年開始畢業(yè)設計和畢業(yè)答辯的要求和難度不斷提升,傳統(tǒng)的畢設題目缺少創(chuàng)新和亮點,往往達不到畢業(yè)答辯的要求,這兩年不斷有學弟學妹告訴學長自己做的項目系統(tǒng)達不到老師的要求。
為了大家能夠順利以及最少的精力通過畢設,學長分享優(yōu)質畢業(yè)設計項目,今天要分享的是
🚩 基于Stm32單片機的音樂播放器設計與實現(xiàn)
🥇學長這里給一個題目綜合評分(每項滿分5分)
- 難度系數(shù):3分
- 工作量:3分
- 創(chuàng)新點:3分
1 簡介
采用STM32實現(xiàn)的多功能音樂播放器。
2 主要器件
- STM32F103RBT6主控芯片
- VS1003解碼芯片(解碼MP3)
- TEA5767立體收音機芯片
- DS18B20數(shù)字溫度溫度傳感器
- RGB彩燈
- EEPROM芯片
- TPA152功率放大芯片
- LM2576-3.3V電源芯片
3 實現(xiàn)效果
整體實物
系統(tǒng)主界面
音樂播放器功能部分展示
4 設計原理
硬件系統(tǒng)框圖
MCU為整個系統(tǒng)的核心,控制著整個系統(tǒng)的運行,讓MCU穩(wěn)定的運行是非常必須的,下圖(圖2.2)為MCU的原理圖,包括一個后備電源UPS1,一個主電源VCC3.3和一個模擬電源,模擬電源通過從VCC3.3加濾波電路得到。MCU外圍的必須電路由濾波電容,下載電路(串口1)以及復位開關組成。同時,考慮到系統(tǒng)需要時鐘功能,給時鐘部分增加了后背電源電路,通過二極管連接到VBAT腳,給實時時鐘供電。這里采用了雙電源結構,即在電源有外部供電的時候,后備電池不給時鐘供電,時鐘的電源來自外部,只有當外部電源斷開的時候,后備電源才給時鐘供電,以保持時鐘的計時,這樣可以延長后備電池的使用時間。
同時,為了調試方便,下面電路還加了一個多余的按鍵和LED燈,方便在調試的時候使用。并且,考慮到某些模塊對速度的要求,特意對MCU的IO口做了安排,這樣雖然增加了布線難度,但是提高了執(zhí)行速度,還是值得的。對多余IO口的安排,則是全部引出,方便以后擴展其他功能,比如:家電控制等。同時,對于STM32F103RBT6自帶的USB接口,也已經引出,日后通過升級,可以實現(xiàn)USB控制的功能。
這里要注意一點:因為PT2314,TEA5767,FM24C16這三個器件都是使用IIC總線控制的,所以,把這三個器件掛在一個IIC總線上,節(jié)省了IO口。MCU和DS18B20模塊電路圖如下:
軟件模塊化設計
- 對于底層驅動軟件子系統(tǒng)包括如下模塊程序:LCD驅動模塊、觸摸屏驅動模塊、SD卡驅動模塊、VS1003驅動模塊、PT2314驅動模塊、FM24C16驅動模塊、TEA5767驅動模塊、溫度傳感器驅動模塊、彩燈驅動模塊、實時時鐘驅動模塊。
- 對于應用軟件子系統(tǒng)包括如下模塊程序:JPEG/BMP解碼模塊、FAT文件系統(tǒng)管理模塊、音樂播放模塊、圖片瀏覽模塊、游戲模塊、鬧鐘模塊、時間模塊、設置管理模塊、電子書模塊、收音機模塊、彩燈控制模塊。
LCD模塊驅動程序設計
本系統(tǒng)用到的LCD是八位數(shù)據模式,驅動IC型號是FMT0371,該芯片為松下合資廠生產的一個LCD驅動IC。最高支持26萬色的TFT LCD,有6位、8位、16位和18位數(shù)據模式,可以方便選擇。本系統(tǒng)配套的LCD使用的是八位數(shù)據模式,65K色。
根據該LCD的DATASHEET,每個像素點的GRAM實際上是一個18bit的數(shù)據寄存器。在16bit模式下與寫入數(shù)據的對應關系如圖3.1 所示:
從圖中可以看出,RGB的有效位數(shù)分別為565,比如寫入0XF800則顯示純紅色,寫入0X07E0則顯示純綠色,寫入0X001F 則顯示純藍色。在處理數(shù)據的時候要把像素值先變換為這樣的結構,然后再寫入LCD。LCD的顯示狀態(tài)都是由LCD的控制命令控制的,通過寫入不同的控制命令和數(shù)據,就可以實現(xiàn)不同的現(xiàn)實功能和效果。分析DATASHEET得到幾個重要的控制命令:
00H:這個命令用來控制內存操作模式,這里我們主要用它來改變LCD的掃描方向。
02H,03H:這兩個命令用來分別設置X,Y方向的開始顯示的點坐標。
04H,05H:這兩個命令用來分別設置X,Y方向的結束顯示的點坐標。
0EH,0FH:這兩個命令用來寫入和讀取顯存。
LCD驅動部分包括幾個關鍵函數(shù):LCD讀寫寄存器函數(shù)、LCD讀寫數(shù)據函數(shù)、LCD初始化函數(shù)和LCD畫點函數(shù)。有了這幾個基本函數(shù),其他的畫線、畫面、甚至畫圖都比較容易了。LCD與MCU的連線包括D0~D7、CS、RS、RST、WR、RD、BL共14根線。
D0~D7:數(shù)據線
CS:LCD的片選線,低電平有效。
RS:LCD的地址/數(shù)據控制,高電平表示數(shù)據,低電平表示地址。
RST:復位線,低電平有效。
WR:寫數(shù)據訪問控制。
RD:讀數(shù)據訪問控制。
BL:LCD背光,高電平有效。
送入數(shù)據,然后通過一個WR的脈沖,就可以把數(shù)據寫入到LCD了。最后釋放RS,CS,完成此次操作。對LCD寄存器的讀操作和寫操作差不多,不同之處就是把WR脈沖改為RD脈沖。
以上四個函數(shù)是LCD的主要函數(shù),是最底層的。其他任何功能的函數(shù)都可以在這幾個底層函數(shù)基礎上實現(xiàn)。其他功能的LCD驅動函數(shù)均在tftlcd.c里面有定義和說明,具體見附件。
VS1003模塊驅動程序設計
VS1003也是采用SPI模式,不過是掛在SPI1上面,這里主要介紹VS1003的初始化操作。在對MCU相關IO口正確配置之后就可以對VS1003模塊進行初始化了。VS1003通過7根線與MCU通信: XRST、XDCS、XCS、DREQ、SCK、SO、SI。
- XRST:VS1003復位線,低電平有效。
- XDCS:數(shù)據片選信號,低電平有效。
- XCS:命令片選信號,低電平有效。
- DREQ:數(shù)據請求,輸入總線。
- SCK、SI、SO:SPI接口線。
VS1003與MCU的通訊都是通過SPI總線來完成的,在默認情況下,數(shù)據將在SCLK的上升沿有效(被讀入VS1003),一次需要在SCLK的下降沿更新數(shù)據,并且字節(jié)發(fā)送以MSB在先。注意VS1003的最大寫入和讀出時鐘分別是CLKI/4和CLKI/6(CLKI為VS1003內部時鐘)。
VS1003模塊初始化步驟:
- 硬復位,XRST =0;
- 延時,XDCS、XCS、XRST置1;
- 等待DREQ為高;
- 軟件復位:SPI_MODE=0X0804;
- 等待DREQ為高(軟件復位結束);
- 設置VS1003的時鐘:SCI_CLOCKF=0X9800,3倍頻;
- 設置VS1003的采樣率:SPI_AUDATA=0XBB81,采樣率48K,立體聲;
- 設置重音:SPI_BASS=0X0055;
- 設置音量:SCI_VOL=0X2020;
- 向VS1003發(fā)送四個字節(jié)無效數(shù)據,啟動SPI發(fā)送;
VS1003的初始化在VS1003x.c里面,通過void Vs1003_Init(void)函數(shù)實現(xiàn)。
TEA5767模塊驅動程序設計
TEA5767收音機模塊支持IIC和三線模式,這里我們使用IIC來控制。TEA5767的器件地址是0XC0,在對TEA5767的讀操作通過寫入0XC1來執(zhí)行。
TEA5767寫操作:
- 發(fā)送IIC起始信號
- 發(fā)送器件地址0XC0
- 等待應答
- 發(fā)送一個字節(jié),等待應答,再發(fā)送一個字節(jié),等待應答,循環(huán)5次
- 發(fā)送IIC停止信號
TEA5767的讀操作與寫操作基本相同,只是IIC開始之后寫入0XC1,將發(fā)送一個字節(jié)改為接收一個字節(jié)就可以了。關于TEA5767的其他操作函數(shù)均在TEA5767.c里面
5 部分核心代碼
#include "vs1003.h" //VS1003的全功能函數(shù) //支持SIN測試和RAM測試 //并加入了VS1003的頻譜顯示代碼,不過說實話不咋地,還不如自己寫的頻譜分析,懷疑是不是真實的頻譜變換? //正點原子@SCUT //V1.1//VS1003設置參數(shù) //0,henh.1,hfreq.2,lenh.3,lfreq 5,主音量 u8 vs1003ram[5]={0,0,0,0,250}; //保存VS1003的設置 //EEPROM地址:486~490 共五個 void Save_VS_Set(void) {u8 t;for(t=0;t<5;t++)FM24C16_WriteOneByte(488+t,vs1003ram[t]);//vs1003ram保存 } //讀取VS1003的設置 //EEPROM地址:486~490 共五個 void Read_VS_Set(void) {u8 t;for(t=0;t<5;t++)vs1003ram[t]=FM24C16_ReadOneByte(488+t);//vs1003ram調用 } //SPI1口讀寫一個字節(jié) //TxData:要發(fā)送的字節(jié) //返回值:讀取到的字節(jié) u8 SPI1_ReadWriteByte(u8 TxData) {while((SPI1->SR&1<<1)==0);//等待發(fā)送區(qū)空 SPI1->DR=TxData; //發(fā)送一個byte while((SPI1->SR&1<<0)==0);//等待接收完一個byte return SPI1->DR; //返回收到的數(shù)據 } //設置SPI1的速度 //SpeedSet:1,高速;0,低速; void SPI1_SetSpeed(u8 SpeedSet) {SPI1->CR1&=0XFFC7;if(SpeedSet==1)//高速{SPI1->CR1|=6<<3;//Fsck=Fpclk/64=1.125Mhz }else//低速{SPI1->CR1|=6<<3; //Fsck=Fpclk/128=562.5Khz}SPI1->CR1|=1<<6; //SPI設備使能 } //軟復位VS1003 void Vs1003SoftReset(void) { u8 retry; while((GPIOC->IDR&MP3_DREQ)==0);//等待軟件復位結束SPI1_ReadWriteByte(0X00);//啟動傳輸retry=0;while(Vs1003_REG_Read(SPI_MODE)!=0x0804)// 軟件復位,新模式 {Vs1003_CMD_Write(SPI_MODE,0x0804);// 軟件復位,新模式delay_ms(2);//等待至少1.35ms if(retry++>100)break; } while ((GPIOC->IDR & MP3_DREQ) == 0);//等待軟件復位結束 retry=0;while(Vs1003_REG_Read(SPI_CLOCKF)!=0X9800)//設置vs1003的時鐘,3倍頻 ,1.5xADD {Vs1003_CMD_Write(SPI_CLOCKF,0X9800);//設置vs1003的時鐘,3倍頻 ,1.5xADDif(retry++>100)break; } retry=0;while(Vs1003_REG_Read(SPI_AUDATA)!=0XBB81)//設置vs1003的時鐘,3倍頻 ,1.5xADD {Vs1003_CMD_Write(SPI_AUDATA,0XBB81);if(retry++>100)break; }//Vs1003_CMD_Write(SPI_CLOCKF,0X9800); //Vs1003_CMD_Write(SPI_AUDATA,0XBB81); //采樣率48k,立體聲 set1003();//設置VS1003的音效 ResetDecodeTime();//復位解碼時間 //向vs1003發(fā)送4個字節(jié)無效數(shù)據,用以啟動SPI發(fā)送MP3_DCS_SET(0);//選中數(shù)據傳輸SPI1_ReadWriteByte(0XFF);SPI1_ReadWriteByte(0XFF);SPI1_ReadWriteByte(0XFF);SPI1_ReadWriteByte(0XFF);MP3_DCS_SET(1);//取消數(shù)據傳輸delay_ms(20); } //硬復位MP3 void Mp3Reset(void) {MP3_RST_SET(0);delay_ms(20);MP3_DCS_SET(1);//取消數(shù)據傳輸MP3_CCS_SET(1);//取消數(shù)據傳輸MP3_RST_SET(1); while((GPIOC->IDR & MP3_DREQ)==0); //等待DREQ為高delay_ms(20); } //正弦測試 void VsSineTest(void) { Mp3Reset(); Vs1003_CMD_Write(0x0b,0X2020); //設置音量 Vs1003_CMD_Write(SPI_MODE,0x0820);//進入vs1003的測試模式 while ((GPIOC->IDR & MP3_DREQ) == 0); //等待DREQ為高//向vs1003發(fā)送正弦測試命令:0x53 0xef 0x6e n 0x00 0x00 0x00 0x00//其中n = 0x24, 設定vs1003所產生的正弦波的頻率值,具體計算方法見vs1003的datasheetMP3_DCS_SET(0);//選中數(shù)據傳輸SPI1_ReadWriteByte(0x53);SPI1_ReadWriteByte(0xef);SPI1_ReadWriteByte(0x6e);SPI1_ReadWriteByte(0x24);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);delay_ms(100);MP3_DCS_SET(1); //退出正弦測試MP3_DCS_SET(0);//選中數(shù)據傳輸SPI1_ReadWriteByte(0x45);SPI1_ReadWriteByte(0x78);SPI1_ReadWriteByte(0x69);SPI1_ReadWriteByte(0x74);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);delay_ms(100);MP3_DCS_SET(1); //再次進入正弦測試并設置n值為0x44,即將正弦波的頻率設置為另外的值MP3_DCS_SET(0);//選中數(shù)據傳輸 SPI1_ReadWriteByte(0x53);SPI1_ReadWriteByte(0xef);SPI1_ReadWriteByte(0x6e);SPI1_ReadWriteByte(0x44);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);delay_ms(100);MP3_DCS_SET(1);//退出正弦測試MP3_DCS_SET(0);//選中數(shù)據傳輸SPI1_ReadWriteByte(0x45);SPI1_ReadWriteByte(0x78);SPI1_ReadWriteByte(0x69);SPI1_ReadWriteByte(0x74);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);delay_ms(100);MP3_DCS_SET(1); } //ram 測試 void VsRamTest(void) {u16 regvalue ; Mp3Reset(); Vs1003_CMD_Write(SPI_MODE,0x0820);// 進入vs1003的測試模式while ((GPIOC->IDR&MP3_DREQ)==0); // 等待DREQ為高MP3_DCS_SET(0); // xDCS = 1,選擇vs1003的數(shù)據接口SPI1_ReadWriteByte(0x4d);SPI1_ReadWriteByte(0xea);SPI1_ReadWriteByte(0x6d);SPI1_ReadWriteByte(0x54);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);SPI1_ReadWriteByte(0x00);delay_ms(50); MP3_DCS_SET(1);regvalue=Vs1003_REG_Read(SPI_HDAT0); // 如果得到的值為0x807F,則表明完好。printf("regvalueH:%x\n",regvalue>>8);//輸出結果 printf("regvalueL:%x\n",regvalue&0xff);//輸出結果 } //向VS1003寫命令 //address:命令地址 //data:命令數(shù)據 void Vs1003_CMD_Write(u8 address,u16 data) { while((GPIOC->IDR&MP3_DREQ)==0);//等待空閑SPI1_SetSpeed(0);//低速 MP3_DCS_SET(1); //MP3_DATA_CS=1;MP3_CCS_SET(0); //MP3_CMD_CS=0; SPI1_ReadWriteByte(VS_WRITE_COMMAND);//發(fā)送VS1003的寫命令SPI1_ReadWriteByte(address); //地址SPI1_ReadWriteByte(data>>8); //發(fā)送高八位SPI1_ReadWriteByte(data); //第八位MP3_CCS_SET(1); //MP3_CMD_CS=1; SPI1_SetSpeed(1);//高速 } //向VS1003寫數(shù)據 void Vs1003_DATA_Write(u8 data) {MP3_DCS_SET(0); //MP3_DATA_CS=0;SPI1_ReadWriteByte(data);MP3_DCS_SET(1); //MP3_DATA_CS=1;MP3_CCS_SET(1); //MP3_CMD_CS=1; } //讀VS1003的寄存器 //讀VS1003 //注意不要用倍速讀取,會出錯 u16 Vs1003_REG_Read(u8 address) { u16 temp=0; while((GPIOC->IDR&MP3_DREQ)==0);//非等待空閑狀態(tài) SPI1_SetSpeed(0);//低速 MP3_DCS_SET(1); //MP3_DATA_CS=1;MP3_CCS_SET(0); //MP3_CMD_CS=0;SPI1_ReadWriteByte(VS_READ_COMMAND);//發(fā)送VS1003的讀命令SPI1_ReadWriteByte(address); //地址temp=SPI1_ReadWriteByte(0xff); //讀取高字節(jié)temp=temp<<8;temp+=SPI1_ReadWriteByte(0xff); //讀取低字節(jié)MP3_CCS_SET(1); //MP3_CMD_CS=1; SPI1_SetSpeed(1);//高速return temp; } //FOR WAV HEAD0 :0X7761 HEAD1:0X7665 //FOR MIDI HEAD0 :other info HEAD1:0X4D54 //FOR WMA HEAD0 :data speed HEAD1:0X574D //FOR MP3 HEAD0 :data speed HEAD1:ID //比特率預定值 const u16 bitrate[2][16]= { {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0} }; //返回Kbps的大小 //得到mp3&wma的波特率 u16 GetHeadInfo(void) {unsigned int HEAD0;unsigned int HEAD1; HEAD0=Vs1003_REG_Read(SPI_HDAT0); HEAD1=Vs1003_REG_Read(SPI_HDAT1);switch(HEAD1){ case 0x7665:return 0;//WAV格式case 0X4D54:return 1;//MIDI格式 case 0X574D://WMA格式{HEAD1=HEAD0*2/25;if((HEAD1%10)>5)return HEAD1/10+1;else return HEAD1/10;}default://MP3格式{HEAD1>>=3;HEAD1=HEAD1&0x03; if(HEAD1==3)HEAD1=1;else HEAD1=0;return bitrate[HEAD1][HEAD0>>12];}} } //重設解碼時間 void ResetDecodeTime(void) {Vs1003_CMD_Write(SPI_DECODE_TIME,0x0000);Vs1003_CMD_Write(SPI_DECODE_TIME,0x0000);//操作兩次 } //得到mp3的播放時間n sec u16 GetDecodeTime(void) { return Vs1003_REG_Read(SPI_DECODE_TIME); } //加載頻譜分析的代碼到VS1003 void LoadPatch(void) {u16 i;for (i=0;i<943;i++)Vs1003_CMD_Write(atab[i],dtab[i]); delay_ms(10); } //得到頻譜數(shù)據 void GetSpec(u8 *p) {u8 byteIndex=0;u8 temp;Vs1003_CMD_Write(SPI_WRAMADDR,0x1804); for (byteIndex=0;byteIndex<14;byteIndex++) { temp=Vs1003_REG_Read(SPI_WRAM)&0x63;//取小于100的數(shù) *p++=temp;} } void SPI1_RST(void) {RCC->APB2RSTR|=1<<12;//復位SPI1delay_ms(10); RCC->APB2RSTR&=~(1<<12);//結束復位SPI1delay_ms(10); SPI1->CR1|=0<<10;//全雙工模式 SPI1->CR1|=1<<9; //軟件nss管理SPI1->CR1|=1<<8; SPI1->CR1|=1<<2; //SPI主機SPI1->CR1|=0<<11;//8bit數(shù)據格式 SPI1->CR1|=1<<1; //空閑模式下SCK為1 CPOL=1SPI1->CR1|=1<<0; //數(shù)據采樣從第二個時間邊沿開始,CPHA=1 SPI1->CR1|=6<<3; //Fsck=Fpclk/128 =562.5khzSPI1->CR1|=0<<7; //MSBfirst SPI1->CR1|=1<<6; //SPI設備使能 } //設定vs1003播放的音量和高低音 void set1003(void) {u8 t;u16 bass=0; //暫存音調寄存器值u16 volt=0; //暫存音量值u8 vset=0; //暫存音量值 vset=255-vs1003ram[4];//取反一下,得到最大值,表示最大的表示 volt=vset;volt<<=8;volt+=vset;//得到音量設置后大小//0,henh.1,hfreq.2,lenh.3,lfreq for(t=0;t<4;t++){bass<<=4;bass+=vs1003ram[t]; } Vs1003_CMD_Write(SPI_BASS,bass);//BASS Vs1003_CMD_Write(SPI_VOL,volt); //設音量 } //初始化VS1003的IO口 void Vs1003_Init(void) {RCC->APB2ENR|=1<<2; //PORTA時鐘使能 RCC->APB2ENR|=1<<12; //SPI1時鐘使能 //存儲器映射,不用理 #ifdef VECT_TAB_RAM NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif GPIOA->CRL&=0X000FFFFF;//PA5.6.7復用輸出GPIOA->CRL|=0XBBB00000; GPIOA->ODR|=0X00E0;//PA5.6.7上拉 SPI1->CR1|=0<<10;//全雙工模式 SPI1->CR1|=1<<9; //軟件nss管理SPI1->CR1|=1<<8; SPI1->CR1|=1<<2; //SPI主機SPI1->CR1|=0<<11;//8bit數(shù)據格式 SPI1->CR1|=1<<1; //空閑模式下SCK為1 CPOL=1SPI1->CR1|=1<<0; //數(shù)據采樣從第二個時間邊沿開始,CPHA=1 SPI1->CR1|=6<<3; //Fsck=Fpclk/128 =562.5khzSPI1->CR1|=0<<7; //MSBfirst SPI1->CR1|=1<<6; //SPI設備使能//以上初始化VS1003的SPI連接口,SPI1口 //所以上拉之前,必須使能時鐘.才能實現(xiàn)真正的上拉輸出RCC->APB2ENR|=1<<2; //PA時鐘使能RCC->APB2ENR|=1<<4; //PC時鐘使能RCC->APB2ENR|=1<<0; //開啟輔助時鐘AFIO->MAPR=0X04000000;//關閉JTAG,只有關閉JTAG,才能使用PA14 GPIOA->CRH&=0XF0FFFFF0;//PA.8/14推挽輸出GPIOA->CRH|=0X03000003; GPIOA->ODR|=0X4100; //上拉GPIOC->CRH&=0XFFFFFF00;GPIOC->CRH|=0X00000083;//PC.8輸出 ,PC9輸入GPIOC->ODR|=1<<8; //PC.8上拉 GPIOC->ODR|=1<<9; //PC.9上拉 }6 最后
總結
以上是生活随笔為你收集整理的毕业设计 单片机音乐播放器设计 - 物联网 嵌入式 Stm32的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么用matlab剔除数据的异常值(3σ
- 下一篇: 多版本node的安装与切换详细操作