C#记录(十):串口上位机实例之一

一、运行效果图

1、开关串口

2、波特率选择

3、串口数据处理(目前对应的是报警器设备,抓取的数据有:红外传感器和CO传感器、以及设备NTC温度值)

4、所有串口接收字串

5、四个Series显示,代表各自的电压值;

6、可以选择显示或者隐藏对应的Series

Chart绘图存在的问题:当显示的数据量很大,达到上万数据时,整个winform窗口变得卡顿。时间允许,会尝试用其他方法,需要源码的见最后链接。

二、程序讲解

1、整个winform程序包含的方法并不多,如下:因并未学习Java或C++等语言,所以整个代码里有很多C语言的影子,风格显得很突兀。

 

2、Chart初始化

        #region Chart初始化
        public void InitChart()
        {
            chart1.BackColor = System.Drawing.Color.White;//设置图表的背景颜色
            //chart1.Series["Series1"].IsValueShownAsLabel = true;//设置是否在Chart中显示坐标点值
            //chart1.Series["Series1"].XValueMember = "name";//设置X轴的数据源
            //chart1.Series["Series1"].YValueMembers = "mobile";//设置Y轴的数据源

            Series SeriesDark = chart1.Series[0];
            // 画样条曲线(Spline)
            SeriesDark.ChartType = SeriesChartType.Spline;
            // 线宽2个像素
            SeriesDark.BorderWidth = 2;
            // 线的颜色:红色
            SeriesDark.Color = System.Drawing.Color.Black;
            // 图示上的文字是否可见          
            SeriesDark.IsVisibleInLegend = true;
            // 线条名称
            SeriesDark.Name = "Dark";
            SeriesDark.Enabled = true;

            Series SeriesLight = chart1.Series[1];
            SeriesLight.ChartType = SeriesChartType.Spline;
            SeriesLight.BorderWidth = 2;
            SeriesLight.Color = System.Drawing.Color.Red;
            SeriesLight.IsVisibleInLegend = true;
            SeriesLight.Name = "Light";
            SeriesLight.Enabled = true;

            Series SeriesL_D = chart1.Series[2];
            SeriesL_D.ChartType = SeriesChartType.Spline;
            SeriesL_D.BorderWidth = 2;
            SeriesL_D.Color = System.Drawing.Color.Blue;
            SeriesL_D.IsVisibleInLegend = true;
            SeriesL_D.Name = "L_D";
            SeriesL_D.Enabled = true;

            Series SeriesCo = chart1.Series[3];
            SeriesCo.ChartType = SeriesChartType.Spline;
            SeriesCo.BorderWidth = 2;
            SeriesCo.Color = System.Drawing.Color.Green;
            SeriesCo.IsVisibleInLegend = true;
            SeriesCo.Name = "Co";
            SeriesCo.Enabled = true;

            // 设置显示范围
            ChartArea chartArea = chart1.ChartAreas[0];
            chartArea.AxisX.Minimum = 0;
            chartArea.AxisX.Maximum = 100000;
            chartArea.AxisX.Interval = 1;
            
            chartArea.AxisY.Minimum = 0;
            chartArea.AxisY.Maximum = 5;
            chartArea.AxisY.Interval = 0.5;

            //显示网格,虚线,且均设为灰色
            chartArea.AxisX.Enabled = AxisEnabled.True;                                 //可见
            chartArea.AxisY.Enabled = AxisEnabled.True;                                 
            chartArea.AxisX.MajorGrid.LineDashStyle = ChartDashStyle.Dash;              //虚线
            chartArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dash;
            chartArea.AxisX.MajorGrid.LineColor = System.Drawing.Color.Silver;          //主网格线为灰色,Major,Minor主次
            chartArea.AxisY.MajorGrid.LineColor = System.Drawing.Color.Silver;

            //刻度显示下属性  
            //chartArea.AxisY.LabelStyle.Format = "{MMM}";
            //chartArea.AxisY.LabelStyle.Interval = 1;
            //chartArea.AxisY.LabelStyle.IntervalType = DateTimeIntervalType.Months;
            //chartArea.AxisY.MajorTickMark.LineColor = System.Drawing.Color.Red;
            //chartArea.AxisY.MajorTickMark.Enabled = true;
            //chartArea.AxisY.MajorTickMark.Interval = 1;
            //chartArea.AxisY.MajorTickMark.IntervalType = DateTimeIntervalType.Days;
            string[] AxisYLabelName = { "0.5v", "1.0v", "1.5v", "2.0v", "2.5v", "3.0v", "3.5v", "4.0v", "4.5v", "5.0v"};
            for (int i = 0; i < 10; i++)
            {
                CustomLabel label = new CustomLabel();
                label.Text = AxisYLabelName[i];
                label.ToPosition = i+1;
                chartArea.AxisY.CustomLabels.Add(label);
            }


            //X,Y轴颜色设定
            //chartArea.AxisX.MajorTickMark.LineColor = System.Drawing.Color.Red;       //X轴颜色
            //chartArea.AxisY.MajorTickMark.LineColor = System.Drawing.Color.Red;       //X轴颜色


            //设置图表显示样式, 即X/Y显示比例
            this.chart1.ChartAreas[0].AxisX.ScaleView.Zoom(1,20);
            this.chart1.ChartAreas[0].CursorX.IsUserEnabled = true;
            this.chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
            this.chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;


            //设置滚动条
            this.chart1.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;                 //将滚动内嵌到坐标轴中
            this.chart1.ChartAreas[0].AxisX.ScrollBar.Size = 20;                                 //设置滚动条的大小
            this.chart1.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.All;   //设置滚动条的按钮的风格,下面代码是将所有滚动条上的按钮都显示出来
            this.chart1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
            this.chart1.ChartAreas[0].AxisX.ScaleView.Scroll(ScrollType.LargeDecrement);
            //chartArea.AxisX.LabelStyle.IsEndLabelVisible = true;                                //show the last label

        }
        #endregion

 

