본문 바로가기
프로그래밍/C Sharp

[ C# ] LS 산전 PLC 이더넷 통신 - 개별 쓰기

by jeong-f 2021. 12. 7.
반응형

데이터 쓰기 명령에는 두 가지의 방법이 있으며 데이를 읽을 목적에 따라 개별과 연속으로 사용할 수 있습니다

  • 개별 쓰기  : 여러 타입의 변수형을 쓸 수 있으며 16개의 블록(번지)을 쓸 수 있음
              일반적으로 쓰기 명령에 사용
  • 연속 : 바이트 형(최대 14000개)의 변수를 블록 단위로 쓸 수 있음
             데이터 초기화를 할 경우에 유용함

 

통신 구성

데이터 읽기와 비슷한 형태이며, 보통 1개의 데이터를 쓰기를 하므로 1개의 영역을 읽는 다고 가정했을 때 고정되는 부분을 적색으로 표시하겠습니다.

  • 명령어(2) : 0x0058
  • 데이터 타입 : 
    0x00 비트(% MX0) ,  0x01 바이트($MB0)
    0x02 워드(% MW0),  0x03 더블 워드(% MD0) , 0x04 롱 워드(% ML0)
  • 예약 영역 : 0x00
  • 변수 개수 : 1개의 블록을 쓸 것이므로 0x01
  • 변수명 길이 : % MW0--> 0x04
  • 변수명 : % MW0
  • 데이터 크기 : 한 개의 영역을 사용한다면 아래와 같습니다.
    0x01 비트(% MX0) ,  0x01 바이트($MB0)
    0x02 워드(% MW0),  0x04 더블 워드(% MD0) , 0x08 롱 워드(% ML0)
  • 데이터 : 쓰고자 하는 데이터를 바이트로 분리하여 크기에 맞게 입력

C# 프로그램으로 명령 만들기

프로그램으로 구현한 내용 중 "롱 워드"의 경우 사용 빈도수가 적어 추가적인 구현을 진행하지 않았습니다.
사용이 필요한 경우 데이터의 크기가 크므로 번지에 주의하여 비트연산을 섞어 전송해야 할 것입니다.(더블워드 전송 참고)

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
void RegisterWriteOne(string szDevice, long pData)
{
    if (string.IsNullOrEmpty(szDevice))
    {
        return;
    }
 
    int nCnt = 0;
    char[] bufBody = new char[2048];
 
    //명령어(2)
    //[ h5400 읽기][ h5800 쓰기 ]
 
    bufBody[nCnt++= (char)0x58;
    bufBody[nCnt++= (char)0x00;
 
    //데이터 타입(2)
    //[ h00 비트 ][ h01 바이트 ][ h02 워드 ][ h03 더블워드 ][ h04 롱워드][ h14 연속]
 
    int nLength = 0;
    switch (szDevice.Substring(21))
    {
        case "X":
            {
                nLength = 1;
                bufBody[nCnt++= (char)0x00;
                bufBody[nCnt++= (char)0x00;
                break;
            }
        case "B":
            {
                nLength = 1;
 
                bufBody[nCnt++= (char)0x01;
                bufBody[nCnt++= (char)0x00;
                break;
            }
        case "W":
            {
                nLength = 2;
 
                bufBody[nCnt++= (char)0x02;
                bufBody[nCnt++= (char)0x00;
                break;
            }
        case "D":
            {
                nLength = 4;
 
                bufBody[nCnt++= (char)0x03;
                bufBody[nCnt++= (char)0x00;
                break;
            }
        case "L":
            {
                // 검증 안됨
                //nLength = 8;
 
                //bufBody[nCnt++] = (char)0x04;
                //bufBody[nCnt++] = (char)0x00;
                break;
            }
    }
 
    //예약 영역(2)
    //0x0000 : Don’t Care.
    bufBody[nCnt++= (char)0x00;
    bufBody[nCnt++= (char)0x00;
 
    //블록수(2)
    //읽고자 하는 블록의 개수. 0x0001
    bufBody[nCnt++= (char)0x01;
    bufBody[nCnt++= (char)0x00;
 
    //변수명 길이(2)
    //변수 명의 길이. 최대 16자.
    bufBody[nCnt++= (char)szDevice.Length;
    bufBody[nCnt++= (char)0x00;
 
    //데이터 주소
    char[] ch = szDevice.ToCharArray();
 
    for (int i = 0; i < ch.Length; i++)
    {
        bufBody[nCnt++= ch[i];
    }
 
    //데이터 개수(2)
    bufBody[nCnt++= (char)(nLength & 0xFF);           // Data length(L)
    bufBody[nCnt++= (char)(nLength >> 8);             // Data length(H)   
 
    switch (szDevice.Substring(21))
    {
        case "X":
        case "B":
            {
                bufBody[nCnt++= (char)pData;
                break;
            }
        case "W":
            {
                bufBody[nCnt++= (char)(pData & 0xFF);
                bufBody[nCnt++= (char)(pData >> 8);
 
                break;
            }
        case "D":
            {
                int nHi = HI_WORD(pData);
                int nLO = LO_WORD(pData);
 
                bufBody[nCnt++= (char)(nLO & 0xFF);
                bufBody[nCnt++= (char)(nLO >> 8);
 
                bufBody[nCnt++= (char)(nHi & 0xFF);
                bufBody[nCnt++= (char)(nHi >> 8);
 
                break;
            }
        case "L":
            {
                // 검증 안됨
                //bufBody[nCnt++] = (char)(pData & 0xFF);
                //bufBody[nCnt++] = (char)(pData >> 8);
 
                //bufBody[nCnt++] = (char)(pData & 0xFFFF);
                //bufBody[nCnt++] = (char)(pData >> 16);
 
                //bufBody[nCnt++] = (char)(pData & 0xFFFF);
                //bufBody[nCnt++] = (char)(pData >> 32);
 
                //bufBody[nCnt++] = (char)(pData & 0xFFFFFFFF);
                //bufBody[nCnt++] = (char)(pData >> 64);
 
                break;
            }
    }
 
    /*헤더와 합치기*/
    _LS_FENET_HRADER ls_Header = new _LS_FENET_HRADER();
    char[] reData = ls_Header.Copy(nCnt, bufBody);
 
    /*reData를 통신으로 전송*/
}
cs

 

통신 시 주의 사항

더블 워드의 경우 데이터의 순번이 밀리는 현상이 있어 번지를 다음과 같이 전송해야 합니다.
M200번인 경우 주소는 200번의 절반인 100번지를 써야 데이터가 밀리지 않고 전송됩니다.

M200 --> % MD100

위와 같이 전송하여야 정상적으로 데이터를 전송 할 수 있습니다,

 

2021.11.17 - [프로그래밍/하드웨어] - [ FA ] LS산전 PLC와 PC와 이더넷 통신하기

 

[ FA ] LS산전 PLC와 PC와 이더넷 통신하기

LS 이더넷 타입 PLC는 자체 적으로 통신 기능을 가지고 있어 설정에서 변경만 해주면 PC와 통신이 가능합니다. 아래 설정을 따라가시면 통신 설정을 할 수 있습니다. 장치 소개 XGB / XGP 등 이더넷

jeong-f.tistory.com

2021.12.07 - [프로그래밍/하드웨어] - LS 산전 PLC 이더넷 통신-헤더 프레임 만들기

 

LS PLC 이더넷 통신-헤더 프레임 만들기

LS PLC의 데이터의 전송은 헤더 + 프레임(읽기/쓰기 명령)을 합쳐서 데이터를 전송하는 방식으로 데이터의 구조를 알아야 정상적으로 통신이 가능합니다, 읽기와 쓰기 명령에 공통적으로 들어가

jeong-f.tistory.com

 

반응형

댓글