WinForm页面





对应Combox按钮里面要书写对应数据
搭建完页面代码后要搭建后端代码,编写完成之后即进行威盟士五插针传感器的数据读取。
后端代码书写
默认窗口配置
cs
private void Form1_Load(object sender, EventArgs e)
{
cbbPortName.DataSource=SerialPort.GetPortNames();
cbbBaudRate.SelectedIndex=1;
cbbDataBit.SelectedIndex=0;
cbbStopBit.SelectedIndex=1;
cbbParity.SelectedIndex=0;
}

编写连接按钮
cs
private void btnConn_Click(object sender, EventArgs e)
{
try
{
if (btnConn.Text=="连接")
{
//设置属性
serialPort1.PortName=cbbPortName.SelectedItem.ToString();
serialPort1.BaudRate = Convert.ToInt32(cbbBaudRate.SelectedItem.ToString());
serialPort1.DataBits = Convert.ToInt32(cbbDataBit.SelectedItem.ToString());
serialPort1.StopBits=(StopBits)Enum.Parse(typeof(StopBits),cbbStopBit.SelectedItem.ToString());
serialPort1.Parity= (Parity)Enum.Parse(typeof(Parity), cbbParity.SelectedItem.ToString());
//判断是否打开 如果没有打开 就调用open()打开
if (!serialPort1.IsOpen) serialPort1.Open();
btnConn.Text = "关闭";
//禁用按钮
cbbPortName.Enabled=false;
cbbBaudRate.Enabled=false;
cbbDataBit.Enabled=false;
cbbStopBit.Enabled=false;
cbbParity.Enabled=false;
}
else
{
btnConn.Text = "连接";
if (serialPort1.IsOpen) serialPort1.Close();
cbbPortName.Enabled= true;
cbbBaudRate.Enabled= true;
cbbDataBit.Enabled= true;
cbbStopBit.Enabled= true;
cbbParity.Enabled= true;
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

编写读取按钮
这里要创建一个自动函数:SendData()
自定义类:CRCHelper
cs
/// <summary>
/// CRC校验帮助类,提供CRC16校验算法实现
/// </summary>
internal class CRCHelper
{
/// <summary>
/// 计算CRC16校验值
/// </summary>
/// <param name="data">需要计算校验值的字节数组</param>
/// <returns>CRC16校验结果,长度为2的字节数组,索引0是低八位,索引1是高八位</returns>
public static byte[] CRC16(byte[] data)
{
// 初始化CRC寄存器为0xFFFF
int crc = 0xffff;
// 遍历数据字节(排除最后两个字节,通常是预留的校验位位置)
for (int i = 0; i < data.Length - 2; i++)
{
// 将当前数据字节与CRC寄存器进行异或运算
crc = crc ^ data[i];
// 对每个数据位进行处理
for (int j = 0; j < 8; j++)
{
// 保存CRC寄存器的最低位
int temp = 0;
temp = crc & 1;
// CRC寄存器右移一位
crc = crc >> 1;
// 确保高位置零,保持16位范围
crc = crc & 0x7fff;
// 如果最低位为1,则与多项式0xA001进行异或
if (temp == 1)
{
crc = crc ^ 0xa001;
}
// 确保结果保持在16位范围内
crc = crc & 0xffff;
}
}
// 准备CRC结果数组,高低位分离
byte[] crc16 = new byte[2];
crc16[0] = (byte)(crc & 0xff); // 取低8位
crc16[1] = (byte)((crc >> 8) & 0xff); // 取高8位
return crc16;
}
}
private void SendData()
{
// *****
//1.buffer 字节数组的前6个字节专门用来生成CRC校验码
byte[] buffer = new byte[8];
buffer[0] = 0X01; //从站地址 0x01 0X01 ===> 1
buffer[1] = 0X03; // 功能码 0x03/0x04 表示读取 0x06/0x10 表示写入
buffer[2] = 0x00; //起始地址的高位
buffer[3] = 0x00; //起始地址的底位
buffer[4] = 0x00;//数据长度的高位
buffer[5] = 0x04;//数据长度的低位
//2.用前面6个字节生成两个自己的crc校验码
byte[] crc= CRCHelper.CRC16(buffer);
//3.设置校验码
buffer[6] = crc[0];//校验码低位
buffer[7]= crc[1];//校验码高位
//buffer 就是咱们准备好的请求报文 也叫做问询帧
//4.发送问询帧
serialPort1.Write(buffer, 0, buffer.Length);
}
private void btnRead_Click(object sender, EventArgs e)
{
//发送请求帧
SendData();
}
编写串口数据接收事件
选择对应创建串口点击属性


编写代码
cs
//DataReceived 事件用来接收从站相应的报文(应答帧)
// DataReceived 是在内部开辟一个分线程接收数据的
//弹窗是主线程的控件,不建议使用弹框
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp=(SerialPort) sender;
//buffer 用来存储接收到的数据
byte[] buffer = new byte[sp.BytesToRead];
sp.Read(buffer, 0, buffer.Length);
//校验一下数据是否符合预期
//13怎么来的 ?
//读取了4个地址,每个地址的数据占用2个字节, ===8
//地址码 ===1
//功能码 ===1
//有效字节数 ==1
//校验码 ===2
//8+1+1+1+2=13
if (buffer.Length != 13) return;
//字节数组 存放的是十六进制的数据
//一个字节是8位 1Byte===8bit 1B=8b
//两个字节是16位 如: 0x02 0x09 0x02 就是高8位 0x09就是低8位
//应答帧
//Tx: 000007 - 01 03 08 00 64 00 C8 00 0A 00 07 30 01
//固定格式
//公式: 10进制 === 16进制高8位 * 256 + 16进制的低8位
int hsl = buffer[3] * 256 + buffer[4];
int wdz = buffer[5] * 256 + buffer[6];
int ddl = buffer[7] * 256 + buffer[8];
int ph = buffer[9] * 256 + buffer[10];
Invoke(new Action(() =>
{
txtHSL.Text=hsl.ToString();
txtWDZ.Text= wdz.ToString();
txtDDL.Text= ddl.ToString();
txtPH.Text= ph.ToString();
}));
}

编写对应按钮
cs
private void cbRealTimeRead_CheckedChanged(object sender, EventArgs e)
{
if (cbRealTimeRead.Checked)
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请先连接,再实时显示数据", "错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
cbRealTimeRead.Checked = false;
return;
}
timer1.Interval = 1000;
timer1.Start();
}
else
{
timer1.Stop();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
SendData();
}

编写对应代码
cs
private void btnWrite_Click(object sender, EventArgs e)
{
//先准备请求帧
byte[] buffer = new byte[8];
buffer[0]= Convert.ToByte(txtSlaveID.Text.Trim()); //从站地址
//功能码
buffer[1] = 0x06;
//寄存地址
int address = Convert.ToInt32(txtStartAddress.Text.Trim());//十进制
//(byte)(address >> 8) 把地址转成高8位
buffer[2] = (byte)(address >> 8);//写入起始地址的高位
//(byte)(address & 0xFF) 把地址转成低8位
buffer[3] = (byte)(address & 0xFF);//写入起始地址的低位
int data = Convert.ToInt32(txtData.Text.Trim());//数据值
//10
//buffer[4] = (byte)(data / 256); //写入的数据的高位
//buffer[5] = (byte)(data % 256);//写入的数据的低位
Console.WriteLine("-------");
Console.WriteLine((byte)(data / 256));//0x00
Console.WriteLine((byte)(data % 256));//0x01
Console.WriteLine("-------");
// >> 位运算符
// & 按位与
buffer[4]= (byte)(data >> 8); //写入的数据的高位
buffer[5]= (byte)(data & 0xFF); //写入的数据的低位
byte[] crc = CRCHelper.CRC16(buffer);
buffer[6] = crc[0];//校验码低位
buffer[7] = crc[1];//校验码高位
//buffer 就是咱们准备好的请求报文 也叫做问询帧
//4.发送问询帧
serialPort1.Write(buffer, 0, buffer.Length);
}
代码功能分析:
串口读写威盟士五插针程序代码综述
该代码是一个基于 C# WinForms 框架开发的串口通信应用程序,专门用于与威盟士五插针设备进行数据交互,实现了设备数据的读取、写入和实时监控功能。
核心功能模块
1. 串口配置与连接模块
- 程序启动时自动获取并显示可用串口列表
- 支持配置串口通信参数:波特率、数据位、停止位和校验位
- 提供连接 / 关闭按钮,实现串口的连接状态切换
- 连接状态变化时自动启用 / 禁用参数配置控件,防止误操作
2. 数据读取模块
- 支持手动读取和定时自动读取两种模式
- 采用标准 Modbus 协议格式发送读取请求帧:
- 包含从站地址 (0x01)、功能码 (0x03)
- 起始地址和数据长度设置
- CRC16 校验码计算与添加
- 对接收到的应答帧进行解析,提取 HSL、WDZ、DDL、PH 四项数据
- 使用 Invoke 方法确保跨线程安全更新 UI 显示
3. 数据写入模块
- 支持向指定从站地址和寄存器地址写入数据
- 采用 Modbus 协议的 0x06 功能码进行单寄存器写入
- 同样包含完整的 CRC16 校验机制
- 支持用户输入从站 ID、起始地址和写入数据值
4. 实时监控模块
- 通过复选框控制是否启用实时数据读取
- 使用定时器实现固定间隔 (1 秒) 自动发送读取请求
- 实时数据变化时自动更新界面显示
技术实现特点
Modbus 协议实现 :遵循 Modbus RTU 通信协议,正确处理功能码、地址、数据长度和校验机制
CRC 校验 :使用 CRCHelper 工具类进行 CRC16 校验码的计算,确保数据传输的准确性
线程安全处理 :在 DataReceived 事件中使用 Invoke 方法,安全地跨线程更新 UI 控件
错误处理 :包含基本的异常捕获机制,当操作出错时显示错误信息
数据转换 :正确处理十六进制与十进制数据的转换,特别是多字节数据的高低位拼接
潜在扩展点
- 可以增加更完善的错误处理机制,特别是针对 Modbus 协议错误码的解析
- 可以添加数据记录和导出功能,方便数据分析
- 可以增加通信日志显示,便于调试和问题排查
- 可以扩展支持更多的 Modbus 功能码,实现更丰富的设备控制
- 可以添加数据报警功能,当监测值超出设定范围时发出提示
该程序整体结构清晰,功能明确,专注于与威盟士五插针设备的串口通信,适合作为工业控制场景中设备监控的基础应用。