Archives de catégorie : comX

Les modules comX font partie de la dernière génération d’interfaces de communication construite autour des systèmes sur puce netX développés par Hilscher.

cifX : Mise en œuvre : API – Configuration PROFIBUS DP Esclave

Bonjour,

Nous avons introduit l’API Hilscher cifX ici et testé quelques fonctions en se servant des cartes cifX configurées avec « netX Configuration Tool » en Modbus TCP Serveur ou en PROFIBUS DP Esclave.

Comme vous suivez ce fil depuis le début, vous savez qu’il est également possible de configurer les cartes cifX, les comX et les netJACK de même, par programme. Bien sûr, c’est plus simple pour les versions esclaves que pour les versions maitres.

Pourquoi devrait-on configurer les cartes par programme alors que Hilscher fournit les logiciels pour le faire ?
Vous avez sans doute vos propres raisons mais je vais en donner une pour ce qui suit.

Votre application nécessite donc une connexion PROFIBUS DP Esclave car votre superbe machine doit s’intégrer dans une ligne de production pilotée par un automate de ligne et cet automate de ligne est Maitre PROFIBUS DP.

Votre client va donc devoir intégrer votre équipement dans son réseau PROFIBUS DP et vous devez pour cela lui fournir d’une part un fichier GSD de votre équipement et d’autre part la configuration que vous avez réalisée pour celui-ci, adresse PROFIBUS, modules d’E/S, options…

Mais il est possible que votre produit ne dispose pas de roues codeuses pour l’adresse, c’est le cas de certaines cifX, ou que des options sélectionnées sur l’IHM de votre équipement induisent une modification des données d’E/S échangées et donc des modules nécessaires.

Dans un cas comme dans l’autre, vous avez le choix d’effectuer cette adaptation avec le « netX Configuration Tool » ou de le faire par programme.

Si c’est à votre client que revient la tâche de réaliser ces modifications il faut qu’il se forme à l’utilisation d’un outil pour ne l’utiliser que très rarement.
Alors que quelques champs de configuration au niveau de l’IHM lui permettraient de réaliser cette configuration très simplement et sans installer l’outil.

Pour réaliser cette configuration par programme il est nécessaire d’utiliser la messagerie Hilscher.

  • Dans netX Dual-Port Memory Interface (DPM) celle-ci est décrite en détail ; on y apprend qu’il existe une boite à lettres pour l’envoi et une pour la réception de messages dont le contenu est constitué d’un en-tête commun et d’une cohorte de données propres à chaque commande. Il existe ainsi des messages indépendants des piles de protocoles et documentés dans ce même manuel et qui fournissent un grand nombre de services.
  • Dans cifX Device Driver – cifX API : on apprend qu’il existe des fonctions pour envoyer et recevoir ces messages.

Enfin, pour chaque pile de protocole, une documentation spécifie les commandes existantes et leur utilisation. Pour notre exemple du jour, la documentation est celle-ci :
PROFIBUS DP Slave Protocol API
Elle est disponible sur le DVD dans le dossier Documentation :
6. Programming Manuals\english\4. Protocol Application Programming Interface\PROFIBUS DP Slave

Ainsi, il est possible d’utiliser la commande « PROFIBUS_APS_SET_CONFIGURATION_REQ/CNF », en p. 75, afin de fournir à notre pile PROFIBUS DP Esclave les paramètres de configuration. On remarque sans trop d’étonnement que ceux-ci sont ceux que l’on trouve au niveau de l’interface du « netX Configuration Tool ».

Il faut donc :

  • envoyer notre requête « PROFIBUS_APS_SET_CONFIGURATION_REQ »,
  • recevoir notre confirmation « PROFIBUS_APS_SET_CONFIGURATION_CNF »,
  • tester le résultat,
  • et procéder à l’initialisation du canal afin que les nouveaux paramètres soient pris en compte.

Nous supprimons donc les fichiers de configuration à l’aide de cifXSetup, on laisse juste le fichier de firmware.

Nous reprenons notre code simplissime de la veille.

