使用C#设计ToF测距传感器VL53L5CX上位机软件

科技   2024-12-09 08:01   北京  
 VL53L5CX  传感器上位机软件设计记录  ...... by  矜辰所致

前言

在上一篇文章,我们已经学会了 VL53L5CX  传感器的基本使用,对于传感器展示的效果来说,通过简单的串口终端显示展示效果不是那么理想,我们还可以做一个效果更好的展示软件。

对于不熟悉 C# 的博主来说,参考了好多资料才完成了一个可以使用的 Demo 软件,想着还是来做个记录供日后参考,所以本文的内容就是 VL53L5CX  传感器上位机软件设计记录 。

再次说明,本文面向于 C# 小白、初学者。

传感器 VL53L5CX 使用说明文章:

ToF 测距传感器 VL53L5CX 使用记录

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

  • 一、 Visual Studio 下载安装

  • 二、设计思路

  • 三、设计流程

    • 3.1 实现串口数据接收

    • 3.2  VL53L5CX 数据格式修改

    • 3.3 数据处理

    • 3.4 表格设计

    • 3.5 区域数据显示

    • 3.6 区域颜色显示

  • 四、效果展示

  • 结语

一、 Visual Studio 下载安装

电脑没有 Visual Studio ,还得重新下载一个,直接网上搜索,然后在官网可以找到下载,如下图:

下载完毕后,点击打开安装,基本上按照流程来就可以,安装的时候选择时候,我们本次只需要选中如下两个应用安装即可,如下图:

剩下的就是等着安装完毕即可。

安装完毕打开工程后,我们要选择一个项目模板,这里不要选错了,如下图:

然后就到了给项目命名,设置项目保存位置的界面:

创建完毕,正式进入到设计界面,我们就可以开始设计我们的软件了:

到这里,我们准备好了开发环境,先别急着直接开始,先简单规划一下我们需要怎么开始。

二、设计思路

我们的目的其实很简单,只是为了展示一下 VL53L5CX 的使用效果,让我们能够直观的感受到它的效果。

我们软件要实现的功能主要有以下几点:

1、传感器示例是通过串口输出数据,所以我们首先要实现一个类似串口助手能够接受串口数据功能的界面;2、传感器上报的数据是分区域的,能够根据传感器手册上面的示意一样,做一个对应的表格,进行对应坐标的数据展示;3、为了效果更加直观,可以给不同的区域用不同的颜色来对应测量的距离。

三、设计流程

3.1 实现串口数据接收

C# 设计一个串口助手,实际上这在网上有太多太多前人做过,这里呢我就不从头到尾一步一步说明,这里我参考了 B 站一个很小白的教程,照着做就行了,而且 UP 主提供了 GitHub 下载。

【单片机课/毕设利器】B站最小白的串口调试助手教程上位机C#编程

这里我记录一下需要用到的基础控件,至于细节修改,大家可以参考上面视频。

GroupBox

分组框,用以区分串口设置,串口接收设置,串口发送等不同区域。

Button

按键,打开关闭串口等操作的按钮。

Label

不可编辑的信息,标题指示等,比如波特率,端口,下图端口用的 button,也可以用 Label 。

ComboBox

下拉列表框,在串口选择,波特率选择的时候需要用到:

对于 ComboBox 的列表选项,如果是需要自己定义,可点击控件,在软件右下角,控件属性界面进行修改,如下图:

RadioButton

单选按钮控件,选择是 HEX ,还是 ASCII 码接收发送,实际上我们可以不需要,这里照着写了,就算了。

TextBox

文本框控件,用来显示串口接收的数据

SeriaPort

串口

串口助手具体的实现大家可自己参考上面视频,或直接下载线程的 DEMO,可以直接运行的。

我们展示并不需要发送,所以我省去了一些功能,照着上面昨晚,我们先来验证一下效果:

没问题,可以正常的显示我们上一篇文章看到的效果。

3.2  VL53L5CX 数据格式修改

数据能够接收到了,接下来,就是数据处理了,但是如果是按照上面的方式发送数据的话,数据也没法处理,而且我们做效果,使用 8x8 的 区域效果就更好。

所以我们在上一篇文章 VL53L5CX 示例 Demo 的基础上,对串口上报的数据需要进行修改,这里我选择把数据修改成标准的 json 格式,这样在解析的时候也方便,这里直接上一下修改后的代码:

static void MX_VL53L5CX_SimpleRanging_Process(void)
{
  uint32_t Id;

  CUSTOM_RANGING_SENSOR_ReadID(CUSTOM_VL53L5CX, &Id);
  CUSTOM_RANGING_SENSOR_GetCapabilities(CUSTOM_VL53L5CX, &Cap);

  Profile.RangingProfile = RS_PROFILE_8x8_CONTINUOUS;
  Profile.TimingBudget = TIMING_BUDGET;
  Profile.Frequency = RANGING_FREQUENCY; /* Ranging frequency Hz (shall be consistent with TimingBudget value) */
  Profile.EnableAmbient = 0/* Enable: 1, Disable: 0 */
  Profile.EnableSignal = 0/* Enable: 1, Disable: 0 */

  /* set the profile if different from default one */
  CUSTOM_RANGING_SENSOR_ConfigProfile(CUSTOM_VL53L5CX, &Profile);

  status = CUSTOM_RANGING_SENSOR_Start(CUSTOM_VL53L5CX, RS_MODE_BLOCKING_CONTINUOUS);

  while (1)
  {
    /* polling mode */
    status = CUSTOM_RANGING_SENSOR_GetDistance(CUSTOM_VL53L5CX, &Result);

    if (status == BSP_ERROR_NONE)
    {
      print_result(&Result);
    }

  HAL_Delay(200);
  }
}
#endif /* USE_BARE_DRIVER */

static void print_result(RANGING_SENSOR_Result_t *Result)
{
  int8_t i;
  int8_t j;
  int8_t k;
  int8_t l;
  uint8_t zones_per_line;

  zones_per_line = ((Profile.RangingProfile == RS_PROFILE_8x8_AUTONOMOUS) ||
                    (Profile.RangingProfile == RS_PROFILE_8x8_CONTINUOUS)) ? 8 : 4;

 printf("{\"Databegin\":[");
  for (j = 0; j < Result->NumberOfZones; j += zones_per_line)
  {
  for (l = 0; l < RANGING_SENSOR_NB_TARGET_PER_ZONE; l++)
    {
      /* Print distance and status */
      for (k = (zones_per_line - 1); k >= 0; k--)
      {
    if((j+k) == 56){
     printf("%ld",(long)Result->ZoneResult[j + k].Distance[l]);
    }
    else
    printf("%ld,",(long)Result->ZoneResult[j + k].Distance[l]);
      }
    }
  
  }
 printf("]}\n");
}

然后,我们在串口助手里面看一下,输出的数据变成如下形式:

当然,在我们上面设计的软件也是可以看到的:

3.3 数据处理

到了这个时候我们就要进行细节修改了。

数据解析说明

先说明一下,上面我们已经把数据修改成标准的 Json 格式,最开始我尝试过使用开源的类库 Newtonsoft.Json 进行解析,如果这样的话当然需要添加命名空间,如下图:

...
using System.IO.Ports;//SerialPort 命名空间
using Newtonsoft.Json;
using System.Runtime.Remoting.Contexts;
...

但是实际上后面出现了点问题,好在我们的数据比较简单,所以我干脆直接就自己写一个针对上面格式字符串的函数。

所以在上面串口接收显示的基础之上,我们修改了一下代码,加入了 ProcessReceivedData 数据处理函数:

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (radioBt_getAscii.Checked)  //如果接收是字符模式
                {
                    string content = serialPort1.ReadExisting();//从串口控件读取输入流返回为string
                    textBox_get.AppendText(content);//将接受到的string数据添加到接收窗

                    receivedDataBuffer.Append(content);

                    // 处理接收的数据,检查是否包含完整的一帧
                    ProcessReceivedData();

                }
                else //如果接收是HEX模式
                {

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("数据接受出错" + ex, "error");
            }
        }

数据处理函数

新建一个64个成员的数组,把接收到的距离数据保存到我们数组中:

函数源码:

        private void ProcessReceivedData()
        {
            lock (lockObject) // 确保线程安全
            {
                string receivedData = receivedDataBuffer.ToString();

                // 检查是否包含完整的一帧数据
                if (receivedData.Contains("Databegin") && receivedData.Contains("]"))
                {
                    // 提取一帧完整的数据
                    int startIndex = receivedData.IndexOf("[");
                    int endIndex = receivedData.IndexOf("]");

                    if (startIndex >= 0 && endIndex > startIndex)
                    {
                        string valuesSubstring = receivedData.Substring(startIndex + 1, endIndex - startIndex - 1);
                        string[] values = valuesSubstring.Split(',');

                        if (values.Length == 64)
                        {
                            try
                            {
                                for (int i = 0; i < values.Length; i++)
                                {
                                    dataArray[i] = uint.Parse(values[i]);
                                }
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show($"无法解析数据数组:{ex.Message}");
                            }
                        }
                        else
                        {
                            MessageBox.Show("接收到的数据无效:值数组长度不为 64。");
                        }
                    }
                    else
                    {
                        MessageBox.Show("接收到的数据无效:未找到值数组的起始和结束标记。");
                    }

                    // 清空缓冲区,准备接收下一帧数据
                    receivedDataBuffer.Clear();

                }
            }
        }

3.4 表格设计

我们要在界面上建立一个表格用来展示这 64 个不同的区域,我们要新建一个TableLayoutPanel 控件。

**TableLayoutPanel **

表格容器,用来对应传感器不同的 64 个区域,设置成 8x8 的表格,如下图:

对于表格我们还需要调整一下间距,选择属性,直接设置百分比,如下图:

这里在设置之前,需要自己先手动拖动一下表格行列的间距,然后再设置,要不然,表格不会自动切换成等份的,这里我也说不出来为什么。

最后做成的表格如下:

好的,上面我们只是做好了表格展示区域,对于我们要实现的效果:

1、不同区域显示对应的距离数据;2、根据不同数据在不同区域显示不同的颜色;

我们都是需要通过代码实现的。

3.5 区域数据显示

我们先在尝试在表格上显示对应的数据,首先我们要初始化一下表格:

private void InitializeTableLabels()
{
    for (int row = 0; row < tableLayoutPanel1.RowCount; row++)
    {
        for (int col = 0; col < tableLayoutPanel1.ColumnCount; col++)
        {
            Label label = new Label
            {
                Dock = DockStyle.Fill,
                TextAlign = ContentAlignment.MiddleCenter,
                Font = new Font("Arial"12),
            };

            tableLayoutPanel1.Controls.Add(label, col, row);
        }
    }
}

记得在开头应用一下:

然后在每次数据接收的地方添加数据显示函数:

函数内容如下:

 private void PrintArray()
 {


     for (int row = 0; row < tableLayoutPanel1.RowCount; row++)
     {
         for (int col = 0; col < tableLayoutPanel1.ColumnCount; col++)
         {
             int index = row * tableLayoutPanel1.ColumnCount + col;
             Control cellControl = tableLayoutPanel1.GetControlFromPosition(col, row);

             if (cellControl != null && cellControl is Label)
             {
                 Label label = (Label)cellControl;
                 label.Text = dataArray[index].ToString(); // 直接更新 Label 的文本
             }
         }
     }

     if (needsTableUpdate)
     {
         tableLayoutPanel1.Invalidate();
         needsTableUpdate = false;
     }
 }

最后看一下效果(当然这里没处理一些细节,最后是需要完善的):

3.6 区域颜色显示

好了,单单只有数值,没有颜色,看上去并不像一个表格,也没有什么效果,我们首先在表格初始化的时候给他上一个背景色:

看下效果:

这…… 不知道怎么解决,我放着先不管把, 反正后面表格的代码中还需要处理颜色,我们先去修改一下表格的设计函数tableLayoutPanel1_Paint

其中的 GetColor 函数很简单,就是根据数值大小来改变颜色:

private Color GetColor(uint value)
{
    if (value > 3000)
    {
        value = 3000;
    }
    else if (value < 0)
    {
        value = 0;
    }
    if (value == 0)
    {
        return Color.Green;
    }

    int red = (int)(255 * (3000 - value) / 3000);
    int green = (int)(255 * value / 3000);

    return Color.FromArgb(red, green, 0);
}

修改完成以后,再来看一下初始化效果:

OK,可以了。

接下来就是需要在接收数据的同时更新表格的数值显示和颜色显示了。

我们还是在数据接收函数ProcessReceivedData中加入颜色更新操作:

函数源码如下:

 private void UpdateTableColor()
 {
     if (tableLayoutPanel1.InvokeRequired)
     {
         tableLayoutPanel1.Invoke(new Action(UpdateTableColor));
         return;
     }

     for (int row = 0; row < tableLayoutPanel1.RowCount; row++)
     {
         for (int col = 0; col < tableLayoutPanel1.ColumnCount; col++)
         {
             int index = row * tableLayoutPanel1.ColumnCount + col;
             Color cellColor = GetColor(dataArray[index]);

             if (row < tableLayoutPanel1.RowCount && col < tableLayoutPanel1.ColumnCount)
             {
                 Control cellControl = tableLayoutPanel1.GetControlFromPosition(col, row);

                 if (cellControl != null && cellControl is Label)
                 {
                     Label label = (Label)cellControl;
                     label.Text = dataArray[index].ToString();
                     label.BackColor = cellColor;
                 }
             }
         }
     }

     if (needsTableUpdate)
     {
         tableLayoutPanel1.Invalidate();
         needsTableUpdate = false;
     }
 }

接下来我们来看看实际效果:

确实有效果,只是200ms 上传一次,刷新的时候会闪白色。

这里我们采用以下双缓冲方式试一试,如下图:

private void SetDoubleBuffered(Control control)
{
    // 启用双缓冲
    typeof(Control).InvokeMember("DoubleBuffered",
        BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
        null, control, new object[] { true });
}

再来看看:

好了,基本上没问题了,哈哈,再处理处理细节,完成。

最后,如果测试没问题,可以生成一下解决方案:

四、效果展示

找了个合适的测试场合,来看一下最终的效果,这里上一下有人走过的效果图:

差不多了,不错不错,效果还是可以的。

结语

本文记录了 VL53L5CX 上位机展示软件的设计流程,最后的效果也是很理想的,对于 VL53L5CX 传感器的测试,基本上就告一段落了。

好了,本文就到这里,感谢大家阅读!

END

来源:矜辰所致

版权归原作者所有,如有侵权,请联系删除

推荐阅读
培养一个优秀的嵌入式工程师有多难?
何同学抄袭风波原作者已接受道歉:不想毁掉他
C/C++大限将至,美国要求2026年前全面剔除!

→点关注,不迷路←

嵌入式微处理器
关注嵌入式相关技术和资讯,你想知道的都在这里。
 最新文章