3、串口打开操作

        #region 串口打开操作button1
        private void button1_Click(object sender, EventArgs e)
        {


            try
            {
                if (serialPort1.IsOpen)                                                 //open状态
                {
                    while (UartBusyFlag)
                    {
                        System.Windows.Forms.Application.DoEvents();
                    }
                    serialPort1.Close();
                    mainForm.button1.Text = "打开串口";
                    mainForm.pictureBox1_On_Off.Image = mainForm.pictureBox1_On_Off.InitialImage;
                    //button1.ForeColor = Color.Gray;
                    //DataProcessThread.Abort();
                    //DataMatchThread.Abort();
                }
                else
                {
                    serialPort1.Encoding = System.Text.Encoding.GetEncoding("GB2312");  //此行非常重要 可解决接收中文乱码问题
                    serialPort1.PortName = comboBox1.Text;                              //得到当前COM选择口
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);             //得到当前设置的波特率
                    serialPort1.Open();
                    mainForm.button1.Text = "关闭串口";
                    //button1.ForeColor = Color.Blue;
                    //mainForm.richTextBox1.Text = "";                                    //清空接收区
                    mainForm.pictureBox1_On_Off.Image = mainForm.pictureBox1_On_Off.ErrorImage;
                    if (FirstClikFlag == true)
                    {
                        FirstClikFlag = false;
                        DataMatchThread.Start();
                    }
                }
            }
            catch (Exception ex)
            {
                SerialPort SerialPort1 = new System.IO.Ports.SerialPort();              //与下面语句作用一致
                string[] ComNum = SerialPort.GetPortNames();
                comboBox1.Text = ComNum[0];                                             //默认第一个数值值作为串口端口显示,COM1
                //SerialPort SerialPort1 = new SerialPort();
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

 

4、串口接收事件处理

        #region 串口接收事件处理
        private void MySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            UartBusyFlag = true;
            Thread.Sleep(50);
#if SUPPORT_READ_BYTE
            int DataLength = serialPort1.BytesToRead;                   //接收到buffer的字节数
            byte[] DataBuf = new byte[DataLength];
            ReceiveTotalCnt += DataLength;
            serialPort1.Read(DataBuf, 0, DataLength);
            sb.Clear();                                                 //清空
            String LastValue = Encoding.ASCII.GetString(DataBuf);       //将整个数组解码为ASCII数组
            sb.Append(LastValue);                                       //遍历数组进行字符串转化及拼接
#else
            //int DataLength = serialPort1.BytesToRead;                   //接收到buffer的字节数
            //char[] DataBuf = new char[DataLength];

            //ReceiveTotalCnt += DataLength;

            //serialPort1.Read(DataBuf, 0, DataLength);
            
            //string LastValue = new string(DataBuf);

            string LastValue = serialPort1.ReadLine();
            //string LastValue = serialPort1.ReadExisting();
#endif

            PausedEvent.Reset();
            try
            {
                //因为要访问UI资源,所以需要使用invoke方式同步ui  
                //在创建this对象的线程上调用匿名委托.
                //匿名委托的代码就是你看见的那个:
                //delegate {
                //xxx b
                //}
                //这是匿名委托的一种写法,算是C#的语法
                //Invoke((EventHandler)(delegate { this.richTextBox1.AppendText(sb.ToString()); }));  //invoke函数括号中的参数类型必须是委托
                //this.richTextBox1.Focus();                                                          //获取焦点
                //this.richTextBox1.Select(this.richTextBox1.TextLength, 0);                          //光标定位到文本最后
                //this.richTextBox1.ScrollToCaret();                                                  //滚动到光标处

                SetRichTextBox(sb.ToString());

            }
            catch (Exception ex)
            {
                //响铃并显示异常给用户
                System.Media.SystemSounds.Beep.Play();
                MessageBox.Show(ex.Message);
            }

            UartDataBuf += LastValue;
            //for (int i = 0; i < 5; i++ )
            //{
            //    ValueObject[i].DataSourceValue += LastValue;
            //    if (ValueObject[i].DataSourceValue.Length > 1024)
            //    {
            //        ValueObject[i].DataSourceValue = ValueObject[i].DataSourceValue.Remove(0, 1000);
            //    }
            //}
            PausedEvent.Set();
            UartBusyFlag = false;
            //Console.WriteLine("{0}", TempValue);
        }

        private delegate void SetTextCallback(string text);
        private void SetRichTextBox(string text)
        {
            // InvokeRequired需要比较调用线程ID和创建线程ID
            // 如果它们不相同则返回true
            if (mainForm.richTextBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetRichTextBox);
                mainForm.Invoke(d, new object[] { text });
            }
            else
            {
                mainForm.richTextBox1.AppendText(text);
                mainForm.richTextBox1.Focus();                                                          //获取焦点
                mainForm.richTextBox1.Select(this.richTextBox1.TextLength, 0);                          //光标定位到文本最后
                mainForm.richTextBox1.ScrollToCaret();                                                  //滚动到光标处
            }
        }

        #endregion

 

