基于STM32+VS1053B的MP3设计

目录

一、整体介绍

二、模块介绍

1.     stm32主控

2.     VS1053B音频解码

3.     按键

4.     OLED显示

 三、程序代码:

资料下载地址:基于STM32+VS1053B的MP3设计

一、整体介绍

话不多说,先看看整体原理图:

制作出来的实物图如下: 

  整体上,分为四个部分:
(1)    STM32主控部分;
(2)    VS1053B音频解码部分;
(3)    按键控制部分;
(4)    OLED显示部分;
 上电,通过一系列的测试和检测后,进入音乐播放界面,如图:
第一排显示的是标签,
第二行,显示的是当前播放歌曲的索引、总歌曲数目以及当前声音大小,
第三行,显示的是当前歌曲的播放进度以及位率,
第四行,显示的是当前歌曲名(因为没有取字库,所以我就把他翻译成了英文,肯定是不准确的,主要目的只是提示自己)。
此外,通过按键,可以对播放歌曲进行切换和音量的控制。
下面我们来分别看看这四个部分:

二、模块介绍

1.     stm32主控

这里我们使用的是stm32F103VET6,内部Flash有512K,100pin的外部引脚,属于大容量芯片,足够我们diy。对于本设计,stm32这一块用到的知识点有:SDIO驱动SD卡,SPI驱动VS1053B芯片,IIC驱动OLED;对于程序,用的是stm32标准库,小伙伴们可以根据自己的需要,自行决定是否需要补一下相关方面的知识。
主控就不再多做介绍了,因为太常见了,这里只是提一下。

2.     VS1053B音频解码

这一部分的原理图如图:

 

VS1053B,是一款功能比较强大的音频解码芯片,该芯片可以实现对MP3/OGG/WMA/FLAC/WAV/AAC/MIDI等音频格式的解码,同时还可以支持ADPCM/OGG等格式的编码,经过我的测试,建议大家用最常用的.MP3 格式的音乐文件;
具体的介绍,请看资料里面的资料手册,那里说的很清楚,我再多说,显得就很尴尬了。

3.     按键

这个常见到不能再常见了,因为需要按键对歌曲进行切换和音量大小的控制,所以,这里只是简单的把他列出来而已。

4.     OLED显示

OLED只是用来显示提示的作用,这里我们用的是0.96寸4pin的IIC驱动的OLED,也是非常常见的玩意儿,不清楚使用的,可以看看相关的资料。

 三、程序代码:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"                       
