打印
[技术讨论]

stm32实现网络音频-原理图单片机程序C#上位机程序

[复制链接]
917|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
感动|  楼主 | 2019-12-7 20:30 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
电子可以一边玩,一边研究,网络音频这个课题特别适合电子爱好者。几方面的挑战如下,单片机实现对接以太网、实时对音频流解码播放,上位机配合单片机做音频流传输控制,音频信号的对接放大处理。在这里分享下资料,自己把这些关键技术整理下。

原理图







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

使用特权

评论回复

相关帖子

沙发
19943183944| | 2019-12-31 08:23 | 只看该作者
谢谢分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:关注我的博客https://blog.csdn.net/gd1984812 淘宝店 https://shop570248211.taobao.

35

主题

57

帖子

4

粉丝