5、保存串口信息(以当前时间为文件名,保存为txt文档,路径固定为当前exe执行文件路径)


        #region 保存串口信息button 2
        private void button2_Click(object sender, EventArgs e)
        {
            string PathStrValue = System.Windows.Forms.Application.StartupPath;
            Console.WriteLine("Path:{0}", PathStrValue);

            System.DateTime currentTime = new System.DateTime();
            currentTime = System.DateTime.Now;
            string DataStrValue = currentTime.Year.ToString() + "-" + currentTime.Month.ToString() + "-" + currentTime.Day.ToString() + "-";
            DataStrValue += currentTime.Hour.ToString() + "-" + currentTime.Minute.ToString() + "-" + currentTime.Second.ToString();
            Console.WriteLine("Time:{0}", DataStrValue);

            PathStrValue = PathStrValue+"\\"+DataStrValue+".txt";

            Console.WriteLine("Last:{0}", PathStrValue);

            StreamWriter SW = File.CreateText(PathStrValue);

            SW.Write(richTextBox1.Text);
            SW.Flush();
            SW.Close();

        }
        #endregion

6、清除串口信息(效果如下)

        #region 清除串口信息button 3
        private void button3_Click(object sender, EventArgs e)
        {
            string ZeroStrChar = "0";
            string NullStrChar = "null";
            richTextBox1.Text = "";         //清空接收文本框
            for(int i = 0; i<5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    ValueObject[i].DataValue[j] = defaultValue[j];
                }
            }

            for (int i = 0; i < 5; i++)
            {
                mainForm.MyCoTextBoxList[i].Text = ZeroStrChar;
            }

            for (int i = 0; i < 5; i++)
            {
                mainForm.MyDarkTextBoxList[i].Text = ZeroStrChar;
            }


            for (int i = 0; i < 5; i++)
            {
                mainForm.MyLightTextBoxList[i].Text = ZeroStrChar;
            }

            for (int i = 0; i < 5; i++)
            {
                mainForm.MyL_DTextBoxList[i].Text = ZeroStrChar;
            }

            for (int i = 0; i < 5; i++)
            {
                mainForm.MyTempTextBoxList[i].Text = ZeroStrChar;
            }
            mainForm.textBox_CO_PPM.Text = ZeroStrChar;
            mainForm.textBox_MCU_BAT.Text = ZeroStrChar;
            mainForm.textBox_CO_BAT.Text = ZeroStrChar;
            mainForm.textBox_Event.Text = NullStrChar;

            for (int i = 0; i < 4; i++)
            {
                mainForm.chart1.Series[i].Points.Clear();
            }
            CoSum = 0;
            DarkSum = 0;
            LightSum = 0;
            L_DSum = 0;
            SeriesSum = 0;
        }
        #endregion

