详解EtherCAT主站SOEM源码_eepromtool.c
EhterCAT_SOEM文章目录EhterCAT_SOEM前言一、eepromtool.c的作用二、读写EEPROM步骤1.读取EEPROM数据2.写入EEPROM数据2.1 写入从站别名2.2 读取0-6字数据2.3 计算校验和2.4 写入校验和总结前言SOEM简单开放式ETherCAT主站,支持Linux,Windows双系统,这里讲解是SOEM 1.3.1版本基于Windows平台编译后的
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”到命令寄存器(寄存器 0x0502 位 [10:8])。
- 将 EEPROM 字地址写入 EEPROM 地址寄存器。
- 只写命令:将写入数据放入 EEPROM 数据寄存器(仅 1 个字/2 个字节)。
- 通过写入控制寄存器发出命令。
a) 对于读取命令,将 001 写入命令寄存器 0x0502[10:8]。
b) 对于写入命令,将 1 写入写入启用位 0x0502[0] 并将 010 写入命令寄存器0x0502[10:8]。两个位都必须写在一帧中。写使能位实现写保护机制。对同一帧下发的后续 EEPROM 命令有效并在之后自行清理。写使能位不需要从 PDI 写入,如果它控制EEPROM接口。
c) 对于重载命令,将 100 写入命令寄存器 0x0502[10:8]。 - 如果 EtherCAT 帧没有错误,则在 EOF 之后执行该命令。通过 PDI 控制,命令立即执行。
- 等到 EEPROM 状态寄存器的 Busy 位被清除。
- 检查 EEPROM 状态寄存器的错误位。错误位通过清除清除命令寄存器。如果缺少 EEPROM 确认,则重试命令(返回步骤 5)。如果必要时,在重试之前等待一些时间,让慢速 EEPROM 在内部存储数据。
- a) 对于读取命令:读取数据在 EEPROM 数据寄存器中可用(2 或 4 个字,取决于 ESC 检查寄存器 0x0502[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函数
/// <summary>
/// 夺回主站控制权
/// </summary>
/// <param name="slave">从站索引</param>
/// <param name="start">开始读取地址,这里是字地址,如从站别名字地址:4</param>
/// <param name="length">读取长度</param>
/// <returns>0:读取失败,1:读取成功</returns>
int eeprom_read(int slave, int start, int length)
{
int i, wkc, ainc = 4;
uint16 estat, aiadr;
uint32 b4;
uint64 b8;
uint8 eepctl;
//源码中限制了读取的大小MAXBUF=32768,也就是只读取32kb数据
if((ec_slavecount >= slave) && (slave > 0) && ((start + length) <= MAXBUF))
{
aiadr = 1 - slave;
eepctl = 2;//二进制10
//ECT_REG_EEPCFG地址0x0500,顺序寻址写0x500.1 == 1,强制清除0x501.0,主站从PDI夺回EEPROM控制权
wkc = ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET3); /* force Eeprom from PDI */
eepctl = 0;
//0x500.0 == 0,主站接管EEPROM控制权
wkc = ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET3); /* set Eeprom to master */
estat = 0x0000;
aiadr = 1 - slave;
//ECT_REG_EEPSTAT地址0x0502,读取EEPRO状态
wkc=ec_APRD(aiadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3); /* read eeprom status */
estat = etohs(estat);
//EC_ESTAT_R64 0x0040 二进制 0100 0000,跟读取的estat进行与操作,及可得知EEPROM支持读取的字节数,ET1100和ET1200支持8字节,其他ESC芯片为4字节。
if (estat & EC_ESTAT_R64)
{
ainc = 8;
for (i = start ; i < (start + length) ; i+=ainc)
{
//读取EEPROM数据
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函数
/** Read EEPROM from slave bypassing cache. APRD method.
* @param[in] context = context struct
* @param[in] aiadr = auto increment address of slave
* @param[in] eeproma = (WORD) Address in the EEPROM
* @param[in] timeout = Timeout in us.
* @return EEPROM data 64bit or 32bit
*/
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;
//对应上述读写EEPRO步骤1,读取0x502,检查EEPROM是否繁忙
if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
{
//EC_ESTAT_EMASK 0x7800 二进制0111 1000 0000 0000 对应步骤2检查错误位是否被清除
if (estat & EC_ESTAT_EMASK) /* error bits are set */
{
//EC_ECMD_NOP 0x0000
estat = htoes(EC_ECMD_NOP); /* clear error bits */
//对应步骤2,清除错误位,写000到0x0502 位 [10:8]
wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
}
do
{
//EC_ECMD_READ 0x0100 二进制 0000 0001 0000 0000 将001写入命令寄存器 0x0502[10:8],读取EEPROM命令
ed.comm = htoes(EC_ECMD_READ);
//读取地址,字地址
ed.addr = htoes(eeproma);
//这里我还没搞懂是什么
ed.d2 = 0x0000;
cnt = 0;
do
{
//ECT_REG_EEPCTL 0x502 ,写入读取命令
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;
//读取0x502.15 等待EEPOM繁忙位清除,0x502.15 == 0;
if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
{
//EC_ESTAT_NACK 0x2000 二进制 0010 0000 0000 0000检查EEPROM应答位0x502.13,是否应答丢失
if (estat & EC_ESTAT_NACK)
{
//EEPROM无应答或命令无效
nackcnt++;
osal_usleep(EC_LOCALDELAY * 5);
}
else
{
//无错误
nackcnt = 0;
//EEPROM支持读取的字节数
if (estat & EC_ESTAT_R64)
{
//8字节
cnt = 0;
do
{
//读取EEPROM数据
wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
}
while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
}
else
{
//4字节
cnt = 0;
do
{
//读取EEPROM数据
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;/* 计算的初始crc值 */
while (len--)
{
crc ^= *ptr++; /* 每次先与需要计算的数据异或,计算完指向下一数据 */
for (i = 8; i > 0; --i) /* 下面这段计算过程与计算一个字节crc一样 */
{
if (crc & 0x80)
crc = (crc << 1) ^ 0x07;//多项式
else
crc = (crc << 1);
}
}
return (crc);
}
2.4 写入校验和
与2.2节一样,写入校验和到字地址7即可。
总结
有什么理解的不正确或是写错的地方,大家可以讨论,另外有不理解的也可以评论,再提供一个简单的读写EEPROM项目,提取码:5679
更多推荐
所有评论(0)