项目分享| STM32打造“云”音乐播放器(源码开源)
不想错过我的推送,记得右上角-查看公众号-设为星标,摘下星星送给我
基于STM32实现的项目种类众多,涉猎应用也非常广泛。今日,达尔闻特邀分享者DBinary(知乎ID:DBinary)为大家开源他制作的STM32实现的“云”音乐播放器——所有音乐存储在服务器上,STM32播放时从服务器上读取播放。
看上去是一个简单的项目,但实现起来要复杂的多。完成这个项目,从简单的单片机模块连线开始,逐步涉及到总线时钟、网络协议、数据结构优化、声学相关的知识。
要打造这个简陋版本的云播放器,需要完成以下程序的开发
1)STM32一些初始化
2)W5500驱动程序
3)VS1053b驱动程序
4)云音乐播放器Server段
5)驱动逻辑调度器
以上程序,在“达尔闻说”微信公众号回复:云音乐播放器,就可以获得。
03项目重点用STM32做一个网络音乐播放器,我们要考虑诸多问题,例如片上的RAM不够大,因此不能和PC端开发APP一样奢侈地开上几十兆的内存做缓存。另外100多MHZ这一并不算高的主频也对编码提出了挑战,我们不仅仅要合理分配处理网络与音频的资源分配问题,内部总线的速率乃至于音乐播放的比特率都应该在严格的计算后进行处理。
最后开发也是个问题,即使是C语言也并没有提供完整的标准库供你调用。不过幸运的是这些代码已经开源给大家了。另外,VS1053的硬件码与W5500自带的Socket也帮我们省下了一大堆解码与协议上的问题。
接下来,就给大家呈现自制“云”音乐播放器的重要步骤。
第一步:开发环境安装
在本项目里,需要安装两个软件:Keil和Visual studio。Keil对主流芯片都是支持的;Visual studio 编辑器用于编辑单片机与音乐服务端的代码。
虽然开源的所有源代码可以直接编译执行或者直接烧录到芯片当中,但是仍然建议大家购买JLINK或者ST-Link等调试器并着手修改代码来完成自己所需要的功能。
第二步:确定总线协议
内部总线的协议选用SPI,不管是W5500还是VS1053模块都支持SPI协议,并且STM32F407ZGT6有三个SPI接口有良好的硬件加速,同时STLibrary(ST公司提供给STM系列的库)中有着对SPI的封装库,这意味着不用花太多功夫在底层通讯协议上。
SPI协议用到的引脚只有四根,连线简单,而且在速度上相对于串口这种龟速接口,至少能达到要求,即便不使用DMA,也不会对效率上产生难以负担的问题。
W5500和VS1053模块上都有SPI的标注,对照其原理图的标注使用杜邦线就可以把它们接到STM32F407ZGT6的核心板上。
VS1053模块SPI接口
第三步:编程
#define_IN
#define_OUT
#definePX_FALSE 0
#definePX_TRUE 1
#definePX_NULL 0
#definePX_PI 3.14159265359f
typedefvoid px_void;
typedefint px_bool;
typedefunsigned int px_dword;
typedefshort px_short;
typedefunsigned short px_word;
typedefunsigned short px_ushort;
typedefunsigned int px_uint;
typedefint px_int;
typedefchar px_char;
typedefchar px_byte;
typedefunsigned char px_uchar;
typedefunsigned long px_ulong;
typedeflong px_long;
typedeffloat px_float;
typedefdouble px_double;
typedeflong long px_qword;
typedefunsigned long long px_u64;
typedefunsigned int px_u32;
typedefunsigned short px_u16;
typedefunsigned char px_u8;
◆ 时间中断
实现的代码实际非常简单,定义一个PX_Linker结构体
typedefstruct __PX_LINKER
{
px_void*data;
px_bool(* _PX_LinkerInit)(px_void *Info);
px_int(* _PX_LinkerWrite)(_IN px_void *buffer,px_int size);
px_int(* _PX_LinkerRead) (_OUT px_void *buffer,px_int size);
px_int(* _PX_LinkerIOCTL)(_IN px_int ioctl,_IN px_int io,_IN px_void *param);
}PX_Linker;
当中包含四个函数指针,一个data类型指针。
其中_PX_LinkerInit用于实现通讯协议的初始化代码, _PX_LinkerWrite用于实现写数据代码,_PX_LinkerRead用于实现读数据代码,_PX_LinkerIOCTL则用于实现所有其他的控制代码,包括延迟和GPIO口的控制。
同时我们提供四个函数对PX_Linker进行操作避免直接操作结构体:
其中PX_LinkerInit用于对PX_Linker进行初始化,包括设置其四个函数指针,函数原型如下:
px_bool PX_LinkerInit(PX_Linker *linker, px_void *Init,px_void *Write,px_void *Read,px_void *ioctl,px_void *param);
PX_LinkerWrite为一个宏定义,其目的仅仅只是调用写函数,其宏定义规则如下.其中lnk为指向PX_Linker的指针,buffer为写buffer,size为希望写入的字节数。#definePX_LinkerWrite(lnk,buffer,size) ((lnk)->_PX_LinkerWrite(buffer,size))
PX_LinkerRead为一个宏定义,其目的仅仅只是调用读函数,其宏定义规则如下.其中lnk为指向PX_Linker的指针,buffer为读取buffer指针,size为缓存区最大的大小。#definePX_LinkerRead(lnk,buffer,size) ((lnk)->_PX_LinkerRead(buffer,size))
PX_LinkerIOCTL用于调用PX_Linker中的控制函数.其中参数IOCTL为控制标识符.io及param都为传入参数。#definePX_LinkerIOCTL(lnk,ioctl,io,param) ((lnk)->_PX_LinkerIOCTL(ioctl,io,param))
在编写驱动时,我们只需要在上层(例如main函数中)去考虑芯片相关的通讯协议,同时实现几个PX_Linker 所需要的代码实现,然后将PX_Linker传递给驱动程序的逻辑实现当中就可以了。因为不包含主控芯片相关的代码,驱动的代码直接拷贝到其它的项目中也不会出什么问题,需要做的就仅仅只是重新在自己喜欢的地方完成通讯总线的实现就可以了。RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex; //全双工
SPI_InitStructure.SPI_Mode= SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize= SPI_DataSize_8b;//8位
SPI_InitStructure.SPI_CPOL= SPI_CPOL_High;//极性
SPI_InitStructure.SPI_CPHA= SPI_CPHA_2Edge;//相位
SPI_InitStructure.SPI_NSS= SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_32;//波特率32分频
SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//高位在前地位在后
SPI_InitStructure.SPI_CRCPolynomial= 7;
SPI_Init(SPI2,&SPI_InitStructure);
SPI_Cmd(SPI2,ENABLE);
W5500读寄存器的步骤如下:1)拉低SS片选2)写入需要读写的地址(2字节word)3)写入读寄存器操作码(这是一个位与操作,请查阅PX_DEVICE_W5500.c)4)读寄存器值5)拉高SS片选W5500写寄存器的步骤如下:1)拉低SS片选2)写入需要读写的地址(2字节word)3)写入写寄存器操作码(这是一个位与操作,请查阅PX_DEVICE_W5500.c)4)写值5)拉高SS片选W5500写数据步骤如下:1)拉低SS片选2)写入写寄存器操作码并与上Socket*32+8的值(这是一个位与操作,请查阅PX_DEVICE_W5500.c)3)写值4)拉高SS片选W5500对网络的读写是通过socket来执行的。在W5500的驱动中,只实现了UDP通讯,使用到了Socket0作为通讯,延迟。使用了GCOSNETWORK对W5500进行封装,不需要关注socket细节,直接进行网络通讯就行了。GCOSNETWORK代码:
#inclide “GCOSNETWORK.h”
char buffer[1460];
int main(void)
{
GCOS_addr_in addr;
int size; if(GCOS_UDPInit(“192.168.1.100”,”255.255.255.0”,”192.168.1.1”,”12345”))
{
if(size=GCOS_UDPRead(&addr,buffer))
GCOS_UDPWrite(&addr,buffer,size);
}
}
最后,这个项目的完整源代码可以在“达尔闻说”微信号回复:云音乐播放器。小伙伴们可以尝试将它复现出来,然后添加一些自己想要的功能。如果你即将要做毕设,这个项目是不错的选择。
END
达尔闻项目分享系列——聚焦物联网、嵌入式、AI、FPGA等热门应用技术,开源分享原理图、代码等项目方案,做你手边的知识库。
项目分享系列集锦:
嵌入式开发板的云计算平台搭建
STM32实现最简单空中鼠标
利用树莓派与Web界面远程控制家电
我们是妮mo,达尔闻创始人,只讲技术不撩汉的小姐姐。达尔闻在线教育平台旨在服务电子行业专业人士,提供技能培训视频,覆盖各细分领域热门话题,比如嵌入式,FPGA,人工智能等。并针对不同人群量身定制分层级学习内容,例如常用知识点,拆解评测,电赛/智能车/考研等,欢迎关注。
官网:www.darwinlearns.com
B站:达尔闻
QQ群:786258064