1. 程式人生 > >C#設計串列埠助手

C#設計串列埠助手

做技術的通病,什麼都想學,什麼都想親手做一遍。不然總感覺心裡不踏實。

考研期間,利用晚上一點時間,照葫蘆畫瓢,練習使用c#快速開發一個簡單的串列埠小助手。

這種前後端分離的設計方法,大大提高了開發速度,比Python的wxPython寫軟體方便很多。

但是使用c#寫軟體在群裡問問題的時候,卻被人家用qt的嘲笑。(悲傷臉)

介面設計:

1、相對重要的串列埠接收事件

首先註冊一個接收事件,相當於微控制器中的串列埠中斷函式。

接下來為了完成接收位元組的計數,需要串列埠按照位元組接收,並區分HEX和ASCII接收形式。

下面是接收函式的實現,其中包括可選擇顯示接收時間:

//串列埠接受事件處理
        private void SerialPort1_DataReceieved(object sender, SerialDataReceivedEventArgs e)
        {
            //方法2:按位元組讀取
            int num = serialPort1.BytesToRead;  //獲取緩衝區位元組數
            byte[] received_buf = new byte[num];

            receieve_count += num;
            serialPort1.Read(received_buf, 0, num); //將緩衝區資料讀取到received_buf

            sb.Clear(); //防止出錯,先清空字串構造器
            
            if(radioButton2.Checked)    //以HEX形式接收
            {
                foreach(byte b in received_buf)
                {
                    sb.Append(b.ToString("X2") + " ");  //byte轉化為2位16進位制文字進行顯示,中間用空格隔開
                }
            }
            else  //預設ascii形式接收
            {
                    sb.Append(Encoding.ASCII.GetString(received_buf));//將接收陣列解碼為ascii陣列
            }
            //顯示到接收文字框內
            try
            {
                Invoke((EventHandler)(delegate
                {
                    if(checkBox1.Checked)
                    {
                        //顯示時間
                        current_time = System.DateTime.Now;
                        textBox_R.AppendText(current_time.ToString("HH:mm:ss") + " " + sb.ToString());
                    }
                    else
                    {
                        textBox_R.AppendText(sb.ToString());
                    }
                    if (checkBox2.Checked)
                    {
                        //接收自動換行
                        textBox_R.AppendText(Environment.NewLine);
                    }
                    //更新狀態列
                    label9.Text = "Rx:" + receieve_count.ToString() + "Bytes";
                }));
            }
            catch (Exception ex)
            {
                //響鈴並顯示異常資訊
                System.Media.SystemSounds.Beep.Play();
                MessageBox.Show(ex.Message);
            }
        }

還有一個重要的問題就是如何解決接收過程中實時顯示接收的字串,而不是等接收完畢後在顯示的問題。

因為在當前工作執行緒中直接修改文字框的話會出現錯誤。

在多執行緒程式設計中,我們經常要在工作執行緒中去更新介面顯示,而在多執行緒中直接呼叫介面控制元件的方法是錯誤的做法,Invoke 和 BeginInvoke 就是為了解決這個問題而出現的,使得多執行緒中安全的更新介面顯示。

正確的做法是將工作執行緒中涉及更新介面的程式碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去呼叫,兩者的區別就是一個導致工作執行緒等待,而另外一個則不會。

而所謂的“一面響應操作,一面新增節點”永遠只能是相對的,使 UI 執行緒的負擔不至於太大而已,因為介面的正確更新始終要通過 UI 執行緒去做,我們要做的事情是在工作執行緒中包攬大部分的運算,而將對純粹的介面更新放到 UI 執行緒中去做,這樣也就達到了減輕 UI 執行緒負擔的目的了。
 

2、定時傳送功能的實現

        //自動定時傳送
        private void checkBox3_CheckedChanged(object sender, EventArgs e)
        {
            if(checkBox3.Checked)
            {
                //選擇自動傳送
                numericUpDown1.Enabled = false;
                timer1.Interval = (int)numericUpDown1.Value;    //定時器賦值,單位:毫秒
                timer1.Start();
                label7.Text = "串列埠已開啟" + "自動傳送中...";
            }

和使用微控制器時差不多,這裡相當於設定了一個定時器,只不過預設定時單位是1ms,所以,按照微控制器程式設計的思想,設定了定時器肯定還需要定時器中斷函式,因此還需要註冊一個定時器事件:

        //定時時間到
        private void timer1_tick(object sender, EventArgs e)
        {
            button2_Click(button2, new EventArgs());//呼叫傳送按鈕的回撥函式
        }

總結:

使用c#的使用過程中,很多新的概念也隨之而來,多執行緒、泛型、事件、委託等等,只能現學現賣。

面向物件的思想得慢慢琢磨,理解之後感覺會大大提高程式設計效率。