电子可以一边玩,一边研究,网络音频这个课题特别适合电子爱好者。几方面的挑战如下,单片机实现对接以太网、实时对音频流解码播放,上位机配合单片机做音频流传输控制,音频信号的对接放大处理。在这里分享下资料,自己把这些关键技术整理下。
原理图
PCB
上位机
实物
单片机关键程序
#include "stm32f10x.h"
#include "usart1.h"
#include "SysTick.h"
#include "vs1053.h"
#include "ch395.h"
#include "sdio_sdcard.h"
#include "ff.h"
#include "diskio.h"
#include <string.h>
#include <stdio.h>
#include "timer.h"
u8 *ch395_T_Buffer="play over";
u8 ch395_R_Buffer[2048];
u8 play_count,Recv_count,Recv_H_flag,Recv_L_flag,play_H_flag,play_L_flag;
u8 ch395_R_flag;
u8 play_H_M,play_L_M;
u8 Play_OK;
ErrorStatus HSEStartUpStatus;
u16 count;
void Play_1063_Duplex(void);
const u8 CH395MACAddr[6] = {0x07,0x76,0x05,0x04,0x03,0x2}; // CH395MACµØÖ·
u8 CH395IPAddr[4] = {192,168,18,200}; // CH395IPµØÖ·
u8 CH395GWIPAddr[4] = {192,168,18,1}; // CH395Íø¹Ø
u8 CH395IPMask[4] = {255,255,255,0}; // CH395×ÓÍøÑÚÂë
u8 Socket0DesIP[4] = {192,168,18,10}; /* Socket 0Ä¿µÄIPµØÖ· */
u16 Socket0DesPort = 8888; /* Socket 0Ä¿µÄ¶Ë¿Ú */
u16 Socket0SourPort = 8888; /* Socket 0Ô´¶Ë¿Ú */
u8 Socket1DesIP[4] = {192,168,18,10}; /* Socket 1Ä¿µÄIPµØÖ· */
u16 Socket1DesPort = 34835; /* Socket 1Ä¿µÄ¶Ë¿Ú */
u16 Socket1SourPort = 34835; /* Socket 1Ô´¶Ë¿Ú */
u16 TIM3_count;
u8 TIM3_flag;
int main(void)
{
SysTick_Init(); /* ÅäÖÃSysTick Ϊ10usÖжÏÒ»´Î */
USART1_Config(); /* ÅäÖô®¿Ú1 115200 8-N-1 */
NVIC_Configuration();
vs1053_SPI_Init();
CH395_SPI_Init();
Delay_us(10000);
GPIO_SetBits(GPIOB, GPIO_Pin_0);
Delay_us(10000);
InitCH395InfParam(); /* ³õʼ»¯CH395Ïà¹Ø±äÁ¿ */
CH395Init(); /* ³õʼ»¯CH395оƬ */
if(CH395CMDGetPHYStatus() == PHY_DISCONN) /* ²éѯCH395ÊÇ·ñÁ¬½Ó */
{
Delay_us(2000); /* δÁ¬½ÓÔòµÈ´ý200MSºóÔٴβéѯ */
}
InitSocketParam(); /* ³õʼ»¯socketÏà¹Ø±äÁ¿ */
CH395SocketInitOpen();
Delay_us(60000);
vs1053_SPI_SPEED_L(); //³õʼ»¯ ĬÈÏΪmp3 ÓÃÓÚ²âÊÔ10KHz ·ñÔò ¿ª»úºóLED³£ÁÁ
MP3_Start();
vs1053_SPI_SPEED_H();
Timerx_Init(400,7199);
ch395_T_Buffer = "play";
CH395SendData(ch395_T_Buffer,sizeof(ch395_T_Buffer),Socket0DesIP,Socket0DesPort,0);
Delay_us(60000);
while (1)
{
GPIOB->BSRR = GPIO_Pin_2 ;
CH395GlobalInterrupt();
GPIOB->BRR = GPIO_Pin_2 ;
Play_1063_Duplex();
}
}
void Play_1063_Duplex(void)
{
u16 i;
GPIOB->BRR = GPIO_Pin_12 ; // Ñ¡ÔñVS1053µÄÊý¾Ý½Ó¿Ú
if(ch395_R_flag==1)
{
ch395_R_flag=0;
while ( count < 1024)
{
GPIOB->BSRR = GPIO_Pin_1 ;
if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)!=0) // µÈ´ýDREQΪ¸ß£¬ÇëÇóÊý¾ÝÊäÈë
{
for (i=0; i<32; i++ ) // VS1053µÄFIFOÖ»ÓÐ32¸ö×ֽڵĻº³å
{
vs1053_WriteByte(ch395_R_Buffer[count]);
count++;
}
}
GPIOB->BRR = GPIO_Pin_1 ;
}
if(count>1023)
{
count=0;
CH395SendData(ch395_T_Buffer,sizeof(ch395_T_Buffer),Socket0DesIP,Socket0DesPort,0);
memset(ch395_R_Buffer, 0, 1024);
TIM3_count=0;
}
}
else
{
if(TIM3_flag==1)
{
TIM3_flag=0;
CH395SendData(ch395_T_Buffer,sizeof(ch395_T_Buffer),Socket0DesIP,Socket0DesPort,0);
}
}
}
void MP3_Start(void)
{
u16 i;
GPIOB->BRR = GPIO_Pin_11;
for (i=0; i<60000; i++ );
for (i=0; i<60000; i++ );
vs1053_WriteByte(0xff); // ·¢ËÍÒ»¸ö×Ö½ÚµÄÎÞЧÊý¾Ý£¬Æô¶¯SPI´«Êä
GPIOB->BSRR = GPIO_Pin_12;
GPIOD->BSRR = GPIO_Pin_8;
GPIOB->BSRR = GPIO_Pin_11;
for (i=0; i<60000; i++ );
for (i=0; i<60000; i++ );
VS_WR_Cmd(SPI_MODE,0x0800); // ½øÈëvs1053µÄ²¥·Åģʽ
//VS_WR_Cmd(SPI_CLOCKF, 0xf800); // ÉèÖÃvs1053µÄʱÖÓ,3±¶Æµ
VS_WR_Cmd(SPI_CLOCKF, 0xf800); // ÉèÖÃvs1053µÄʱÖÓ,3±¶Æµ
VS_WR_Cmd(SPI_AUDATA, 0xbb81); // ²ÉÑùÂÊ48k£¬Á¢ÌåÉù
VS_WR_Cmd(SPI_BASS, 0x0000);// ÉèÖÃÖصÍÒô
VS_WR_Cmd(SPI_VOL,0); // vs1053 ÒôÁ¿
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)==0);//µÈ´ý¿ÕÏÐ // µÈ´ýDREQΪ¸ß ±íʾÄܹ»½ÓÊÜÒôÀÖÊý¾ÝÊäÈë
}
C#上位机
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Threading;
namespace WindowsFormsApplication12
{
public partial class Form1 : Form
{
IPEndPoint udpPoint; //本机IP地址,端口号
IPEndPoint targetPoint; //服务器的IP地址,端口号
IPEndPoint Remote;
UdpClient udpClient;
UdpClient udpRecvClient;
int num = 0, readoffset = 0;
byte[] byData = new byte[1024];
private long Send_count;
private long Recv_count;
private long file_len;
private long file_close = 0;
private string file_nem;
int Recv_flag;
int Button_flag=0;
Thread Sendthread;
Thread Rcvethread;
Thread Showthread;
public Form1()
{
InitializeComponent();
udpPoint = new IPEndPoint(IPAddress.Parse("192.168.18.10"), 8888);
targetPoint = new IPEndPoint(IPAddress.Parse("192.168.18.200"), 8888);
Remote = new IPEndPoint(IPAddress.Any, 0);
Showthread = new Thread(Showstate);
Showthread.IsBackground = true;
Showthread.Start();
}
public delegate void CallSetTextbox();
private void SetTextbox()
{
try
{
if (this.InvokeRequired)
{
CallSetTextbox myCallSetTextbox = new CallSetTextbox(SetTextbox);
this.Invoke(myCallSetTextbox);
}
else
{
textBox1.Text = file_len.ToString();
}
}
catch { }
}
private void SetSendTextbox()
{
try
{
if (this.InvokeRequired)
{
CallSetTextbox myCallSetTextbox = new CallSetTextbox(SetSendTextbox);
this.Invoke(myCallSetTextbox);
}
else
{
textBox2.Text = Send_count.ToString();
}
}
catch { }
}
private void SetRecvTextbox()
{
try
{
if (this.InvokeRequired)
{
CallSetTextbox myCallSetTextbox = new CallSetTextbox(SetRecvTextbox);
this.Invoke(myCallSetTextbox);
}
else
{
textBox3.Text = Recv_count.ToString();
}
}
catch { }
}
private void Showstate()
{
while (true)
{
SetTextbox();
SetSendTextbox();
SetRecvTextbox();
}
}
private void Udp_SendFunction()
{
try
{
FileStream file = new FileStream(file_nem, FileMode.Open);//打开txt文件
file_len=file.Length;
file.Seek(0, SeekOrigin.Begin);
while (true)
{
if (Recv_flag == 1)
{
Recv_flag = 0;
num = file.Read(byData, readoffset, 1024);//读取txt文件内容
udpClient.Send(byData, byData.Length, targetPoint);//发送数据
Send_count = Send_count + 1024;
if (num <= 0)
{
//MessageBox.Show("完成");
break;
}
}
if (file_close == 1)
{
file_close = 0;
break;
}
}
file.Close();//关闭txt文件
}
catch (IOException e)
{
//MessageBox.Show("Send" + e.ToString());
}
}
private void Udp_RecvFunction()
{
//udpRecvClient = new UdpClient(udpPoint);//初始化udp
while (true)
{
try
{
byte[] recBuffer = udpClient.Receive(ref Remote);
if (recBuffer != null)
{
Recv_flag = 1;
Recv_count = Recv_count + recBuffer.Length;
}
}
catch (Exception ex)
{
//MessageBox.Show("Recv" + ex.ToString());
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (Button_flag == 0)
{
file_close = 0;
Send_count=0;
Recv_count =0;
file_len = 0;
Button_flag = 1;
udpClient = new UdpClient(udpPoint);//初始化udp
using (OpenFileDialog ofd = new OpenFileDialog())
{
if (ofd.ShowDialog() == DialogResult.OK) //如果点击的是打开文件
{
textBox4.Text = ofd.FileName; //获取全路径文件名
file_nem = ofd.FileName;
Sendthread = new Thread(Udp_SendFunction);
Sendthread.IsBackground = true;
Sendthread.Start();
Rcvethread = new Thread(Udp_RecvFunction);
Rcvethread.IsBackground = true;
Rcvethread.Start();
}
}
button1.ForeColor= Color.FromArgb(255, 155, 0, 0);
button1.Text = "关闭文件";
label5.Text = "播放中 ...";
}
else
{
file_close = 1;
Button_flag = 0;
Send_count = 0;
Recv_count = 0;
file_len = 0;
Sendthread.Abort();
Rcvethread.Abort();
button1.ForeColor = Color.FromArgb(255, 0, 155, 0);
button1.Text = "打开文件";
label5.Text = "已停止播放";
udpClient.Close();
}
}
}
}
关注我的博客:https://blog.csdn.net/gd1984812
|