Modbus Protocol
Modicon이라는 회사에서 만든 직렬 통신 프로토콜이다.
공장 자동화에 사용되는 PLC(Programmable Logic Controller)의 통신을 위해 만들어졌다.
용어
프로토콜
사람이 소통하려면 소통하고자 하는 사람끼리 정한 규칙(문법, 맞춤법 등)을 지켜야 한다.
마찬가지로 기계끼리 통신하려면 물리적인 규약이 필요하다.
선의 가닥 수, 무선 주파수 등
신호 처리를 위한 전압의 범위 (3V, 5V 등)
위와 같은 통신 규약을 프로토콜(Protocol)이라고 부른다.
통신 방식
직렬 통신
- 한번에 1비트만 보낸다.
병렬 통신
여러 선을 통해 한번에 다수의 비트를 보낸다.
32bit 컴퓨터는 선이 32개여서 한번에 32개의 비트를 보낼 수 있는 것이다.
이중 통신(Duplex)
두 지점 사이에서 정보를 주고 받는 전자 통신을 칭한다.
전송로를 아끼기 위해 여러 종류의 전송 방식이 사용된다.
전이중 통신(Full Duplex)
두 대의 단말기가 데이터 송수신을 위해 동시에 각각 독립된 회선 사용한다.
대표적으로 전화망, 고속 데이터 통신이 있다.
반이중 통신(Half Duplex)
한 쪽이 송신하는 동안 다른 쪽이 수신하는 통신 방식이다.
대표적으로 마스터 슬레이브 방식의 센서 네트워크가 있다.
- Master-Slave 방식 : Master가 요청하면 Slave가 응답하는 방식
단방향 통신(SImplex)
한쪽 방향으로만 전송할 수 있다.
대표적으로 방송, 감시 카메라가 있다.
참고
통신 주소
하드웨어의 Data Sheet를 볼 때 주소를 두 가지로 표현된다면 잘 구분해야 한다.
Register Address
- 일반적으로 생각하는 주소 (1부터 시작)
Protocol Address
- 실제 프로그래밍할 때 접근해야 하는 주소 (0부터 시작)
모드버스 종류
Modbus RTU
- 일반적으로 산업현장에서 많이 사용하는 전송 방식이다.
Modbus ASCII
Modbus TCP/IP
- RTU 방식과 유사하지만 TCP/IP 계층에서 사용할 수 있다.
모드버스 RTU 프로토콜의 구성 요소
bitrate(bps, bit per second)
초당 전달되는 비트의 수
통신하는 기기끼리 주기가 맞아야 한다.
주로 9600을 사용한다.
높을 수록 전송 속도는 빠르지만 전송 거리가 제한된다.
데이터 길이
최소 데이터 전송 길이
주로 8비트(1바이트)를 사용한다.
패리티 비트(Parity bit)
에러 검출
시리얼 통신 자체가 불안하기 때문에 옵션으로 사용할 수 있도록 한 것이다.
N : 안쓴다는 뜻
E : Even, 짝수 패리티
O : Odd, 홀수 패리티
주로 N을 사용한다.
정지 비트(Stop bit)
시리얼 통신은 데이터만 전송되는게 아니라 시작과 끝을 나타내는 비트도 전송한다.
데이터 비트를 모두 전송하고 나서 끝을 알리는 비트의 개수이다.
주로 1비트를 사용한다.
흐름 제어(Flow Control)
버퍼가 꽉 찼을 때 멈춰달라고 하는 기능이다.
보통 사용하지 않음
- 신호선이 하나 더 필요하기 때문에 비용 추가
대표적 시리얼 통신
RS 232
1:1 통신만 지원한다.
외부 영향을 많이 받는다.
- 전자파 등에 의해 데이터가 변형될 수 있다.
심플한 기계가 바로 옆에 있을 때 주로 사용한다.
RS 422
전이중 통신
매우 좋지만 4가닥의 선이 필요하므로 가격이 비싸다.
RS 485
가장 많이 사용하는 방식이다.
결선에 따라 전이중 통신, 반이중 통신 모두 가능하다.
멀티 드롭 방식
여러 대의 단말기들을 한 개의 통신 회선에 연결한다.
주소 판단 기능과 데이터 버퍼 기억 장치가 있어야 한다.
브로드캐스트로 보내기 때문에 타겟이 아닌 기기에게도 데이터가 전송된다.
따라서 패킷에 수신자를 정해서 보내야 한다.
수신자는 패킷의 타겟에 해당되면 데이터를 받고, 해당되지 않으면 무시한다.
이를 약속한 것이 모드버스 프로토콜이다.
패킷의 구조
요구하는 기능마다 필요한 데이터가 다르기 때문에, Function Code에 따라 구성이 다르다.
Slave ID Function Code Address Number of Data Data Size Data CRC 1 Byte 1 Byte 2 Bytes 2 Bytes 1 Byte n Bytes 2 Bytes Slave ID
1 Byte
하드웨어를 구분하기 위한 주소이다.
Device Address, 국번이라고도 부른다.
Function Code
1 Byte
미리 정해진 함수의 번호를 나타낸다.
Function Code Register Type 기능 1 Read Coil 1비트 읽기 2 Read Discrete Input 1비트 읽기 3 Read Holding Registers 여러 워드 읽기 4 Read Input Registers 여러 워드 읽기 5 Write Single Coil 1비트 쓰기 6 Write Single Holding Register 1워드 쓰기 15 Write Multiple Coils 여러 비트 쓰기 16 Write Multiple Holding Registers 여러 워드 쓰기
Address
2 Bytes
데이터의 시작 주소를 나타낸다.
Number of Data
2 Bytes
전달하려는 데이터의 개수이다.
Data Size
1 Byte
데이터의 실제 크기이다.
단위는 Byte이다.
Data
n Bytes
각 데이터는 2바이트로 표현된다.
CRC
2 Bytes
에러 검출 시 사용된다.
예제
패킷 예제 1
Master(Poll)
가Slave
에게 주소0x0
부터 3개의 데이터 읽기를 요청하는 예제Master
->Slave
Slave ID Function Address Number of Data CRC 01 03 00 00 00 03 05 CB Slave
->Master
Slave ID Function Data Size(Byte) Data CRC 01 03 06 00 01 00 02 00 03 FD 74
패킷 예제 2
Master
가Slave
에게 주소0x0
부터 3개의 데이터 쓰기를 요청하는 예제여러개의 데이터를 변경하므로
0x16
Function Code를 사용한다.Master
->Slave
Slave ID Function Address Number of Data Data Size(Byte) Data CRC 01 10 00 00 00 03 06 00 04 00 05 00 06 87 43 Slave
->Master
Slave ID Function Address Number of Data CRC 01 10 00 00 00 03 80 08
CRC
시리얼 통신의 불안정함을 보완하기 위해 등장했다.
2바이트로 구성된다.
알고리즘
공식 라이브러리의 알고리즘은 다음과 같다.
CRC 2바이트를 모두 1로 초기화한다. (
0xffff
)반복적으로
update_crc_16
함수를 호출하여 CRC 값을 변경한다./* * uint16_t update_crc_16( uint16_t crc, unsigned char c ); * * The function update_crc_16() calculates a new CRC-16 value based on the * previous value of the CRC and the next byte of data to be checked. */ uint16_t update_crc_16( uint16_t crc, unsigned char c ) { uint16_t tmp; uint16_t short_c; short_c = 0x00ff & (uint16_t) c; if ( ! crc_tab16_init ) init_crc16_tab(); tmp = crc ^ short_c; crc = (crc >> 8) ^ crc_tab16[ tmp & 0xff ]; return crc; } /* update_crc_16 */
crc
는 직전의 CRC 값이다.입력으로 준 값을 하나씩
c
에 넣으며 반복적으로 계산한다.short_c
는 수신한 값의 1바이트이다. 이를 CRC 값과^(xor)
연산하여 나온 값의 1바이트를crc_tab16
테이블에서 찾아서 CRC 값의 상위 8바이트와^
연산한 값을 새로운 CRC 값으로 설정한다.
init_crc16_tab()
의 코드는 프로그램에서 한 번만 실행되며,crc_tab16
테이블을 만들어 성능을 높인다.static void init_crc16_tab( void ) { uint16_t i; uint16_t j; uint16_t crc; uint16_t c; for (i=0; i<256; i++) { crc = 0; c = i; for (j=0; j<8; j++) { if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ CRC_POLY_16; else crc = crc >> 1; c = c >> 1; } crc_tab16[i] = crc; } crc_tab16_init = true; } /* init_crc16_tab */
모드버스 TCP/IP
패킷의 구조
TCP/IP 계층에서 오류 검증을 하므로, 따로 CRC 코드를 사용할 필요가 없다.
Transaction ID Protocol ID Length Unit ID Function Code Address Number of Data Data Size Data 2 Bytes 2 Bytes 2 Bytes 1 Byte 1 Byte 2 Bytes 2 Bytes 1 Byte n Bytes Transaction ID
트랜잭션이란 한번에 수행되는 작업의 단위를 의미한다.
0부터 시작해서 마스터의 요청과 슬레이브의 응답을 한 쌍으로 하여 카운트한다.
예를 들면, 처음 마스터->슬레이브 요청이 전송될 때는 Transaction ID가 0이다.
해당 요청에 대한 슬레이브->마스터 응답이 전송될 때도 Transaction ID가 0이다.
그 다음 마스터->슬레이브 요청이 전송될 때는 Transaction ID가 1이다.
해당 요청에 대한 슬레이브->마스터 응답이 전송될 때도 Transaction ID가 1이다.
여기서는 어떤 요청에 대한 응답인지를 구분할 수 있도록 숫자를 표기한 것 같다.
- 만약 요청이 연속으로 전송되면, 어떤 요청에 대한 응답인지 구분해야 하기 때문이다.
Protocol ID
- 항상 0을 사용한다.
Length
- Unit ID(Slave ID)부터 데이터의 끝까지의 비트 수를 의미한다.
패킷 예제
1부터 10까지의 데이터를 전송하는 모드버스 TCP/IP 패킷
Tx : 10개의 데이터를 읽도록 요청
Transaction ID Protocol ID Length Unit ID Function Code Address Number of Data Data Size Data 00 00 00 00 00 06 01 03 00 00 00 0A - - Rx
Transaction ID Protocol ID Length Unit ID Function Code Address Number of Data Data Size Data 00 00 00 00 00 17 01 03 - - 14 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0a 16진수임에 유의하자
모드버스 RTU Over TCP/IP
모드버스 TCP/IP 프로토콜과는 달리 모드버스 RTU 프로토콜의 구성을 그대로 사용한다.
- 단순히 전송할 때 직렬 포트가 아닌 TCP/IP 스택을 이용하는 것 뿐이다.
괜히 프로토콜을 따로 만드는 것보단 이 방식이 더 범용성 높고 좋은 것 같은데, 굳이 위의 모드버스 TCP/IP 프로토콜을 따로 만든 이유가 궁금하다.