ethercat主站APP问题调试寄录
记录个人在实现最小EC主站时遇到的问题和解决思路
问题一:电机使能后,模式为0、驱动器报"ECAT控制模式设置错误"。
设置CSP模式,上电后推电机状态,推到op后,pdo控制阶段,驱动器报错:"ECAT控制模式设置错误"
首先怀疑ENI或者驱动器有问题,把ENI放到官方DEMO跑一遍,无问题,鉴定为程序错误。
接着怀疑是结构体字段有问题,核对程序中的字段,发现顺序,字段数量都没有问题。
打印日志,读从站的模式的地址0x6061,发现PREOP 和 SAFEOP 下 mode=8 ,是正常的,但是进了 OP 阶段,就变成 0了
不确定是哪里有问题,怀疑是程序没写进去,添加验证写0x6060参数8,验证返回值,都返回0表示成功,说明写入没有问题,但读 0x6061 还是 0,官方demo没问题,所以不是sdk的问题,应该是我自己写的逻辑的问题。
又检查了一遍结构体字段,对比官方的demo,发现问题所在:官方demo对包设置了无对齐,原因是ec协议为了节省开销,发的结构体包是没有字节对齐的。
使用下面的宏后,其中间的结构体是没有对齐的!是在内存中紧挨着的。这样才能和驱动器中的值一一对应!否则就会因为错位而赋值错误,其实不止是模式有问题,其他的字段都因为对齐错了。
成功破案!
#pragma pack(push, 1)
struct ...{
...
}
#pragma pack(pop)
如果用sizeof(),也可以验证,结构体的长度是不是能对应上。
结论:C 的结构体默认会按成员对齐,而ec协议下的结构体是紧凑排列的,是没有对齐的,字段偏移会跟 ENI 映射错位,必须把ec的结构体设置成无对齐模式!
问题二:程序线程间发生死锁
程序在运行时,JobTask心跳卡住不发了,推测可能是线程出了问题,查看所有线程的状态
切进线程里查看:
sudo gdb -p 182082
(gdb) thread apply all bt
#0 0x00007fd884ad523f in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7fd8840a1ce0, rem=rem@entry=0x0) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
#1 0x00007fd884adaec7 in __GI___nanosleep (requested_time=requested_time@entry=0x7fd8840a1ce0, remaining=remaining@entry=0x0) at nanosleep.c:27
#2 0x00007fd884b0da7f in usleep (useconds=<optimized out>) at ../sysdeps/posix/usleep.c:32
#3 0x000055853b9402e8 in OsSleep (ms=100) at min_ec_demo.cpp:37
#4 0x000055853b94031c in MbxTfer (timeout_ms=100) at min_ec_demo.cpp:43
#5 0x000055853b94033a in CoeSdoDownload (timeout_ms=100) at min_ec_demo.cpp:47
其他的省略
看到时钟线程只是正常休眠
0 clock_nanosleep 卡在这里等待,往下找到是调用pdo发送函数阻塞了
而负责发送sdo的是jobtask后面的代码
而jobtask调用了发送sdo的函数在等待发送结果返回,但是发送的真实代码是在jobtask后面,所以被自己锁了。
而main线程也调用了sdo发送函数,被一起jobtask线程卡住。
看到线程3卡住(pdo线程,负责收发pdo),在等自己发的sdo应答,自锁了。
在排查问题一时,在pdo线程中误调用sdo,导致死锁。
总结:通过设置心跳观察线程健康度。注意防止死锁情况
问题三:状态机未推进/SDO线程未创建
在排查问题二时,发现的问题
sdo发送job线程没创建
问题四:程序进入main函数后崩溃
gnu排查:
gdb -batch -ex 'run' -ex 'bt' ./main
输出:
=== min_ec_demo: 启动 ===
[INFO] 初始化主站...
Program received signal SIGSEGV, Segmentation fault.
0xffffffff00007fff in ?? ()
#0 0xffffffff00007fff in ?? ()
#1 0x0000555555555203 in OsQueryMsecCount (pOsParms=0x7fffffffdef0) at min_ec_demo.cpp:43
#2 0x0000555555555314 in EcMaster::CEcMaster::CEcMaster (this=0x55555556b2c0, os=0x7fffffffdef0) at min_ec_demo.cpp:65
#3 0x0000555555555362 in EcMaster::CEcDeviceFactory::DeviceFactoryCreateMaster (os=0x7fffffffdef0) at min_ec_demo.cpp:73
#4 0x00005555555553b0 in EcMaster::CEcDeviceFactory::CreateDevice (os=0x7fffffffdef0) at min_ec_demo.cpp:78
#5 0x00005555555553ce in EcMaster::CAtEmInterface::InitMasterEx (os=0x7fffffffdef0) at min_ec_demo.cpp:83
#6 0x00005555555553ec in EcMaster::CAtEmInterface::InitMaster (os=0x7fffffffdef0) at min_ec_demo.cpp:84
#7 0x0000555555555262 in emInitMaster (os=0x7fffffffdef0) at min_ec_demo.cpp:91
#8 0x0000555555555280 in ecatInitMaster (pParms=0x7fffffffdef0) at min_ec_demo.cpp:96
#9 0x00005555555552cd in main () at min_ec_demo.cpp:106
发生崩溃,分析为程序跳转到野指针 0xffffffff00007fff ,发生位置是OsQueryMsecCount(),查到源头是ecatInitMaster(),继续排查:
(gdb) run
Starting program: /home/lab/minecdemo/main
=== min_ec_demo: 启动 ===
[INFO] 初始化主站...
Program received signal SIGSEGV, Segmentation fault.
0xffffffff00007fff in ?? ()
(gdb) frame 1
#1 0x0000555555555203 in OsQueryMsecCount (pOsParms=0x7fffffffdef0) at min_ec_demo.cpp:43
43 return pOsParms->pfSystemQueryMsecCount();
(gdb) print *pOsParms
$1 = {dwSize = 24, dwSignature = 1537212416, dwSupportedFeatures = 4294967295, pfSystemQueryMsecCount = 0xffffffff00007fff}
(gdb) quit
原来是函数指针有问题,和前面一样,没用memset初始化EC_T_OS_PARMS,没用初始化成NULL,就出现了野指针。
同样有这个问题的还有后面的DCM配置
结论:填写sdk的结构体最好要初始化。
问题五:异步帧的参数全 0
扫描发不出帧
dc没设置dwMaxAcycBytesPerCycle / FramesPerCycle / CmdsPerCycle / dwEcatCmdMaxRetries ,都留的0,导致异步帧(含扫描、SDO)没缓冲。

浙公网安备 33010602011771号