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

news2024/11/23 20:18:24

今日尝试使用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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1984908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计选题推荐-出租车服务管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

ArrayList顺序表

目录 一、ArrayList变量的创建 二、ArrayList的三种构造方法 2.1 ArrayList() 2.2 ArrayList(int) 2.3 ArrayList(Collection) 三、ArrayList常用方法介绍 3.1 boolean add(E e) 3.2 E remove(int index) 和 boolean remove(Object o) 3.3 List subList(int fromInde…

hive udtf 函数:输入一个字符串,将这个字符串按照特殊的逻辑处理之后,输出4个字段

这里要继承GenericUDTF 这个抽象类,直接上代码: package com.xxx.hive.udf; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.ql.exec.Description; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hi…

2024年最建议买哪几款蓝牙耳机?四大2024性价比王牌机型推荐

随着科技的飞速发展&#xff0c;蓝牙耳机已经从昔日的奢侈品转变为如今生活中的必需品。面对日益丰富的产品线和不断更新的技术特性&#xff0c;选择一款适合自己的蓝牙耳机变得越来越具有挑战性&#xff0c;那么在2024年最建议买哪几款蓝牙耳机&#xff1f;作为蓝牙耳机爱好者…

48天笔试训练错题——day42

目录 选择题 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 编程题 1. 走迷宫 选择题 1. 动态分配&#xff1a;使用 DHCP 协议动态分配 IP。 IP 地址不但有单播地址&#xff0c;多播地址&#xff0c;还有广播地址。 如果一个主机有块个网卡&#xff0c;那么每块网卡都可以拥…

人工智能时代,程序员如何保持核心竞争力,不会一觉醒来,被自己写的代码淘汰了 ≧ ﹏ ≦

随着AIGC&#xff08;人工智能生成内容&#xff09;技术的飞速发展&#xff0c;尤其是像ChatGPT、Midjourney、Claude等大语言模型的崛起&#xff0c;人工智能正以前所未有的速度渗透到编程领域&#xff0c;彻底改变了程序员的工作方式。这一变革既带来了挑战&#xff0c;也孕育…

2024/08 近期关于AI的阅读和理解[笔记]

#Cohere 就像商业能力很强的云数仓公司 Snowflake 一样&#xff0c;Cohere 也采用了按需付费模式而不是按月或按年付费&#xff0c;而且它的付费模式很精细。Cohere 按照模型的不同能力&#xff0c;包括文本生成&#xff0c;文本总结&#xff0c;重新排名&#xff0c;文本分类…

pikachu文件下载

一&#xff1a;简介 在许多 web 系统中都有文件下载功能&#xff0c;点击下载链接会向后台发送含文件名的下载请求&#xff0c;后台收到后执行下载代码将对应文件返回给浏览器完成下载。若后台收到文件名后直接拼进下载路径而不做安全判断&#xff0c;可能引发不安全的文件下载…

【楚怡杯】职业院校技能大赛 “Python程序开发”赛项样题四

一、竞赛任务概述 本赛项包括“网络爬虫”、“数据清洗”、“数据分析与可视化”、“机器学习”4个竞赛任务&#xff0c;各任务分值分别为15分、30分、35分、20分&#xff0c;本赛项满分为100分。 二、注意事项 1.请根据大赛所提供的竞赛环境&#xff0c;检查所列的硬件设备…

2024最新版Python基础入门学习路线

Python基础入门学习路线可以概括为以下几个阶段&#xff0c;每个阶段都包含了关键的学习内容和目标&#xff1a; 一、Python语言基础 1. 初识Python语言 Python语言概述&#xff1a;了解Python的起源、特点、应用领域以及发展趋势。环境安装&#xff1a;学习如何在不同的操作系…

Web API 渗透测试指南

概述 API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;是一个允许不同软件应用程序之间进行通信和数据交换的接口。API定义了一组规则和协议&#xff0c;软件开发者可以使用这些规则和协议来访问操作系统、库、服务或其他应用程序的…

【HarmonyOS NEXT星河版开发学习】小型测试案例02-华为登录

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面&#xff08;还未发布&#xff09; 前言 通过此案例&#xff0c;不得不感叹鸿蒙的强大了&#xff0c;仅仅使用了26行代码就构建出来了这个界面&#xff0c;确实特别方便&#…

k8s-service暴露pod

service暴露pod----nginx 1.编写nginx-deployment.yaml文件 [rootk8s-master deployment]# vim nginx-deployment.yaml [rootk8s-master deployment]# cat nginx-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deployment namespace: default s…

二进制部署Mysql8.0.31

一、软件包下载 企业版&#xff1a;Enterprise , 互联网行业一般不选择.社区版本&#xff1a;选择源码包 编译安装&#xff1a; source code .tar.gz 通用二进制 公司用什么版本数据库? 具体什么小版本号? 5.6.20 5.6.34 5.6.36 5.6.38 5.6.40 5.7.18 5.7.20 5…

【初阶数据结构】详解顺序表(上)

文章目录 1. 数据结构2. 顺序表2.1 顺序表的概念及结构2.1.1 线性表2.1.2 顺序表与数组的差别 2.2 顺序表的分类2.2.1 静态顺序表2.2.2 动态顺序表 2.3 静态顺序表和动态顺序表的区别 在学完C语言的知识后&#xff0c;我们也该跨入到学习数据结构的领域中来。毕竟学习语法是为解…

带风扇工业电脑行业分析:预计2030年全球市场规模将达到45.8亿美元

工业电脑是用于工业用途&#xff08;生产产品和服务&#xff09;的计算机&#xff0c;其外形尺寸介于上网本和服务器机架之间。工业电脑的可靠性和精度标准更高&#xff0c;价格通常比消费电子产品更昂贵。它们通常使用复杂的指令集&#xff0c;例如 x86&#xff0c;而其他指令…

用的到linux-tomcat端口占用排查-Day5

前言&#xff1a; 最近使用tomcat搭建了一套测试环境的应用&#xff0c;整个搭建过程也很简单&#xff0c;就是将部署包上传至服务器☞解压☞启动tomcat服务器&#xff0c;当然服务器也是成功启动了&#xff0c;但是发现前端应用报404&#xff0c;具体如下图所示。 一、现象及思…

新手买智能猫砂盆怎样不踩雷?三大热门款测评推荐!

上班族养猫最害怕的就是&#xff0c;辛苦一天回到家&#xff0c;发现家里弥漫着猫便便的味道和满盆的猫屎&#xff0c;满地的猫砂&#xff0c;就感觉整个人都绝望了&#xff0c;你们现在是不是这样&#xff1f;以前的我真是为了解决这个问题想破了脑袋&#xff0c;后面才了解到…

电话营销机器人革新电销行业

第一&#xff0c;减少企业各方面的支出 企业需要各方面的支出。例如&#xff0c;招聘成本和管理成本、员工薪资和社保都是非常大的支出。但AI智能电销机器人&#xff0c;只要购买费用和电话费的一小部分&#xff0c;就没有更多的费用。经计算&#xff0c;该机器人的成本仅相当于…

java之静态内部类

1.什么是静态内部类 答:静态内部类是一种特殊的成员内部类 2.直接创建静态内部类对象的方式? Outer.Inner oinew Outer.Inner(); public class Outer {int a10;static int b20;static class Inner{public void show1(){System.out.println("非静态里的方法被调用了&qu…