3. 从站扫描与识别:ec_slave结构体、从站信息读取、厂商ID与产品代码匹配
好,咱们继续。上一节我们把SOEM的初始化流程跑通了,网卡也绑定了。但EtherCAT总线上一堆从站,你得知道它们是谁、是什么型号、能不能干活。这就像你进到一个车间,先得清点一下设备,看看铭牌上写的啥。
这一节,我们就来聊聊怎么“点名”。核心就是三个东西:ec_slave结构体、从站信息读取、以及厂商ID与产品代码匹配。
3.1 ec_slave结构体:从站的“身份证”
在SOEM里,每个从站的信息都存到一个叫 ec_slave 的结构体里。你可以把它想象成从站的“身份证”。
这个结构体定义在 ethercat.h 里,我挑几个最常用的字段给你看:
typedef struct {
uint16 state; // 从站当前状态
uint16 ALstatuscode; // 状态码,出错了看这个
uint16 configadr; // 配置地址,一般用这个寻址
uint16 aliasadr; // 别名地址,可以自己设
uint32 eep_man; // 厂商ID,从EEPROM读出来的
uint32 eep_id; // 产品代码,也是从EEPROM读的
uint32 eep_rev; // 版本号
uint8 name[EC_MAXNAME]; // 从站名字,字符串形式
int mbxproto; // 邮箱协议类型
// ... 还有很多,但上面这些最常用
} ec_slave;
我个人习惯,拿到一个从站后,第一件事就是看 eep_man 和 eep_id。这俩字段决定了这个从站是谁家的、什么型号。你想想看,如果厂商ID对不上,后面的PDO映射、SDO配置全白搭。
核心要点:
eep_man:厂商ID,比如Beckhoff是0x00000002,松下是0x00000123(具体值去官网查)eep_id:产品代码,每个型号唯一eep_rev:版本号,有时候不同版本PDO不一样,要注意
嗯,这里要注意:eep_man 和 eep_id 是从从站的EEPROM里读出来的,不是从XML文件里来的。有些初学者会混淆,以为从站描述文件(ESI)里的ID就是实际值。其实不一定,EEPROM里的才是“硬编码”。
3.2 从站信息读取:怎么把数据拉回来?
知道了结构体长啥样,下一步就是怎么把数据填进去。SOEM提供了一个非常方便的函数:ec_readstate()。
这个函数会遍历总线上的所有从站,把每个从站的状态、ID、名字等信息,全部更新到全局数组 ec_slave[] 里。调用一次,所有从站的信息就都拉回来了。
用法很简单:
// 假设已经 ec_init() 和 ec_config() 过了
// 调用 ec_readstate() 读取所有从站信息
if (ec_readstate() > 0) {
// 成功读取,ec_slave[] 里已经有数据了
printf("从站数量: %d\n", ec_slavecount);
for (int i = 1; i <= ec_slavecount; i++) {
printf("从站 %d: 厂商ID=0x%08X, 产品代码=0x%08X\n",
i, ec_slave[i].eep_man, ec_slave[i].eep_id);
}
} else {
printf("读取从站状态失败!\n");
}
我在项目中遇到过一个问题:有时候 ec_readstate() 返回的从站数量是对的,但某些字段是0。后来发现是因为从站还没进入OP状态,EEPROM没完全加载。所以我的建议是:在调用 ec_readstate() 之前,先确保从站至少处于PREOP状态。你可以用 ec_statecheck() 等一等。
小技巧:
如果你只想读某个特定从站的信息,可以用 ec_slave[slave_index] 直接访问。索引从1开始,0是主站自己。
3.3 厂商ID与产品代码匹配:怎么确认“找对了人”?
好了,现在数据都读回来了。但你怎么知道这个从站就是你想要的那个?比如你项目里用的是松下A6伺服,结果总线上挂了个别的牌子,那后面配置PDO肯定出错。
匹配的逻辑其实很简单:把你读到的 eep_man 和 eep_id,跟你的期望值做比较。
我一般会写一个匹配函数:
// 检查从站是否匹配指定的厂商ID和产品代码
int check_slave_match(int slave_index, uint32 expected_man, uint32 expected_id) {
if (slave_index < 1 || slave_index > ec_slavecount) {
return 0; // 索引无效
}
if (ec_slave[slave_index].eep_man == expected_man &&
ec_slave[slave_index].eep_id == expected_id) {
return 1; // 匹配成功
}
return 0; // 不匹配
}
然后你在初始化代码里这样用:
// 假设我们期望的松下A6伺服:厂商ID=0x00000123, 产品代码=0x000000A1
#define EXPECTED_MAN 0x00000123
#define EXPECTED_ID 0x000000A1
for (int i = 1; i <= ec_slavecount; i++) {
if (check_slave_match(i, EXPECTED_MAN, EXPECTED_ID)) {
printf("从站 %d 是目标伺服!\n", i);
// 这里可以记录下索引,后面配置PDO用
} else {
printf("从站 %d 不匹配,厂商ID=0x%08X, 产品代码=0x%08X\n",
i, ec_slave[i].eep_man, ec_slave[i].eep_id);
}
}
我曾经在一个产线上遇到过坑:同一批次的伺服,产品代码居然有两个版本,一个带后缀A,一个不带。EEPROM里读出来的ID不一样。后来查资料才发现是固件版本不同导致的。所以匹配的时候,最好也把 eep_rev 版本号考虑进去,或者至少打印出来留个日志。
避坑指南:
我曾经因为厂商ID写错了一个字节,导致整条产线动不了。排查了整整一天才发现是0x00000123写成了0x00000122。所以建议你把厂商ID和产品代码定义成宏,并且从官方文档里复制粘贴,不要手打。
3.4 知识体系梳理:一张图看懂
说了这么多,我画张图帮你理一理。从站扫描与识别的核心流程,其实就是三步:
这张图把流程串起来了。说白了就是:先读、再解析、最后匹配。匹配上了,才能放心地往下走。
3.5 小结
这一节我们干了三件事:
- 认识了
ec_slave结构体,知道了从站的“身份证”长啥样 - 学会了用
ec_readstate()把从站信息拉回来 - 掌握了厂商ID和产品代码的匹配方法,确保找对了设备
嗯,这些内容看起来简单,但实际项目中80%的通信问题都出在“没找对人”上。你想想看,如果厂商ID都匹配错了,后面的PDO配置、SDO读写全是白费功夫。所以这一步,值得你多花点心思。
下一节,我们会深入PDO映射,看看怎么把伺服的位置、速度、转矩这些数据,跟主站的内存对上号。到时候你会回来感谢今天认真匹配ID的自己的。
公众号:蓝海资料掘金营,微信deep3321