如何通过驱动程序设置与获取Anybus CompactCom 40模块MAC地址

11 9月 2023

本文主要介绍了如何通过Anybus CompactCom 40 驱动程序设置与获取模块的MAC地址,首先是介绍了与MAC地址相关的对象、实例与属性,随后构建了获取模块MAC地址的命令序列。

适用产品

AB6605、AB6675、AB6604、AB6674、AB6603、AB6673

AB6610(烧写PROFINET、EtherNet/IP、Modbus TCP协议固件)、AB6680(烧写PROFINET、EtherNet/IP、Modbus TCP协议固件)

 

注意事项

本文简要的介绍了如何通过Anybus CompactCom 40驱动程序设置/获取模块MAC地址,更多详细信息请参考官网手册:

《Anybus CompactCom 40 - Software Design Guide》

《Anybus CompactCom 40 - Host Application Implementation Guide》

《Anybus CompactCom 40 - PROFINET IRT Network Guide》

PROFINET设备本身具有MAC地址,设备的每一个以太网端口也具有一个MAC地址,因此Anybus CompactCom M40/B40运行PROFINET协议接口时,具有一个设备MAC地址与2个端口MAC地址。

目录

  • 与MAC地址有关的对象、实例、属性
  • 设置模块MAC地址
  • 获取模块MAC地址

 

与MAC地址有关的对象、实例、属性

  1. Anybus CompactCom 40模块固件中,已经预置了MAC地址,模块预置的MAC地址可以通过访问 Network Ethernet Object (0Ch)的实例1~3获取,实例2~3,PORT 1/2 MAC Address 仅适用于PROFINET协议。
    •  
  2. 模块预置的MAC地址可被驱动程序中 Ethernet Host Object (F9h)对象中设置的MAC地址覆盖,当Anybus CompactCom 40作为PROFINET协议接口时,设备MAC地址与端口地址值满足连续递增加一的关系,详情请参考《Anybus CompactCom 40 - PROFINET IRT Network Guide》14.8小节。

 

设置MAC地址

模块上电后会主动向驱动程序的 Ethernet Host Object (F9h)对象请求驱动设置的MAC地址,若未在驱动程序中设置MAC地址,则模块使用固件中预置的MAC地址。
使能驱动程序的 Ethernet Host Object (F9h)对象定义。
  1. 在驱动程序示例代码的\abcc_adapt\abcc_obj_cfg.h中,使能ETN_OBJ_ENABLE开关。
  2. 在\abcc_adapt\abcc_identification.h文件中找到ETN_IA_MAC_ADDRESS_ENABLE宏,并使能,将ETN_IA_MAC_ADDRESS_VALUE赋值为需要设置的MAC地址

    • 如果模块运行PROFINET协议,在\abcc_adapt\abcc_obj_cfg.h中找到ETN_IA_PORT1_MAC_ADDRESS_ENABLE、ETN_IA_PORT2_MAC_ADDRESS_ENABLE宏定义并设置为TRUE,向ETN_IA_PORT1_MAC_ADDRESS_VALUE、ETN_IA_PORT1_MAC_ADDRESS_VALUE赋值,并注意其值与模块MAC地址的连续性。
  3. 设置完毕,模块上电后MAC地址会被驱动程序中设置的MAC地址覆盖。

 

获取MAC地址

