SOEM source code analysis – ecx_siistring (read SII string information)

0 Tool preparation

1.SOEM-master-1.4.0 source code

1 Overview of ecx_siistring function

/** Get string from SII string section in slave EEPROM. Get string from SII string section
 * @param[in] context = context struct handle
 * @param[out] str = requested string, 0x00 if not found requested string information, 0x00 means not found
 * @param[in] slave = slave number slave serial number
 * @param[in] Sn = string number string serial 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 Find string section */
   /* The character segment format is: 1Byte string length + string */
   a = ecx_siifind (context, slave, ECT_SII_STRING);
   if (a > 0)
   {<!-- -->
      ba = a + 2; /* skip SII section header skip SII section header */
      n = ecx_siigetbyte(context, slave, ba + + ); /* read number of strings in section Read the number of strings in SII section */
      if (Sn <= n) /* is req string available? If the target string number is less than the actual number of strings */
      {<!-- -->
         for (i = 1; i <= Sn; i + + ) /* walk through strings read strings one by one to get the target string */
         {<!-- -->
            l = ecx_siigetbyte(context, slave, ba + + ); /* length of this string Get the length of this string */
            if (i < Sn)
            {<!-- -->
               ba + = l;
            }
            else
            {<!-- -->
               ptr = str;
               for (j = 1; j <= l; j + + ) /* copy one string copy string */
               {<!-- -->
                  if(j <= EC_MAXNAME)
                  {<!-- -->
                     *ptr = (char)ecx_siigetbyte(context, slave, ba + + );
                     ptr + + ;
                  }
                  else
                  {<!-- -->
                     ba + + ;
                  }
               }
            }
         }
         *ptr = 0; /* add zero terminator Add terminator */
      }
      else
      {<!-- -->
         ptr = str;
         *ptr = 0; /* empty string empty string */
      }
   }
   if (eectl)
   {<!-- -->
      /* if eeprom control was previously pdi then restore If the previous EEPROM access control was PDI, then restore it */
      ecx_eeprom2pdi(context, slave);
   }
}

As you can see, the work of the ecx_siistring function is divided into 2 parts:
(1) Get the starting address of the string classification segment
(2) Copy the string with the specified serial number

1.1 Get the starting address of the string classification segment

This part of the work is completed by ecx_siifind. The function is as follows:

/** Find SII section header in slave EEPROM. Find the section header in slave eeprom
 * @param[in] context = context struct handle
 * @param[in] slave = slave number slave serial number
 * @param[in] cat = section category The section header name of the SII category section
 * @return byte address of section at section length entry, if not available then 0 SII category section length address, if not found, return 0
 */
int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat)
{<!-- -->
   int16 a;
   uint16 p;
   uint8 eectl = context->slavelist[slave].eep_pdi;
   /* Get the byte address from the word address x2 */
   a = ECT_SII_START << 1;
   /* read first SII section category Read the section header address value */
   p = ecx_siigetbyte(context, slave, a + + );
   p + = (ecx_siigetbyte(context, slave, a + + ) << 8);
   /* traverse SII while category is not found and not EOF Traverse SII when the SII category segment header is not found and is not EOF (0xffff, end of SII)*/
   while ((p != cat) & amp; & amp; (p != 0xffff))
   {<!-- -->
      /* The first 4 bytes of SII category segment rules: Segment name 0x00 word length low bit word length high bit */
      /* read section length Read the length of the SII category section */
      p = ecx_siigetbyte(context, slave, a + + );
      p + = (ecx_siigetbyte(context, slave, a + + ) << 8);
      /* locate next section category Locate to the next SII category section */
      a + = p << 1;
      /* read section category Read the section header name */
      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 If the EEPROM access control was previously PDI, restore it back */
      ecx_eeprom2pdi(context, slave);
   }

   return a;
}

The main function of this function is to find the corresponding keyword at the starting position of ECT_SII_START (category additional information, word address 0x40) (the function uses a byte address, so shift ECT_SII_START to the left by one x2 to get the byte address), determine the need The classification segment to be searched is at the starting address of the EEPROM. The keyword definitions for classification segments are as follows:

As you can see from the table above, what we need to look for is STRINGS, which is 10. Therefore, there is the following statement at the front of the ecx_siistring function:

a = ecx_siifind (context, slave, ECT_SII_STRING);

The value of ECT_SII_STRING is 10, which is to find the address of the string segment header.
It is worth mentioning that when searching for classified segments, you need to jump to the next segment header according to the length of the segment for matching instead of comparing the EEPROM data one by one. This can avoid the interference of some EEPROM data that overlaps with keywords. The SII segment header is defined as follows:

Note: Length is in words.

1.2 Copy the string with the specified serial number

After obtaining the segment header address of the string classification segment, you only need to obtain the string according to the EEPROM string segment information structure defined in the following table:

The relevant statements are as follows:

if (Sn <= n) /* is req string available? If the target string number is less than the actual number of strings */
      {<!-- -->
         for (i = 1; i <= Sn; i + + ) /* walk through strings read strings one by one to get the target string */
         {<!-- -->
            l = ecx_siigetbyte(context, slave, ba + + ); /* length of this string Get the length of this string */
            if (i < Sn)
            {<!-- -->
               ba + = l;
            }
            else
            {<!-- -->
               ptr = str;
               for (j = 1; j <= l; j + + ) /* copy one string copy string */
               {<!-- -->
                  if(j <= EC_MAXNAME)
                  {<!-- -->
                     *ptr = (char)ecx_siigetbyte(context, slave, ba + + );
                     ptr + + ;
                  }
                  else
                  {<!-- -->
                     ba + + ;
                  }
               }
            }
         }
         *ptr = 0; /* add zero terminator Add terminator */
      }
      else
      {<!-- -->
         ptr = str;
         *ptr = 0; /* empty string empty string */
      }

The main operation of the above statement is to read the string content in the specified order. In the SOEM master station, only the string information in the order 1 is read, and this string is the slave station name. There is something that requires special attention here. Do not write out-of-bounds when copying strings. The SOEM master station has added an if statement to prevent out-of-bounds writing to memory. In addition, 0x00 should be added to the end of the string to indicate the end.

2 Summary

During the initialization phase, the SOEM master station will read some string information including the slave station name from SII. This work is completed through the ecx_siistring function, and its main work can be summarized as follows:
(1) Get the starting address of the string classification segment
(2) Copy the string with the specified serial number