Et l’on crée une fonction dont le rôle est de remplir notre message de configuration, de l’envoyer, de récupérer la réponse et la tester.

Si cette fonction fournit un résultat positif on initialise le canal et l’on continue notre démonstration.

Voilà le code du jour :

#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include "cifxuser.h"
#include "cifxErrors.h"
#include "TLR_Types.h"
#include "ProfibusAps_Public.h"
#include "ProfibusDl_BusParameter.h"

#define IO_WAIT_TIMEOUT     10

/*****************************************************************************/
/*! Show error
 *                                                                           */

/*****************************************************************************/
void ShowError( long lError)
{
  if( lError != CIFX_NO_ERROR)
  {
    /* Read driver error description */
    char szError[1024] ={0};
    xDriverGetErrorDescription( lError,  szError, sizeof(szError));
    printf("Error: 0x%X, <%s>\r\n", lError, szError);
  }
}

static uint32_t      ulMsgId         = 0;

/*****************************************************************************/
/*! Configure PROFIBUS DP Slave
 *   \return 0 on success                                                    */

/*****************************************************************************/

int DPS_SetConfig(HANDLE hChannel)
{
  long          lRet            = CIFX_NO_ERROR;
  int           iResult         = -1;

  CIFX_PACKET   tSendPacket     = {0};
  CIFX_PACKET   tRecvPacket     = {0};

  PROFIBUS_APS_PACKET_SET_CONFIGURATION_REQ_T    *pPktSetConfReq;
  PROFIBUS_APS_PACKET_SET_CONFIGURATION_CNF_T    *pPktSetConfCnf;

  pPktSetConfReq = (PROFIBUS_APS_PACKET_SET_CONFIGURATION_REQ_T *)&tSendPacket;

  pPktSetConfReq->tHead.ulDest   = 0x20; /* Channel mailbox */
  pPktSetConfReq->tHead.ulSrc    = 0x00;
  pPktSetConfReq->tHead.ulDestId = 0x00;
  pPktSetConfReq->tHead.ulSrcId  = 0x00;
  pPktSetConfReq->tHead.ulId     = ulMsgId++;
  pPktSetConfReq->tHead.ulSta    = 0x00;
  pPktSetConfReq->tHead.ulCmd    = PROFIBUS_APS_SET_CONFIGURATION_REQ;
  pPktSetConfReq->tHead.ulExt    = 0x00;
  pPktSetConfReq->tHead.ulRout   = 0x00;

  pPktSetConfReq->tData.ulSystemFlags   =    0;   /* Auto start */
  pPktSetConfReq->tData.ulWdgTime       = 1000;

  pPktSetConfReq->tData.usIdentNumber   = 0x0B69; /* cifX Id */
  pPktSetConfReq->tData.bBusAddr        =      2; /* Station adress */
  pPktSetConfReq->tData.bBaudRate       = PROFIBUS_DL_DATA_RATE_AUTO;

  pPktSetConfReq->tData.bFlags          =  WRMSTRT_FLG_DPV1_ENABLE
                                          |WRMSTRT_FLG_SYNC_SUPP
                                          |WRMSTRT_FLG_FREEZE_SUPP
                                          |WRMSTRT_FLG_FAILSAFE_SUPP
                                      /*  |WRMSTRT_FLG_AUTOCONFIG */
                                          |WRMSTRT_FLG_NO_ADDR_CHANGE;

  pPktSetConfReq->tData.bRes[0]         = 0;
  pPktSetConfReq->tData.bRes[1]         = 0;

  pPktSetConfReq->tData.bCfgLen         = 4;
  pPktSetConfReq->tHead.ulLen           = 16 + pPktSetConfReq->tData.bCfgLen;
  /* 32 Word Out */
  pPktSetConfReq->tData.abCfgData[0]    = 0x80;
  pPktSetConfReq->tData.abCfgData[1]    = 0xDF;
  /* 64 Word In */
  pPktSetConfReq->tData.abCfgData[2]    = 0x40;
  pPktSetConfReq->tData.abCfgData[3]    = 0xFF;

  lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
  if(lRet != CIFX_NO_ERROR)
  {
    ShowError( lRet);
  }
  else
  {
    printf("DPS Configuration Request sent...\r\n");
    lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
    if(lRet != CIFX_NO_ERROR)
    {
      ShowError( lRet);
    }
    else
    {
      pPktSetConfCnf = (PROFIBUS_APS_PACKET_SET_CONFIGURATION_CNF_T *)&tRecvPacket;
      if(pPktSetConfCnf->tHead.ulCmd != PROFIBUS_APS_SET_CONFIGURATION_CNF)
      {
        printf("Wrong CNF received : pPktSetConfCnf->tHead.ulCmd = 0x%08X\r\n",
               pPktSetConfCnf->tHead.ulCmd);
      }
      else if(pPktSetConfCnf->tHead.ulSta != 0)
      {
        printf("Wrong CNF received : pPktSetConfCnf->tHead.ulSta = 0x%08X\r\n",
               pPktSetConfCnf->tHead.ulSta);
      }
      else
      {
        printf("Configuration confirmation received.\r\n");
        iResult = 0;
      }
    }
  }
  return iResult;
}