通过驱动程序获取模块MAC地址,需要驱动发送Get_Attribute 命令到模块的Network Ethernet Object (0Ch)对象,首先定义获取模块MAC地址的命令序列(命令序列(Command sequencer)说明请参考《Anybus Compact 40 Host application implementation guide》第40页)。命令序列通过ABCC_CMD_SEQ( CmdBuilder1, RespHandler1 )方法添加命令序列中的单个命令。CmdBuilder1为命令构建函数的指针,RespHandler1为模块回复消息处理函数指针,无需处理命令的消息回复时可设置为NULL。  

  1. 获取MAC地址可在模块初始化的过程中,也可在模块初始化完成后,根据实际需要选择,如果在驱动程序中使能了 Ethernet Host Object (F9h)对象定义的MAC地址设置宏,则在模块初始化时读取MAC地址是模块固件预置的,会被覆盖,若无需设置模块MAC地址且需要在模块上电后获取其MAC地址,可在用户初始化命令序列中添加获取模块MAC地址的命令,该命令序列已在驱动示例程序中定义。
    • static const ABCC_CmdSeqType appl_asUserInitCmdSeq[] =
      {
      ABCC_CMD_SEQ( UpdateIpAddress, NULL ),
      ABCC_CMD_SEQ( UpdateNetmask, NULL ),
      ABCC_CMD_SEQ( UpdateGateway, NULL ),
      ABCC_CMD_SEQ( UpdateDhcp, NULL ),
      ABCC_CMD_SEQ( UpdateNodeAddress, NULL ),
      ABCC_CMD_SEQ( UpdateBaudRate, NULL ),

      ABCC_CMD_SEQ( GetDeviceMAC, HandleGetDeviceMACResponse ),
      ABCC_CMD_SEQ( GetPort1MAC, HandleGetport1MACResponse ),
      ABCC_CMD_SEQ( GetPort2MAC, HandleGetport2MACResponse ),

      ABCC_CMD_SEQ_END()
      };
  2.  实现获取模块MAC地址的命令构建函数
    • ABCC_CmdSeqCmdStatusType GetDeviceMAC(ABP_MsgType* psMsg )函数:为提高命令执行的可靠性,可在函数中设置命令执行的判定条件,并通过函数返回值告知驱动该命令是否需要执行。命令构建函数返回值类型必须为ABCC_CmdSeqCmdStatusType,为一个枚举类型,其定义如下:
      • typedef enum ABCC_CmdSeqCmdStatus
        {
        ABCC_SEND_COMMAND,
        ABCC_SKIP_COMMAND,
        ABCC_CMD_ABORT_SEQ
        }
        ABCC_CmdSeqCmdStatusType;  
    •   ABCC_CmdSeqCmdStatusType GetDeviceMAC(ABP_MsgType* psMsg )
      • /*------------------------------------------------------------------------------
        ** Get Device MAC.
        **------------------------------------------------------------------------------
        */
        static ABCC_CmdSeqCmdStatusType GetDeviceMAC( ABP_MsgType* psMsg)
        {
        if((appl_fNwSupportsEthernetMAC)&&(appl_fNwGetEthernetMAC))
        {
        /*
        **0x0c :Network Ethernet Object (0Ch)
        **1:Instance code 1
        **1:Attribute code 1
        */
        ABCC_GetAttribute( psMsg, 0x0C, 1, 1, ABCC_GetNewSourceId() );
        return ( ABCC_SEND_COMMAND );
        }
        else
        return ( ABCC_SKIP_COMMAND );
        }
    • 由于MAC地址是驱动程序向模块读取,因此需要在驱动程序中处理获取的数据,构建模块响应消处理函数,该函数在命令执行完成后被驱动程序调用。

    • static ABCC_CmdSeqRespStatusType HandleGetDeviceMACResponse( ABP_MsgType* psMsg ),该函数的返回值固定为ABCC_CmdSeqRespStatusType,其值定义如下

      • typedef enum ABCC_CmdSeqRespStatus
        {
        ABCC_EXEC_NEXT_COMMAND,//执行命令序列中的下一个命令
        ABCC_EXEC_CURR_COMMAND,//再次执行本命令
        ABCC_RESP_ABORT_SEQ//终止整个命令序列
        }
        ABCC_CmdSeqRespStatusType;
    • 函数代码示例,作为测试,此函数仅将MAC地址打印至驱动运行的log中。
      • /*------------------------------------------------------------------------------
        ** Handler Device MAC.
        **------------------------------------------------------------------------------
        */
        static ABCC_CmdSeqRespStatusType HandleGetDeviceMACResponse( ABP_MsgType* psMsg )
        {
        UINT8 bException;
        if( ABCC_VerifyMessage( psMsg ) != ABCC_EC_NO_ERROR )
        {
        APPL_UnexpectedError();
        return( ABCC_EXEC_NEXT_COMMAND );
        }
        /*
        **user handler MAC
        */
        /********eg:printf to log*******/
        for(int i=0;i<6;i++)
        {
        if(i==0)
        {
        ABCC_PORT_DebugPrint( ( "Device MAC address is[0x%02X ", psMsg->abData[i] ) );
        }
        else if(i>0&&i<5)
        {
        ABCC_PORT_DebugPrint( ( "0x%02X ", psMsg->abData[i] ) );
        }
        else if(i==5)
        {
        ABCC_PORT_DebugPrint( ( "0x%02X]:\n\n", psMsg->abData[i] ) );
        }
        }
        return ( ABCC_EXEC_NEXT_COMMAND );//ABCC_EXEC_NEXT_COMMAND
        }
  3. 如果模块运行PROFINET协议,则再分别实现获取端口1与端口2MAC地址的函数。

    • /*------------------------------------------------------------------------------
      ** Get Port1 MAC.
      **------------------------------------------------------------------------------
      */
      static ABCC_CmdSeqCmdStatusType GetPort1MAC( ABP_MsgType* psMsg)
      {
      UINT16 iNetworkType;
      BOOL f_isPROFINET=0;
      iNetworkType=ABCC_NetworkType();
      if((iNetworkType==ABP_NW_TYPE_PIR)||
      (iNetworkType==ABP_NW_TYPE_PRT))
      {
      f_isPROFINET=1;
      }
      if((appl_fNwSupportsEthernetMAC)&&(appl_fNwGetEthernetMAC)&&(f_isPROFINET))
      {
      ABCC_GetAttribute( psMsg, 0x0C, 1, 2, ABCC_GetNewSourceId() ); //port1 MAC
      return ( ABCC_SEND_COMMAND );
      }
      else
      return ( ABCC_SKIP_COMMAND );
      }
      /*------------------------------------------------------------------------------
      ** Get Port2 MAC.
      **------------------------------------------------------------------------------
      */
      static ABCC_CmdSeqCmdStatusType GetPort2MAC( ABP_MsgType* psMsg) //port2 MAC
      {
      UINT16 iNetworkType;
      BOOL f_isPROFINET=0;
      iNetworkType=ABCC_NetworkType();
      if((iNetworkType==ABP_NW_TYPE_PIR)||
      (iNetworkType==ABP_NW_TYPE_PRT))
      {
      f_isPROFINET=1;
      }
      if((appl_fNwSupportsEthernetMAC)&&(appl_fNwGetEthernetMAC)&&(f_isPROFINET))
      {
      ABCC_GetAttribute( psMsg, 0x0C, 1, 3, ABCC_GetNewSourceId());
      return ( ABCC_SEND_COMMAND );
      }
      else
      return ( ABCC_SKIP_COMMAND );
      }
    • 消息处理函数
    • /*------------------------------------------------------------------------------
      ** Handler Port1 MAC.
      **------------------------------------------------------------------------------
      */
      static ABCC_CmdSeqRespStatusType HandleGetport1MACResponse( ABP_MsgType* psMsg )
      {
      UINT8 bException;
      if( ABCC_VerifyMessage( psMsg ) != ABCC_EC_NO_ERROR )
      {
      APPL_UnexpectedError();
      return( ABCC_EXEC_NEXT_COMMAND );
      }
      /******
      user handler MAC
      ******/
      /********eg:printf to log*******/
      for(int i=0;i<6;i++)
      {
      if(i==0)
      {
      ABCC_PORT_DebugPrint( ( "PORT1 MAC address is[0x%02X ", psMsg->abData[i] ) );
      }
      else if(i>0&&i<5)
      {
      ABCC_PORT_DebugPrint( ( "0x%02X ", psMsg->abData[i] ) );
      }
      else if(i==5)
      {
      ABCC_PORT_DebugPrint( ( "0x%02X]:\n\n", psMsg->abData[i] ) );
      }
      }
      return ( ABCC_EXEC_NEXT_COMMAND );//ABCC_EXEC_NEXT_COMMAND

      /*------------------------------------------------------------------------------
      ** Handler Port2 MAC.
      **------------------------------------------------------------------------------
      */
      static ABCC_CmdSeqRespStatusType HandleGetport2MACResponse( ABP_MsgType* psMsg )
      {
      UINT8 bException;
      if( ABCC_VerifyMessage( psMsg ) != ABCC_EC_NO_ERROR )
      {
      APPL_UnexpectedError();
      return( ABCC_EXEC_NEXT_COMMAND );
      }
      /******
      user handler MAC
      ******/
      /********eg:printf to log*******/
      for(int i=0;i<6;i++)
      {
      if(i==0)
      {
      ABCC_PORT_DebugPrint( ( "PORT2 MAC address is[0x%02X ", psMsg->abData[i] ) );
      }
      else if(i>0&&i<5)
      {
      ABCC_PORT_DebugPrint( ( "0x%02X ", psMsg->abData[i] ) );
      }
      else if(i==5)
      {
      ABCC_PORT_DebugPrint( ( "0x%02X]:\n\n", psMsg->abData[i] ) );
      }
      }
      return ( ABCC_EXEC_NEXT_COMMAND );//ABCC_EXEC_NEXT_COMMAND ABCC_RESP_ABORT_SEQ
      }
  4. 定义获取模块MAC地址命令序列执行条件判断的函数。
    • void Sdef_GetDeviceMAC(void)
      {
      UINT16 iNetworkType;
      iNetworkType=ABCC_NetworkType();
      if(iNetworkType==ABP_NW_TYPE_CCL||
      iNetworkType==ABP_NW_TYPE_COP||
      iNetworkType==ABP_NW_TYPE_PDPV0||
      iNetworkType==ABP_NW_TYPE_PDPV1||
      iNetworkType==ABP_NW_TYPE_COP||
      iNetworkType==ABP_NW_TYPE_DEV||
      iNetworkType==ABP_NW_TYPE_RTU||
      iNetworkType==ABP_NW_TYPE_CNT)
      {
      appl_fNwSupportsEthernetMAC=FALSE;
      }
      else
      {
      appl_fNwSupportsEthernetMAC=TRUE;
      }
  5. 定义应用层函数,用于驱动程序的主循环调用
    • void APPL_GetDeviceMAC_InInit(void)
      {
      if( appl_fUserInitDone == FALSE )
      {
      appl_fNwGetEthernetMAC=TRUE;
      Sdef_GetDeviceMAC();
      }
      }
  6. 在main.c主循环中调用void APPL_GetDeviceMAC_InInit(void),调用位置如代码所示。
    • while(eAbccHandlerStatus == APPL_MODULE_NO_ERROR )
      {
      APPL_GetDeviceMAC_InInit();
      eAbccHandlerStatus = APPL_HandleAbcc();
      }
  7. 如果需要在模块运行过程中获取模块MAC地址,则需要用户自行定义命令序列,并调用。
    • 命令序列定义如下
      • static const ABCC_CmdSeqType Sdef_GetDeviceMAC[] =
        {define
        ABCC_CMD_SEQ( GetDeviceMAC, HandleGetDeviceMACResponse ),
        ABCC_CMD_SEQ( GetPort1MAC, HandleGetport1MACResponse ),
        ABCC_CMD_SEQ( GetPort2MAC, HandleGetport2MACResponse ),
        ABCC_CMD_SEQ_END()
        };
  8.  定义应用层函数,void APPL_GetDeviceMAC(void),用于调用自定义命令序列。
    • void APPL_GetDeviceMAC(void)
      {
      if( appl_fUserInitDone == TRUE )
      {
      appl_fNwGetEthernetMAC=TRUE;
      Sdef_GetDeviceMAC();
      ABCC_AddCmdSeq(Sdef_GetDeviceMAC,NULL);
      }
      }
  9. 在主循环中调用void APPL_GetDeviceMAC(void),本示例中通过模拟外部条件触发(如HMI、设备配置上位机软件等)的方式判断执行命令,本示例中的外部触发条件为串口接收触发指令(用户请根据需求自行实现触发方式),并在程序主循环中通过Switch语句判断指令类型并执行相应命令。
    1. while(eAbccHandlerStatus == APPL_MODULE_NO_ERROR )
      {
      eAbccHandlerStatus = APPL_HandleAbcc();
      //set attribute after abbc init through UART1 Command protocal further information in usart.c
      if(appl_fUserInitDone==TRUE&&Sdef_NewCommandFlag==1)
      {
      switch(Sdef_setcommand)
      {
      case Sdef_GetDeviceMAC_Cmd://Get module MAC
      APPL_GetDeviceMAC();
      Sdef_NewCommandFlag=0;
      break;
      default:
      def_NewCommandFlag=0;
      break;
      }
      }
      调用
  10. Message log

 

 

附加信息

以上代码仅作参考,不作为功能的最终实现,最终实现需要根据实际情况而定,请您先梳理该功能实现的流程,并参考官网英文手册实现功能需求。

官网手册下载地址如下:

Anybus文件和文档 - CompactCom