Bonjour,
J’ai déjà abordé l’API cifX dans cet article d’introduction et les messages de configuration dans celui-ci entre autres.
Comme je dispose d’un équipement Endress + Hauser Micropilot M FMR 244, un instrument de mesure de niveau radar qui peut répondre à des requêtes PROFIBUS DP V1 Class 2 pour la lecture / écriture des paramètres de l’instrument, j’en profite pour vous donner cet autre exemple d’utilisation de la messagerie Hilscher.
Le document suivant de chez Endress + Hauser nous renseigne sur les éléments adressables de cet instrument :
https://portal.endress.com/wa001/dla/5000415/7572/000/08/BA00249FEN_1311.pdf
L’instrument est DP V0 et supporte une communication PROFIBUS DP V1 Class 2 mais pas la communication PROFIBUS DP V1 Class 1.
La carte Hilscher cifX 50-DP est donc configurée en PROFIBUS DP Maitre et la documentation suivante est nécessaire :
PROFIBUS DP Master Protocol API
On la trouve bien sûr également sur le DVD dans le répertoire Documentation :
6. Programming Manuals\english\4. Protocol Application Programming Interface\PROFIBUS DP Master
Cette documentation nous indique que nous pouvons utiliser les services d’un maitre DP V1 Class 1 ou ceux d’un maitre DP V1 Class 2. Dans cette manipulation nous utiliserons donc la seconde option.
Nous y trouvons la commande à employer pour la lecture d’un bloc de données d’un esclave DP V1 en p.192 :
PROFIBUS_FSPMM2_CMD_READ_REQ/CNF – V1 Class 2 Read Request
Cependant, avant il nous faut ouvrir une connexion entre le maitre Class 2, notre carte cifX, et l’esclave DP.
Nous disposons dans le document E+H au chapitre 5.5.6 Slot/index tables en p.48 de la table des données avec les paramètres nécessaires à l’élaboration de notre requête.
Ainsi essayons de lire par exemple :
Block parameters
Software revision 1 73 16 OSTRING X constant
Il nous faut l’adresse de l’esclave, dans notre cas 2, le numéro de slot, ici 1, l’index, ici 73 et la taille des données, soit 16 pour notre chaine de caractères.
L’algorithme est simple :
- S’enregistrer auprès de la pile de protocole PROFIBUS DP Maitre pour pouvoir recevoir les indications, notamment celle que nous allons recevoir à notre demande de déconnexion, cf. « Register / Unregister an Application » dans le document netX Dual-Port Memory Interface (DPM).
- Se connecter à l’esclave, cf. PROFIBUS_FSPMM2_CMD_INITIATE_REQ/CNF– Initiate DPV1C2 Connection
- Envoyer notre requête de lecture, cf. PROFIBUS_FSPMM2_CMD_READ_REQ/CNF – V1 Class 2 Read Request
- Envoyer notre requête de déconnexion, cf. PROFIBUS_FSPMM2_CMD_ABORT_REQ/CNF – Request Abort of Connection
- Purger les indications, cf. PROFIBUS_FSPMM2_CMD_CLOSED_IND/RES – Closed Indication
- Se dés-enregistrer.
Bien sûr, c’est un code de démonstration. On peut et on doit faire mieux !
D’où le code du jour :
#include <conio.h>
#include <windows.h>
#include "rcX_Public.h"
#include "cifxuser.h"
#include "cifxErrors.h"
#include "TLR_Types.h"
#include "ProfibusFspmm_Public.h"
#include "ProfibusFspmm2_Public.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;
static uint32_t ulCRef = 0;
/*****************************************************************************/
/*! Register Application Request
* \return 0 on success */
/*****************************************************************************/
int Register_Application(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
CIFX_PACKET tSendPacket = {0};
CIFX_PACKET tRecvPacket = {0};
RCX_REGISTER_APP_REQ_T *pPktRegisterReq;
RCX_REGISTER_APP_CNF_T *pPktRegisterCnf;
pPktRegisterReq = (RCX_REGISTER_APP_REQ_T *)&tSendPacket;
pPktRegisterReq->tHead.ulDest = 0x20; /* Channel mailbox */
pPktRegisterReq->tHead.ulSrc = 0x00;
pPktRegisterReq->tHead.ulDestId = 0x00;
pPktRegisterReq->tHead.ulSrcId = 0x00;
pPktRegisterReq->tHead.ulLen = 0;
pPktRegisterReq->tHead.ulId = ulMsgId++;
pPktRegisterReq->tHead.ulSta = 0x00;
pPktRegisterReq->tHead.ulCmd = RCX_REGISTER_APP_REQ;
pPktRegisterReq->tHead.ulExt = 0x00;
pPktRegisterReq->tHead.ulRout = 0x00;
lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Register Request sent...\r\n");
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
pPktRegisterCnf = (RCX_REGISTER_APP_CNF_T *)&tRecvPacket;
if(pPktRegisterCnf->tHead.ulCmd != RCX_REGISTER_APP_CNF)
{
printf("Wrong CNF received : pPktRegisterCnf->tHead.ulCmd = 0x%08X\r\n",
pPktRegisterCnf->tHead.ulCmd);
}
else if(pPktRegisterCnf->tHead.ulSta != 0)
{
printf("Wrong CNF received : pPktRegisterCnf->tHead.ulSta = 0x%08X\r\n",
pPktRegisterCnf->tHead.ulSta);
}
else
{
printf("Register Request confirmation received.\r\n");
iResult = 0;
}
}
}
return iResult;
}
/*****************************************************************************/
/*! Unregister Application Request
* \return 0 on success */
/*****************************************************************************/
int Unregister_Application(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
CIFX_PACKET tSendPacket = {0};
CIFX_PACKET tRecvPacket = {0};
RCX_UNREGISTER_APP_REQ_T *pPktUnregisterReq;
RCX_UNREGISTER_APP_CNF_T *pPktUnregisterCnf;
pPktUnregisterReq = (RCX_UNREGISTER_APP_REQ_T *)&tSendPacket;
pPktUnregisterReq->tHead.ulDest = 0x20; /* Channel mailbox */
pPktUnregisterReq->tHead.ulSrc = 0x00;
pPktUnregisterReq->tHead.ulDestId = 0x00;
pPktUnregisterReq->tHead.ulSrcId = 0x00;
pPktUnregisterReq->tHead.ulLen = 0;
pPktUnregisterReq->tHead.ulId = ulMsgId++;
pPktUnregisterReq->tHead.ulSta = 0x00;
pPktUnregisterReq->tHead.ulCmd = RCX_UNREGISTER_APP_REQ;
pPktUnregisterReq->tHead.ulExt = 0x00;
pPktUnregisterReq->tHead.ulRout = 0x00;
lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Unregister Request sent...\r\n");
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
pPktUnregisterCnf = (RCX_UNREGISTER_APP_CNF_T *)&tRecvPacket;
if(pPktUnregisterCnf->tHead.ulCmd != RCX_UNREGISTER_APP_CNF)
{
printf("Wrong CNF received : pPktUnregisterCnf->tHead.ulCmd = 0x%08X\r\n",
pPktUnregisterCnf->tHead.ulCmd);
}
else if(pPktUnregisterCnf->tHead.ulSta != 0)
{
printf("Wrong CNF received : pPktUnregisterCnf->tHead.ulSta = 0x%08X\r\n",
pPktUnregisterCnf->tHead.ulSta);
}
else
{
printf("Unregister Request confirmation received.\r\n");
iResult = 0;
}
}
}
return iResult;
}
/*****************************************************************************/
/*! DPV1 Class 2 Initiate Connection Request to a PROFIBUS DP Slave
* \return 0 on success */
/*****************************************************************************/
int DPV1Class2_Initiate(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
CIFX_PACKET tSendPacket = {0};
CIFX_PACKET tRecvPacket = {0};
PROFIBUS_FSPMM2_PACKET_INITIATE_REQ_T *pPktInitiateReq;
PROFIBUS_FSPMM2_PACKET_INITIATE_CNF_T *pPktInitiateCnf;
pPktInitiateReq = (PROFIBUS_FSPMM2_PACKET_INITIATE_REQ_T *)&tSendPacket;
pPktInitiateReq->tHead.ulDest = 0x20; /* Channel mailbox */
pPktInitiateReq->tHead.ulSrc = 0x00;
pPktInitiateReq->tHead.ulDestId = 0x00;
pPktInitiateReq->tHead.ulSrcId = 0x00;
pPktInitiateReq->tHead.ulLen = PROFIBUS_FSPMM2_INITIATE_REQ_SIZE + 4 + 4;
pPktInitiateReq->tHead.ulId = ulMsgId++;
pPktInitiateReq->tHead.ulSta = 0x00;
pPktInitiateReq->tHead.ulCmd = PROFIBUS_FSPMM2_CMD_INITIATE_REQ;
pPktInitiateReq->tHead.ulExt = 0x00;
pPktInitiateReq->tHead.ulRout = 0x00;
pPktInitiateReq->tData.ulRemAdd = 2; /* Slave address */
pPktInitiateReq->tData.usSendTimeout = 100;
pPktInitiateReq->tData.bFeaturesSupported1 = 1;
pPktInitiateReq->tData.bFeaturesSupported2 = 0;
pPktInitiateReq->tData.bProfileFeaturesSupported1 = 0;
pPktInitiateReq->tData.bProfileFeaturesSupported2 = 0;
pPktInitiateReq->tData.usProfileIdentNumber = 0;
pPktInitiateReq->tData.tAddAddrParam.bS_Type = 0;
pPktInitiateReq->tData.tAddAddrParam.bS_Len = 2;
pPktInitiateReq->tData.tAddAddrParam.bD_Type = 0;
pPktInitiateReq->tData.tAddAddrParam.bD_Len = 2;
pPktInitiateReq->tData.tAddAddrParam.abAddParam[0] = 0;
pPktInitiateReq->tData.tAddAddrParam.abAddParam[1] = 0;
pPktInitiateReq->tData.tAddAddrParam.abAddParam[2] = 0;
pPktInitiateReq->tData.tAddAddrParam.abAddParam[3] = 0;
lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Initiate Request sent...\r\n");
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
pPktInitiateCnf = (PROFIBUS_FSPMM2_PACKET_INITIATE_CNF_T *)&tRecvPacket;
if(pPktInitiateCnf->tHead.ulCmd != PROFIBUS_FSPMM2_CMD_INITIATE_CNF)
{
printf("Wrong CNF received : pPktInitiateCnf->tHead.ulCmd = 0x%08X\r\n",
pPktInitiateCnf->tHead.ulCmd);
}
else if(pPktInitiateCnf->tHead.ulSta != 0)
{
printf("Wrong CNF received : pPktInitiateCnf->tHead.ulSta = 0x%08X\r\n",
pPktInitiateCnf->tHead.ulSta);
printf("ulRemAdd = 0x%08X\r\n"
"bErrorDecode = 0x%02X\r\n"
"bErrorCode1 = 0x%02X\r\n"
"bErrorCode2 = 0x%02X\r\n"
"usDetail = 0x%04X\r\n",
pPktInitiateCnf->tData.tCnfNeg.ulRemAdd,
pPktInitiateCnf->tData.tCnfNeg.bErrorDecode,
pPktInitiateCnf->tData.tCnfNeg.bErrorCode1,
pPktInitiateCnf->tData.tCnfNeg.bErrorCode2,
pPktInitiateCnf->tData.tCnfNeg.usDetail);
}
else
{
printf("Initiate Request confirmation received.\r\n");
ulCRef = pPktInitiateCnf->tData.tCnfPos.ulCRef;
iResult = 0;
}
}
}
return iResult;
}
/*****************************************************************************/
/*! DPV1 Class 2 Read Request PROFIBUS DP Slave
* \return 0 on success */
/*****************************************************************************/
int DPV1Class2_Read(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
CIFX_PACKET tSendPacket = {0};
CIFX_PACKET tRecvPacket = {0};
PROFIBUS_FSPMM2_PACKET_READ_REQ_T *pPktReadReq;
PROFIBUS_FSPMM2_PACKET_READ_CNF_T *pPktReadCnf;
pPktReadReq = (PROFIBUS_FSPMM2_PACKET_READ_REQ_T *)&tSendPacket;
pPktReadReq->tHead.ulDest = 0x20; /* Channel mailbox */
pPktReadReq->tHead.ulSrc = 0x00;
pPktReadReq->tHead.ulDestId = 0x00;
pPktReadReq->tHead.ulSrcId = 0x00;
pPktReadReq->tHead.ulLen = PROFIBUS_FSPMM2_READ_REQ_SIZE;
pPktReadReq->tHead.ulId = ulMsgId++;
pPktReadReq->tHead.ulSta = 0x00;
pPktReadReq->tHead.ulCmd = PROFIBUS_FSPMM2_CMD_READ_REQ;
pPktReadReq->tHead.ulExt = 0x00;
pPktReadReq->tHead.ulRout = 0x00;
pPktReadReq->tData.ulCRef = ulCRef; /* Connection Reference */
pPktReadReq->tData.ulSlot = 1; /* Requested slot */
pPktReadReq->tData.ulIndex = 73; /* Requested index */
pPktReadReq->tData.ulLength = 16; /* Requested data length */
lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Read Request sent...\r\n");
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
pPktReadCnf = (PROFIBUS_FSPMM2_PACKET_READ_CNF_T *)&tRecvPacket;
if(pPktReadCnf->tHead.ulCmd != PROFIBUS_FSPMM2_CMD_READ_CNF)
{
printf("Wrong CNF received : pPktReadCnf->tHead.ulCmd = 0x%08X\r\n",
pPktReadCnf->tHead.ulCmd);
}
else if(pPktReadCnf->tHead.ulSta != 0)
{
printf("Wrong CNF received : pPktReadCnf->tHead.ulSta = 0x%08X\r\n",
pPktReadCnf->tHead.ulSta);
}
else
{
printf("Read Request confirmation received.\r\n");
printf("Software revision : %.16s\r\n", pPktReadCnf->tData.tCnfPos.abData);
iResult = 0;
}
}
}
return iResult;
}
/*****************************************************************************/
/*! DPV1 Class 2 Abort Request
* \return 0 on success */
/*****************************************************************************/
int DPV1Class2_Abort(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
CIFX_PACKET tSendPacket = {0};
CIFX_PACKET tRecvPacket = {0};
PROFIBUS_FSPMM2_PACKET_ABORT_REQ_T *pPktAbortReq;
PROFIBUS_FSPMM2_PACKET_ABORT_CNF_T *pPktAbortCnf;
pPktAbortReq = (PROFIBUS_FSPMM2_PACKET_ABORT_REQ_T *)&tSendPacket;
pPktAbortReq->tHead.ulDest = 0x20; /* Channel mailbox */
pPktAbortReq->tHead.ulSrc = 0x00;
pPktAbortReq->tHead.ulDestId = 0x00;
pPktAbortReq->tHead.ulSrcId = 0x00;
pPktAbortReq->tHead.ulLen = PROFIBUS_FSPMM2_ABORT_REQ_SIZE;
pPktAbortReq->tHead.ulId = ulMsgId++;
pPktAbortReq->tHead.ulSta = 0x00;
pPktAbortReq->tHead.ulCmd = PROFIBUS_FSPMM2_CMD_ABORT_REQ;
pPktAbortReq->tHead.ulExt = 0x00;
pPktAbortReq->tHead.ulRout = 0x00;
pPktAbortReq->tData.ulCRef = ulCRef; /* Connection Reference */
pPktAbortReq->tData.bSubnet = PROFIBUS_FSPMM2_SUBNET_NO;
pPktAbortReq->tData.bInstance = PROFIBUS_FSPMM2_INSTANCE_USER;
pPktAbortReq->tData.bReasonCode = 0;
lRet = xChannelPutPacket(hChannel, &tSendPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Abort Request sent...\r\n");
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
pPktAbortCnf = (PROFIBUS_FSPMM2_PACKET_ABORT_CNF_T *)&tRecvPacket;
if(pPktAbortCnf->tHead.ulCmd != PROFIBUS_FSPMM2_CMD_ABORT_CNF)
{
printf("Wrong CNF received : pPktAbortCnf->tHead.ulCmd = 0x%08X\r\n",
pPktAbortCnf->tHead.ulCmd);
}
else if(pPktAbortCnf->tHead.ulSta != 0)
{
printf("Wrong CNF received : pPktAbortCnf->tHead.ulSta = 0x%08X\r\n",
pPktAbortCnf->tHead.ulSta);
}
else
{
printf("Abort Request confirmation received.\r\n");
iResult = 0;
}
}
}
return iResult;
}
int Purge(HANDLE hChannel)
{
long lRet = CIFX_NO_ERROR;
int iResult = -1;
int iCount = 3; /* Wait 3 times */
CIFX_PACKET tRecvPacket = {0};
do
{
iCount--;
lRet = xChannelGetPacket(hChannel, sizeof(tRecvPacket), &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Packet received :\r\n"
"ulCmd = 0x%08X\r\n"
"ulDest = 0x%08X\r\n"
"ulSrc = 0x%08X\r\n"
"ulDestId = 0x%08X\r\n"
"ulSrcId = 0x%08X\r\n",
tRecvPacket.tHeader.ulCmd,
tRecvPacket.tHeader.ulDest,
tRecvPacket.tHeader.ulSrc,
tRecvPacket.tHeader.ulDestId,
tRecvPacket.tHeader.ulSrcId);
switch (tRecvPacket.tHeader.ulCmd)
{
case PROFIBUS_FSPMM2_CMD_CLOSED_IND:
{
printf("Closed Indication received : tRecvPacket->tHead.ulSta = 0x%08X\r\n",
tRecvPacket.tHeader.ulState);
tRecvPacket.tHeader.ulDest = 0x20; /* Channel mailbox */
tRecvPacket.tHeader.ulCmd = PROFIBUS_FSPMM2_CMD_CLOSED_RES;
lRet = xChannelPutPacket(hChannel, &tRecvPacket, 1000);
if(lRet != CIFX_NO_ERROR)
{
ShowError( lRet);
}
else
{
printf("Closed Response sent...\r\n");
iResult = 0;
}
break;
}
default:
{
printf("Message received : tRecvPacket->tHead.ulCmd = 0x%08X\r\n",
tRecvPacket.tHeader.ulCmd);
break;
}
}
}
}
while (iCount > 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--- DPV1 Class 2 Read Request PROFIBUS DP Slave 2, Slot 1, Index 73 ---\r\n");
printf("\n--- Register Application Request ---\r\n");
if(Register_Application(hChannel) != 0)
{
printf("Error registering application\r\n");
}
else
{
printf("\n--- DPV1 Class 2 Initiate Request PROFIBUS DP Slave 2 ---\r\n");
if(DPV1Class2_Initiate(hChannel) != 0)
{
printf("Error initiating connection\r\n");
}
else
{
printf("\n--- DPV1 Class 2 Read Request PROFIBUS DP Slave 2 ---\r\n");
if(DPV1Class2_Read(hChannel) != 0)
{
printf("Error reading\r\n");
}
printf("\n--- DPV1 Class 2 Abort Request PROFIBUS DP Slave 2 ---\r\n");
if(DPV1Class2_Abort(hChannel) != 0)
{
printf("Error aborting\r\n");
}
Purge(hChannel);
}
}
if(Unregister_Application(hChannel) != 0)
{
printf("Error unregistering application\r\n");
}
/* Close the communication channel */
xChannelClose(hChannel);
}
/* Close the cifX driver */
xDriverClose(hDriver);
}
return 0;
}
Et la trace qui nous montre que c’est tombé en marche !
On lit bien la version du logiciel : Software revision : 01.05.00
--- DPV1 Class 2 Read Request PROFIBUS DP Slave 2, Slot 1, Index 73 ---
--- Register Application Request ---
Register Request sent...
Register Request confirmation received.
--- DPV1 Class 2 Initiate Request PROFIBUS DP Slave 2 ---
Initiate Request sent...
Initiate Request confirmation received.
--- DPV1 Class 2 Read Request PROFIBUS DP Slave 2 ---
Read Request sent...
Read Request confirmation received.
Software revision : 01.05.00
--- DPV1 Class 2 Abort Request PROFIBUS DP Slave 2 ---
Abort Request sent...
Abort Request confirmation received.
Packet received :
ulCmd = 0x00004428
ulDest = 0x00000000
ulSrc = 0x80169C80
ulDestId = 0xA009A021
ulSrcId = 0x00000000
Closed Indication received : tRecvPacket->tHead.ulSta = 0x00000000
Closed Response sent...
Error: 0x800C0019, <No packet available>
Error: 0x800C0019, <No packet available>
Unregister Request sent...
Unregister Request confirmation received.
Cordialement,
Stéphane