禾川官方的资料也还是比较多的,但有关Modbus通讯控制的,相对较少;毕竟常规的都是使用PLC来发送脉冲进行运动控制,想用上位机使用Modbus-RTU的方式来控制电机,就还是要看着资料研究一番。
资料下载:https://download.csdn.net/download/rotion135/89934507
资料包含:通讯说明文档,通讯控制示例程序(C#)
目录
软件界面:
1.通讯默认参数
文档中有介绍
默认波特率:9600
数据位:8
停止位:1
校验位:None
也可以自行设置
2.通讯地址计算方式
文档中也有介绍
常规读写问题不大,如果需要断电后恢复的参数,就需要同时写入断电保持区域
3.DI功能说明
下面重点来说下DI功能的设置与控制,主要就是这个功能,其他的设置都用常规的通讯去设置值就可以,程序中也有示例,直接下载来看就行
首先要启动虚拟DI功能,就要将端子的功能都置为0,否则会报错功能冲突
//iskeep-- 是否断电保持,如果是则还需要再写入 地址+0xE000
//先清零P04的功能设置
int[] p40_DI = new int[8];
lock (_lock)
{
modbus.WriteMultipleRegisters(0x0401, p40_DI);
if(iskeep)
{
modbus.WriteMultipleRegisters(0x0401+ 0xE000, p40_DI);
}
}
Thread.Sleep(20);
int[] p40_DIL = new int[8];
lock (_lock)
{
modbus.WriteMultipleRegisters(0x040B, p40_DIL);
if (iskeep)
{
modbus.WriteMultipleRegisters(0x040B + 0xE000, p40_DIL);
}
}
Thread.Sleep(20);
int[] p40_DO = new int[5];
lock (_lock)
{
modbus.WriteMultipleRegisters(0x0415, p40_DO);
if (iskeep)
{
modbus.WriteMultipleRegisters(0x0415 + 0xE000, p40_DO);
}
}
Thread.Sleep(20);
int[] p40_DOL = new int[5];
lock (_lock)
{
modbus.WriteMultipleRegisters(0x041F, p40_DOL);
if (iskeep)
{
modbus.WriteMultipleRegisters(0x041F + 0xE000, p40_DOL);
}
}
Thread.Sleep(20);
然后就是设置DI端子都需要什么功能,一定要设置过的触发了才有效,如果需要动态的,就可以再程序运行过程中替换设置,然后再触发;
功能点挺多的,但不能设置重复
功能点设置,就是按4位这样拆分,然后启用就是1 ,不启用就是0,这样4位4位的组装成16进制的数字,再将这个数字写入 0x0905 和 0x0906地址中
0x0905 对应0-15的功能点 0x0906对应16-31的功能点
软件中有写好的转换方式,可自行查看;
不算复杂,就是每个下拉框都能得到对应的数字,然后定义一个16长度的int数组,用来存放二进制,将选择的功能设置到二进制对应的位置,最后转成16进制数字,再写入地址中
int[] di_s = new int[9];//DI1-DI9的功能选项
di_s[0] = this.cb_DI1.SelectedIndex;
di_s[1] = this.cb_DI2.SelectedIndex;
di_s[2] = this.cb_DI3.SelectedIndex;
di_s[3] = this.cb_DI4.SelectedIndex;
di_s[4] = this.cb_DI5.SelectedIndex;
di_s[5] = this.cb_DI6.SelectedIndex;
di_s[6] = this.cb_DI7.SelectedIndex;
di_s[7] = this.cb_DI8.SelectedIndex;
di_s[8] = this.cb_DI9.SelectedIndex;
//判断是否有重复的,0除外
var res = HasDuplicates(di_s);
if (res)
{
MessageBox.Show("DI1-9 存在重复的功能");
return false;
}
//进行排序
// 提取非零元素并排序
var nonZeroElements = di_s.Where(x => x != 0).OrderBy(x => x).ToArray();
int index = 0;
// 重新填充数组,跳过0的位置
for (int i = 0; i < di_s.Length; i++)
{
if (di_s[i] != 0)
{
di_s[i] = nonZeroElements[index++];
}
}
var B0905 = new int[4 * 4];
var B0906 = new int[4 * 4];
int index_0905 = B0905.Length - 1;
int index_0906 = B0906.Length - 1;
for (int i = 0; i < B0905.Length; i++)
{
if (di_s.Contains(i))
{
B0905[index_0905] = 1;
}
index_0905--;
}
for (int i = 0; i < B0906.Length; i++)
{
if (di_s.Contains(i + 16))
{
B0906[index_0906] = 1;
}
index_0906--;
}
//根据数组,获取设置的十六进制值
string H0905 = "";
string H0906 = "";
for (int i = 0; i < B0905.Length; i = i + 4)
{
var bin = B0905[i].ToString() + B0905[i + 1].ToString() + B0905[i + 2].ToString() + B0905[i + 3].ToString();
var hex = LS.Helper.NumberConvert.DEC_HEX(LS.Helper.NumberConvert.BIN_DEC(bin));
H0905 += hex;
}
for (int i = 0; i < B0906.Length; i = i + 4)
{
var bin = B0906[i].ToString() + B0906[i + 1].ToString() + B0906[i + 2].ToString() + B0906[i + 3].ToString();
var hex = LS.Helper.NumberConvert.DEC_HEX(LS.Helper.NumberConvert.BIN_DEC(bin));
H0906 += hex;
}
int D0905 = NumberConvert.HEX_DEC(H0905);
int D0906 = NumberConvert.HEX_DEC(H0906);
//再根据设置,对P09.05 -P09.08 设置相应的功能点
lock (_lock)
{
modbus.WriteSingleRegister(0x0905, D0905);
if (iskeep)
{
modbus.WriteSingleRegister(0x0905 + 0xE000, D0905);
}
}
Thread.Sleep(20);
lock (_lock)
{
modbus.WriteSingleRegister(0x0906, D0906);
if (iskeep)
{
modbus.WriteSingleRegister(0x0906 + 0xE000, D0906);
}
}
设置完之后,如何触发呢;
两个地址 3607H 3608H;同样是两个地址,然后4位4位的转成二进制,0/1触发或关闭相应的功能; 注意的是:只能触发上边DI端子设置过的功能,没有设置过的触发了也无效
3607H- 对应0-15 3608H 对应 16-31
软件做了自动计算触发的值 如何实现的就自行下载查看了:
启用DO输出功能也是差不多的逻辑,DO就只能读取,不能写入; 对应的地址位和对应的读取地址可查看文档或看软件了。
可以介绍的东西太多了,文章就不一一列举了,有问题可以评论区提出一起讨论下;
程序也是直接提供的源码,可自行学习和修改;