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

news2024/9/21 22:32:04

今日尝试使用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/1982765.html

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

相关文章

一款绿色免费免安装的hosts文件编辑器

BlueLife Hosts Editor 是一款免费的 Hosts 文件编辑工具&#xff0c;主要用于管理和修改电脑系统的 Hosts 文件。该软件具有多种功能&#xff0c;包括添加、删除和更新域名记录&#xff0c;以及调整 IP 与网域名称的交叉对应关系&#xff0c;类似于 DNS 的功能。 该软件特别适…

鸿蒙应用服务开发【华为账号服务】

Account Kit 介绍 本示例展示了使用Account Kit提供的登录、授权头像昵称、实时验证手机号、收货地址、发票抬头、未成年人模式的能力。 本示例模拟了在应用里&#xff0c;调用一键登录Button组件拉起符合华为规范的登录页面&#xff1b;调用获取头像昵称接口获取头像昵称&a…

七天打造一套量化交易系统:Day8-阶段性总结、未完待续...

七天打造一套量化交易系统&#xff1a;Day8-阶段性总结、未完待续... 阅读数据分析私信情况汇总如何收费代写策略功能拓展商务合作如何联系我 下一阶段规划 从 2024-07-18 准备进行【七天打造一套量化交易系统】系列分享&#xff0c;到昨天&#xff08;2024-08-04&#xff09;&…

【Kubernetes】k8s集群的pod基础

目录 一.Pod概述 1.什么是pod 2.k8s中pod的两种使用方式 3.pod容器的常规使用流程 4.创建pod的3种方式 二.Pod中容器概述 1.容器分类 2.Pod容器的3种镜像拉取策略 3.Pod容器的3种重启策略 4.pod的dns策略 一.Pod概述 1.什么是pod Pod是kubernetes中最小的资源管理组…

登录相关功能的优化【JWT令牌+拦截器+跨域】

登录相关功能的优化 登录后显示当前登录用户el-dropdown: Element - The worlds most popular Vue UI framework <el-dropdown style"float: right; height: 60px; line-height: 60px"><span class"el-dropdown-link" style"color: white;…

【香橙派系列教程】(八)一小时速通Python

【八】一小时速通Python 本章内容服务于香橙派下的开发&#xff0c;用C语言的视角来学习即可&#xff0c;会改就行。 详细说明&#xff0c;请看链接:python全篇教学 Python是一种动态解释型的编程语言&#xff0c;Python可以在Windows、UNIX、MAC等多种操作系统上 使用&…

codetop标签双指针题目大全解析(C++解法),双指针刷穿地心!!!

写在前面&#xff1a;此篇博客是以[双指针总结]博客为基础的针对性训练&#xff0c;题源是codetop标签双指针近一年&#xff0c;频率由高到低 1.无重复字符的最长子串2.三数之和3.环形链表4.合并两个有序数组5.接雨水6.环形链表II7.删除链表的倒数第N个节点8.训练计划II9.最小覆…

SMU Summer 2024 div2 4th

文章目录 The Fourth Week一、前言二、算法1.最近公共祖先lca倍增算法2.Dijkstra算法<1>&#xff08;游戏&#xff09; 3.拓扑排序3. Bellman-Ford算法4. SPFA算法 三、总结 The Fourth Week 不须计较苦劳心&#xff0c;万事原来有命。 ————宋朱敦儒《西江月世事短如…

大模型岗位面试总结,靠它轻松拿下offer

节前技术群邀请了一些参加大模型面试&#xff08;含实习&#xff09;的同学&#xff0c;分享他们面试算法工程师(大模型方向)的宝贵经验。 之前总结链接如下&#xff1a; 超全总结&#xff01;大模型算法岗面试真题来了&#xff01; 面了 5 家知名企业的NLP算法岗(大模型方向…

计算机组成原理(1):计算机系统概述

