C#学习笔记12:SYN6288语音模块_Winform上位机控制软件

今日尝试使用C# Winform写一个上位机软件控制 SYN6288语音模块

这里不讲什么基本原理(或者讲的比较略简),直接讲实现了就......

文章提供测试代码讲解、测试效果图、整体测试工程下载

目录

控件的摆放:

SYN6288介绍:

代码编程:

对16进制发送长串的处理:

对中文语句发送的处理:

将字符串按照GB2312编码进行编码检查:

GB2312转HEX:

构建数据包:

整体代码贴出:

测试视频:

整体测试工程下载:

网上查阅资料贴出:


控件的摆放:

 如图摆放控件:主要有Button、label、picturebox、listview、check、serialport、imaginelist

SYN6288介绍:

代码编程:

对16进制发送长串的处理:

  1. 条件判断:首先,它通过一个else语句块来确定当前是处于“16进制发送”模式(这通常是通过某个界面元素,如复选框checkBox2的选中状态来控制的,但在这段代码中并没有直接显示这个条件)。

  2. 异常处理(外层):使用了一个try-catch块来捕获并处理在尝试发送16进制数据时可能发生的任何异常。这个外层try-catch块主要是用来捕获由数据转换(即,将字符串转换为字节数组)过程中可能发生的异常,比如如果输入字符串包含无法转换为16进制字节的字符(尽管在这个特定的实现中,通过移除空格和转换为大写,以及检查字符串长度为偶数,已经减少了这种可能性)。

  3. 字符串处理

    • textBox1中获取用户输入的字符串,并使用Replace(" ", "").ToUpper()方法移除所有空格并将字符串转换为大写。这是为了确保输入数据的一致性,因为空格和大小写差异在16进制表示中是有意义的。
    • 检查处理后的字符串长度是否为偶数。因为每两个16进制字符代表一个字节,所以字符串长度必须是偶数才能正确转换为字节数组。如果不是偶数,则记录一条日志消息(通过调用myaddlog函数)并返回,不执行发送操作。
  4. 数据转换

    • 创建一个字节数组data,其大小等于处理后的字符串长度除以2(因为每两个字符代表一个字节)。
    • 使用一个for循环遍历处理后的字符串,每次迭代处理两个字符,并使用Convert.ToByte方法将它们从16进制字符串转换为字节,然后存储在data数组中。
  5. 发送数据

    • 在内层的try-catch块中,尝试使用serialPort1.Write方法将data数组发送到串口。如果发送成功,则记录一条“16进制数据发送成功”的日志消息。
    • 如果在发送过程中发生异常(例如,串口已关闭或硬件问题),则捕获该异常,并记录一条包含异常消息的日志。
  6. 数据转换错误处理

    • 外层的catch块(这里没有指定异常类型,因此会捕获所有类型的异常)用于处理数据转换过程中可能发生的任何错误(尽管在这个特定的实现中,由于前面的字符串处理和数据转换逻辑,这种错误的可能性很小)。如果发生这种错误,则记录一条“数据转换错误,请输入16进制数”的日志消息。然而,需要注意的是,由于这个catch块紧跟在字符串处理和数据转换代码之后,并且没有更具体的异常类型指定,它实际上可能会捕获到任何在try块中发生的异常,而不仅仅是数据转换错误。
                    //16进制发送:
                    else                                                                    //数据模式
                    {
                        try  //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示)
                        {
                            string hexString = textBox1.Text.Replace(" ", "").ToUpper(); // 移除空格并转换为大写 
                            if (hexString.Length % 2 != 0)
                            {
                                myaddlog(1, "输入的16进制数据长度必须为偶数!");
                                // MessageBox.Show("输入的16进制数据长度必须为偶数!");
                                return;
                            }
                            byte[] data = new byte[hexString.Length / 2];
                            for (int i = 0; i < hexString.Length; i += 2)
                            {
                                data[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
                            }
                            try
                            {
                                serialPort1.Write(data, 0, data.Length);
                                myaddlog(0, "16进制数据发送成功");
                            }
                            catch (Exception ex)
                            {
                                myaddlog(1, "串口数据写入错误: " + ex.Message);
                            }
                        }
                        catch
                        {
                            myaddlog(1, "数据转换错误,请输入16进制数");
                        }
                    }

对中文语句发送的处理:

在C#中,SerialPort.Write 方法实际上并不直接接受十六进制字符串作为输入。它接受一个字节数组(byte[])或者一个字符串(但字符串会被按照当前编码转换为字节序列发送,这通常不是我们想要的,特别是当想发送特定的十六进制数据时)。

因此,如果想要以十六进制形式发送数据(即 packet 字符串所表示的数据),需要先将这个十六进制字符串转换为一个字节数组。

其中以下函数在文章之后有单独定义与解释:

GB2312转HEX:

public static string StringToHexString(string input, Encoding encoding)

将字符串按照GB2312编码进行编码检查:

public static bool IsStringEncodableInGB2312(string input)

构建数据包:

  public string BuildPacket()

             if (textBox1.Text != "")
             {
                 //如果不是16进制发送
                 if (!checkBox2.Checked)
                 {
                     try
                     {
                         string text = textBox1.Text;
                         isencodable= IsStringEncodableInGB2312(text);
                         if (isencodable == false)
                         {
                             myaddlog(1, "你输入的不是GB2312中文编码格式");
                             return;
                         }
                         else
                         {
                             Encoding gb2312 = Encoding.GetEncoding("GB2312"); // 获取GB2312编码(注意:在某些系统上可能需要使用GBK)
                             hexString1 = StringToHexString(text, gb2312);

                             //MessageBox.Show(hexString1);
                             string packet = BuildPacket();
                             // 将十六进制字符串转换为字节数组  
                             byte[] dataToSend = StringToByteArray(packet);
                             //MessageBox.Show(packet);   //取消这行注释可以看到发送的数据包原始数据
                             serialPort1.Write(dataToSend, 0, dataToSend.Length);
                             myaddlog(0, "GB2312语音数据发送成功");
                         }
                         //serialPort1.Write(textBox1.Text);
                         myaddlog(0, "单条发送成功");
                         //serialPort1.WriteLine();                             //字符串写入
                     }
                     catch
                     {
                         myaddlog(1, "串口数据写入错误");
                     }

将字符串按照GB2312编码进行编码检查:

public static bool IsStringEncodableInGB2312(string input) 


            // 尝试将字符串按照GB2312编码进行编码,并检查是否成功  
            public static bool IsStringEncodableInGB2312(string input)
            {
               try
                {
                    // 注意:这里使用GBK作为替代,因为GBK是GB2312的超集  
                    // 如果你确定只需要GB2312中的字符,并且你的环境支持GB2312,也可以尝试使用Encoding.GetEncoding("GB2312")  
                    Encoding.GetEncoding("GBK").GetBytes(input);
                    return true; // 如果没有抛出异常,则认为字符串可以被GB2312(或GBK)编码表示  
                }
                catch (EncoderFallbackException)
                {
                    // 如果在编码过程中遇到了无法用GBK表示的字符,则会抛出此异常  
                    return false; // 字符串包含无法被GB2312(或GBK)编码表示的字符  
                }
                catch (ArgumentException)
                {
                    // 如果指定的编码名称无效,则会抛出此异常  
                    // 注意:在.NET Core或.NET 5/6/7等较新版本中,直接使用"GB2312"可能会抛出此异常  
                    // 在这种情况下,你应该使用"GBK"作为替代,或者确保你的环境支持GB2312编码  
                    return false; // 编码名称无效,无法进行检查  
                }

            }

GB2312转HEX:

public static string StringToHexString(string input, Encoding encoding)

            //GB2312转HEX
            // 将GB2312(或GBK)编码的字符串转换为16进制字符串  
            public static string StringToHexString(string input, Encoding encoding)
            {
                if (input == null) throw new ArgumentNullException(nameof(input));
                if (encoding == null) throw new ArgumentNullException(nameof(encoding));
                byte[] bytes = encoding.GetBytes(input);
                StringBuilder hex = new StringBuilder(bytes.Length * 2);
                foreach (byte b in bytes)
                {
                    hex.AppendFormat("{0:x2}", b);
                }
                return hex.ToString();
            }

构建数据包:


        // 构建并返回完整的十六进制数据包字符串  
        public string BuildPacket()
        {
            // 计算数据区长度(字节数),注意hexString1的长度  不需要除以2  
            //int dataLength = hexString1.Length / 2;
            //int dataLength = hexString1.Length+3;
            // 将hexString1转换为字节数组  
            byte[] dataBytes = StringToByteArray(hexString1);
            // 数据区长度(字节)  

            int dataLengthBytes = dataBytes.Length+3;
            // 数据区长度的高位和低位(十六进制)  
            // 注意:如果数据区长度小于0x100,高位总是0  
            byte lengthHigh = (byte)(dataLengthBytes >> 8);
            byte lengthLow = (byte)(dataLengthBytes & 0xFF);

            // 数据区长度的高位和低位(十六进制)  
            //byte lengthHigh = (byte)(dataLength >> 8); // 对于小于0x100的长度,这个值总是0  
            //byte lengthLow = (byte)(dataLength & 0xFF);
            // 拼接整个数据包  
            StringBuilder packet = new StringBuilder();
            // 帧头  
            packet.AppendFormat("{0:X2}", 0xFD);
            // 数据区长度(高位和低位)  
            packet.AppendFormat("{0:X2}{1:X2}", lengthHigh, lengthLow);
           // 命令字和命令参数  
            packet.AppendFormat("{0:X2}{1:X2}", commandWord, commandParam);
            // 数据区(直接从hexString1获取,无需再次转换)  
            packet.Append(hexString1);
            // 计算异或校验值  
            byte xorChecksum = CalculateXorChecksum(packet.ToString().Substring(2)); // 跳过帧头 
            xorChecksum ^= 0xfd; //异或帧头
            // 计算异或校验值(不包括帧头、长度、命令字和命令参数)  

            // 注意:这里我们使用dataBytes来计算校验,而不是packet的字符串表示  

            //byte xorChecksum = CalculateXorChecksum(dataBytes);

            // 添加异或校验值到数据包末尾  
            packet.AppendFormat("{0:X2}", xorChecksum);
            return packet.ToString();
        }

        // 计算异或校验值(从给定的字符串起始位置开始,不包括帧头)  
        private byte CalculateXorChecksum(string hexData)
        {
            byte checksum = 0;
            for (int i = 0; i < hexData.Length; i += 2)
            {
                // 将每两个十六进制字符转换为一个字节,并计算异或 
                checksum ^= Convert.ToByte(hexData.Substring(i, 2), 16);
            }
            return checksum;
        }

        // 辅助方法:将十六进制字符串转换为字节数组
        static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }

整体代码贴出:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SYN6288_Control
{
    //info 表示报警级别 ,log 表示报警信息
    public delegate void AddLog(int info, string log);

    public partial class Form1 : Form
    {
        //创建这个窗体的addlog ,需要绑定一个实际方法
        private AddLog myaddlog;
        bool Form1_FClosing = false;//用于防止二次Form1_FormClosing()事件发生的
        string formattedLogMessage; //用于临时拼接字符串
        bool OPEN_SERIAL_flag = false;//打开串口标志 false:未打开

        string hexString1;         //转化GB2312用
        // 命令字和命令参数  
        private byte commandWord = 0x01;
        private byte commandParam = 0x00;


        public Form1()
        {
            InitializeComponent();
            this.Load += Form1_Load;
            myaddlog = this.AddLog;//绑定方法
            serialPort1.Encoding = Encoding.GetEncoding("GB2312");     //串口接收编码
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        //表单初始化
        private void Form1_Load(object sender, EventArgs e)
        {
            设置第一列的宽度=整个宽度 减去 第0页宽度
            lstInfo.Columns[1].Width = lstInfo.ClientSize.Width - lstInfo.Columns[0].Width;

            for (int i = 1; i < 10; i++)//初始化串口 号下拉框内容
            {
                comboBox4.Items.Add("COM" + i.ToString()); //添加串口
            }

            for (int H = 0; H < 5; H++)//初始化串口 波特率下拉框内容
            {
                switch (H)
                {
                    case 0: comboBox5.Items.Add("2400"); break;
                    case 1: comboBox5.Items.Add("4800"); break;
                    case 2: comboBox5.Items.Add("9600"); break;
                    case 3: comboBox5.Items.Add("115200"); break;
                }
            }

            //停止位 下拉框内容
            for (int j = 0; j < 3; j++)
            {
                switch (j)
                {
                    case 0: comboBox7.Items.Add("1"); break;
                    case 1: comboBox7.Items.Add("1.5"); break;
                    case 2: comboBox7.Items.Add("2"); break;
                }
            }

            comboBox4.Text = "COM1";//端口下拉框初始值
            comboBox5.Text = "9600";//波特率下拉框初始值
            comboBox7.Text = "1";//停止位
            comboBox6.Text = "8";//数据位

            serialPort1.Close();   //关闭串行端口连接
        }



        //写入日志委托方法
        //创建委托
        private void AddLog(int info, string Log)
        {
            if (!lstInfo.InvokeRequired)
            {
                //创建ListViewItem ,将时间与info放进去
                ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
                lst.SubItems.Add(Log);
                lstInfo.Items.Insert(0, lst);
            }
            else
            {
                Invoke(new Action(() =>
                {
                    ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
                    lst.SubItems.Add(Log);
                    lstInfo.Items.Insert(0, lst);
                }));
            }
        }

        //串口测试发送
        private void button7_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[1];                                                         //单字节发数据     
            bool isencodable = false; //检查GB2312用
            if (serialPort1.IsOpen)
            {
                if (textBox1.Text != "")
                {
                    //如果不是16进制发送
                    if (!checkBox2.Checked)
                    {
                        try
                        {
                            string text = textBox1.Text;
                            isencodable= IsStringEncodableInGB2312(text);
                            if (isencodable == false)
                            {
                                myaddlog(1, "你输入的不是GB2312中文编码格式");
                                return;
                            }
                            else
                            {
                                Encoding gb2312 = Encoding.GetEncoding("GB2312"); // 获取GB2312编码(注意:在某些系统上可能需要使用GBK)
                                hexString1 = StringToHexString(text, gb2312);

                                //MessageBox.Show(hexString1);
                                string packet = BuildPacket();
                                // 将十六进制字符串转换为字节数组  
                                byte[] dataToSend = StringToByteArray(packet);
                                //MessageBox.Show(packet);   //取消这行注释可以看到发送的数据包原始数据
                                serialPort1.Write(dataToSend, 0, dataToSend.Length);
                                myaddlog(0, "GB2312语音数据发送成功");
                            }
                            //serialPort1.Write(textBox1.Text);
                            myaddlog(0, "单条发送成功");
                            //serialPort1.WriteLine();                             //字符串写入
                        }
                        catch
                        {
                            myaddlog(1, "串口数据写入错误");
                        }
                    }
                    //16进制发送:
                    else                                                                    //数据模式
                    {
                        try  //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示)
                        {
                            string hexString = textBox1.Text.Replace(" ", "").ToUpper(); // 移除空格并转换为大写 
                            if (hexString.Length % 2 != 0)
                            {
                                myaddlog(1, "输入的16进制数据长度必须为偶数!");
                                // MessageBox.Show("输入的16进制数据长度必须为偶数!");
                                return;
                            }
                            byte[] data = new byte[hexString.Length / 2];
                            for (int i = 0; i < hexString.Length; i += 2)
                            {
                                data[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
                            }
                            try
                            {
                                serialPort1.Write(data, 0, data.Length);
                                myaddlog(0, "16进制数据发送成功");
                            }
                            catch (Exception ex)
                            {
                                myaddlog(1, "串口数据写入错误: " + ex.Message);
                            }
                        }
                        catch
                        {
                            myaddlog(1, "数据转换错误,请输入16进制数");
                        }
                    }
                }
            }
            else if (serialPort1.IsOpen==false)
            {
                myaddlog(1, "错误警告: 端口无设备连接");
            }

        }

            //GB2312转HEX
            // 将GB2312(或GBK)编码的字符串转换为16进制字符串  
            public static string StringToHexString(string input, Encoding encoding)
            {
                if (input == null) throw new ArgumentNullException(nameof(input));
                if (encoding == null) throw new ArgumentNullException(nameof(encoding));
                byte[] bytes = encoding.GetBytes(input);
                StringBuilder hex = new StringBuilder(bytes.Length * 2);
                foreach (byte b in bytes)
                {
                    hex.AppendFormat("{0:x2}", b);
                }
                return hex.ToString();
            }


            // 尝试将字符串按照GB2312编码进行编码,并检查是否成功  
            public static bool IsStringEncodableInGB2312(string input)
            {
               try
                {
                    // 注意:这里使用GBK作为替代,因为GBK是GB2312的超集  
                    // 如果你确定只需要GB2312中的字符,并且你的环境支持GB2312,也可以尝试使用Encoding.GetEncoding("GB2312")  
                    Encoding.GetEncoding("GBK").GetBytes(input);
                    return true; // 如果没有抛出异常,则认为字符串可以被GB2312(或GBK)编码表示  
                }
                catch (EncoderFallbackException)
                {
                    // 如果在编码过程中遇到了无法用GBK表示的字符,则会抛出此异常  
                    return false; // 字符串包含无法被GB2312(或GBK)编码表示的字符  
                }
                catch (ArgumentException)
                {
                    // 如果指定的编码名称无效,则会抛出此异常  
                    // 注意:在.NET Core或.NET 5/6/7等较新版本中,直接使用"GB2312"可能会抛出此异常  
                    // 在这种情况下,你应该使用"GBK"作为替代,或者确保你的环境支持GB2312编码  
                    return false; // 编码名称无效,无法进行检查  
                }

            }

        // 构建并返回完整的十六进制数据包字符串  
        public string BuildPacket()
        {
            // 计算数据区长度(字节数),注意hexString1的长度  不需要除以2  
            //int dataLength = hexString1.Length / 2;
            //int dataLength = hexString1.Length+3;
            // 将hexString1转换为字节数组  
            byte[] dataBytes = StringToByteArray(hexString1);
            // 数据区长度(字节)  

            int dataLengthBytes = dataBytes.Length+3;
            // 数据区长度的高位和低位(十六进制)  
            // 注意:如果数据区长度小于0x100,高位总是0  
            byte lengthHigh = (byte)(dataLengthBytes >> 8);
            byte lengthLow = (byte)(dataLengthBytes & 0xFF);

            // 数据区长度的高位和低位(十六进制)  
            //byte lengthHigh = (byte)(dataLength >> 8); // 对于小于0x100的长度,这个值总是0  
            //byte lengthLow = (byte)(dataLength & 0xFF);
            // 拼接整个数据包  
            StringBuilder packet = new StringBuilder();
            // 帧头  
            packet.AppendFormat("{0:X2}", 0xFD);
            // 数据区长度(高位和低位)  
            packet.AppendFormat("{0:X2}{1:X2}", lengthHigh, lengthLow);
           // 命令字和命令参数  
            packet.AppendFormat("{0:X2}{1:X2}", commandWord, commandParam);
            // 数据区(直接从hexString1获取,无需再次转换)  
            packet.Append(hexString1);
            // 计算异或校验值  
            byte xorChecksum = CalculateXorChecksum(packet.ToString().Substring(2)); // 跳过帧头 
            xorChecksum ^= 0xfd; //异或帧头
            // 计算异或校验值(不包括帧头、长度、命令字和命令参数)  

            // 注意:这里我们使用dataBytes来计算校验,而不是packet的字符串表示  

            //byte xorChecksum = CalculateXorChecksum(dataBytes);

            // 添加异或校验值到数据包末尾  
            packet.AppendFormat("{0:X2}", xorChecksum);
            return packet.ToString();
        }

        // 计算异或校验值(从给定的字符串起始位置开始,不包括帧头)  
        private byte CalculateXorChecksum(string hexData)
        {
            byte checksum = 0;
            for (int i = 0; i < hexData.Length; i += 2)
            {
                // 将每两个十六进制字符转换为一个字节,并计算异或 
                checksum ^= Convert.ToByte(hexData.Substring(i, 2), 16);
            }
            return checksum;
        }

        // 辅助方法:将十六进制字符串转换为字节数组
        static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }



        //串口接收
        //一个接收数据事件获取串口发送来的数据
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            //处理事件这块可以加上延时确保不定数的数据可以全部收到缓冲后,才去读缓冲内容--单位: 毫秒
            Thread.Sleep(50);
            //如果16进制转换没被勾选
            if (!checkBox1.Checked)
            {
                myaddlog(0, serialPort1.ReadExisting());
                myaddlog(0, "串口消息接收回传:");
            }
            //如果16进制转换被勾选了
            else
            {
                try
                {
                    //定义缓冲区数组大小为串口缓冲区数据的字节数
                    //因为串口事件触发时有可能收到不止一个字节
                    byte[] data = new byte[serialPort1.BytesToRead];
                    serialPort1.Read(data, 0, data.Length);

                    foreach (byte Member in data)  //遍历用法
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();
                        formattedLogMessage = string.Format("0x" + (str.Length == 1 ? "0" + str : str) + " ");
                        myaddlog(0, formattedLogMessage);
                    }
                    myaddlog(0, "串口消息接收回传:");
                }
                catch { }
            }
        }


        private void label2_Click(object sender, EventArgs e) { }
        private void label1_Click(object sender, EventArgs e) { }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }

        //清除日志区
        private void button8_Click(object sender, EventArgs e)
        {
            lstInfo.Items.Clear();         //清除日志listview 的内容
            MessageBox.Show("已成功清除日志区", "清除接收区");
        }

        //打开/关闭串口
        private void button6_Click_1(object sender, EventArgs e)
        {
            if (OPEN_SERIAL_flag == false)
            {
                try
                {
                    serialPort1.PortName = comboBox4.Text;//设置端口号
                    serialPort1.BaudRate = Convert.ToInt32(comboBox5.Text);//设置端口波特率
                    serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox7.Text);//设置停止位
                    serialPort1.DataBits = Convert.ToInt32(comboBox6.Text);//设置数据位
                    serialPort1.ReceivedBytesThreshold = 1;
                    serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
                    serialPort1.Open();                   //打开串口
                    OPEN_SERIAL_flag = true;   //标记打开了串口
                    myaddlog(0, "当前串口有设备连接,串口已成功打开");
                    button6.Text = "关闭串口";
                }
                catch
                {
                    myaddlog(1, "错误警告: 端口无设备连接");
                    button6.Text = "打开串口";
                }

            }
            else if (OPEN_SERIAL_flag == true)
            {
                try
                {
                    serialPort1.Close(); //关闭串口        
                    myaddlog(0, "已关闭串口 ");
                    OPEN_SERIAL_flag = false;
                    button6.Text = "打开串口";
                }
                catch { }
            }
        }

    }
}

测试视频:

发送数据区的[v15]是调节音量大小的,数字范围是0~16,数字越大,音量越大!

打包的整体测试工程压缩包 里的测试软件路径如下

打开这个SYN6288_Control.exe程序就是我写好的上位机了

SYN6288语音模块_Winform上位机控制软件

整体测试工程下载:

https://download.csdn.net/download/qq_64257614/89616214

网上查阅资料贴出:

STM32 使用SYN6288语音模块-CSDN博客

STM32传感器外设集--语音模块(SYN6288)_syn6288语音模块-CSDN博客

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NULL指向我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值