Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Current »

Introduction

Some general question regarding the product are covered in the Frequently Asked Questions section: FAQ & Troubleshooting . In this document methods for debugging and checking proper functionality of the communication between application and communication module shall be shown.

Communication

Basics

Following table shows the basic structure of the SPI frame.

Range

Length

Description

0 .. 3

4 byte

MCTC SPI Header

4 .. 76

73 byte

cyclic data (data mapper)

77 .. 126

50 byte

RPC data

127

1 byte

reserved

The block MCTC SPI Header contains the following elements:

/**< SPI cyclic data header */
typedef struct {
    uint16_t crc_le16;                          /**< checksum */
    uint8_t seq;                                /**< sequence number */
    uint8_t lenData;                            /**< data length */
} GOAL_MI_MCTC_SPI_CYCLIC_T;

The field crc_le16 contains a checksum (Fletcher-16, Wikipedia) with a starting value of 0x0007. The field seq contains a sequence counter which increases with each updated frame. The field lenData contains the length of valid data after the MCTC header..

With an attached Logic Analyzer, it is possible to check the header for valid values. The third value (0x89) is the incrementing sequence number and the fouth byte shows the actual length of data (0x7C or 124).

grafik-20241023-171507.png

Is the application controller receiving valid frames?

If incoming frames from the communication module can be checked by content, following hints should determine wether valid frames are received:

  • The first 2 bytes (crc) contains a value != 0

  • The 3rd byte changes with each frame (cyclic counter)

  • The 4th byte (length) has a value != 7

Programmatically, valid frames are detected by checking the CRC field in the header.

For uGOAL this is determined within sapi.c:

/****************************************************************************/
/** Basic cyclic communication function
 *
 * This function initiates SPI transfer and dispatches received data to
 * upper layers data callback and RPC handling.
 *
 */
void sapi_loop(
        void
)
{
    /* update frame header */
    sapi_pdUpdateHeader(sendData, GOAL_MI_MCTC_LEN - sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T));

    /* transfer data via SPI */
    plat_spiTransfer((const char *) &sendData[0], (char *) receiveData, BUF_LENGTH);

    /* control LED if Connection valid or Wink active */
    if (true == sapi_pdCheckHeader(receiveData, GOAL_MI_MCTC_LEN - sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T))) {

        /* inform application */
        som_dataCb(&receiveData[0], &sendData[0]);

        /* trigger RPC */
        goal_miMctcDmCbRead();
   }
}

If line 22 within the function sapi_loop is executed, valid frames from the communication module are received.

For GOAL based applications this can be determined in the function goal_miMctcSpiProcessTask within the goal_mi_mctc_spi.c module:

        do {

            /* check frame CRC */
            res = goal_miMctcCrc(&pBufSpi[sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T)], pHdr->lenData, GOAL_le16toh_p(&pHdr->crc_le16));
            if (GOAL_RES_ERR(res)) {
                break;
            }

            /* check sequence counter */
            if (pMiMctcSpi->seqCyclicRem != pHdr->seq) {

                /* inform MI MCTC that connection is running */
                goal_miMctcMonitorRx(pMiMctc, GOAL_TRUE);

                /* re-arm watchdog */
                pMiMctcSpi->tsTout = tsNow + pMiMctc->cfgToutValue;

                /* update seen sequence */
                pMiMctcSpi->seqCyclicRem = pHdr->seq;
            }

            if (pHdr->lenData) {
                /* process MI DM incoming data */
                goal_miDmReadSync(pMiMctcSpi->pMiDmRead, &pBufSpi[sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T)]);
            }
        } while (0);

If the checks shown in the code are performed successfully a valid frame was received.

Manually this could be checked by evaluating the crc from the SPI frame and calculating the expected value over the given data with the given length from the header. The header itself is skipped from crc calculation.

Is the communication module receiving the frames from the application controller?

The communication module will provide valid mCTC frames even if the application controller does not. Thus it is not that easy to determine, if the frames sent by the application controller are evaluated by the communication module.

A good measurement whether the communication module receives the frames from the application controller is the capability to perform the RPC synchronisation. This is executed initially with beginning of the communication.

The RPC synchronisation follows a state machine with the following states:

State

Description

GOAL_MI_MCTC_RPC_STATE_INIT

Initial state

Entered initially or in error case

GOAL_MI_MCTC_RPC_STATE_SYNC_REQ

Initial request state

Entered after INIT state

GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL

Initial acknowledge state

Entered after SYNC REQ state

GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE

Initial sync request was acknowledged

Entered if SYNC request was acknowledged, the peer module received our request!

GOAL_MI_MCTC_RPC_STATE_RUN_ONCE

Setup state after Sync

Entered when SYNC was successful

GOAL_MI_MCTC_RPC_STATE_RUN

Normal operating state

Entered after SYNC is done

If the state GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE is entered, the communication module received the frames sent by the application controller.

Following code shows the state machine in exceprts:

/****************************************************************************/
/** MCTC MI - RPC Sync Loop
 */
