EhterCAT_SOEM
前言
SOEM简单开放式ETherCAT主站,支持Linux,Windows双系统,这里讲解是SOEM 1.3.1版本基于Windows平台编译后的源码eepromtool.c,在SOEM-1.3.1\test\win32\eepromtool文件夹中。 源码可以到这里下载:百度网盘,提取码:5679
一、eepromtool.c的作用
就如其名字一样,作用就是实现对ESC SII的读写,这里主要就是讲解源码中读写函数应该怎么使用,但是大家应该知道EEPROM数据不能随便更改,很有可能更改后就不能与从站通讯了,需要重新烧写EEPROM,大家谨慎操作,本文章主要讲解对0-7字的读写。
二、读写EEPROM步骤
根据倍福官网 手册 描述操作EEPROM步骤如下:
- 检查 EEPROM 状态寄存器的 Busy 位是否清零(0x0502[15]=0)和 EEPROM接口不忙,否则等到 EEPROM 接口不忙。
- 检查 EEPROM 状态寄存器的错误位是否被清除。如果没有,请写入“000”到命令寄存器(寄存器 0x??0502 位 [10:8])。
- 将 EEPROM 字地址写入 EEPROM 地址寄存器。
- 只写命令:将写入数据放入 EEPROM 数据寄存器(仅 1 个字/2 个字节)。
- 通过写入控制寄存器发出命令。
a) 对于读取命令,将 001 写入命令寄存器 0x??0502[10:8]。 b) 对于写入命令,将 1 写入写入启用位 0x0502[0] 并将 010 写入命令寄存器0x0502[10:8]。两个位都必须写在一帧中。写使能位实现写保护机制。对同一帧下发的后续 EEPROM 命令有效并在之后自行清理。写使能位不需要从 PDI 写入,如果它控制EEPROM接口。 c) 对于重载命令,将 100 写入命令寄存器 0x??0502[10:8]。 - 如果 EtherCAT 帧没有错误,则在 EOF 之后执行该命令。通过 PDI 控制,命令立即执行。
- 等到 EEPROM 状态寄存器的 Busy 位被清除。
- 检查 EEPROM 状态寄存器的错误位。错误位通过清除清除命令寄存器。如果缺少 EEPROM 确认,则重试命令(返回步骤 5)。如果必要时,在重试之前等待一些时间,让慢速 EEPROM 在内部存储数据。
- a) 对于读取命令:读取数据在 EEPROM 数据寄存器中可用(2 或 4 个字,取决于 ESC 检查寄存器 0x??0502[6])。
b) 对于重载命令:ESC 配置被重载到适当的寄存器中。 注意:命令寄存器位是自清零的。手动清除命令寄存器也将清除状态信息。
使用VS的命令行工具 x86 Nactive Tools Command Prompt for VC2019进入eepromtool文件夹之后,输入eepromtool可以看到如下提示: 使用方法: eepromtool 网卡ID 从站索引 操作 文件地址 如使用我的电脑读取1号站的EEPROM数保存为16进制数据到read.txt中: eepromtool \Device\NPF_{36CBEDF6-B690-4C67-8C8B-9A2D1811234E} 1 ri read.txt 写入数据同理就不讲解了。使用eepromtool.exe直接读写EEPROM较简单就不说了,直接开始讲解源码。
1.读取EEPROM数据
eepromtool.c中关于读取的函数如下: 1.首先是主站夺回控制权,eeprom_read函数
int eeprom_read(int slave, int start, int length)
{
int i, wkc, ainc = 4;
uint16 estat, aiadr;
uint32 b4;
uint64 b8;
uint8 eepctl;
if((ec_slavecount >= slave) && (slave > 0) && ((start + length) <= MAXBUF))
{
aiadr = 1 - slave;
eepctl = 2;
wkc = ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET3);
eepctl = 0;
wkc = ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET3);
estat = 0x0000;
aiadr = 1 - slave;
wkc=ec_APRD(aiadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3);
estat = etohs(estat);
if (estat & EC_ESTAT_R64)
{
ainc = 8;
for (i = start ; i < (start + length) ; i+=ainc)
{
b8 = ec_readeepromAP(aiadr, i >> 1 , EC_TIMEOUTEEP);
ebuf[i] = (uint8) b8;
ebuf[i+1] = (uint8) (b8 >> 8);
ebuf[i+2] = (uint8) (b8 >> 16);
ebuf[i+3] = (uint8) (b8 >> 24);
ebuf[i+4] = (uint8) (b8 >> 32);
ebuf[i+5] = (uint8) (b8 >> 40);
ebuf[i+6] = (uint8) (b8 >> 48);
ebuf[i+7] = (uint8) (b8 >> 56);
}
}
else
{
for (i = start ; i < (start + length) ; i+=ainc)
{
b4 = (uint32)ec_readeepromAP(aiadr, i >> 1 , EC_TIMEOUTEEP);
ebuf[i] = (uint8) b4;
ebuf[i+1] = (uint8) (b4 >> 8);
ebuf[i+2] = (uint8) (b4 >> 16);
ebuf[i+3] = (uint8) (b4 >> 24);
}
}
return 1;
}
return 0;
}
2.读取EEPROM数据,ec_readeepromAP函数
uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout)
{
uint16 estat;
uint32 edat32;
uint64 edat64;
ec_eepromt ed;
int wkc, cnt, nackcnt = 0;
edat64 = 0;
edat32 = 0;
if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
{
if (estat & EC_ESTAT_EMASK)
{
estat = htoes(EC_ECMD_NOP);
wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
}
do
{
ed.comm = htoes(EC_ECMD_READ);
ed.addr = htoes(eeproma);
ed.d2 = 0x0000;
cnt = 0;
do
{
wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
}
while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
if (wkc)
{
osal_usleep(EC_LOCALDELAY);
estat = 0x0000;
if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
{
if (estat & EC_ESTAT_NACK)
{
nackcnt++;
osal_usleep(EC_LOCALDELAY * 5);
}
else
{
nackcnt = 0;
if (estat & EC_ESTAT_R64)
{
cnt = 0;
do
{
wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
}
while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
}
else
{
cnt = 0;
do
{
wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
}
while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
edat64=(uint64)edat32;
}
}
}
}
}
while ((nackcnt > 0) && (nackcnt < 3));
}
return edat64;
}
这里举两个例子,比如 1.使用eeprom_read函数读取从站1别名: eeprom_read(1,8,2);
2.直接使用ec_readeepromAP函数读取从站1别名: ec_readeepromAP(1,4, EC_TIMEOUTEEP); 但是需要注意的是直接使用ec_readeepromAP函数时,要先让主站夺回控制权。
2.写入EEPROM数据
查看源码中eeprom_write和ec_writeeepromAP函数就会发现,其作用与对应的eeprom_read和ec_readeepromAP一模一样。但是写入EEPROM步骤可不是直接写入那么简单。这里还是以写入从站别名为例。
2.1 写入从站别名
同样举两个例子,比如 1.使用eeprom_write函数读取从站1别名: //赋值 ebuf[8] = 0x04;//写入别名0x04 eeprom_read(1,8,2);
2.直接使用ec_readeepromAP函数读取从站1别名: //写入别名 ec_writeeepromAP(1, 4, 0x04, EC_TIMEOUTEEP); 同样需要注意的是直接使用ec_writeeepromAP函数时,要先让主站夺回控制权。
2.2 读取0-6字数据
读取0-6字数据,用于计算校验和
2.3 计算校验和
计算0-6字的校验和在写入EEPROM数据的步骤中最为关键,能不能写入成功最后就是看校验和是否正确,ESC在上电或是复位后会自动读取0-7字数据(ESC寄存器配置区)并装入相应的寄存器,并检查校验和,校验和如果不正确则写入不成功。 倍福官网介绍的校验和计算方式为: Low byte contains remainder of division of word 0 to word 6 as unsigned number divided by the polynomial x8+x2+x+1(initial value0xFF). NOTE: For debugging purposes it is possible to disable the checksum validation with a checksum value of 0x88A4. Never use this for production! 低字节包含字 0 到字 6 的除以无符号数除以多项式 x8+x2+x+1(初始值 0xFF)的余数。 注意:出于调试目的,可以使用校验和值 0x88A4 禁用校验和验证。切勿将其用于生产! 这里可以百度一下CRC8计算器,给大家推荐一个CRC8计算器 计算器设置:
或者使用代码
unsigned char crc_high_first(unsigned char* ptr, unsigned char len)
{
unsigned char i;
unsigned char crc = 0xff;
while (len--)
{
crc ^= *ptr++;
for (i = 8; i > 0; --i)
{
if (crc & 0x80)
crc = (crc << 1) ^ 0x07;
else
crc = (crc << 1);
}
}
return (crc);
}
2.4 写入校验和
与2.2节一样,写入校验和到字地址7即可。
总结
有什么理解的不正确或是写错的地方,大家可以讨论,另外有不理解的也可以评论,再提供一个简单的读写EEPROM项目,提取码:5679
|