VS2022C#语言实现上位机通信,算是实况?

前期已经有了:Keil5代码,基于STM32,使用串口通信,同时遵循Modbus_RTU通信协议。

学习过程:分为多个小部分,如下逐点描述。


1.学习并总结C#基础语法

using: 包含命名空间,写在程序开头,类似KEIL的import。有官方的“库”,也有自己写的同项目其他cs文件的命名空间。

1
2
using System.IO.Ports;//官方“库”
using PC_code_test.Model;//其他cs文件的命名空间

命名空间:使用namespace开辟,其中可以定义变量、编写方法等。一个cs文件可以有多个命名空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;//引用,如果用到同一个工程其他cs文件的方法,才需要using他们的命名空间
namespace first_space//第一个命名空间,就叫first_space
{
class namespace_cl//定义一个类
{
public void func()//定义一个公共的方法
{
Console.WriteLine("Inside first_space");//写出“Inside first_space”
}//console指控制台窗口
}
}
namespace second_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass//定义一个类
{
static void Main(string[] args)//static指不需要创建实例就能调用
{
first_space.namespace_cl fc = new first_space.namespace_cl();
second_space.namespace_cl sc = new second_space.namespace_cl();
fc.func();//.是成员访问运算符
sc.func();//func()是调用实例方法
Console.ReadKey();//等待用户按键(防止窗口关闭)
}
}

注释单行// 多行/ / 开辟新空间#region(可以直接加文字) #endregion

方法:类似C语言的函数,输入+执行一系列任务+输出。输入可为空(),输出可为空void。

1
2
3
4
5
6
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
//访问修饰符 返回类型 方法的名称(参数列表)
//{方法主体,包含完成一系列任务的指令集}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//这个例子主要说明实例方法怎么调用
using System;
class NumberManipulator
{
public int FindMax(int num1, int num2)//定义了一个实例方法
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
//类内调用,public指Printmax是实例方法,调用时不需要创建实例。
public void PrintMax(int a, int b)
{
int max = FindMax(a, b); // 类内部直接调用,不需要类名.
Console.WriteLine("{0} 和 {1} 的最大值是 {2}", a, b, max);//输出
}
//主程序入口,是静态方法,需要创建实例才能调用实例方法。
static void Main(string[] args)
{
NumberManipulator nm = new NumberManipulator(); // 创建实例
//调用 FindMax
int result = nm.FindMax(100, 200);
Console.WriteLine("100 和 200 的最大值是 {0}", result);
Console.ReadKey();
}
}
class TEST
{
//同一命名空间,不同类间的调用
public static void text()
{
// 调用实例方法:先创建实例
NumberManipulator nm = new NumberManipulator();
int result = nm.FindMax(10, 20);
Console.WriteLine("最大值是:" + result);
Console.ReadKey();
}
}
//不同命名空间时,开头包含了using nameForNamespace就能调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//这个例子主要说明静态方法怎么调用
using System;
class NumberManipulator
{
public static int FindMax(int num1, int num2)//定义了一个静态方法
{
return num1 > num2 ? num1 : num2;
}
//直接用类名调用静态方法。
static void Main(string[] args)
{
int r2 = NumberManipulator.FindMax_Static(100, 200);
Console.WriteLine("静态方法结果:" + r2);
Console.ReadKey();
}
}

class: 声明一个类,类不定义任何数据,只是一个蓝图,说明了xx的名称,数据类型,访问规则。由类造出来的具体东西就是实例。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
namespace BoxApplication
{
class Box
{
public double length; // 长度
public double breadth; // 宽度
public double height; // 高度
}
class Boxtester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // 声明 Box1,类型为 Box
Box Box2 = new Box(); // 声明 Box2,类型为 Box
double volume = 0.0; // 初始化体积

// Box1 详述
Box1.height = 5.0;//用.访问类中的变量
Box1.length = 6.0;
Box1.breadth = 7.0;

// Box2 详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;

// Box1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
Console.WriteLine("Box1 的体积: {0}", volume);
Console.ReadKey();//等待用户按键
}
}
}

2.找到串口助手代码并学习

看起来蛮简单的哎,感觉逻辑上能“记住”很麻烦。所以一定要写注释!!!尤其是变量!!!

大体做的事情就是,判断串口打开关闭,读数据&写数据。注意数据格式的处理。


 3.学习modbus协议

依旧整合知乎经验贴,CSDN资源,github代码,B站视频和商家说明书……

好像每次学习东西都是从四面八方东拼西凑,直到信息在脑海中整合了70%以上,才能够看出问题如何解决。(思维很发散,逻辑不清晰)

下载并使用到的工具:(当然也包括VS2022,很多功能自己写也可以的)

Xcom 很正常的串口助手,能够收发信息,满分
modbusSlave 模拟modbus协议从设备,满分
modbuspoll 模拟modbus协议主设备,满分
Configure Virtual Serial Port Driver 提供虚拟串口连接,满分
串口监听工具,我没找到能用的 理论上能在串口工作的时候监听这个串口读/写的数据

事实上我一开始拿到的是八千多行的工业化上位机代码,一开始的心理预期也是程序会非常复杂,加上之前没接触过C#,哪怕是在AI的帮助下,也没找到具体的收发数据流。而且还认为“硬件开始工作按钮”是需要很多类、状态机、处理多线程等等一层套一层的操作之后才能实现的。

之后看到了命令之下串口的收发数据流,只有

1
01 10 00 3B 00 01 02 00 04 A3 18

这一串数据。也就是说,我就算写一个

1
2
byte[] data = new byte[] { 0x01, 0x10, 0x00, 0x3B, 0x00, 0x01, 0x02, 0x00, 0x04, 0xA3, 0x18 };
serialPort1.Write(data, 0, data.Length);

单片机也就开始工作了。(硬件是好用的,提前写好了通信协议)

之后为了不用记这么长串数字,使用了modbus类,每次使用都创建实例,不用明文摆在台面上了。

看起来我又把问题想得太复杂了,怎么能困在这里两个星期呢?!(DDL万岁(很抱歉说出这句话,但是好像到了紧急的时候我才能放下所有情绪,只是解决问题))