void goal_miMctcRpcSyncLoop(
    MCTC_INST_T *pMiMctc                        /**< MI MCTC handle */
)
{
    GOAL_STATUS_T res = GOAL_OK;                /* result */
    GOAL_BOOL_T flgSetupLocal = GOAL_FALSE;     /* local setup done flag */
    GOAL_BOOL_T flgSetupRemote = GOAL_FALSE;    /* remote setup done flag */
    GOAL_MCTC_T *pHdlMctcTx = NULL;             /* call handle */

    /* leave if not in sync-mode */
    if ((GOAL_MI_MCTC_RPC_STATE_RUN == pMiMctc->stateRpc)
        || (GOAL_MI_MCTC_RPC_STATE_STOP == pMiMctc->stateRpc)) {
        return;
    }

    /* check if send is permitted */
    if (GOAL_TRUE != goal_miMctcRpcSendAllow(pMiMctc)) {
        return;
    }

    /* handle state machine */
    switch (pMiMctc->stateRpc) {

        case GOAL_MI_MCTC_RPC_STATE_RUN_ONCE:

            /* .... */
            
            break;

        /* initialize synchronisation */
        case GOAL_MI_MCTC_RPC_STATE_SYNC_INIT:

            goal_logDbg("state: SYNC_INIT");

            /* reset sync state */
            GOAL_MASK_SET(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_REQ);
            GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_ACK);
            pMiMctc->seqRpcLocal = 0;
            pMiMctc->seqRpcLocalAck = 0;
            pMiMctc->seqRpcRemoteAck = 0;
            pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_REQ;

            /* intended fallthrough */
            GOAL_TARGET_FALLTHROUGH;

        /* request synchronisation from partner */
        case GOAL_MI_MCTC_RPC_STATE_SYNC_REQ:

            goal_logDbg("state: SYNC_REQ");

            /* send RPC sync frame until ACK is received */
            if (!(GOAL_MI_MCTC_RPC_FLG_SYNC_ACK & pMiMctc->flgRpcRemote)) {

                /* send sync request frame */
                goal_miMctcRpcSend(pMiMctc, NULL, 0);

                break;
            }

            /* request acknowledge of first sequence */
            GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_REQ);
            GOAL_MASK_SET(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_REQ_ACK);
            pMiMctc->seqRpcLocal = 1;
            pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL;

            /* intended fallthrough */
            GOAL_TARGET_FALLTHROUGH;

        case GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL:

            goal_logDbg("state: SYNC_ACK_LOCAL");

            /* send empty sequence 1 until ACK is received and we confirmed remote sequence */
            if (1 != pMiMctc->seqRpcLocalAck) {

                /* send empty RPC request */
                goal_miMctcRpcSend(pMiMctc, NULL, 0);

                break;
            }

            /* wait for remote sync */
            pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE;

            break;

        case GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE:

            goal_logDbg("state: SYNC_ACK_REMOTE");

            if (0 == pMiMctc->seqRpcRemoteAck) {

                /* send empty RPC request */
                goal_miMctcRpcSend(pMiMctc, NULL, 0);

                break;
            }

            /* enter run mode */
            GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_REQ_ACK);
            pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_RUN_ONCE;

            break;
    }
}

Debugging

Disabling Peer-Loss Detection

By default both, the basic SPI communication and the RPC communication, are monitored and in case of loss of communication or a missing response an error is detected and propagated. This will stop the system from working any further. This can be disabled for debugging purpose. However this should never be done in a productive system, since this will mask potential communication problems.

For uGOAL application, the following defines need to be set in goal_config.h:

/* for debugging purpose disable any timeout detection on the peer */
#define CONFIG_UGOAL_TIMEOUT_RPC 0
#define CONFIG_UGOAL_TIMEOUT_MEDIA 0

or using the make system, using the command in a project directory, like ugoal/projects/ugoal/02_profinet/gcc:

user: ~/.../02_profinet/gcc: make debug
removed directory: build/
 *** Debugging Mode enabled ***

and building the project:

user: ~/ugoal/projects/ugoal/02_profinet/gcc: make PLATFORM=stm32f429zi

 *** Debugging Mode enabled - note that: ***
    > this mode should only be used for debugging purposes
    > peer-loss detection on application controller is disabled
    > logging might be enabled

 *** Disabling per 'make debug' ***

platform pre
[CC] plat/stm32/plat.c
...

To switch the Debugging Mode off, use ‘make debug’ too.

For GOAL based application, following code needs to be called before any other functions. So best place the following calls in appl_init():

#include <goal_media/goal_mi_mctc_spi.h>


/****************************************************************************/
/** Application Init
 *
 * Build up the device structure and initialize the Profinet stack.
 */
GOAL_STATUS_T appl_init(
    void
)
{
    GOAL_STATUS_T res = GOAL_OK;                /* result */

    /* disable media and RPC timeout */
    goal_miMctcCfgTout(0);
    goal_miMctcSpiCfgTout(0);

    /* initialize DD */
    res = goal_ddInit();
    if (GOAL_RES_ERR(res)) {
        goal_logErr("Initialization of DD failed");
    }

    /* ..... */
}

This will disable both the RPC timeout and the SPI communication timeout. If the application controller is halted for an arbitrary time, operation can be resumed without any error.

General Debugging procedure

If debugging an application some precautions need to be considered. The correct sequence for debugging a application would be:

  1. stop the application (on the application controller)

  2. reset the SoM module (e.g. using the reset button on the arduino shield)

  3. start debugging of the application (application controller)

  4. run the application

Then the application should be able to start the communication module as required. It would be possible to automate this process by performing a reset using the reset input input of the communication module, however remarks regarding the reset handling in the appliation node 1001 (firmware update and reset) should be considered.

  • No labels