加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一、新建項(xiàng)目
    • 二、控件布局
    • 三、編寫程序
    • 四、運(yùn)行
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

VS上位機(jī)開發(fā)——串口助手

11/21 14:58
1488
閱讀需 32 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

第一次接觸上位機(jī)的開發(fā),單純是為了玩一下,淺度學(xué)習(xí),參考了一下其他文章,做了一個(gè)簡單的串口助手,算是邁出了第一步。寫博客記錄一下學(xué)習(xí)的過程。

一、新建項(xiàng)目

第1步:創(chuàng)建一個(gè)Window窗體應(yīng)用(.NET Framework)

我安裝的是vs2019版本。

在這里插入圖片描述

第2步:配置項(xiàng)目

提示:框架要選.NET Framework 4以上,如果沒有,先確認(rèn)項(xiàng)目選的是不是Window窗體應(yīng)用,再確認(rèn)是否安裝NET Framework。

在這里插入圖片描述

二、控件布局

我們先利用控件把串口助手的界面搭建出來。

提示:我們用到的控件都在工具箱里面。

在這里插入圖片描述

我這里主要用了以下幾個(gè)控件:

提示:控件名稱是一個(gè)比較關(guān)鍵的參數(shù),因?yàn)楹竺娴拇a要根據(jù)名稱來寫。

控件類型 控件名稱 Text 說明
TextBox TextBox1 TextBox1 接收顯示窗口
TextBox TextBox2 TextBox2 發(fā)送輸入窗口
label label1 端口號 文本提示
label label2 波特率 文本提示
comboBox comboBox1 comboBox1 端口號下拉菜單
comboBox comboBox2 comboBox2 波特率下拉菜單
button button1 打開串口 打開串口按鍵
button button2 清除接收 清除接收按鍵
button button3 發(fā)送 發(fā)送按鍵
checkBox checkBox1 hex發(fā)送 切換發(fā)送格式
checkBox checkBox2 hex接收 切換接收格式
serialPort serialPort1 serialPort1 串口通信控件
Time Tiime1 Tiime1 定時(shí)器,用于定時(shí)刷新端口

先把控件從工具箱里面拉出來,調(diào)整好大小和布局。

提示:TextBox要自由調(diào)整窗口大小的話需要把屬性里面的MultiLine設(shè)置為True。

在這里插入圖片描述

修改控件屬性里面的Text,串口助手的界面就出來了。

在這里插入圖片描述

再添加serialPort和Time控件,這兩個(gè)是隱藏的控件,在窗口下方,實(shí)際運(yùn)行的時(shí)候是看不見的。

在這里插入圖片描述

選擇波特率對應(yīng)的comboBox控件,在Items屬性里面添加常用的波特率。

在這里插入圖片描述

三、編寫程序

提示:可以雙擊控件窗口打開代碼,也可以選中From,右鍵,選中查看代碼

在這里插入圖片描述

在實(shí)際應(yīng)用中,最常用到的代碼在Form1.cs和Form1.Designer.cs兩個(gè)文件中。

注:Form1是新建窗體默認(rèn)的名稱,實(shí)際使用也可能不是這個(gè)名字。

Form1.cs是應(yīng)用部分的代碼,也就是我后面要編寫的代碼所在的文件。

Form1.Designer.cs里面存放各種控件的參數(shù)定義,在我們雙擊某一個(gè)控件的時(shí)候,實(shí)際上這個(gè)文件會在相應(yīng)的生成一行代碼,意思是在這個(gè)控件里面添加一個(gè)事件。

1、端口更新函數(shù)

這個(gè)函數(shù)是自定義的,需要自己添加進(jìn)去

/* 新增自定義函數(shù):更新可用串口 */
private void Updata_Serialport_Name(ComboBox MycomboBox)
{
	string[] ArryPort;                               // 定義字符串?dāng)?shù)組,數(shù)組名為 ArryPort
    ArryPort = SerialPort.GetPortNames();            // SerialPort.GetPortNames()函數(shù)功能為獲取計(jì)算機(jī)所有可用串口,以字符串?dāng)?shù)組形式輸出
    MycomboBox.Items.Clear();                        // 清除當(dāng)前組合框下拉菜單內(nèi)容                  
    for (int i = 0; i < ArryPort.Length; i++)
    {
        MycomboBox.Items.Add(ArryPort[i]);           // 將所有的可用串口號添加到端口對應(yīng)的組合框中
    }
}

