02. RS485 통신 프로토콜 & 레지스터 맵
출처: resource/ZLAC8015D V4 Series RS485 Communication Version 1.06-20251111.pdf,
xbox_motor_control.cpp
2.1 시리얼 포트 설정
| 항목 |
값 |
| 프로토콜 |
Modbus RTU |
| 보드레이트 |
115200 bps (기본값, 코드에서 사용) — 9600/19200/38400/57600/128000 선택 가능 |
| 데이터 비트 |
8 |
| 패리티 |
None |
| 스톱 비트 |
1 |
| 드라이버 주소 범위 |
1 ~ 127 (본 프로젝트: 드라이버#1=1, 드라이버#2=2) |
xbox_motor_control.cpp의 SerialPort::openPort()가 termios로 위 설정(8N1, RAW 모드,
VTIME=1 → 0.1s read timeout)을 그대로 구성한다.
2.2 Modbus 프레임 포맷
CRC는 표준 Modbus CRC16(다항식 0xA001, 초기값 0xFFFF) — calcCRC16()이 이를 그대로 구현.
지원 기능 코드
| 기능 |
코드 |
에러 응답 코드 |
| 다중 레지스터 읽기 |
0x03 |
0x83 |
| 단일 레지스터 쓰기 |
0x06 |
0x86 |
| 다중 레지스터 쓰기 |
0x10 |
0x90 |
| 에러 코드 |
이름 |
의미 |
0x01 |
Illegal function code |
지원하지 않는 기능 코드 |
0x02 |
Illegal data address |
잘못된 레지스터 주소 |
0x03 |
Illegal data value |
잘못된 데이터 값 |
코드 구현 매핑
| 클래스 함수 |
기능 코드 |
용도 |
SerialPort::readRegs() |
0x03 |
피드백/상태/전류한계 조회 |
SerialPort::writeReg() |
0x06 |
제어워드/모드/브레이크 등 단일 레지스터 쓰기 |
SerialPort::writeRegs() |
0x10 |
가감속시간, 목표속도(L/R 동시) 쓰기 |
응답 검증은 CRC16 재계산 + 슬레이브 주소 일치 여부로 이루어지며(writeReg/writeRegs/readRegs
모두 동일), 브로드캐스트(slave==0)일 때는 응답을 기다리지 않는다(--bcast 옵션).
읽기 타임아웃은 25ms(100Hz=10ms 주기 루프 안에서 여유를 둔 값), 쓰기 응답 타임아웃은 50ms로
설정되어 있다.
2.3 코드가 실제로 사용하는 레지스터 (초기화/제어/피드백 경로)
| 주소 |
이름 |
방향 |
코드 사용처 |
0x200D |
Control mode |
W |
initDriver(): 3 = 속도 제어 모드 고정 |
0x200E |
Control word |
W |
initDriver(): 0x0006 알람 클리어 → 0x0007 Disable → (가감속 설정 후) 0x0008 Enable |
0x2080 / 0x2081 |
가속시간 좌/우 |
W |
initDriver(acl_ms)가 writeRegs(0x2080,{acl,acl})로 2레지스터 동시 기록 (기본 150ms) |
0x2082 / 0x2083 |
감속시간 좌/우 |
W |
initDriver(dcl_ms)가 writeRegs(0x2082,{dcl,dcl})로 동시 기록 (기본 150ms) |
0x2088 / 0x2089 |
목표속도 좌/우 (RPM, I16, ±3000) |
W |
setRPMs(): 매 틱 writeRegs(0x2088,{l,r})로 좌/우 동시 지령 |
0x201A |
출력단자 B0 (좌 브레이크) |
W |
setBrakes(): 0=해제, 1=잠금 |
0x201B |
출력단자 B1 (우 브레이크) |
W |
setBrakes(): 좌와 동일 값 동시 기록 |
0x2033 / 0x2034 |
좌 정격전류 / 좌 최대전류 (0.1A) |
R/W |
readCurrentLimits()로 조회, setMaxCurrent()로 0x2034만 기록 |
0x2063 / 0x2064 |
우 정격전류 / 우 최대전류 (0.1A) |
R/W |
좌와 동일 패턴 (우 채널) |
0x20A5 ~ 0x20AE |
에러코드~토크 (아래 표 참고) |
R |
readFeedback()가 10레지스터 연속 읽기(0x03, count=10)로 한 번에 확보 |
readFeedback()의 10레지스터 연속 읽기 상세 (0x20A5 시작, count=10)
| offset |
주소 |
이름 |
코드 내 변수 |
스케일 |
| 0 |
0x20A5 |
에러코드(좌) |
err_l |
비트마스크 (그대로) |
| 1 |
0x20A6 |
에러코드(우) |
err_r |
비트마스크 (그대로) |
| 2 |
0x20A7 |
실제위치 상위16비트(좌) |
val_l 상위 |
int32_t 합성 → l_tick |
| 3 |
0x20A8 |
실제위치 하위16비트(좌) |
val_l 하위 |
〃 |
| 4 |
0x20A9 |
실제위치 상위16비트(우) |
val_r 상위 |
int32_t 합성 → r_tick |
| 5 |
0x20AA |
실제위치 하위16비트(우) |
val_r 하위 |
〃 |
| 6 |
0x20AB |
실제속도(좌), 단위 0.1r/min |
vl |
× 0.1f → l_fb (RPM) |
| 7 |
0x20AC |
실제속도(우), 단위 0.1r/min |
vr |
× 0.1f → r_fb (RPM) |
| 8 |
0x20AD |
실제토크/전류(좌), 단위 0.1A |
tl |
× 0.1f → l_torque_a (A) |
| 9 |
0x20AE |
실제토크/전류(우), 단위 0.1A |
tr |
× 0.1f → r_torque_a (A) |
이 한 번의 통신으로 에러코드·엔코더 위치(주행거리 계산용)·속도 피드백·전류(부하 진단용)를
모두 얻기 때문에, 100Hz 루프에서 드라이버 1대당 통신 1회(읽기)+1회(쓰기)만 필요하다.
2대 드라이버 × 2회 = 틱당 최대 4회 왕복, comm_ms로 HUD에 실측 소요시간을 표시한다.
위치(0x20A7~0x20AA)는 엔코더 라인수 1024 기준 4체배(quadrature)로 회전당
1024 × 4 = 4096이 아니라, README 기준 실측 16,384 ticks/rev를 사용한다
(ZLLG80ASM250-4096-B 모터명의 4096은 엔코더 라인수이며, 4체배 카운팅 시
4096 × 4 = 16384가 된다. 0x2030/0x2060 "Encoder line" 파라미터 공장 기본값 1024와는
별개로, 실제 장착된 ZLLG80ASM250-4096-B 모터는 라인수 4096 사양이므로 드라이버의
Encoder line 파라미터가 모터 사양(4096)에 맞춰 설정되어 있어야 meters_per_tick 계산이
정확하다. 드라이버 실측정격전류가 15A인 것도 이 모터의 정격과 일치.)
2.4 에러코드 (0x20A5 / 0x20A6)
| 값 |
의미 |
코드 내 한글 표기 |
0x0000 |
정상 (No error) |
(없음) |
0x0001 |
과전압 (Over voltage) |
과전압 |
0x0002 |
저전압 (Under voltage) |
저전압 |
0x0004 |
과전류 (Over current) |
과전류 |
0x0008 |
과부하 (Over load) |
과부하 |
0x0010 |
전류 이상 (예약) |
전류이상(예약) |
0x0020 |
엔코더 오차 (Encoder out of tolerance) |
엔코더오차 |
0x0040 |
속도 이상 (예약) |
속도이상(예약) |
0x0080 |
기준전압 오류 (Reference voltage error) |
기준전압오류 |
0x0100 |
EEPROM 오류 |
EEPROM오류 |
0x0200 |
홀센서 오류 |
홀센서오류 |
0x0400 |
모터 과열 |
모터과열 |
0x0800 |
엔코더 오류 |
엔코더오류 |
0x2000 |
속도설정 오류 (지령속도가 정격속도 초과) |
속도설정오류 |
여러 비트가 동시에 서면 코드가 "+"로 이어붙여 표시한다 (decodeDriverError()).
드라이버 알람 상세 진단과 대응은 05-error-troubleshooting.md 참고.
2.5 제어워드(0x200E)와 초기화 시퀀스
| 값 |
의미 |
0x05 |
비상 정지 (Emergency stop) |
0x06 |
알람 클리어 (Clear fault) |
0x07 |
정지 (Stop / Disable) |
0x08 |
활성화 (Enable) |
0x10 |
동기 시작 (Start Synchronous, 포지션 모드 전용) |
0x11 |
좌측 단독 시작 (Start Left, 포지션 모드) |
0x12 |
우측 단독 시작 (Start Right, 포지션 모드) |
MotorDriver::initDriver()의 실제 시퀀스 (각 단계 사이 30ms 대기):
0x200E ← 0x0006 (알람 클리어)
0x200E ← 0x0007 (Disable)
0x200D ← 3 (속도 제어 모드)
0x2080/0x2081 ← 가속시간 (좌우 동시, 기본 150ms)
0x2082/0x2083 ← 감속시간 (좌우 동시, 기본 150ms)
0x200E ← 0x0008 (Enable)
setBrakes(true) — 초기 상태는 브레이크 잠금
이 프로젝트는 속도 제어 모드(Profile Velocity Mode)만 사용하며, 포지션/토크 모드 관련 레지스터
(0x208A~0x208F, 0x2090/0x2091 등)는 사용하지 않는다. 참고용으로 §2.7에 전체 표를 남긴다.
2.6 전류 한계 레지스터
| 주소 |
이름 |
단위 |
기본값 |
범위 |
0x2033 |
좌 정격전류 |
0.1A |
150 (15.0A) |
0-150 |
0x2034 |
좌 최대전류 |
0.1A |
300 (30.0A) |
0-300 |
0x2063 |
우 정격전류 |
0.1A |
150 (15.0A) |
0-150 |
0x2064 |
우 최대전류 |
0.1A |
300 (30.0A) |
0-300 |
readCurrentLimits()가 시작 시 1회 조회해 콘솔에 출력하고, --max_current_a 인자
(기본 20.0A)로 최대전류를 4바퀴 동일하게 낮춘다. 정격전류(150=15A)는 쓰지 않고 최대전류만
변경한다 — 근거와 튜닝 배경은 05-error-troubleshooting.md 참고.
2.7 전체 레지스터 주소록 (Address Directory, 매뉴얼 §4 원문 기준)
공통 상수 (Left/Right 공통)
| 주소 |
이름 |
접근 |
기본값 |
설명 |
2000h |
Communication offline time |
RW/S |
0 |
ms, 0-32000. 시간 내 통신 없으면 정지 |
2001h |
RS485 Node ID |
RW/S |
1 |
1-127 |
2002h |
RS485 Baud Rate |
RW/S |
2 |
1:128000 2:115200 3:57600 4:38400 5:19200 6:9600 |
2003h |
Input signal status |
RO |
0 |
bit0-1: X0-X1 입력 레벨 |
2004h |
Output signal status |
RO |
0 |
bit0-1: Y0-Y1 출력 레벨 |
2005h |
Clear feedback position |
RW |
0 |
0 무효, 1 좌, 2 우, 3 좌우 (미저장) |
2006h |
Reset absolute zero point |
RW |
0 |
0 무효, 1 좌, 2 우, 3 좌우 (미저장) |
2007h |
Shaft state after power on |
RW/S |
0 |
0 Enable 안 됨/락 안 됨, 1 Enable 안 됨/락 |
2008h |
Maximum motor speed |
RW/S |
1000 |
r/min, 1-1000 |
2009h |
Register parameter settings |
RW |
0 |
0 무효, 1 공장 초기화 |
200Ah |
CAN Node ID |
RW/S |
1 |
1-127 |
200Bh |
CAN Baud rate |
RW/S |
1 |
0:1000 1:500 2:250 3:125 4:100 Kbit/s |
200Ch |
Parking mode |
RW/S |
0 |
0 Close, 1 Open |
200Dh |
Control mode |
RW/S |
0 |
0 undefined, 1 위치(상대), 2 위치(절대), 3 속도, 4 토크 |
200Eh |
Control word |
RW |
0 |
§2.5 참고 |
200Fh |
Sync/Async control status |
RW/S |
0 |
0 동기, 1 비동기 |
2010h |
RW 레지스터 EEPROM 저장 여부 |
RW |
0 |
0 무효, 1 저장 |
2011h |
Quick stop control |
RW/S |
5 |
5 정지, 6 감속시간 적용 급정지, 7 감속시간 미적용 급정지 |
2012h |
Close operation control |
RW/S |
1 |
0 무효, 1 정상정지 |
2013h |
Disable control |
RW/S |
1 |
0 무효, 1 정지(switch on 상태로) |
2014h |
Halt control |
RW/S |
1 |
1 정지, 2 감속 급정지, 3 무감속 급정지 |
2016h |
Input effective level |
RW/S |
0 |
bit0 X0, bit1 X1 (0 기본 High, 1 반전 Low) |
2017h |
Input X0 function |
RW/S |
9 |
0 none, 1-8 NC, 9 비상정지 |
2018h |
Input X1 function |
RW/S |
0 |
〃 |
2019h |
Output effective level |
RW/S |
0 |
bit0 Y0, bit1 Y1, bit2 B0, bit3 B1 |
201Ah |
출력 B0 (좌 브레이크) |
RW/S |
0 |
0 Open, 1 Close — 코드 사용 |
201Bh |
출력 B1 (우 브레이크) |
RW/S |
0 |
0 Open, 1 Close — 코드 사용 |
201Ch |
Output Y0 function |
RW/S |
0 |
0 undefined, 1 알람신호, 2 상태신호, 3 목표위치도달(예약) |
201Dh |
Output Y1 function |
RW/S |
0 |
〃 |
201Eh |
드라이버 온도 보호 임계값 |
RW/S |
800 |
0.1℃, 0-1200 |
201Fh |
Alarm PWM 처리 방식 |
RW/S |
0 |
0 close, 1 open |
2020h |
Overload 처리 방식 |
RW/S |
0 |
0 close, 1 open |
2021h |
I/O 비상정지 처리 모드 |
RW/S |
0 |
0 축고정(우선I), 1 댐핑(우선II), 2 축해제(우선III); low8=INPUT1 high8=INPUT2 |
2022h |
Given speed resolution |
RW/S |
1 |
1:1RPM ... A:0.1RPM |
2023h |
Velocity overshoot |
RW/S |
1 |
0 close, 1 open |
2024h |
Regen resistance value |
RW/S |
50 |
0.1Ω, 0-1000 |
2025h |
Regen resistance power |
RW/S |
100 |
W, 0-1000 |
2026h |
Regen opening voltage |
RW/S |
650 |
0.1V, 360-750 |
2027h |
Regen close voltage |
RW/S |
600 |
0.1V, 310-700 |
2028h |
Regen function control |
RW/S |
1 |
0 close, 1 open |
2029h |
Default running direction |
RW/S |
0 |
0 CW, 1 CCW |
좌모터 파라미터 (2030h204Ah) / 우모터 파라미터 (2060h207Ah, 좌+0x30 오프셋)
| 좌 주소 |
우 주소 |
이름 |
접근 |
기본값 |
설명 |
2030h |
2060h |
Encoder line |
RW/S |
1024 |
0-4096 |
2031h |
2061h |
Hall offset angle |
RW/S |
0 |
1°, -360~360 |
2032h |
2062h |
Overload factor |
RW/S |
200 |
%, 0-300 |
2033h |
2063h |
정격전류 |
RW/S |
150 |
0.1A, 0-150 — 코드 사용 |
2034h |
2064h |
최대전류 |
RW/S |
300 |
0.1A, 0-300 — 코드 사용 |
2035h |
2065h |
Overload protection time |
RW/S |
300 |
10ms, 0-6553 |
2036h |
2066h |
Position following error threshold |
RW/S |
409 |
10counts, 1-6553 |
2037h |
2067h |
Velocity smoothing factor |
RW/S |
1000 |
0-30000 |
2038h |
2068h |
Current Loop Kp |
RW/S |
600 |
0-30000 |
2039h |
2069h |
Current Loop Ki |
RW/S |
300 |
0-30000 |
203Ah |
206Ah |
Feedforward output smoothing |
RW/S |
100 |
0-30000 |
203Bh |
206Bh |
Torque output smoothing |
RW/S |
100 |
0-30000 |
203Ch |
206Ch |
Velocity Loop Kp |
RW/S |
500 |
0-30000 |
203Dh |
206Dh |
Velocity Loop Ki |
RW/S |
100 |
0-30000 |
203Eh |
206Eh |
Velocity Loop Kf |
RW/S |
500 |
0-30000 |
203Fh |
206Fh |
Position Loop Kp |
RW/S |
100 |
0-30000 |
2040h |
2070h |
Position Loop Kf |
RW/S |
50 |
0-30000 |
2043h |
2073h |
Initial velocity (속도모드) |
RW/S |
1 |
r/min, 1-250 |
2044h |
2074h |
Initial velocity (위치모드) |
RW/S |
1 |
r/min, 1-250 |
2045h |
2075h |
Motor poles |
RW/S |
15 |
4-64 |
2046h |
2076h |
Over temperature threshold |
RW/S |
800 |
0.1℃, 0-1200 |
2047h-204Ah |
2077h-207Ah |
Velocity observer coefficients 1-4 |
RW/S |
1000/750/350/1000 |
0-30000 |
제어 파라미터 (2080h~2091h)
| 주소 |
이름 |
접근 |
기본값 |
설명 |
2080h |
S자 가속시간(좌) |
RW/S |
500ms |
0-32767ms — 코드 사용 (150ms) |
2081h |
S자 가속시간(우) |
RW/S |
500ms |
0-32767ms — 코드 사용 (150ms) |
2082h |
S자 감속시간(좌) |
RW/S |
500ms |
0-32767ms — 코드 사용 (150ms) |
2083h |
S자 감속시간(우) |
RW/S |
500ms |
0-32767ms — 코드 사용 (150ms) |
2084h |
급정지 감속시간(좌) |
RW/S |
10ms |
0-32767ms |
2085h |
급정지 감속시간(우) |
RW/S |
10ms |
0-32767ms |
2086h |
토크 슬로프(좌) |
RW/S |
300ms |
mA/s(1/1000초 단위) |
2087h |
토크 슬로프(우) |
RW/S |
300ms |
〃 |
2088h |
목표속도(좌) |
RW |
0 |
I16, r/min, ±3000 — 코드 사용, 매 틱 기록 |
2089h |
목표속도(우) |
RW |
0 |
I16, r/min, ±3000 — 코드 사용, 매 틱 기록 |
208Ah/208Bh |
목표위치 상/하위(좌) |
RW |
0 |
포지션 모드 전용 (미사용) |
208Ch/208Dh |
목표위치 상/하위(우) |
RW |
0 |
포지션 모드 전용 (미사용) |
208Eh/208Fh |
최대속도(좌/우, 포지션모드) |
RW/S |
120 r/min |
포지션 모드 전용 (미사용) |
2090h/2091h |
목표토크(좌/우) |
RW |
0 |
mA, ±30000 — 토크 모드 전용 (미사용) |
읽기 전용 상태 (20A0h~20B0h)
| 주소 |
이름 |
단위/범위 |
코드 사용 여부 |
20A0h |
Software version |
- |
미사용 |
20A1h |
Bus voltage |
0.01V |
미사용 (배터리 전압 모니터링에 활용 가능) |
20A2h |
Status word |
L:bit7-6, R:bit15-14 → 00 해제/40 축고정/80 비상정지/C0 알람; bit0(L)/bit8(R) 0정지 1구동 |
미사용 |
20A3h |
Hall input state |
0-7 (0,7이면 홀 오류), high8=좌 low8=우 |
미사용 |
20A4h |
Motor temperature |
1℃, -55~120, high8=좌 low8=우 |
미사용 (과열 사전감지에 활용 가능) |
20A5h |
에러코드(좌) |
비트마스크 (§2.4) |
코드 사용 |
20A6h |
에러코드(우) |
비트마스크 (§2.4) |
코드 사용 |
20A7h/20A8h |
실제위치 상/하위(좌) |
counts, I16×2 |
코드 사용 → l_tick |
20A9h/20AAh |
실제위치 상/하위(우) |
counts, I16×2 |
코드 사용 → r_tick |
20ABh |
실제속도(좌) |
0.1r/min |
코드 사용 → l_fb |
20ACh |
실제속도(우) |
0.1r/min |
코드 사용 → r_fb |
20ADh |
실제토크/전류(좌) |
0.1A, ±300 |
코드 사용 → l_torque_a |
20AEh |
실제토크/전류(우) |
0.1A, ±300 |
코드 사용 → r_torque_a |
20AFh |
Software connected status |
01 |
미사용 |
20B0h |
Driver temperature |
0.1℃, -550~1200 |
미사용 (과열 사전감지에 활용 가능) |
미사용이지만 유용한 레지스터: 20A1h(버스 전압), 20A4h/20B0h(모터/드라이버 온도)는
현재 코드가 읽지 않는다. 배터리 저전압 경보나 열 관리 기능을 추가하려면 readFeedback()의
연속 읽기 범위를 확장하거나 별도 저빈도 폴링을 추가하는 것을 검토할 수 있다.