#include "oled.h"
#include "bmp.h"
#include "key.h"      
#include "malloc.h"
#include "sdio_sdcard.h"  
#include "vs10xx.h"
#include "mp3player.h"
#include "exfuns.h"
//通过串口打印SD卡相关信息
void show_sdcard_info(void)
{switch(SDCardInfo.CardType){caseSDIO_STD_CAPACITY_SD_CARD_V1_1:printf("Card Type:SDSCV1.1\r\n");break;caseSDIO_STD_CAPACITY_SD_CARD_V2_0:printf("Card Type:SDSCV2.0\r\n");break;caseSDIO_HIGH_CAPACITY_SD_CARD:printf("Card Type:SDHC V2.0\r\n");break;caseSDIO_MULTIMEDIA_CARD:printf("Card Type:MMC Card\r\n");break;}      printf("Card ManufacturerID:%d\r\n",SDCardInfo.SD_cid.ManufacturerID);    //制造商IDprintf("CardRCA:%d\r\n",SDCardInfo.RCA);                 //卡相对地址printf("CardCapacity:%d MB\r\n",(u32)(SDCardInfo.CardCapacity>>20));        //显示容量printf("CardBlockSize:%d\r\n\r\n",SDCardInfo.CardBlockSize);                          //显示块大小
}
intmain(void)
{      delay_init();                   //延时函数初始化         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级uart_init(115200);          //串口初始化为115200LED_Init();                               //初始化与LED连接的硬件接口KEY_Init();                                  //初始化按键VS_Init();                         //初始化VS1053delay_ms(1000);   //适当延时OLED_Init();   //OLED初始化OLED_ColorTurn(0);//0正常显示,1 反色显示OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示OLED_Refresh();  delay_ms(1000);   //适当延时my_mem_init(SRAMIN);                  //初始化内部内存池exfuns_init();                             //为fatfs相关变量申请内存  f_mount(fs[0],"0:",1);            //挂载SD卡f_mount(fs[1],"1:",1);            //挂载FLASH.while(SD_Init())//检测不到SD卡{OLED_ShowString(0,0,"SD_ERROR!!",16);    //错误提示信息闪烁delay_ms(200);OLED_ShowString(0,0,"          ",16);delay_ms(200);LED1=!LED1;//DS1闪烁}show_sdcard_info();       //打印SD卡相关信息//检测SD卡成功OLED_ShowString(0,0,"SD_OK      ",16);         delay_ms(1000);OLED_ShowString(0,0,"LHSMD- MP3",16);while(1){LED1=0;OLED_ShowString(0,16,"storagetest",16);printf("RamTest:0X%04X\r\n",VS_Ram_Test());//打印RAM测试结果OLED_ShowString(0,16,"sintest    ",16);VS_Sine_Test();   //正弦波测试delay_ms(1000);LED1=1;OLED_Clear();OLED_ShowString(0,0,"  LHSMD - MP3",16);mp3_play();  //放歌操作}   
}
mp3player.c文件:
#include "mp3player.h"
#include "vs10xx.h"         
#include "delay.h"
#include "led.h"
#include "key.h"
//#include "lcd.h"            
#include "malloc.h"
//#include "text.h"
#include "string.h"
#include "exfuns.h"  
#include "ff.h"   
#include "flac.h"     
#include "usart.h"   
#include "oled.h"
//显示曲目索引
//index:当前索引
//total:总文件数
void mp3_index_show(u16 index,u16 total)
{//显示当前曲目的索引,及总曲目数OLED_ShowNum(0,16,index,3,16);OLED_ShowString(24,16,"/",16);  OLED_ShowNum(32,16,total,3,16);
}
//显示当前音量
void mp3_vol_show(u8 vol)
{                                      OLED_ShowString(64,16,"VOL:",16);   OLED_ShowNum(105,16,vol,2,16);       //显示音量
}
u8 time_buf[16];
u16 f_kbps=0;//歌曲文件位率     
//显示播放时间,比特率 信息
//lenth:歌曲总长度
void mp3_msg_show(u32 lenth)
{      staticu16 playtime=0;//播放时间标记            u16 time=0;// 时间变量u16sec=0;// 时间变量u16temp=0;   if(f_kbps==0xffff)//未更新过{playtime=0;f_kbps=VS_Get_HeadInfo();     //获得比特率}               time=VS_Get_DecodeTime();//得到解码时间if(playtime==0)playtime=time;elseif((time!=playtime)&&(time!=0))//1s时间到,更新显示数据{playtime=time;//更新时间                                       temp=VS_Get_HeadInfo();//获得比特率                                    if(temp!=f_kbps){f_kbps=temp;//更新KBPS                                          }                          if(f_kbps)sec=(lenth/f_kbps)/125;//得到秒钟数(文件长度(字节)/(1000/8)/比特率=持续秒钟数elsesec=0;//非法位率                  //显示播放时间     sprintf((char*)time_buf,"%02d:%02d/%02d:%02d%003d",time/60,time%60,sec/60,sec%60,f_kbps);OLED_ShowString(0,32,time_buf,16);LED1=!LED1;            //DS0翻转}            
}                                         
//得到path路径下,目标文件的总个数
//path:路径               
//返回值:总有效文件数
u16 mp3_get_tnum(u8 *path)
{         u8res;u16rval=0;DIR tdir;                  //临时目录FILINFOtfileinfo;    //临时文件信息            u8*fn;                                                                          res=f_opendir(&tdir,(const TCHAR*)path); //打开目录tfileinfo.lfsize=_MAX_LFN*2+1;                                                //长文件名最大长度tfileinfo.lfname=mymalloc(SRAMIN,tfileinfo.lfsize);   //为长文件缓存区分配内存if(res==FR_OK&&tfileinfo.lfname!=NULL){while(1)//查询总的有效文件数{res=f_readdir(&tdir,&tfileinfo);                       //读取目录下的一个文件if(res!=FR_OK||tfileinfo.fname[0]==0)break;      //错误了/到末尾了,退出               fn=(u8*)(*tfileinfo.lfname?tfileinfo.lfname:tfileinfo.fname);                       res=f_typetell(fn);  if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件   {rval++;//有效文件数增加1}         }  }myfree(SRAMIN,tfileinfo.lfname);returnrval;
}
//播放音乐
void mp3_play(void)
{u8res;DIR mp3dir;                     //目录FILINFOmp3fileinfo;//文件信息u8*fn;                           //长文件名u8*pname;                      //带路径的文件名u16totmp3num;            //音乐文件总数u16curindex;           //图片当前索引u8key;                               //键值               u16 temp;u16*mp3indextbl; //音乐索引表while(f_opendir(&mp3dir,"0:/music"))//打开图片文件夹{           OLED_ShowString(0,32,"musicfile ERR!",16);            
delay_ms(200);      OLED_ShowString(0,32,"               ",16);delay_ms(200);               }                                                                             totmp3num=mp3_get_tnum("0:/music");//得到总有效文件数while(totmp3num==NULL)//音乐文件总数为0         {           OLED_ShowString(0,32,"nomusic file  ",16); //没有音乐文件提示delay_ms(200);                                                            }                                                                                         mp3fileinfo.lfsize=_MAX_LFN*2+1;                                                  //长文件名最大长度mp3fileinfo.lfname=mymalloc(SRAMIN,mp3fileinfo.lfsize);       //为长文件缓存区分配内存pname=mymalloc(SRAMIN,mp3fileinfo.lfsize);    //为带路径的文件名分配内存mp3indextbl=mymalloc(SRAMIN,2*totmp3num);        //申请2*totmp3num个字节的内存,用于存放音乐文件索引while(mp3fileinfo.lfname==NULL||pname==NULL||mp3indextbl==NULL)//内存分配出错{           OLED_ShowString(0,32,"storageERR    ",16);delay_ms(200);                                                   }     VS_HD_Reset();    //VS1053硬复位VS_Soft_Reset();  //VS1053软复位vsset.mvol=200;                                                 //默认设置音量为200.mp3_vol_show((vsset.mvol-100)/5);    //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30   //记录索引res=f_opendir(&mp3dir,"0:/music"); //打开目录if(res==FR_OK){curindex=0;//当前索引为0while(1)//全部查询一遍{temp=mp3dir.index;                                                                     //记录当前indexres=f_readdir(&mp3dir,&mp3fileinfo);                  //读取目录下的一个文件if(res!=FR_OK||mp3fileinfo.fname[0]==0)break;        //错误了/到末尾了,退出               fn=(u8*)(*mp3fileinfo.lfname?mp3fileinfo.lfname:mp3fileinfo.fname);                     res=f_typetell(fn);  if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件   {mp3indextbl[curindex]=temp;//记录索引curindex++;}         }}   curindex=0;                                                                                             //从0开始显示res=f_opendir(&mp3dir,(constTCHAR*)"0:/music");  //打开目录while(res==FR_OK)//打开成功{      dir_sdi(&mp3dir,mp3indextbl[curindex]);                       //改变当前目录索引      res=f_readdir(&mp3dir,&mp3fileinfo);                  //读取目录下的一个文件if(res!=FR_OK||mp3fileinfo.fname[0]==0)break;        //错误了/到末尾了,退出fn=(u8*)(*mp3fileinfo.lfname?mp3fileinfo.lfname:mp3fileinfo.fname);                     strcpy((char*)pname,"0:/music/");                                  //复制路径(目录)strcat((char*)pname,(constchar*)fn);                          //将文件名接在后面OLED_ShowString(0,48,"                ",16);     //清楚之前的显示OLED_ShowString(0,48,fn,16);                                //显示歌曲名字mp3_index_show(curindex+1,totmp3num);key=mp3_play_song(pname);                                                //播放这个MP3   if(key==2)                 //上一曲{if(curindex)curindex--;elsecurindex=totmp3num-1;}else if(key<=1)//下一曲{curindex++;                  if(curindex>=totmp3num)curindex=0;//到末尾的时候,自动从头开始}else break;     //产生了错误         }                                                                                               myfree(SRAMIN,mp3fileinfo.lfname);  //释放内存                          myfree(SRAMIN,pname);                                 //释放内存                          myfree(SRAMIN,mp3indextbl);                       //释放内存      
}
//播放一曲指定的歌曲                                                                                                                                       
//返回值:0,正常播放完成
//              1,下一曲
//      2,上一曲
//      0XFF,出现错误了
u8 mp3_play_song(u8 *pname)
{      FIL* fmp3;u16 br;u8res,rval;        u8*databuf;                    u16i=0;u8key;               rval=0;         fmp3=(FIL*)mymalloc(SRAMIN,sizeof(FIL));//申请内存databuf=(u8*)mymalloc(SRAMIN,4096);               //开辟4096字节的内存区域if(databuf==NULL||fmp3==NULL)rval=0XFF;//内存申请失败.if(rval==0){         VS_Restart_Play();                                           //重启播放VS_Set_All();                                              //设置音量等信息                          VS_Reset_DecodeTime();                                          //复位解码时间     res=f_typetell(pname);                                   //得到文件后缀                                                                                       if(res==0x4c)//如果是flac,加载patch{      VS_Load_Patch((u16*)vs1053b_patch,VS1053B_PATCHLEN);}                                                                                                                  res=f_open(fmp3,(constTCHAR*)pname,FA_READ);//打开文件        if(res==0)//打开成功.{VS_SPI_SpeedHigh();      //高速                                                   while(rval==0){res=f_read(fmp3,databuf,4096,(UINT*)&br);//读出4096个字节  i=0;do//主播放循环{     if(VS_Send_MusicData(databuf+i)==0)//给VS10XX发送音频数据{i+=32;}else   {key=KEY_Scan(0);switch(key){caseKEY1_PRES:rval=1;              //下一曲break;caseKEY3_PRES:rval=2;              //上一曲break;caseKEY2_PRES:    //音量增加if(vsset.mvol<250){vsset.mvol+=5;VS_Set_Vol(vsset.mvol);         }elsevsset.mvol=250;mp3_vol_show((vsset.mvol-100)/5);//音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30   break;caseKEY4_PRES:    //音量减if(vsset.mvol>100){vsset.mvol-=5;VS_Set_Vol(vsset.mvol);         }else vsset.mvol=100;mp3_vol_show((vsset.mvol-100)/5);    //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30   break;}mp3_msg_show(fmp3->fsize);//显示信息      }                       }while(i<4096);//循环发送4096个字节if(br!=4096||res!=0){rval=0;break;//读完了.               }                                                           }f_close(fmp3);}elserval=0XFF;//出现错误         }                                                               myfree(SRAMIN,databuf);                                    myfree(SRAMIN,fmp3);returnrval;                                      
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/296650.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Pt】马灯贴图绘制过程 05-铁丝与渲染出图

目录 效果 步骤 一、基本材质 二、浮尘 三、渲染 效果 步骤 一、基本材质 CtrlAlt鼠标右键选中指定的纹理集 在智能材质中将“Iron Forged Old”加入图层 将智能材质“Iron Forged Old”文件夹打开&#xff0c;将图层“Base”和“Edge”的基本颜色改暗一点 二、浮尘 新…

推荐算法策略需求-rank model优化

1.pred_oobe (base) [rusxx]$ pwd /home/disk2/data/xx/icode/baidu/oxygen/rus-pipeline/pipeline-migrate/UserBaseActiveStatPipeline/his_session (base) [rusxx]$ sh test.sh 2. user_skill_history_dict_expt2包含userid [workxx]$ vim /home/work/xx/du-rus/du_rus_o…

B树、B+树、哈夫曼树

目录 1. B树2. B树3. 哈夫曼树 1. B树 特点&#xff1a;一个节点当中可以有多个值&#xff0c;节点内部key 值是有序的&#xff0c;节点内部存储的是key-value类型的数据 磁盘中文件存储用B树。 4阶B树一个节点最多三个key值 5阶B树一个节点最多四个key值 B树有很多的分支&…

[源码] Android 上的一些快捷方式,如通知、快捷方式等

目录 一、通知0. 配置权限1. 测试发送通知代码2. 打开通知设置界面代码3. 前台服务创建常驻通知 二、快捷方式1. 测试添加动态快捷方式代码 三、开发者图块四、桌面小部件 基于jetpack compose 框架的使用代码 一、通知 参见 官方文档 0. 配置权限 <uses-permission andr…

剖析C++中的菱形继承

剖析C中的菱形继承 文章目录 剖析C中的菱形继承前言菱形继承虚拟继承与虚基表总结 前言 在面向对象编程中&#xff0c;继承允许我们构建出复杂的类关系和对象模型。然而&#xff0c;当多个类继承自同一个基类时&#xff0c;可能会引发结构上的冲突和数据冗余。这种情况在C中被…

Linux 命令 top 详解

1 top命令介绍 Linux系统中&#xff0c;Top命令主要用于实时运行系统的监控&#xff0c;包括Linux内核管理的进程或者线程的资源占用情况。这个命令对所有正在运行的进程和系统负荷提供不断更新的概览信息&#xff0c;包括系统负载、CPU利用分布情况、内存使用、每个进程的内容…

Vue2电商前台项目(一):项目前的初始化及搭建

一、项目初始化 创建项目&#xff1a;sudo vue create app 1.项目配置 &#xff08;1&#xff09;浏览器自动打开 在package.json文件中&#xff0c;serve后面加上 --open "scripts": {"serve": "vue-cli-service serve --open","buil…

JimuReport 积木报表

一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于 excel 操作风格&#xff0c;通过拖拽完成报表设计…

spring总结-基于XML管理bean超详细

spring ioc总结-基于XML管理bean 前言实验一 [重要]创建bean1、目标和思路①目标②思路 2、创建Maven Module3、创建组件类4、创建spring配置文件7、无参构造器8、用IOC容器创建对象和自己建区别 实验二 [重要]获取bean1、方式一&#xff1a;根据id获取2、方式二&#xff1a;根…

【详解旋转编码器原理与应用】:从类型到作用全面解读

旋转编码器是一种精密的传感器装置&#xff0c;主要用于测量旋转物体的角度、速度、方向或位移等机械运动参数&#xff0c;并将其转换为相应的电脉冲信号或数字信号输出。这种装置广泛应用于工业自动化、机器人技术、伺服控制系统、电梯、电机控制、音视频设备、游戏控制器以及…

数据结构——二叉树(堆)

大家好我是小峰&#xff0c;今天我们开始学习二叉树。 首先我们来学习什么是树&#xff1f; 树概念及结构 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的…

前端三剑客 —— CSS (第三节)

目录 上节回顾&#xff1a; 1.CSS使用有以下几种样式; 2.选择器 1.基本选择器 2.包含选择器 3.属性选择器 [] 4.伪类选择器 &#xff1a; 5.伪元素选择器 ::before :after 3.常见样式的使用 常见样式参考表 一些特殊样式 媒体查询 自定义字体 变换效果 translate&…

从 Redis 开源协议变更到 ES 国产化:一次技术自主的机遇

引言 近日&#xff0c;Redis Labs 宣布其主导的开源项目 Redis 将采用双重源代码可用许可证&#xff08;RSALv2&#xff09;和服务器端公共许可证&#xff08;SSPLv1&#xff09;。这一重大决策标志着 Redis 从传统的 BSD 许可证向更加严格的控制权转变&#xff0c;同时也引发…

Servlet Response的常用方法 缓存和乱码处理

前言 Servlet Response相关的信息&#xff0c;在service方法中使用的是HttpServletResponse&#xff0c;它继承自ServletResponse&#xff0c;扩展了Http协议相关的内容&#xff0c;下面简单记录一下它的基本用法。 一、response组成内容 以下是一个常见response响应的内容&…

Ruby 之交租阶段信息生成

题目 我看了一下&#xff0c;这个题目应该不是什么机密&#xff0c;所以先放上来了。大概意思是根据合同信息生成交租阶段信息。 解答 要求是要使用 Ruby 生成交租阶段信息&#xff0c;由于时间比较仓促&#xff0c;变量名那些就用得随意了些。要点主要有下面这些&#xff1a…

Redission--布隆过滤器解决缓存穿透问题

布隆过滤器在缓存穿透问题中的使用 布隆过滤器的核心是一个位数组 布隆过滤器的误判 使用Redission的布隆过滤器步骤 添加 Redission 依赖&#xff1a;首先需要将 Redission 添加到你的 Java 项目中&#xff0c;你可以通过 Maven 来添加 Redission 的依赖。 创建 Redissio…

机器学习全攻略:概念、流程、分类与行业应用案例集锦

目录 1.引言 2.从零开始认识机器学习&#xff1a;基本概念与重要术语 3.五步走&#xff1a;掌握机器学习项目执行的完整流程 3.1.问题定义与数据收集 3.2.数据预处理与特征工程 3.3.模型选择与训练 3.4.模型评估与优化 3.5.模型部署与监控 4.深入了解各类机器学习方法…

LeetCode-146. LRU 缓存【设计 哈希表 链表 双向链表】

LeetCode-146. LRU 缓存【设计 哈希表 链表 双向链表】 题目描述&#xff1a;解题思路一&#xff1a;双向链表&#xff0c;函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。一张图&#xff1a;知识点__slots__ 解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&am…

使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流

使用 Flume 将 CSV 数据导入 Kafka&#xff1a;实现实时数据流 文介绍了如何使用 Apache Flume 将 CSV 格式的数据从本地文件系统导入到 Apache Kafka 中&#xff0c;以实现实时数据流处理。通过 Flume 的配置和操作步骤&#xff0c;我们可以轻松地将数据从 CSV 文件中读取并发…

医院云HIS系统源码,二级医院、专科医院his系统源码,经扩展后能够应用于医联体/医共体

基于云计算技术的B/S架构的HIS系统&#xff0c;为医疗机构提供标准化的、信息化的、可共享的医疗信息管理系统&#xff0c;实现医患事务管理和临床诊疗管理等标准医疗管理信息系统的功能。 系统利用云计算平台的技术优势&#xff0c;建立统一的云HIS、云病历、云LIS&#xff0…