/*****************************************************************************/
/*! The main function
 *   \return 0 on success                                                    */

/*****************************************************************************/
int main(int argc, char* argv[])
{
  HANDLE hDriver  = NULL;
  HANDLE hChannel = NULL;
  long   lRet     = CIFX_NO_ERROR;
  long   lOldRet  = CIFX_NO_ERROR;

  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);

  /* Open the cifX driver */
  lRet = xDriverOpen(&hDriver);
  if(CIFX_NO_ERROR != lRet)
  {
    printf("Error opening driver!\r\n");
    ShowError(lRet);
  } else
  {
    lRet = xChannelOpen(hDriver, "cifX0", 0, &hChannel);
    if(CIFX_NO_ERROR != lRet)
    {
      printf("Error opening Channel!\r\n");
      ShowError(lRet);
    } else
    {
      printf("\n--- Configuration ---\r\n");

      if(DPS_SetConfig(hChannel) == 0)
      {
        printf("\n--- Channel Init ---\r\n");
        lRet = xChannelReset( hChannel, CIFX_CHANNELINIT, 5000);
        if(lRet != CIFX_NO_ERROR)
        {
          ShowError( lRet);
        }else
        {
          /* Read and write I/O data (32Bytes) */
          unsigned char abSendData[32] = {0};
          unsigned char abRecvData[32] = {0};

          printf("\n--- I/O Data exchange ---\r\n");
          /* Do I/O Data exchange until a key is hit */
          printf("Hit any key to stop the process!\r\n");
          while(!kbhit())
          {
            lRet = xChannelIORead(hChannel, 0, 0, sizeof(abRecvData), abRecvData, IO_WAIT_TIMEOUT);
            if(CIFX_NO_ERROR != lRet)
            {
              if(lOldRet != lRet)
              {
                printf("Error reading IO Data area!\r\n");
                ShowError(lRet);
              }
            }else
            {
              memcpy(abSendData, abRecvData, sizeof(abSendData));

              lRet = xChannelIOWrite(hChannel, 0, 0, sizeof(abSendData), abSendData, IO_WAIT_TIMEOUT);
              if(CIFX_NO_ERROR != lRet)
              {
                if(lOldRet != lRet)
                {
                  printf("Error writing to IO Data area!\r\n");
                  ShowError(lRet);
                }
              }
            }
            lOldRet = lRet;
          }
        }
      }
      /* Close the communication channel */
      xChannelClose(hChannel);
    }
    /* Close the cifX driver */
    xDriverClose(hDriver);
  }
  return 0;
}

Avec un peu de chance, ça tombe en marche.
N’hésitez pas à nous solliciter si besoin.
Si vous souhaitez le projet dans son intégralité, faites m’en la demande et je vous le transmettrais bien volontiers.

Cordialement,
Stéphane