计算机底层和计算机原理&#xff01;&#xff01;&#xff01;&#xff01; 研究计算机硬件在底层是怎末运行的&#xff01; 计算机硬件能识别的数据 用低电平表示0 用高电平表示1 皮卡丘使高电压&#xff01; 计算机传递数据是用的电信号&#xff01;&#xff01;&#xff…

云原生-搭建dhcp服务并测试kickstart脚本

# 安装DHCP服务 【为其他服务器提供分配ip地址的功能&#xff0c;前提是其他服务器网卡必须设置成DHCP获取IP地址模式】 [rootpxe ~]# yum install dhcp.x86_64 -y[rootpxe ~]# rpm -qc dhcp /etc/dhcp/dhcpd.conf[rootpxe -]# cat /etc/dhcp/dhcpd.conf## DHCP Server Configu…

NLP论文阅读PALM

NLP论文阅读PALM 模型构成Joint Modeling of Autoencoding and AutoregressionInput&Output RepresentationsCopying Tokens from Context扩展词汇的分布复制分布最终分布 PALM: Pre-training an Autoencoding&Autoregressive Language Model for Context-conditioned …

[flink]部署模式

部署模式 在一些应用场景中&#xff0c;对于集群资源分配和占用的方式&#xff0c;可能会有特定的需求。 Flink为各种场景提供了不同的部署模式&#xff0c;主要有以下三种&#xff1a;会话模式&#xff08;Session Mode&#xff09;、单作业模式&#xff08;Per-Job Mode&…

Linux系统驱动(四)自动创建设备节点

自动创建设备节点 &#xff08;一&#xff09;创建设备节点的机制 1. mknod 将驱动编译到内核中&#xff0c;在内核启动时驱动自动被安装执行 2.devfs&#xff08;2.4内核&#xff09; 3. udev&#xff08;2.6内核至今&#xff09; 注&#xff1a;hotplug — 热插拔 &…

KamaCoder 101. 孤岛的总面积

题目描述 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域&#xff0c;且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。 现在你需…

Bugku -----Web-----全题目解析 (二) 超详细步骤

————————————————————分割线———————————————————— 6.矛盾 这一行从 URL 查询字符串中获取名为 num 的参数值&#xff0c;并将其赋值给 $num 变量。如果 URL 中没有提供 num 参数&#xff0c;或者参数值不是有效的字符串&#xff0c;则…

Modbus-RTU详解

目录 Modbus-RTU协议 帧结构示例 CRC16校验算法 CRC16算法的过程 modbus-rtu的使用 发送数据 接收数据 tcp网口完整实现modbus-rtu协议 使用NModbus4实现modbus-rtu协议 安装NModbus4库。 串口实现NModbus4 Modbus-RTU协议 Modbus RTU 协议是一种开放的串行协议&#xff0c;广…

基于51单片机的无线模块PWM电机调速设计

一、概述 为了实现对直流电机无极调速的需求&#xff0c;提出了一种基于STC 89C52微控制器的直流PWM可调速系统设计方案。根据系统所需达到的控制目的&#xff0c;UL2003驱动芯片作为电动机驱动电路&#xff0c;实现对电机的驱动。控制算法采用经典PWM脉宽调制算法作为控制策略…

充电宝哪个牌子好?学生党适合哪种充电宝?推荐四款性价比充电宝

对于学生党而言&#xff0c;保持手机电量充足是学习、社交和娱乐的基本保证。然而&#xff0c;面对频繁的使用&#xff0c;手机电量常常不够用&#xff0c;这时一款性能优良的充电宝就显得尤为重要。那么&#xff0c;充电宝哪个牌子好呢&#xff1f;对于学生党来说&#xff0c;…

番茄钟工作法

目录 1.使用番茄钟的注意事项和技巧: 2.番茄工作法的优点: 3.番茄钟案例: 从棉花糖实验说起 我得了什么「病」&#xff1f; 外界的诱惑 失效的 Deadline 永远停留在纸上的计划 番茄土豆大作战&#xff1a;番茄工作法简明教程 计划 执行 记录与分析 番茄工作法怎么…