引用命名空間System.IO.Ports:

因?yàn)樯厦嬲{(diào)用了SerialPort.GetPortNames()函數(shù),需要引用這個(gè)命名空間才能使用

using System.IO.Ports;

在這里插入圖片描述

2、啟動窗口加載函數(shù)

雙擊設(shè)計(jì)界面窗口的空白區(qū)域,會自動生成一個(gè)Form1_Load空函數(shù)。

在Form1_Load函數(shù)里面添加以下代碼:

Updata_Serialport_Name(comboBox1); // 調(diào)用更新可用串口函數(shù),comboBox1為端口號組合框的名稱

在默認(rèn)啟動函數(shù)里添加以下代碼:

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//設(shè)置該屬性 為false

在這里插入圖片描述

提示:函數(shù)名不一定是Form1,這個(gè)函數(shù)名和自己的使用的窗體名稱是一致的

3、"打開串口"按鍵回調(diào)函數(shù)

雙擊“打開串口”按鍵,會自動生成一個(gè)空函數(shù)。

在函數(shù)里面添加以下代碼:

if (button1.Text == "打開串口")                                  // 如果當(dāng)前是串口設(shè)備是關(guān)閉狀態(tài)
{
    try                                                          // try 是嘗試部分,如果嘗試過程中出現(xiàn)問題,進(jìn)入catch部分,執(zhí)行錯誤處理代碼  
    {
        serialPort1.PortName = comboBox1.Text;                   // 將串口設(shè)備的串口號屬性設(shè)置為comboBox1復(fù)選框中選擇的串口號
        serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);  // 將串口設(shè)備的波特率屬性設(shè)置為comboBox2復(fù)選框中選擇的波特率
        serialPort1.Open();                                      // 打開串口,如果打開了繼續(xù)向下執(zhí)行,如果失敗了,跳轉(zhuǎn)至catch部分
        comboBox1.Enabled = false;                               // 串口已打開,將comboBox1設(shè)置為不可操作
        comboBox2.Enabled = false;                               // 串口已打開,將comboBox2設(shè)置為不可操作
        button1.BackColor = Color.Red;                           // 將串口開關(guān)按鍵的顏色,改為紅色
        button1.Text = "關(guān)閉串口";                               // 將串口開關(guān)按鍵的文字改為“關(guān)閉串口”
    }
    catch
    {
        MessageBox.Show("打開串口失敗,請檢查串口", "錯誤");     // 彈出錯誤對話框
    }
}
else                                             // 如果當(dāng)前串口設(shè)備是打開狀態(tài)
{
    try
    {
        serialPort1.Close();                     // 關(guān)閉串口
        comboBox1.Enabled = true;                // 串口已關(guān)閉,將comboBox1設(shè)置為可操作
        comboBox2.Enabled = true;                // 串口已關(guān)閉,將comboBox2設(shè)置為可操作
        button1.BackColor = Color.Lime;          // 將串口開關(guān)按鍵的顏色,改為青綠色
        button1.Text = "打開串口";               // 將串口開關(guān)按鍵的文字改為“打開串口”
    }
    catch
    {
        MessageBox.Show("關(guān)閉串口失敗,請檢查串口", "錯誤");   // 彈出錯誤對話框
    }
}

4、"清除接收"按鍵回調(diào)函數(shù)

雙擊“清除接收”按鍵,會自動生成一個(gè)空函數(shù)。

在函數(shù)里面添加以下代碼:

textBox1.Text = "";

5、"發(fā)送"按鍵回調(diào)函數(shù)

雙擊“發(fā)送”按鍵,會自動生成一個(gè)空函數(shù)。

在函數(shù)里面添加以下代碼:

if (serialPort1.IsOpen)            // 如果串口設(shè)備已經(jīng)打開了
{
    if (!checkBox1.Checked)        // 如果是以字符的形式發(fā)送數(shù)據(jù)
    {
        char[] str = new char[1];  // 定義一個(gè)字符數(shù)組,只有一位

        try
        {
            for (int i = 0; i < textBox2.Text.Length; i++)
            {
                str[0] = Convert.ToChar(textBox2.Text.Substring(i, 1));  // 取待發(fā)送文本框中的第i個(gè)字符
                serialPort1.Write(str, 0, 1);                            // 寫入串口設(shè)備進(jìn)行發(fā)送
            }
        }
        catch
        {
            MessageBox.Show("串口字符寫入錯誤!", "錯誤");   // 彈出發(fā)送錯誤對話框
            serialPort1.Close();                          // 關(guān)閉串口
            button1.BackColor = Color.Lime;               // 將串口開關(guān)按鍵的顏色,改為青綠色
            button1.Text = "打開串口";                    // 將串口開關(guān)按鍵的文字改為“打開串口”
        }
    }
    else                                                  // 如果以數(shù)值的形式發(fā)送
    {
        byte[] Data = new byte[1];                        // 定義一個(gè)byte類型數(shù)據(jù),相當(dāng)于C語言的unsigned char類型
        int flag = 0;                                     // 定義一個(gè)標(biāo)志,標(biāo)志這是第幾位
        try
        {
            for (int i = 0; i < textBox2.Text.Length; i++)
            {
                if (textBox2.Text.Substring(i, 1) == " " && flag == 0)                // 如果是第一位,并且為空字符
                {
                    continue;
                }

                if (textBox2.Text.Substring(i, 1) != " " && flag == 0)                // 如果是第一位,但不為空字符
                {
                    flag = 1;                                                         // 標(biāo)志轉(zhuǎn)到第二位數(shù)據(jù)去
                    if (i == textBox2.Text.Length - 1)                                // 如果這是文本框字符串的最后一個(gè)字符
                    {
                        Data[0] = Convert.ToByte(textBox2.Text.Substring(i, 1), 16);  // 轉(zhuǎn)化為byte類型數(shù)據(jù),以16進(jìn)制顯示
                        serialPort1.Write(Data, 0, 1);                                // 通過串口發(fā)送
                        flag = 0;                                                     // 標(biāo)志回到第一位數(shù)據(jù)去
                    }
                    continue;
                }
                else if (textBox2.Text.Substring(i, 1) == " " && flag == 1)           // 如果是第二位,且第二位字符為空
                {
                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 1), 16);  // 只將第一位字符轉(zhuǎn)化為byte類型數(shù)據(jù),以十六進(jìn)制顯示
                    serialPort1.Write(Data, 0, 1);                                    // 通過串口發(fā)送
                    flag = 0;                                                         // 標(biāo)志回到第一位數(shù)據(jù)去
                    continue;
                }
                else if (textBox2.Text.Substring(i, 1) != " " && flag == 1)           // 如果是第二位字符,且第一位字符不為空
                {
                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 2), 16);  // 將第一,二位字符轉(zhuǎn)化為byte類型數(shù)據(jù),以十六進(jìn)制顯示
                    serialPort1.Write(Data, 0, 1);                                    // 通過串口發(fā)送
                    flag = 0;                                                         // 標(biāo)志回到第一位數(shù)據(jù)去
                    continue;
                }
            }
        }
        catch
        {
            MessageBox.Show("串口數(shù)值寫入錯誤!", "錯誤");
            serialPort1.Close();
            button1.BackColor = Color.Lime;   // 將串口開關(guān)按鍵的顏色,改為青綠色
            button1.Text = "打開串口";        // 將串口開關(guān)按鍵的文字改為  “打開串口”
        }
    }
}

6、串口接收函數(shù)

點(diǎn)擊serialPort控件,在該控件的事件里面有一個(gè)DataReceived事件,雙擊它會生成一個(gè)數(shù)據(jù)接收的空函數(shù)

