update : 2018.07.26
今天來分享FX3U程式的部分好了,
三菱PLC的通訊,主要是Modbus,
PC下一道指令到PLC,PLC回覆一個回碼給PC。
一台PC可能有多個COM Port,
每個COM Port可能連接多個設備,
那...我們先建立一個串列埠的執行序,
養成好習慣,能拆的就拆吧,
所有程式都在主Form裡是不好的,
建立一個 class ThreadSerial
Form1中去new ThreadSerial 物件
(下圖用實心箭頭表示使用or擁有)
PLC有狀態要監控,又要送控制訊號,
因此我用排程的方式,因此這個物件主要的元件不外乎就是執行序、串列埠、指令陣列
( 基本的執行序建立可以看這篇: Thread in C# )
因為有兩個串列埠,因此用兩個執行序,
執行序1 與執行序2,主要基本的功能只做通訊,
每傳一道指令過去,就必須等待回碼。
再來就是一些細部的功能,就看設計的人怎麼處理,
EX:
- 送碼後回碼Timeout
- 回碼錯誤
- 狀態多久問一次
- 沒有連線
- 離線
例如:
//執行緒 - 通道1
private void Execute_1()
{
while (true)
{
Thread.Sleep(1);
if (bClose) break;
//通訊未開啟
if (!serialPort1.IsOpen)
{
continue;
}
//離線
if (bDisconnectSerial1)
{
serialPort1.Close();
bDisconnectSerial1 = false;
continue;
}
//增加監視指令
Query();
//沒有指令要傳送
if (CmdList_1.Count == 0) continue;
//清除佇列中殘存的資料
serialPort1.DiscardOutBuffer();
serialPort1.DiscardInBuffer();
//取得第一筆指令
Command Cmd = CmdList_1[0];
//傳送指令
Write_1(Cmd);
//等待回碼
Listen_1();
//收到回碼
if (Cmd.ErrorCode == 0)
{
//執行收到回碼要做的事情
Synchronize(OnReceive_1);
}
//有錯誤發生時
if (Cmd.ErrorCode != 0)
{
//顯示錯誤訊息
Synchronize(OnError_1);
//重送計數+1
RetryCount_1++;
//重送計數到達前重送
if (RetryCount_1 <= RetryMax_1) continue;
}
//重送歸零
RetryCount_1 = 0;
//移除第一筆指令
CmdList_1.RemoveAt(0);
}
//執行緒關閉完成
bCloseFinish = true;
}
Modbus 指令分為ASCII 與 RTU ,
依照手冊將指令寫出,懂的話,並不會太難,
public void Write_1(Command Cmd)
{
Cmd.ErrorCode = 0;
//ASCII
if (Mode == CmdMode.ASCII)
{
Cmd.Str = ":" + Cmd.Str + CheckSum.LRC(Cmd.Str) + "\r\n";
Byte[] buf = Encoding.ASCII.GetBytes(Cmd.Str);
try
{
serialPort1.Write(buf, 0, buf.Length);
}
catch (Exception)
{
Cmd.ErrorCode = -1;
return;
}
}
//RTU
else
{
Cmd.Str += CheckSum.CRC_16(Cmd.Str);
//ASCII轉RTU,兩個字為一個Byte的資料,長度會減半
int len = Cmd.Str.Length / 2;
byte[] buf = new byte[len];
for (int i = 0; i < len; i++)
{
buf[i] = (byte)(Byte)Int16.Parse(Cmd.Str.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
}
try
{
serialPort1.Write(buf, 0, len);
}
catch (Exception)
{
Cmd.ErrorCode = -1;
return;
}
}
}
等待回碼的部分
public void Listen_1()
{
ReceiveMsg = "";
int iStart = Environment.TickCount;
int data = -1;
String msg = "";
do
{
//超時
int iTime = Environment.TickCount - iStart;
if (iTime > Timeout)
{
ErrorCode = -1;
return;
}
//讀取一個字元,此函式會一直等到有訊號
try
{
data = serialPort1.ReadByte();
}
catch (TimeoutException)
{
ErrorCode = -1;
return;
}
if (data < 0) continue;
iStart = Environment.TickCount;
if (Mode==CmdMode.ASCII)
{
msg += (char)(data);
//計算是否為符合LRC
if (msg.Length > 2)
{
String StrData = msg.Substring(0, msg.Length - 2);
String StrLRC = msg.Substring(msg.Length - 2, 2);
if (CheckSum.LRC(StrData) == StrLRC) break;
}
}
else
{
msg += data.ToString("X2");
//計算是否為符合CRC
if (msg.Length > 4)
{
String StrData = msg.Substring(0, msg.Length - 4);
String StrCRC = msg.Substring(msg.Length - 4, 4);
if (CheckSum.CRC_16(StrData) == StrCRC) break;
}
}
} while (true);
if (ErrorCode == 0) ReceiveMsg = msg;
}
我的問卷調查
相關文章:
留言列表