杭电嵌入式课程设计——上位机



课程设计要求

如下图:
在这里插入图片描述
我们选择采用C#进行开发,因为串口有距离限制不方便我们采集数据,所以我们决定用C#做一个TCP的服务器,进行数据观察以及数据采集。


将电脑作为TCP服务器

C#提供了丰富的网络开发API,根据socket通信基本流程图
总结通信的基本步骤:

  1. 创建一个用于监听连接的Socket对像;
  2. 用指定的端口号和服务器的ip建立一个EndPoint对像;
  3. 用socket对像的Bind()方法绑定EndPoint;
  4. 用socket对像的Listen()方法开始监听;
  5. 接收到客户端的连接,用socket对像的Accept()方法创建一个新的用于和客户端进行通信的socket对像;
  6. 第六步:通信结束后一定记得关闭socket;
//创建socket对象
Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
    
    
	//绑定IP和申请端口
    tcpServer.Bind(new IPEndPoint(IPAddress.Any, 8888));
}
catch (Exception ex)
{
    
    
    MessageBox.Show("错误: ip地址设置失败,请正确连接热点并检查ip地址", "提示",MessageBoxButtons.OK);
    t.Abort();//结束本线程
}
server_open_flag = true;    //标志服务器开启成功
server_open_change = true;  //标志服务器开启状态改变

tcpServer.Listen(100);//设置客户端最大连接数
clientSocket = tcpServer.Accept();              //会一直阻塞等待连接 若没有链接则不会进行

处理接收到的数据

处理接收到的数据就较为简单,我们每个数据包会发送14个字节的数据,也就是两位的校验位和6个int16的数据,分别代表三个轴上的加速度计和陀螺仪数据。具体步骤如下:

  1. 对收到的首位数据进行判断,若不为帧头,那么在缓冲区数组中移除改数据,并等待下一个数据,以保证数据的准确性。
  2. 将接收到的byte数据进行移位操作后得到正确的int16的数据。
while (true)
 {
    
    
     int buffer_num = data_buffer.Count;    //提前存好,防止处理过程中Count变化
     if (buffer_num >= 14)   //至少14字节
     {
    
    
         if (data_buffer[0] == 0x5A)     //帧头
         {
    
    
             if (data_buffer[1] == 0x01)  //判断这一帧是否是心跳波形数据
             {
    
    
                 Action act = delegate () {
    
      cur_data.a_x = (Int16)(data_buffer[2] | data_buffer[3] << 8);
                                             cur_data.a_y = (Int16)(data_buffer[4] | data_buffer[5] << 8);
                                             cur_data.a_z = (Int16)(data_buffer[6] | data_buffer[7] << 8);
                                             cur_data.g_x = (Int16)(data_buffer[8] | data_buffer[9] << 8);
                                             cur_data.g_y = (Int16)(data_buffer[10]| data_buffer[11] << 8);
                                             cur_data.g_z = (Int16)(data_buffer[12]| data_buffer[13] << 8);
                                             ecg_add_data(cur_data.a_x, cur_data.a_y, cur_data.a_z, cur_data.g_x, cur_data.g_y, cur_data.g_z);
                 };
                 data_save.Add(new datatype(cur_data.a_x, cur_data.a_y, cur_data.a_z, cur_data.g_x, cur_data.g_y, cur_data.g_z));
                 this.Invoke(act);
                 data_buffer.RemoveRange(0, 13);      //从缓存中删除前14个字节
             }
         }
         else
         {
    
    
             //这里是很重要的,如果数据开始不是帧头,就删除一位
             data_buffer.RemoveAt(0);
         }
     }
     if (main_form_close_flag == true)
     {
    
    
         t1.Abort();      //结束本线程
     }
 }

曲线绘制

图表

我们采用自带的chart控件,在form中点击工具箱搜索即可寻找到该控件。

在这里插入图片描述
将chart移动到合适的位置后,我们可以在窗体加载函数中,将chart进行初始化配置,具体代码如下:

ChartArea chartArea = new ChartArea();
chartArea.Name = "FirstArea";

chartArea.CursorX.AutoScroll = true;            //是否滚动
chartArea.AxisX.ScrollBar.Enabled = true;       //开启滚动条
chartArea.CursorX.IsUserEnabled = true;
chartArea.CursorX.IsUserSelectionEnabled = true;
chartArea.AxisX.ScaleView.Zoomable = true;
chartArea.CursorX.SelectionColor = Color.SkyBlue;

chartArea.CursorY.IsUserEnabled = true;
chartArea.CursorY.AutoScroll = true;
chartArea.CursorY.IsUserSelectionEnabled = true;
chartArea.CursorY.SelectionColor = Color.SkyBlue;

chartArea.CursorX.IntervalType = DateTimeIntervalType.Auto;


chartArea.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.All;//启用X轴滚动条按钮


chartArea.BackColor = Color.AliceBlue;                      //背景色
chartArea.BackSecondaryColor = Color.White;                 //渐变背景色
chartArea.BackGradientStyle = GradientStyle.TopBottom;      //渐变方式
chartArea.BackHatchStyle = ChartHatchStyle.None;            //背景阴影
chartArea.BorderDashStyle = ChartDashStyle.NotSet;          //边框线样式
chartArea.BorderWidth = 1;                                  //边框宽度
chartArea.BorderColor = Color.Black;