在這里插入圖片描述

在函數(shù)里面添加以下代碼:

if (!checkBox2.Checked)                        // 如果以字符串形式讀取
{
    string str = serialPort1.ReadExisting();   // 讀取串口接收緩沖區(qū)字符串

    textBox1.AppendText(str + "");             // 在接收文本框中進(jìn)行顯示
}
else                                           // 以數(shù)值形式讀取
{
    int length = serialPort1.BytesToRead;      // 讀取串口接收緩沖區(qū)字節(jié)數(shù)

    byte[] data = new byte[length];            // 定義相同字節(jié)的數(shù)組

    serialPort1.Read(data, 0, length);         // 串口讀取緩沖區(qū)數(shù)據(jù)到數(shù)組中

    for (int i = 0; i < length; i++)
    {
        string str = Convert.ToString(data[i], 16).ToUpper();                          // 將數(shù)據(jù)轉(zhuǎn)換為字符串格式
        textBox1.AppendText("0X" + (str.Length == 1 ? "0" + str + " " : str + " "));   // 添加到串口接收文本框中
    }
}

7、定時(shí)器中斷回調(diào)函數(shù)

在timer控件的屬性里面打開使能,設(shè)置定時(shí)時(shí)間為500ms

在這里插入圖片描述

雙擊timer控件,會自動生成一個(gè)空函數(shù)

在函數(shù)里面添加以下代碼:

Updata_Serialport_Name(comboBox1);   // 定時(shí)刷新可用串口,可以保證在程序啟動之后連接的設(shè)備也能被檢測到

最后再貼一個(gè)完整的代碼:

提示:不能直接跳過前面的步驟直接把完整的代碼拷貝過去,因?yàn)榍懊骐p擊控件的操作不僅僅是生成空函數(shù),也會在Designer里面添加對應(yīng)的事件,如果直接拷貝就不會產(chǎn)生事件。當(dāng)然,如果非要這樣操作也不是不行,只要在Form1.Designer.cs文件里面把每個(gè)控件對應(yīng)的事件加上即可。

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.IO.Ports;

