admin 管理员组文章数量: 887031
2024年1月10日发(作者:新冠能自己好吗)
C# WPF新手入门之串口Modbus通讯
本文是基于C# WPF制作的上位机软件,用于和红外气体分析模块通讯。由于电脑串口是232标准,就是 -12v代表1 ,+12v代表0;而模块是单片机采用的TTL电平,+5v代表1, -5v代表0;因此,在硬件方面需要MAX232芯片做以下转换,以下为硬件原理图:
实物图如下:
1
实际运行效果如下:
上图只是吹了口气测的浓度O(∩_∩)O,主要是测试和模块的通讯,下面介绍界面和程序实现方法。
2
1. 界面设计
用到的主要控件有GroupBox, ComboBox,Button,ListView,TextBox。
此处需要按CTRL+F1展开
内部使用O(∩_∩)O~
以下为XAML代码:
xmlns="/winfx/2006/xaml/presentation" xmlns:x="/winfx/2006/xaml" xmlns:d="/expression/blend/2008" xmlns:mc="/markup-compatibility/2006" xmlns:local="clr-namespace:SmartGasComm" mc:Ignorable="d" Title="smartGASComm" Height="450" Width="1085" Background="#FFF0D0D0" Loaded="Window_Load" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen"> Margin="22,11,0,0" VerticalAlignment="Top" Width="213"> VerticalAlignment="Top" Width="95" FontSize="16"/> 3
5
2. 程序设计
2.1 类库
为了方便使用,把对模块操作的一些指令写成一个类库,在项目中调用就行。
1.新建一个类库
2.项目中引用
如果不想给别人看到源代码,可以引用.DLL文件,此处引用的是工程,可以看到源代码
如果只引用DLL文件,双击只能看到类库中的方法,是不能看到每个方法具体怎么实现的
6
此处主要介绍读取模块浓度方法:
此模块采用Modbus协议通讯:
波特率:2400
数据位:7
校验:奇校验
停止位:1
发送数据格式如下:
校验和按照LRC(纵向冗余校验)形式。所有被传输的字节进行校验计算,不带有CR和LF字符。所有字节相加,并在最后减去0xFF,在这个结果上会再加上0x01,至此完成LRC校验
Excel简易计算
简单来说就是加起来的和对256求余,最后用256减去求余后不满256的值,转换成16进制就是最后的结果。
7
C# 代码如下:
public string Lrc_Checksum(string full)//对发送数据求校验和
{
string[] s = new string[];
int ascii_value = 0;
for (int i = 0; i < ; i++)
{
s[i] = ing(i, 1);
ascii_value += (int)((s[i]));
}
int checksum = 255 - (ascii_value % 256) + 1;
if (checksum < 16)
return "0" + ng(checksum, 16).ToUpper();
else
return ng(checksum, 16).ToUpper();
}
因此,假如模块地址为1F,需要读取浓度,则发送的字符串为
“:1F03000A000194/r/n”
模块接收到发送的指令后,会把电脑发送的字符串和模块发出的字符串发送给电脑。
假如在串口调试小工具里发送如下:
发送::1F03000A000194 这里最后是有回车符号的
接收::1F03000A000194
:1F
此处根据手册如下,可知浓度为0000,最后把数据转成10进制就可以了。
8
C# 代码如下:
public int GasModule_getConcentration(int adr, int errorcode)//读取模块浓度
{
string addr = Addr_Convert_Hex(adr);
string sum = Lrc_Checksum(addr + dataConcentrat);
_(":" + addr + dataConcentrat + sum + dataEndSign);
(timeOut);
int n = _oRead;
if (n < 19)//初步判断,小于19则说明模块没有接收到发送的数据
return -1;
char[] original = new char[n];
_(original, 0, n);
buf = new string[];
for (int i = 0; i < 13; i++) // :1F02000AXXXXnn
{
buf[i] = ng(original[i + 17]);
if (buf[i] == ":")
{
index = i;
}
}
//简单粗暴,直接截取从:开始后面的XXXX4位浓度
string Datacon = buf[index + 7] + buf[index + 8] + buf[index + 9] + buf[index
+ 10];
return 32(Datacon, 16);
//对返回数据进行校验
/*for (int i = 0; i < 13; i++)
{
buf[i] = ng(original[i + 18]);
}
if (Lrc_Check())
{
int len = 32(buf[5]);
string concentration = "";
for (int i = 0; i < len * 2; i++)
concentration += buf[i + 6];
int concen = 32(concentration, 16);
return concen;
}
else
return -1;*/
9
}
至此,读取模块浓度方法完成。
2.2 主程序
在主界面增加了隐藏功能,需要CTRL+F1开启,代码如下:
n += MainWindow_KeyDown;//添加按键事件,打开隐藏画面
private void MainWindow_KeyDown(object sender, KeyEventArgs e)//按键事件
{
if ((ers & l) == l &&
== Key.F1)
{
if (HideHMI == true)
{
= 1085;
HideHMI = false;
}
else
{
= 820;
HideHMI = true;
}
}
}
完整界面如下,增加了对模块零点和量程的校准,以及更改模块地址:
正常显示此部分,组合键开启后边部分显示
此部分位为隐藏画面,按CTRL+F1展开
10
以下为完整代码(不包括类库):
using System;
using c;
using entModel;
using ;
using ;
using ;
using ing;
using ;
using s;
using ls;
using ;
using nts;
using ;
using ;
using g;
using tion;
using ;
using ing;
using Modbus;
namespace SmartGasComm
{
///
/// 的交互逻辑
///
public partial class MainWindow : Window
{
private bool HideHMI = true;
private int ReadFlag = 1;
private int DataCount = 1;
private string GasType = "CO2";
private string Addr = "------";
private int GasCon = 0;
private string Spare1 = "------";
private string CurrentTime = " ";
private int StartAddr = 1;
private SerialPort serialPort;
private ModbusComm modbusComm;
public MainWindow()
{
11
InitializeComponent();
}
private void Window_Load(object sender, RoutedEventArgs e)
{
serialPort = new SerialPort
{
DataBits = 7,//数据位
Parity = ,//奇偶校验
StopBits = //停止位
};
for (int i = 1; i < 11; i++)
{
("COM"+ng());
}
= "COM1";//默认串口号
= "2400";//默认波特率
= 820;//默认隐藏右边画面
n += MainWindow_KeyDown;//添加按键事件
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)//按键事件
{
if ((ers & l) == l &&
== Key.F1)
{
if (HideHMI == true)
{
= 1085;
HideHMI = false;
}
else
{
= 820;
HideHMI = true;
}
}
}
12
private void Serial_Btn_Click(object sender, RoutedEventArgs e)//串口打开关闭
{
try
{
if ()
{
();
Serial_ound = ;
Serial_t = "打开串口";
}
else
{
me = ;
te = 32(, 10);
();
Serial_ound = ;
Serial_t = "关闭串口";
}
}
catch (Exception err)
{
if ()
{
();
}
(ng(), "错误");
}
}
private void Btn1_Click(object sender, RoutedEventArgs e)//开始读取数据
{
if ()
{
ReadFlag = 1;
led = false;//点击后不能再次点击
Serial_led = false;//读取数据时不能关闭串口
modbusComm = new ModbusComm(serialPort);
StartAddr = 32(Address_, 16);//获取界面输入的地址
Addr = _Convert_Hex(StartAddr); //界面显示当前模块地址
Thread thread = new Thread(Reflash)//新建一个线程
{
13
IsBackground = true//随着主线程的退出而退出
};
();
}
else
{
("请先打开串口");
}
}
private void Reflash()//界面刷新
{
while (ReadFlag == 1)
{
GasCon = ule_getConcentration(StartAddr,0);
(new Action(() =>
{
CurrentTime = ng("HH:mm:ss");//获取当前时间
(new ModuleInfo(DataCount,Addr ,GasType, GasCon,
Spare1, CurrentTime));//模块信息显示
edIndex = - 1;//自动滚动至最后
IntoView(edItem);//自动滚动至最后
}), null);
DataCount++;
}
}
private void Btn2_Click(object sender, RoutedEventArgs e)//停止读取
{
ReadFlag = 0;
led = true;
Serial_led = true;
}
private void Btn3_Click(object sender, RoutedEventArgs e)//清除数据
{
();
DataCount = 1;
}
private void Btn4_Click(object sender, RoutedEventArgs e)//零点校准
{
if (ReadFlag == 0)
{
14
int p = ule_SetZero(StartAddr, 0);
if (-1 == p)
{
("零点校准失败!");
}
else
{
("零点校准完成!");
}
}
else
{
("请先停止读取数据!");
}
}
private void Btn5_Click(object sender, RoutedEventArgs e)//量程校准
{
if (ReadFlag == 0)
{
int p = ule_SetZero(StartAddr, 0);
if (-1 == p)
{
("量程校准失败!");
}
else
{
("量程校准完成!");
}
}
else
{
("请先停止读取数据!");
}
}
private void Btn6_Click(object sender, RoutedEventArgs e)//设置新地址 {
if (ReadFlag == 0)
{
string newAdr = ing(4, 2);
int newAdr_1 = 32(newAdr, 16);
if (newAdr_1 == 0)
15
{
("地址不能设置为0!");
}
else
{
ddr = ule_ChangeAddr(StartAddr, newAdr,
0);
if (-1 == StartAddr)
{
("设置新地址失败!");
}
else
{
("设置新地址完成!");
}
}
}
else
{
("请先停止读取数据!");
}
}
}
public class ModuleInfo//模块信息
{
public int Num1 { get; set; }
public string Num2 { get; set; }
public string Num3 { get; set; }
public int Num4 { get; set; }
public string Num5 { get; set; }
public string Num6 { get; set; }
public ModuleInfo(int datacount, string addr, string gastype, int con, string spare1,
string time)
{
1 = datacount;
2 = addr;
3 = gastype;
4 = con;
5 = spare1;
6 = time;
}
}
16
}
17
版权声明:本文标题:C# WPF新手入门之串口Modbus通讯 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1704871255h465161.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论