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)没缓冲。

posted @ 2026-08-25 14:13  Hynaya  阅读(3)  评论(0)    收藏  举报