chartArea.AxisX.MajorGrid.Enabled = false;
chartArea.AxisY.MajorGrid.Enabled = false;

// Axis
chartArea.AxisY.Title = @"数值";
chartArea.AxisY.LabelAutoFitMinFontSize = 5;
chartArea.AxisY.LineWidth = 2;
chartArea.AxisY.LineColor = Color.Black;
chartArea.AxisY.Enabled = AxisEnabled.True;

chartArea.AxisX.Title = @"时间";
chartArea.AxisX.IsLabelAutoFit = true;
chartArea.AxisX.LabelAutoFitMinFontSize = 5;
chartArea.AxisX.LabelStyle.Angle = -15;


chartArea.AxisX.LabelStyle.IsEndLabelVisible = true;        //show the last label
chartArea.AxisX.Interval = 10;
chartArea.AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
chartArea.AxisX.IntervalType = DateTimeIntervalType.NotSet;
chartArea.AxisX.TextOrientation = TextOrientation.Auto;
chartArea.AxisX.LineWidth = 2;
chartArea.AxisX.LineColor = Color.Black;
chartArea.AxisX.Enabled = AxisEnabled.True;
chartArea.AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Months;
chartArea.AxisX.Crossing = 0;

chartArea.Position.Height = 85;
chartArea.Position.Width = 85;
chartArea.Position.X = 5;
chartArea.Position.Y = 7;

chart_ecg.ChartAreas.Add(chartArea);
chart_ecg.BackGradientStyle = GradientStyle.TopBottom;
//图表的边框颜色、
chart_ecg.BorderlineColor = Color.FromArgb(26, 59, 105);
//图表的边框线条样式
chart_ecg.BorderlineDashStyle = ChartDashStyle.Solid;
//图表边框线条的宽度
chart_ecg.BorderlineWidth = 2;
//图表边框的皮肤
//chart_ecg.BorderSkin.SkinStyle = BorderSkinStyle.FrameThin3;

chart_ecg.ChartAreas[0].AxisX.Interval = 0.5;        //设置横坐标的分辨率
chart_ecg.ChartAreas[0].AxisX.ScaleView.Size = 1000; //设置横坐标长度

曲线

除了图表,我们还需要绘制曲线,我们采用Series进行绘制,一共需要6条曲线,初始化操作相同,下面就给出一条曲线的初始化操作:

series1 = new Series();
series1.ChartArea = "FirstArea";
chart_ecg.Series.Add(series1);

//Series1 style
series1.ToolTip = "#VALX,#VALY";    //鼠标停留在数据点上,显示XY值

series1.Name = "a_x";
series1.ChartType = SeriesChartType.Line;  // type:折线
series1.BorderWidth = 2;
series1.Color = Color.Black;
series1.XValueType = ChartValueType.Time;//x axis type
series1.YValueType = ChartValueType.Int32;//y axis type

//Marker
series1.MarkerStyle = MarkerStyle.Square;
series1.MarkerSize = 2;
series1.MarkerColor = Color.Black;

然后就是绘制曲线了,代码如下:

series1.Points.AddXY(osc_x, data1);

最后还有选择显示的曲线,直接用check控件即可,代码如下:

series1.Enabled = check1.Checked;

综上,我们的曲线绘制部分就完成了。

文件写入

为了方便数据的处理,需要将接受到的数据写入到文件中,具体实现步骤如下:

  1. 将接收到的数据写入缓冲区
  2. 将缓冲区的数据写入到文件

为了方便数据存储以及写入,我定义了一个类(偷懒了,写的不严谨):

class datatype
{
    
    
      public Int16 a_x, a_y, a_z;
      public Int16 g_x, g_y, g_z;

      public datatype(Int16 ax = 0, Int16 ay = 0, Int16 az = 0, Int16 gx = 0, Int16 gy = 0, Int16 gz = 0)
      {
    
    
          this.a_x = ax;
          this.a_y = ay;
          this.a_z = az;

          this.g_x = gx;
          this.g_y = gy;
          this.g_z = gz;
      }
	
      public string data_to_string()
      {
    
    
          return a_x + "," + a_y + "," + a_z + "," + g_x + "," + g_y + "," + g_z ;
      }
}

定义一个缓冲区,用于存放数据:

private List<datatype> data_save = new List<datatype>();

写入缓冲区很简单,直接写入该list即可。
然后便是写入文件:

if (data_save.Count == 0)
{
    
    
    MessageBox.Show("错误:缓存区为空", "提示", MessageBoxButtons.OK);
    return;
}
//文件名为当前时间
string Child_Address = Save_File_Address + "\\" + DateTime.Now.ToString().Replace(@"/", ".").Replace(":", ".") + ".dyy";//创建文件
FileStream fs = new FileStream(Child_Address, FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs); // 创建写入baidu流

for (int i = 0; i < data_save.Count; i++)
{
    
    
    sw.WriteLine(data_save[i].data_to_string());
}
//删除缓存区数据
data_save.RemoveRange(0, data_save.Count);
sw.Close(); //关闭文件

最后加个按键控制就完事了。

工程链接

工程链接:https://pan.baidu.com/s/11celASODrhJEwRoCH_7OVQ
提取码:dfds

猜你喜欢

转载自blog.csdn.net/qq_43550173/article/details/112101565
今日推荐