7、实时动态曲线绘制方法

        #region 实时动态曲线绘制方法
        private static uint CoSum = 0;
        private static uint DarkSum = 0;
        private static uint LightSum = 0;
        private static uint L_DSum = 0;
        private static uint SeriesSum = 0;
        private delegate void DynamicDrawLine(int SeriesIndex, int DrawValue);
        private static void DynamicDrawLineFunc(int SeriesIndex, int DrawValue)
        {
            ChartArea chartArea = mainForm.chart1.ChartAreas[0];
            double DisplayData = DrawValue / 1000.0;
            if (mainForm.chart1.InvokeRequired)
            {
                DynamicDrawLine d = new DynamicDrawLine(DynamicDrawLineFunc);
                mainForm.Invoke(d, new object[] { SeriesIndex, DrawValue });
            }
            else
	        {
                chartArea.AxisX.ScaleView.Size = 50D;
                switch (SeriesIndex)
                {
                    case 0:
                        CoSum++;
                        Series SeriesCo = mainForm.chart1.Series[3];
                        SeriesCo.Points.AddXY(CoSum, DisplayData);
                        break;

                    case 1:
                        DarkSum++;
                        Series SeriesDark = mainForm.chart1.Series[0];
                        SeriesDark.Points.AddXY(DarkSum, DisplayData);
                        break;

                    case 2:
                        LightSum++;
                        Series SeriesLight = mainForm.chart1.Series[1];
                        SeriesLight.Points.AddXY(LightSum, DisplayData);
                        break;

                    case 3:
                        L_DSum++;
                        Series SeriesL_D = mainForm.chart1.Series[2];
                        SeriesL_D.Points.AddXY(L_DSum, DisplayData);
                        break;

                    default:
                        break;
                }

                SeriesSum++;
                chartArea.AxisX.ScaleView.Position = 0;
                if ((CoSum % 50 > 0) || (DarkSum % 50 > 0) || (LightSum % 50 > 0) || (L_DSum % 50 > 0))
                {
                    double max = chartArea.AxisX.Maximum;
                    max = (SeriesSum / 10 + 1) * 10;
                    chartArea.AxisX.Interval = max / 10;
                    chartArea.AxisX.ScaleView.Size = SeriesSum * 1.1;
                }
            }
        }
        #endregion

