SOEM源码解析——ecx_siistring(读取SII的字符串信息)

0 工具准备

1.SOEM-master-1.4.0源码

1 ecx_siistring函数总览

/** Get string from SII string section in slave EEPROM.从SII的字符串段内获取字符串
 *  @param[in]  context = context struct 句柄
 *  @param[out] str     = requested string, 0x00 if not found 请求的字符串信息,0x00表示没找到
 *  @param[in]  slave   = slave number 从站序号
 *  @param[in]  Sn      = string number 字符串序号
 */
void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn)
{
   uint16 a,i,j,l,n,ba;
   char *ptr;
   uint8 eectl = context->slavelist[slave].eep_pdi;

   ptr = str;
   /* find string section 寻找字符串段 */
   /* 字符段格式是:1Byte的字符串长度+字符串 */
   a = ecx_siifind (context, slave, ECT_SII_STRING);
   if (a > 0)
   {
      ba = a + 2; /* skip SII section header 跳过SII段头 */
      n = ecx_siigetbyte(context, slave, ba++); /* read number of strings in section 读取SII段字符串数量 */
      if (Sn <= n) /* is req string available? 如果目标字符串序号小于实际的字符串数量 */
      {
         for (i = 1; i <= Sn; i++) /* walk through strings 逐个读字符串以获取目标字符串 */
         {
            l = ecx_siigetbyte(context, slave, ba++); /* length of this string 获取该字符串长度 */
            if (i < Sn)
            {
               ba += l;
            }
            else
            {
               ptr = str;
               for (j = 1; j <= l; j++) /* copy one string 拷贝字符串 */
               {
                  if(j <= EC_MAXNAME)
                  {
                     *ptr = (char)ecx_siigetbyte(context, slave, ba++);
                     ptr++;
                  }
                  else
                  {
                     ba++;
                  }
               }
            }
         }
         *ptr = 0; /* add zero terminator 添加结束符 */
      }
      else
      {
         ptr = str;
         *ptr = 0; /* empty string 空字符串 */
      }
   }
   if (eectl)
   {
      /* if eeprom control was previously pdi then restore 如果先前EEPROM访问控制权为PDI,则还原回去 */
      ecx_eeprom2pdi(context, slave);
   }
}

可以看到,ecx_siistring函数的工作分为2部分:
(1)获取字符串分类段起始地址
(2)拷贝指定序号的字符串

1.1 获取字符串分类段起始地址

该部分的工作由ecx_siifind完成,该函数如下:

/** Find SII section header in slave EEPROM.在从站eeprom中寻找节头
 *  @param[in]  context        = context struct 句柄
 *  @param[in] slave   = slave number 从站序号
 *  @param[in] cat     = section category SII类别段的段头名
 *  @return byte address of section at section length entry, if not available then 0 SII类别段的长度地址,如果未找到,返回0
 */
int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat)
{
   int16 a;
   uint16 p;
   uint8 eectl = context->slavelist[slave].eep_pdi;
   /* 将字地址x2得到字节地址 */
   a = ECT_SII_START << 1;
   /* read first SII section category 读取段头地址值 */
   p = ecx_siigetbyte(context, slave, a++);
   p += (ecx_siigetbyte(context, slave, a++) << 8);
   /* traverse SII while category is not found and not EOF 在没找到SII类别段段头且不是EOF(0xffff,SII结束)时遍历SII*/
   while ((p != cat) && (p != 0xffff))
   {
      /* SII类别段规则前4Byte: 段名 0x00 字长度低位 字长度高位 */
      /* read section length 读取SII类别段的长度 */
      p = ecx_siigetbyte(context, slave, a++);
      p += (ecx_siigetbyte(context, slave, a++) << 8);
      /* locate next section category 定位到下一个SII类别段 */
      a += p << 1;
      /* read section category 读取段头名 */
      p = ecx_siigetbyte(context, slave, a++);
      p += (ecx_siigetbyte(context, slave, a++) << 8);
   }
   if (p != cat)
   {
      a = 0;
   }
   if (eectl)
   {
      /* if eeprom control was previously pdi then restore 如果EEPROM访问控制权之前是PDI,则还原回去 */
      ecx_eeprom2pdi(context, slave);
   }

   return a;
}

这个函数的主要功能就是在ECT_SII_START(分类附加信息,字地址0x40)开始位置查找相应的关键字(函数中使用的是字节地址,因此将ECT_SII_START左移一位x2得到字节地址),确定需要查找的分类段在EEPROM的起始地址。关于分类段的关键字定义如下:
在这里插入图片描述
从上表可以看到,我们需要寻找的是STRINGS,也就是10。因此在ecx_siistring函数最前面有如下语句:

a = ecx_siifind (context, slave, ECT_SII_STRING);

ECT_SII_STRING的值为10,也就是去寻找字符串段段头地址。
值得说明的是,查找分类段时需要根据段的长度跳转到下一个段头进行匹配,而不是逐个对比EEPROM数据,这样可以避免一些和关键字重复的EEPROM数据的干扰。SII的段头定义如下:
在这里插入图片描述

注:长度是以字为单位的。

1.2 拷贝指定序号的字符串

在获取了字符串分类段的段头地址后,接下来只需要按照下表定义的EEPROM字符串段信息结构获取字符串即可:
在这里插入图片描述
相关语句如下:

if (Sn <= n) /* is req string available? 如果目标字符串序号小于实际的字符串数量 */
      {
         for (i = 1; i <= Sn; i++) /* walk through strings 逐个读字符串以获取目标字符串 */
         {
            l = ecx_siigetbyte(context, slave, ba++); /* length of this string 获取该字符串长度 */
            if (i < Sn)
            {
               ba += l;
            }
            else
            {
               ptr = str;
               for (j = 1; j <= l; j++) /* copy one string 拷贝字符串 */
               {
                  if(j <= EC_MAXNAME)
                  {
                     *ptr = (char)ecx_siigetbyte(context, slave, ba++);
                     ptr++;
                  }
                  else
                  {
                     ba++;
                  }
               }
            }
         }
         *ptr = 0; /* add zero terminator 添加结束符 */
      }
      else
      {
         ptr = str;
         *ptr = 0; /* empty string 空字符串 */
      }

上述语句的主要操作就是读取指定顺序的字符串内容,在SOEM主站中,只去读取顺序为1的字符串信息,该字符串便是从站名。这里有个地方需要特别注意,拷贝字符串时不要写越界,这里SOEM主站已经加上了防止越界写内存的if语句。此外,字符串末尾应添加0x00表示结束。

2 总结

SOEM主站在初始化阶段,会从SII读取包括从站名在内的一些字符串信息。该工作通过ecx_siistring函数完成,它主要的工作可以概括如下:
(1)获取字符串分类段起始地址
(2)拷贝指定序号的字符串

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NW嵌入式开发

感谢您的支持,让我们一起进步!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值