namespace 串口測試工具
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//設(shè)置該屬性 為false
        }

        /* 新增自定義函數(shù):更新可用串口 */
        private void Updata_Serialport_Name(ComboBox MycomboBox)
        {
            string[] ArryPort;                               // 定義字符串?dāng)?shù)組,數(shù)組名為 ArryPort
            ArryPort = SerialPort.GetPortNames();            // SerialPort.GetPortNames()函數(shù)功能為獲取計(jì)算機(jī)所有可用串口,以字符串?dāng)?shù)組形式輸出
            MycomboBox.Items.Clear();                        // 清除當(dāng)前組合框下拉菜單內(nèi)容                  
            for (int i = 0; i < ArryPort.Length; i++)
            {
                MycomboBox.Items.Add(ArryPort[i]);           // 將所有的可用串口號添加到端口對應(yīng)的組合框中
            }
        }

        /* 啟動窗口加載函數(shù) */
        private void Form1_Load(object sender, EventArgs e)
        {
            Updata_Serialport_Name(comboBox1);               // 調(diào)用更新可用串口函數(shù),comboBox1為端口號組合框的名稱
        }

        /* "打開串口"按鍵回調(diào)函數(shù) */
        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "打開串口")                                  // 如果當(dāng)前是串口設(shè)備是關(guān)閉狀態(tài)
            {
                try                                                          // try 是嘗試部分,如果嘗試過程中出現(xiàn)問題,進(jìn)入catch部分,執(zhí)行錯誤處理代碼  
                {
                    serialPort1.PortName = comboBox1.Text;                   // 將串口設(shè)備的串口號屬性設(shè)置為comboBox1復(fù)選框中選擇的串口號
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);  // 將串口設(shè)備的波特率屬性設(shè)置為comboBox2復(fù)選框中選擇的波特率
                    serialPort1.Open();                                      // 打開串口,如果打開了繼續(xù)向下執(zhí)行,如果失敗了,跳轉(zhuǎn)至catch部分
                    comboBox1.Enabled = false;                               // 串口已打開,將comboBox1設(shè)置為不可操作
                    comboBox2.Enabled = false;                               // 串口已打開,將comboBox2設(shè)置為不可操作
                    button1.BackColor = Color.Red;                           // 將串口開關(guān)按鍵的顏色,改為紅色
                    button1.Text = "關(guān)閉串口";                               // 將串口開關(guān)按鍵的文字改為“關(guān)閉串口”
                }
                catch
                {
                    MessageBox.Show("打開串口失敗,請檢查串口", "錯誤");     // 彈出錯誤對話框
                }
            }
            else                                             // 如果當(dāng)前串口設(shè)備是打開狀態(tài)
            {
                try
                {
                    serialPort1.Close();                     // 關(guān)閉串口
                    comboBox1.Enabled = true;                // 串口已關(guān)閉,將comboBox1設(shè)置為可操作
                    comboBox2.Enabled = true;                // 串口已關(guān)閉,將comboBox2設(shè)置為可操作
                    button1.BackColor = Color.Lime;          // 將串口開關(guān)按鍵的顏色,改為青綠色
                    button1.Text = "打開串口";               // 將串口開關(guān)按鍵的文字改為“打開串口”
                }
                catch
                {
                    MessageBox.Show("關(guān)閉串口失敗,請檢查串口", "錯誤");   // 彈出錯誤對話框
                }
            }
        }

        /* "清除接收"按鍵回調(diào)函數(shù) */
        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
        }

        /* "發(fā)送"按鍵回調(diào)函數(shù) */
        private void button3_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)            // 如果串口設(shè)備已經(jīng)打開了
            {
                if (!checkBox1.Checked)        // 如果是以字符的形式發(fā)送數(shù)據(jù)
                {
                    char[] str = new char[1];  // 定義一個(gè)字符數(shù)組,只有一位

                    try
                    {
                        for (int i = 0; i < textBox2.Text.Length; i++)
                        {
                            str[0] = Convert.ToChar(textBox2.Text.Substring(i, 1));  // 取待發(fā)送文本框中的第i個(gè)字符
                            serialPort1.Write(str, 0, 1);                            // 寫入串口設(shè)備進(jìn)行發(fā)送
                        }
                    }
                    catch
                    {
                        MessageBox.Show("串口字符寫入錯誤!", "錯誤");   // 彈出發(fā)送錯誤對話框
                        serialPort1.Close();                          // 關(guān)閉串口
                        button1.BackColor = Color.Lime;               // 將串口開關(guān)按鍵的顏色,改為青綠色
                        button1.Text = "打開串口";                    // 將串口開關(guān)按鍵的文字改為“打開串口”
                    }
                }
                else                                                  // 如果以數(shù)值的形式發(fā)送
                {
                    byte[] Data = new byte[1];                        // 定義一個(gè)byte類型數(shù)據(jù),相當(dāng)于C語言的unsigned char類型
                    int flag = 0;                                     // 定義一個(gè)標(biāo)志,標(biāo)志這是第幾位
                    try
                    {
                        for (int i = 0; i < textBox2.Text.Length; i++)
                        {
                            if (textBox2.Text.Substring(i, 1) == " " && flag == 0)                // 如果是第一位,并且為空字符
                            {
                                continue;
                            }

                            if (textBox2.Text.Substring(i, 1) != " " && flag == 0)                // 如果是第一位,但不為空字符
                            {
                                flag = 1;                                                         // 標(biāo)志轉(zhuǎn)到第二位數(shù)據(jù)去
                                if (i == textBox2.Text.Length - 1)                                // 如果這是文本框字符串的最后一個(gè)字符
                                {
                                    Data[0] = Convert.ToByte(textBox2.Text.Substring(i, 1), 16);  // 轉(zhuǎn)化為byte類型數(shù)據(jù),以16進(jìn)制顯示
                                    serialPort1.Write(Data, 0, 1);                                // 通過串口發(fā)送
                                    flag = 0;                                                     // 標(biāo)志回到第一位數(shù)據(jù)去
                                }
                                continue;
                            }
                            else if (textBox2.Text.Substring(i, 1) == " " && flag == 1)           // 如果是第二位,且第二位字符為空
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 1), 16);  // 只將第一位字符轉(zhuǎn)化為byte類型數(shù)據(jù),以十六進(jìn)制顯示
                                serialPort1.Write(Data, 0, 1);                                    // 通過串口發(fā)送
                                flag = 0;                                                         // 標(biāo)志回到第一位數(shù)據(jù)去
                                continue;
                            }
                            else if (textBox2.Text.Substring(i, 1) != " " && flag == 1)           // 如果是第二位字符,且第一位字符不為空
                            {
                                Data[0] = Convert.ToByte(textBox2.Text.Substring(i - 1, 2), 16);  // 將第一,二位字符轉(zhuǎn)化為byte類型數(shù)據(jù),以十六進(jìn)制顯示
                                serialPort1.Write(Data, 0, 1);                                    // 通過串口發(fā)送
                                flag = 0;                                                         // 標(biāo)志回到第一位數(shù)據(jù)去
                                continue;
                            }
                        }
                    }
                    catch
                    {
                        MessageBox.Show("串口數(shù)值寫入錯誤!", "錯誤");
                        serialPort1.Close();
                        button1.BackColor = Color.Lime;   // 將串口開關(guān)按鍵的顏色,改為青綠色
                        button1.Text = "打開串口";        // 將串口開關(guān)按鍵的文字改為  “打開串口”
                    }
                }
            }
        }

        /* 串口接收函數(shù) */
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (!checkBox2.Checked)                        // 如果以字符串形式讀取
            {
                string str = serialPort1.ReadExisting();   // 讀取串口接收緩沖區(qū)字符串

                textBox1.AppendText(str + "");             // 在接收文本框中進(jìn)行顯示
            }
            else                                           // 以數(shù)值形式讀取
            {
                int length = serialPort1.BytesToRead;      // 讀取串口接收緩沖區(qū)字節(jié)數(shù)

                byte[] data = new byte[length];            // 定義相同字節(jié)的數(shù)組

                serialPort1.Read(data, 0, length);         // 串口讀取緩沖區(qū)數(shù)據(jù)到數(shù)組中

                for (int i = 0; i < length; i++)
                {
                    string str = Convert.ToString(data[i], 16).ToUpper();                          // 將數(shù)據(jù)轉(zhuǎn)換為字符串格式
                    textBox1.AppendText("0X" + (str.Length == 1 ? "0" + str + " " : str + " "));   // 添加到串口接收文本框中
                }
            }
        }

        /* 定時(shí)器中斷回調(diào)函數(shù) */
        private void timer1_Tick(object sender, EventArgs e)
        {
            Updata_Serialport_Name(comboBox1);   // 定時(shí)刷新可用串口,可以保證在程序啟動之后連接的設(shè)備也能被檢測到
        }
    }
}

四、運(yùn)行

在vs里面調(diào)試運(yùn)行結(jié)果如下:

在這里插入圖片描述

我這里連接了一個(gè)樹莓派,數(shù)據(jù)收發(fā)測試正常

如果需要在其他PC端運(yùn)行,可以把工程目錄下bin文件里面的Debug拷貝出來,運(yùn)行exe文件即可,不需要再安裝vs

在這里插入圖片描述

五、結(jié)束語

簡單的做了一個(gè)串口助手,總體來說其實(shí)不難,不熟悉C#語法也沒關(guān)系,我也是第一次接觸C#,根據(jù)C語言的經(jīng)驗(yàn)去摸索,代碼基本都能看的懂,有些語法也是即學(xué)即用的。好了,關(guān)于這一講的內(nèi)容就到這里,如果有什么問題,歡迎在評論區(qū)留言討論,謝謝。

源碼下載:https://download.csdn.net/download/ShenZhen_zixian/21712034

相關(guān)推薦

電子產(chǎn)業(yè)圖譜