8、串口端口号实时获取

        #region 串口端口号实时获取
        private delegate void SerialPortGet();
        public void SerialPortGetFunc()
        {
            mainForm.serialPort1.PortName = mainForm.comboBox1.Text;   //得到当前COM选择口
            mainForm.comboBox1.Items.Clear();
            foreach (string portName in SerialPort.GetPortNames())
            {
                mainForm.comboBox1.Items.Add(portName);
            }
        }

        private void comboBox1_MouseClick(object sender, MouseEventArgs e)
        {
            if (mainForm.serialPort1.IsOpen)
            {

            }
            else
            {
                if (mainForm.comboBox1.InvokeRequired)
                {
                    SerialPortGet d = new SerialPortGet(SerialPortGetFunc);
                    mainForm.Invoke(d, new object[] { });
                }
                else
                {
                    SerialPortGetFunc();
                }
            }
        }
        #endregion

9、选择Series RadioButton (只选择Light, Co可见)

        #region 选择Series RadioButton 
        private void Dark_Series_Button_Click(object sender, EventArgs e)
        {
            Series SeriesDark = chart1.Series[0];
            if (SeriesDarkSelectFlag == true)
            {
                Dark_Series_Button.Checked = false;
                SeriesDarkSelectFlag = false;
                SeriesDark.IsVisibleInLegend = false;           //Series 名称是否可见
                SeriesDark.Enabled = false;
            }
            else
            {
                Dark_Series_Button.Checked = true;
                SeriesDarkSelectFlag = true;
                SeriesDark.IsVisibleInLegend = true;
                SeriesDark.Enabled = true;
            }
        }

        private void Light_Series_Button_Click(object sender, EventArgs e)
        {
            Series SeriesLight = chart1.Series[1];
            if (SeriesLightSelectFlag == true)
            {
                Light_Series_Button.Checked = false;
                SeriesLightSelectFlag = false;
                SeriesLight.IsVisibleInLegend = false;           //Series 名称是否可见
                SeriesLight.Enabled = false;
            }
            else
            {
                Light_Series_Button.Checked = true;
                SeriesLightSelectFlag = true;
                SeriesLight.IsVisibleInLegend = true;
                SeriesLight.Enabled = true;
            }
        }

        private void L_D_Series_Button_Click(object sender, EventArgs e)
        {
            Series SeriesL_D = chart1.Series[2];
            if (SeriesL_DSelectFlag == true)
            {
                L_D_Series_Button.Checked = false;
                SeriesL_DSelectFlag = false;
                SeriesL_D.IsVisibleInLegend = false;           //Series 名称是否可见
                SeriesL_D.Enabled = false;
            }
            else
            {
                L_D_Series_Button.Checked = true;
                SeriesL_DSelectFlag = true;
                SeriesL_D.IsVisibleInLegend = true;
                SeriesL_D.Enabled = true;
            }
        }

        private void Co_Series_Button_Click(object sender, EventArgs e)
        {
            Series SeriesCo = chart1.Series[3];
            if (SeriesCoSelectFlag == true)
            {
                Co_Series_Button.Checked = false;
                SeriesCoSelectFlag = false;
                SeriesCo.IsVisibleInLegend = false;           //Series 名称是否可见
                SeriesCo.Enabled = false;
            }
            else
            {
                Co_Series_Button.Checked = true;
                SeriesCoSelectFlag = true;
                SeriesCo.IsVisibleInLegend = true;
                SeriesCo.Enabled = true;
            }
        }
        #endregion

