当前位置:网站首页>STM32+HC05串口蓝牙设计简易的蓝牙音箱

STM32+HC05串口蓝牙设计简易的蓝牙音箱

2022-07-06 10:17:00 华为云

一、环境介绍

MCU: STM32F103C8T6

蓝牙模块: HC05 (串口蓝牙)

音频解码模块: VS1053B

OLED显示屏: 0.96寸SPI接口OLED

开发软件: Keil5

上位机: 使用QT设计Android端APP

二、功能介绍

Android手机打开APP,设置好参数之后,选择音乐文件发送给蓝牙音箱设备端,HC05蓝牙收到数据之后,再传递给VS1053进行播放。程序里采用环形缓冲区,接收HC05蓝牙传递的数据,设置好传递的参数之后,基本播放音乐是很流畅的。

三、硬件实物

image.png

VS1053可以接耳机或者接音箱设备即可听音乐。

四、设置HC05蓝牙波特率

image.png
image.png

HC05蓝牙串口默认波特率是38400,为了提高蓝牙传输速率,需要修改波特率为: 921600。

五、APP端界面

image.png
image.png
image.png

六、设备端:核心代码

6.1 vs1053.c

#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;}	/*函数功能:软复位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);//设音量 }

七、Android手机APP核心源码

7.1 代码页面

image.png

7.2 mainwindow.cpp代码

