121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
#!/usr/bin/env python
|
||||
|
|
"""EtherCAT master bring-up test for the DCU LAN9252 slave (pysoem >= 1.1 API).
|
|||
|
|
|
|||
|
|
Usage:
|
|||
|
|
ecat_scan.py list all Npcap adapters and exit
|
|||
|
|
ecat_scan.py <name-substr> open the first adapter whose description matches
|
|||
|
|
the substring (e.g. "I226"), scan the bus and
|
|||
|
|
print slave identity + SII EEPROM dump
|
|||
|
|
ecat_scan.py <substr> --op additionally request OP and run PDO cycles for 2s
|
|||
|
|
"""
|
|||
|
|
import sys
|
|||
|
|
import time
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
import pysoem
|
|||
|
|
except ImportError:
|
|||
|
|
print("pysoem 无法加载:请先安装 Npcap(本目录下的 npcap-1.88.exe,"
|
|||
|
|
"安装时保持勾选 'Install WinPcap API-compatible Mode')")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
AL_STATE = {1: "INIT", 2: "PREOP", 4: "SAFEOP", 8: "OP"}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def al_state_name(code):
|
|||
|
|
return AL_STATE.get(code, f"0x{code:02x}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def list_adapters():
|
|||
|
|
for i, a in enumerate(pysoem.find_adapters()):
|
|||
|
|
desc = a.desc.decode() if isinstance(a.desc, bytes) else a.desc
|
|||
|
|
print(f"[{i}] {desc}\n {a.name}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def find_adapter(substr):
|
|||
|
|
for a in pysoem.find_adapters():
|
|||
|
|
desc = a.desc.decode() if isinstance(a.desc, bytes) else a.desc
|
|||
|
|
if substr.lower() in desc.lower():
|
|||
|
|
return a
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def dump_eeprom(slave, count=0x10):
|
|||
|
|
print(" SII EEPROM dump (每行 8 个 word 地址,原始 32bit 返回):")
|
|||
|
|
for base in range(0, count, 8):
|
|||
|
|
words = []
|
|||
|
|
for w in range(base, base + 8):
|
|||
|
|
try:
|
|||
|
|
v = bytes(slave.eeprom_read(w))
|
|||
|
|
words.append(v.hex().upper())
|
|||
|
|
except Exception:
|
|||
|
|
words.append("ERRRRRRRR")
|
|||
|
|
print(f" 0x{base:02X}: {' '.join(words)}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
if len(sys.argv) < 2 or sys.argv[1] in ("-l", "--list"):
|
|||
|
|
list_adapters()
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
substr = sys.argv[1]
|
|||
|
|
want_op = "--op" in sys.argv[2:]
|
|||
|
|
|
|||
|
|
adapter = find_adapter(substr)
|
|||
|
|
if adapter is None:
|
|||
|
|
print(f"没有找到描述包含 {substr!r} 的适配器,当前列表:")
|
|||
|
|
list_adapters()
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
desc = adapter.desc.decode() if isinstance(adapter.desc, bytes) else adapter.desc
|
|||
|
|
master = pysoem.Master()
|
|||
|
|
master.open(adapter.name)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
n = master.config_init()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"总线上没有发现从站({type(e).__name__}: {e}):"
|
|||
|
|
"请检查 DCU 板子是否上电、网线是否接到 I226-V 网口")
|
|||
|
|
sys.exit(2)
|
|||
|
|
|
|||
|
|
if not n:
|
|||
|
|
print("总线上没有发现从站:请检查 DCU 板子是否上电、网线是否接到 I226-V 网口")
|
|||
|
|
sys.exit(2)
|
|||
|
|
|
|||
|
|
print(f"\n适配器: {desc}")
|
|||
|
|
print(f"发现 {n} 个从站:")
|
|||
|
|
for i, s in enumerate(master.slaves):
|
|||
|
|
print(f" [{i}] name={s.name!r}")
|
|||
|
|
print(f" VendorID=0x{s.man:08X} ProductCode=0x{s.id:08X} Revision={s.rev}")
|
|||
|
|
print(f" AL state={al_state_name(s.state)} AL status code=0x{s.al_status:04X}"
|
|||
|
|
f" DC={s.dc_sync}")
|
|||
|
|
|
|||
|
|
dump_eeprom(master.slaves[0])
|
|||
|
|
|
|||
|
|
if not want_op:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
master.config_map()
|
|||
|
|
if master.state_check(pysoem.SAFEOP_STATE, 2000) != pysoem.SAFEOP_STATE:
|
|||
|
|
print(f"进入 SAFEOP 失败, 当前 AL 状态: {al_state_name(master.state)}")
|
|||
|
|
sys.exit(3)
|
|||
|
|
|
|||
|
|
t_end = time.time() + 2.0
|
|||
|
|
frames = 0
|
|||
|
|
while time.time() < t_end:
|
|||
|
|
master.send_processdata()
|
|||
|
|
master.receive_processdata(2000)
|
|||
|
|
frames += 1
|
|||
|
|
time.sleep(0.001)
|
|||
|
|
|
|||
|
|
master.state = pysoem.OP_STATE
|
|||
|
|
master.write_state()
|
|||
|
|
if master.state_check(pysoem.OP_STATE, 2000) == pysoem.OP_STATE:
|
|||
|
|
print(f"已进入 OP, PDO 循环 {frames} 帧正常")
|
|||
|
|
else:
|
|||
|
|
print(f"进入 OP 失败, 当前 AL 状态: {al_state_name(master.state)}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|