10、窗体的关闭事件处理函数、chart鼠标滚动事 、显示帮助信息 button 4

        #region 窗体的关闭事件处理函数,在该事件中将之前创建的线程全部终止
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort1.IsOpen)                                                 //open状态
            {
                while (UartBusyFlag)
                {
                    System.Windows.Forms.Application.DoEvents();
                }
                serialPort1.Close();
            }

            DataMatchThread.Abort();

            if (MyThreadList.Count > 0)
            {
                //编列自定义队列,将所有线程终止
                foreach (Thread tWorkingThread in MyThreadList)
                {
                    tWorkingThread.Abort();
                }
            }
        }
        #endregion

        #region chart鼠标滚动事
        private void chart1_MouseEnter(object sender, MouseEventArgs e)
        {
            ChartArea chartArea = mainForm.chart1.ChartAreas[0];
            if (e.Delta > 0)//鼠标向上
            {
                if (chartArea.AxisX.ScaleView.Size < 5000)//判断显示的最大数值
                    chartArea.AxisX.ScaleView.Size += 5;//+=5---滚动一次显示5个


            }
            else//鼠标向下滚动
            {
                if (chartArea.AxisX.ScaleView.Size > 50)
                    chartArea.AxisX.ScaleView.Size -= 5;// - = 5---滚动一次减小显示5个
            }
        }

        private void chart1_MouseEnter(object sender, EventArgs e)
        {
            MouseWheel += new MouseEventHandler(chart1_MouseEnter);         //调用滚轮事件
        }
        #endregion

        #region 显示帮助信息 button 4
        private void button4_Click(object sender, EventArgs e)
        {
            //DialogResult MsgBoxResult;//设置对话框的返回值
            //MsgBoxResult = MessageBox.Show("请选择你要按下的按钮",//对话框的显示内容
            //"提示",//对话框的标题
            //MessageBoxButtons.YesNo,//定义对话框的按钮,这里定义了YSE和NO两个按钮
            //MessageBoxIcon.Exclamation,//定义对话框内的图表式样,这里是一个黄色三角型内加一个感叹号
            //MessageBoxDefaultButton.Button2);//定义对话框的按钮式样
            //if (MsgBoxResult == DialogResult.Yes)//如果对话框的返回值是YES(按"Y"按钮)
            //{
            //    this.label1.ForeColor = System.Drawing.Color.Red;//字体颜色设定
            //    label1.Text = " 你选择了按下”Yes“的按钮!";
            //}
            //if (MsgBoxResult == DialogResult.No)//如果对话框的返回值是NO(按"N"按钮)
            //{
            //    this.label1.ForeColor = System.Drawing.Color.Blue;//字体颜色设定
            //    label1.Text = " 你选择了按下”No“的按钮!";
            //}
            DialogResult MsgBoxResult;//设置对话框的返回值
            MsgBoxResult = MessageBox.Show("采样电压,精度最大mV\nCO:      [Co=xmV] 或 [Co=x.xV]\nDark:   [Dark=xmV] 或 [Dark=x.xV]\nLight:  [Light=xmV] 或 [Light=x.xV]\nL_D:     [L_D=xmV] 或   [L_D=x.xV]\nT:         [T=xx^C] \n电池和事件\nppm:    [CO:xxxppm]\nMCU_Bat:  [MCU_Bat:xmV] 或 [MCU_Bat:x.xV]\nCO_Bat:     [CO_Bat:xmV] 或 [CO_Bat:x.xV]\nEvent:     [Event:xxxx\\n]",//对话框的显示内容
            "帮助(输出格式)",//对话框的标题
            MessageBoxButtons.OK,//定义对话框的按钮,这里定义了YSE和NO两个按钮
            MessageBoxIcon.Information,//定义对话框内的图表式样,这里是一个黄色三角型内加一个感叹号
            MessageBoxDefaultButton.Button1);//定义对话框的按钮式样
        }
        #endregion

 

  • 3
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值