#include "mainwindow.h"#include "ui_mainwindow.h"/* * 设置QT界面的样式*/void MainWindow::SetStyle(const QString &qssFile) {    QFile file(qssFile);    if (file.open(QFile::ReadOnly)) {        QString qss = QLatin1String(file.readAll());        qApp->setStyleSheet(qss);        QString PaletteColor = qss.mid(20,7);        qApp->setPalette(QPalette(QColor(PaletteColor)));        file.close();    }    else    {        qApp->setStyleSheet("");    }}static const QLatin1String serviceUuid("00001101-0000-1000-8000-00805F9B34FB");//这个字符串里面的内容就是串口模式的UuidMainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::MainWindow){    ui->setupUi(this);    this->SetStyle(":/qss/blue.css");     //设置样式表    this->setWindowTitle("HC05蓝牙音箱"); //设置标题    this->setWindowIcon(QIcon(":/wbyq.ico")); //设置图标    /*1. 实例化蓝牙相关的对象*/    discoveryAgent = new QBluetoothDeviceDiscoveryAgent();    localDevice = new QBluetoothLocalDevice();    socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);    //RfcommProtocol表示该服务使用RFCOMM套接字协议。RfcommProtocol属于模拟RS232模式,就叫串口模式    /*2. 关联蓝牙设备相关的信号*/    /*2.1 关联发现设备的槽函数,当扫描发现周围的蓝牙设备时,会发出deviceDiscovered信号*/    connect(discoveryAgent,            SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),            this,            SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))            );    //蓝牙有数据可读    connect(socket,            SIGNAL(readyRead()),            this,            SLOT(readBluetoothDataEvent())            );    //蓝牙连接建立成功    connect(socket,            SIGNAL(connected()),            this,            SLOT(bluetoothConnectedEvent())            );    //蓝牙断开连接    connect(socket,            SIGNAL(disconnected()),            this,            SLOT(bluetoothDisconnectedEvent())            );    //蓝牙写成功的数据    connect(socket,            SIGNAL(bytesWritten(qint64)),            this,            SLOT(bluetoothbytesWritten(qint64))            );    /*3.2 设置标签显示本地蓝牙的名称*/    QString name_info("本机蓝牙:");    name_info+=localDevice->name();    ui->label_BluetoothName->setText(name_info);     ui->plainTextEdit->setEnabled(false); //不可编辑     /*定时器用于定时发送文件*/     timer = new QTimer(this); //创建定时器     connect(timer, SIGNAL(timeout()), this, SLOT(update())); //关联槽函数     ConnectStat=0;     //连接状态     SendStat=0;        //文件打开状态     FileSendTime=100;  //默认每次发送的时间 单位ms     ui->lineEdit_Timer->setText(QString::number(FileSendTime));     FileSendCnt=32;    //默认每包数据值32字节     ui->lineEdit_SendFileCnt->setText(QString::number(FileSendCnt));     //创建存放音乐文件的目录     QDir dir;     if(dir.mkpath("/sdcard/WBYQ_MP3"))     {          ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目录创建成功!\n");     }     else     {          ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目录创建失败!\n");     }      MusicFileDir="assets:/nansannan.mp3";  //目录的路径      Def_MusicName="assets:/nansannan.mp3";      file.setFileName(Def_MusicName); //设置文件名称      ui->lineEdit_MusicName->setText(Def_MusicName); //默认文件名称}int count=0;//更新void MainWindow::update(){    if(SendStat)    {        int len;        if(file.atEnd()==false)  //文件未结束        {            len=file.read(FileBuff,FileSendCnt);            len=socket->write(FileBuff,len); //发送数据        }        else        {           file.close();           timer->stop(); //停止定时器           SendStat=0;           ui->plainTextEdit->setPlainText("文件读取写入完毕!\n");        }    }}MainWindow::~MainWindow(){    delete ui;    delete discoveryAgent;    delete localDevice;    delete socket;    timer->stop();    delete timer;}void MainWindow::on_pushButton_CloseBluetooth_clicked(){    /*关闭蓝牙设备*/    localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);}void MainWindow::on_pushButton_BluetoothScan_clicked(){    /*3.1 检查蓝牙是否开启*/    if(localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)    {        /*请求打开蓝牙设备*/        localDevice->powerOn();    }     /*开始扫描周围的蓝牙设备*/    discoveryAgent->start();    ui->comboBox_BluetoothDevice->clear(); //清除条目}void MainWindow::on_pushButton_DeviceVisible_clicked(){    /*设置蓝牙可见,可以被周围的设备搜索到,在Android上,此模式最多只能运行5分钟。*/    // localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);    static bool cnt=1;    cnt=!cnt;    if(cnt)    {        MusicFileDir="assets:/nansannan.mp3";  //目录的路径        QMessageBox::information(this,"提示","路径切换为内部路径!\n请选择文件!",QMessageBox::Ok,QMessageBox::Ok);    }    else    {        MusicFileDir="/mnt/sdcard";  //目录的路径        QMessageBox::information(this,"提示","路径切换为SD路径!\n请选择文件!",QMessageBox::Ok,QMessageBox::Ok);    }}void MainWindow::on_pushButton_StopScan_clicked(){    /*停止扫描周围的蓝牙设备*/    discoveryAgent->stop();}/*当扫描到周围的设备时会调用当前的槽函数*/void MainWindow::addBlueToothDevicesToList(const QBluetoothDeviceInfo &info){   // QString label = QString("%1 %2").arg(info.name()).arg(info.address().toString());    QString label = QString("%1 %2").arg(info.address().toString()).arg(info.name());    ui->comboBox_BluetoothDevice->addItem(label); //添加字符串到comboBox上}//有数据可读void MainWindow::readBluetoothDataEvent(){    QByteArray all = socket->readAll();    ui->plainTextEdit->insertPlainText(all);}//建立连接void MainWindow::bluetoothConnectedEvent(){    QMessageBox::information(this,tr("连接提示"),"蓝牙连接成功!");    ConnectStat=1;    /*停止扫描周围的蓝牙设备*/    discoveryAgent->stop();}//断开连接void MainWindow::bluetoothDisconnectedEvent(){    ConnectStat=0;    QMessageBox::information(this,tr("连接提示"),"蓝牙断开连接!");}/*在说蓝牙设备连接之前,不得不提一个非常重要的概念,就是蓝牙的Uuid,引用一下百度的:在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。UUID类可表现为短整形(16或32位)和长整形(128位)UUID。他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。UUID实例是不可改变的(immutable),只有被UUID标示的服务可以被发现。在Linux下你用一个命令uuidgen -t可以生成一个UUID值;在Windows下则执行命令uuidgen 。UUID看起来就像如下的这个形式:2d266186-01fb-47c2-8d9f-10b8ec891363。当使用生成的UUID去创建一个UUID对象,你可以去掉连字符。*///发送音乐文件void MainWindow::on_pushButton_SendData_clicked(){    if(ConnectStat)    {        if(file.open(QIODevice::ReadOnly))        {            SendStat=1;            count=0;            ui->plainTextEdit->insertPlainText("系统提示: 开始发送文件!\n");            ui->lineEdit_fileSizef->setText(QString::number(file.size()));            ui->lineEdit_fileCount->setText("");            ui->progressBar_SendCount->setMaximum(file.size()); //设置进度条显示最大值            ui->progressBar_SendCount->setValue(0); //设置进度条的值            timer->start(FileSendTime); //启动定时器        }    }}//连接蓝牙void MainWindow::on_pushButton_ConnectDev_clicked(){    QString text = ui->comboBox_BluetoothDevice->currentText();    int index = text.indexOf(' ');    if(index == -1) return;    QBluetoothAddress address(text.left(index));    QString connect_device="开始连接蓝牙设备:\n";    connect_device+=ui->comboBox_BluetoothDevice->currentText();    QMessageBox::information(this,tr("连接提示"),connect_device);    //开始连接蓝牙设备    socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);}//帮助提示void MainWindow::on_pushButton_help_clicked(){    QMessageBox::information(this,tr("帮助提示"),"本软件用于HC-05/06系列串口蓝牙连接!\n"                                             "暂时不支持BLE4.0版本蓝牙!\n"                                             "用于发送音乐文件数据,每次发送32字节,默认100ms发送间隔时间\n"                                             "软件作者:DS小龙哥\n"                                             "BUG反馈:[email protected]");}//选择音乐文件void MainWindow::on_pushButton_select_clicked(){    QString filename=QFileDialog::getOpenFileName(this,"选择发送的音乐文件",MusicFileDir,tr("*.mp3 *.MP3 *.WAV"));    //filename==选择文件的绝对路径    file.setFileName(filename);    ui->lineEdit_MusicName->setText(filename);    Def_MusicName=filename; //保存文件名称}//清除void MainWindow::on_pushButton_clear_clicked(){     ui->plainTextEdit->setPlainText("");}void MainWindow::on_pushButton_StopSend_clicked(){       timer->stop(); //停止定时器}//设置发送间隔时间void MainWindow::on_pushButton_SendTime_clicked(){    QString str=ui->lineEdit_Timer->text();    int time=str.toInt();    FileSendTime=time; //保存时间    if(time<=0)    {        QMessageBox::warning(this,"警告提示","设置错误: 发送的间隔时间最小1ms\n",QMessageBox::Ok,QMessageBox::Ok);    }    else    ui->plainTextEdit->insertPlainText(tr("设置发送间隔时间为%1ms\n").arg(time));}//修改每包发送的数量void MainWindow::on_pushButton_SendFileCnt_clicked(){    QString str=ui->lineEdit_SendFileCnt->text();    int cnt=str.toInt();    if(cnt>4096 || cnt<=0)    {         QMessageBox::warning(this,"警告提示","设置错误: 每包发送的数量范围: 1~4096\n",QMessageBox::Ok,QMessageBox::Ok);    }    else    {        FileSendCnt=cnt;        ui->plainTextEdit->insertPlainText(tr("发送数量设置为%1字节.\n").arg(cnt));    }}//写成功的数量void MainWindow::bluetoothbytesWritten(qint64 byte){   count+=byte;   ui->lineEdit_fileCount->setText(QString::number(count));   ui->progressBar_SendCount->setValue(count); //设置进度条的值}如果需要完整的代码可以从这里下载:https://download.csdn.net/download/xiaolong1126626497/18621270
原网站

版权声明
本文为[华为云]所创,转载请带上原文链接,感谢
https://bbs.huaweicloud.com/blogs/363857