NorthStar-Endurance-TestBench/EnduranceTestBench/scheduler/sdl.c

559 lines
16 KiB
C

/**
* @file sdl.c
*
* @copyright Nehemis SARL reserves all rights even in the event of industrial
* property rights. We reserve all rights of disposal such as
* copying and passing on to third parties.
*
* @brief This file contains the initialization and execution logic for
* the Valve Controller and CANopen stack. It defines the main
* scheduler loop for running these components within the firmware.
*
* @todo SDL_MIN_OPERATIONAL_NODES to be changed to 20, are the node is fixed.
* (electronic issue in the board)
*
*/
/******************************************************************************
* Include Header Files
******************************************************************************/
/* CANopen includes */
#include <co_canopen.h>
#include <gen_define.h>
#include <co_canopen.h>
#include "co_sdo.h"
#include "co_odaccess.h"
/* User includes */
#include "sdl.h"
#include "processBoard.h"
#include "enduranceTestBench.h"
#include "hal_system.h"
#include "stdlib.h"
/******************************************************************************
* Macro constant declarations
******************************************************************************/
#define SDL_CANOPEN_BITRATE 250u
#define SDL_PU_MASTER_NODE_ID 1u
#define SDL_LSS_TIMEOUT 20u /* Defined in stack examples */
#define SDL_LSS_CONFIG_TIMEOUT 1000u
#define SDL_LSS_CONFIG_INTERVAL 4u
#define SDL_APPL_TIMER_TIME 250000uL
#define SDL_LSS_NODE_COUNT 20u
#define SDL_LSS_NODE_HB_MS 1000uL
#define SDL_LSS_NODE_STATE_RESET_INTERVAL 3u
#define SDL_TEST_BENCH_SDO_TIMEOUT 1000u
#define SDL_TEST_BENCH_STARTUP_TIMEOUT 10000uLL
#define SDL_POSITION_SETPOINT_INDEX 0x6002
#define SDL_POSITION_SETPOINT_SUB_INDEX 0x0
#define SDL_POSITION_FEEDBACK_INDEX 0x6004
#define SDL_POSITION_FEEDBACK_SUB_INDEX 0x0
#define SDL_SDO_CLIENT_COB 0x1280
#define SDL_SDO_CLIENT_TO_SERVER_COB 0x600
#define SDL_SDO_SERVER_TO_CLIENT_COB 0x580
#define SDL_MIN_OPERATIONAL_NODES 15u /* To be 20, one node not in working state currently */
/******************************************************************************
* Type Declarations
******************************************************************************/
typedef struct
{
uint32 vendorId_u32;
uint32 productId_u32;
uint32 versionNbr_u32;
uint32 serialNbr_u32;
uint8 nodeId_u8;
} SdlLssNodeInfo_t;
/******************************************************************************
* Global Declarations
******************************************************************************/
static bool testBenchStarted_b;
SdlLssNodeInfo_t var_gst;
static CO_TIMER_T monitorTimer_gst; /**< application timer */
static bool monitorSleep_gb = CO_FALSE; /**< sleep flag */
static bool masterStarted_gb; /**< master started flag */
static const SdlLssNodeInfo_t nodeLookupTable_gast[SDL_LSS_NODE_COUNT] =
{
{0x319, 0x4d2, 0x1, 0x01, 0x5} ,
{0x319, 0x4d2, 0x1, 0x02, 0x6} ,
{0x319, 0x4d2, 0x1, 0x03, 0x7} ,
{0x319, 0x4d2, 0x1, 0x04, 0x8} ,
{0x319, 0x4d2, 0x1, 0x05, 0x9} ,
{0x319, 0x4d2, 0x1, 0x06, 0xA} ,
{0x319, 0x4d2, 0x1, 0x07, 0xB} ,
{0x319, 0x4d2, 0x1, 0x08, 0xC} ,
{0x319, 0x4d2, 0x1, 0x09, 0xD} ,
{0x319, 0x4d2, 0x1, 0x0A, 0xE} ,
{0x319, 0x4d2, 0x1, 0x0B, 0xF} ,
{0x319, 0x4d2, 0x1, 0x0C, 0x10},
{0x319, 0x4d2, 0x1, 0x0D, 0x11},
{0x319, 0x4d2, 0x1, 0x0E, 0x12},
{0x319, 0x4d2, 0x1, 0x0F, 0x13},
{0x319, 0x4d2, 0x1, 0x10, 0x14},
{0x319, 0x4d2, 0x1, 0x11, 0x15},
{0x319, 0x4d2, 0x1, 0x12, 0x16},
{0x319, 0x4d2, 0x1, 0x13, 0x17},
{0x319, 0x4d2, 0x1, 0x14, 0x18}
};
static CO_NMT_STATE_T nodeNMTState_gaen[SDL_LSS_NODE_COUNT];
/******************************************************************************
* Static function Declarations
******************************************************************************/
static void SdlInitCanopen(void);
static void SdlRunCanopen(void);
static RET_T SdlOverwriteLoadIndication(uint8 index_u8);
static void SdlLssIndCallback(CO_LSS_MASTER_SERVICE_T service_en, uint16 errorCode_u16, uint8 errorSpec_u8, uint32 *pIdentity_pu32);
static void SdlLssHbMonitorCallback(uint8 nodeID_u8, CO_ERRCTRL_T state_en, CO_NMT_STATE_T nmtState_en);
static void SdlLssResetNodesCallback(void *pData_pv); /**< callback of reset handler */
static uint8 SdlLssGetNodeIndex(uint8 nodeId_u8);
static void SdlLssToggleMonitorFlag(void *pData);
static void SdlLssNodeHandlerRun(void);
static uint32 SdlLssConfigureNode(uint8 arrIndex_u8);
static RET_T SdlLssSetSdoCobID(uint8 sdoNr_u8, uint8 nodeId_u8);
/* Supress warnings for implicit declaration.
* Need this function for overwriting the manual COB ids
* for the TPDO. */
extern void userOverwriteCobIdSettings(void);
static bool SdlAreAllNodesOperational(void);
/******************************************************************************
* Public Function Definitions
******************************************************************************/
void SdlInit(void)
{
testBenchStarted_b = false;
SdlInitCanopen();
ProcessBoardInit();
}
void SdlRun(void)
{
SdlRunCanopen();
SdlLssNodeHandlerRun();
if (SdlAreAllNodesOperational())
{
EnduranceTestBenchRun(&testBenchStarted_b);
if (testBenchStarted_b == true)
{
ProcessBoardRun();
}
}
}
/******************************************************************************
* Private Function Declarations
******************************************************************************/
/**
* @brief Init the CANopen stack.
*
*/
static void SdlInitCanopen(void)
{
RET_T retval;
HalSystemInit();
codrvHardwareInit();
retval = codrvCanInit(SDL_CANOPEN_BITRATE);
retval = codrvTimerSetup(CO_TIMER_INTERVAL);
/* CANopen Initialization */
retval = coCanOpenStackInit(SdlOverwriteLoadIndication);
retval = coEventRegister_LSS_MASTER(SdlLssIndCallback);
retval = coEventRegister_ERRCTRL(SdlLssHbMonitorCallback);
/* Register SDO event handlers */
coEventRegister_SDO_CLIENT_READ(EnduranceTestBenchReadInd);
coEventRegister_SDO_CLIENT_WRITE(EnduranceTestBenchWriteInd);
/* enable CAN communication */
retval = codrvCanEnable();
retval = coTimerStart(&monitorTimer_gst, SDL_APPL_TIMER_TIME, SdlLssToggleMonitorFlag, NULL, CO_TIMER_ATTR_ROUNDUP_CYCLIC);
//retval = coTimerStart(&nodeResetTimer_gst, (SDL_LSS_NODE_STATE_RESET_INTERVAL * 1000000uLL), SdlLssResetNodesCallback, NULL, CO_TIMER_ATTR_ROUNDUP_CYCLIC);
coLssIdentifyNonConfiguredSlaves(SDL_LSS_CONFIG_TIMEOUT, SDL_LSS_CONFIG_INTERVAL);
retval = coNmtStateReq(SDL_PU_MASTER_NODE_ID, CO_NMT_STATE_OPERATIONAL, CO_TRUE);
}
/**
* @brief Runs the CANopen stack.
*
*/
static void SdlRunCanopen(void)
{
coCommTask();
SdlLssNodeHandlerRun();
}
/******************************************************************************
* Private Function Definitions
******************************************************************************/
/**
* @brief Handler for the defined nodes in the network.
* This function starts the configuration of a node if needed
*
* @return void
*
*/
static void SdlLssNodeHandlerRun(void)
{
if (monitorSleep_gb != CO_TRUE)
{
for (uint8 i_u8 = 0u; i_u8 < SDL_LSS_NODE_COUNT; i_u8++)
{
if (nodeNMTState_gaen[i_u8] == CO_NMT_STATE_PREOP)
{
SdlLssConfigureNode(i_u8);
}
if (nodeNMTState_gaen[i_u8] == CO_NMT_STATE_OPERATIONAL)
{
/* Do nothing */
}
}
}
monitorSleep_gb = CO_TRUE;
}
/**
* @brief Load function for overwriing the TPDO COB id values.
*
* @param index_u8 Subindex parameter to point parameter area(unused)
*
* @return Error Code
*/
static RET_T SdlOverwriteLoadIndication(uint8 index_u8)
{
userOverwriteCobIdSettings();
return RET_OK;
}
/**
* @brief This function is called if a new NMT state of a node,
* is recordnized by the CANopen stack.
*
* @param nodeID_u8 Node id of the node that has registered a change.
* state_en Error control state
* nmtState_en NMT state of the particular node.
*
* @return Array index of the node in the network.
*
*/
static uint8 SdlLssGetNodeIndex(uint8 nodeId_u8)
{
uint8 arrIndex_u8;
/* find node ID array arrIndex_u8 */
for (arrIndex_u8 = 0u; arrIndex_u8 < SDL_LSS_NODE_COUNT; arrIndex_u8++)
{
if (nodeId_u8 == nodeLookupTable_gast[arrIndex_u8].nodeId_u8)
{
return arrIndex_u8;
}
}
return NMS_UINT8_MAX;
}
/**
* @brief This function is called if a new NMT state of a node,
* is recordnized by the CANopen stack.
*
* @param nodeID_u8 Node id of the node that has registered a change.
* state_en Error control state
* nmtState_en NMT state of the particular node.
*
* @return Array index of the node in the network.
*
*/
static void SdlLssToggleMonitorFlag(void *pData)
{
(void)pData;
/* deactivate application sleep flag */
monitorSleep_gb = CO_FALSE;
}
/**
* @brief Configure a specific node.
* This function adds the defined node to heartbeat consumers,
* and sets its heartbeat interval.
*
* @param arrIndex_u8 Index of the node in the array which is to be configured.
*
* @return Status of the operation.
*
*/
static uint32 SdlLssConfigureNode(uint8 arrIndex_u8)
{
uint32 error_u32 = SDL_DEFAULT_ERROR;
RET_T retVal_en = RET_INTERNAL_ERROR;
/* Get the node ID from the lookup table using the index passed */
uint8 nodeId_u8 = nodeLookupTable_gast[arrIndex_u8].nodeId_u8;
/* Add node to heartbeat consumers with +25% tolerance
* Rationale - Allows some flexibility in detecting timeouts due to minor clock drifts,
* bus delays, or transmission issues.*/
retVal_en = coHbConsumerSet(nodeId_u8,
((SDL_LSS_NODE_HB_MS / 100u * 25u) + SDL_LSS_NODE_HB_MS));
retVal_en = coHbConsumerStart(nodeId_u8);
/* setup SDO channel */
retVal_en = SdlLssSetSdoCobID((arrIndex_u8 + 1u), nodeLookupTable_gast[arrIndex_u8].nodeId_u8);
/* start node */
retVal_en = coNmtStateReq(nodeId_u8, CO_NMT_STATE_OPERATIONAL, CO_FALSE);
if (retVal_en != RET_OK)
{
error_u32 = SDL_LSS_NODE_CONFIG_ERROR;
}
/* set local state to operational, if not operational yet */
if (masterStarted_gb == CO_FALSE)
{
/* set local state */
coNmtLocalStateReq(CO_NMT_STATE_OPERATIONAL);
/* save started flag */
masterStarted_gb = CO_TRUE;
}
return error_u32;
}
/**
* @brief This function configures a SDO client for the
* given node ID and SDO number.
*
* @param sdoNr_u8 Sdo number
* nodeId_u8 Node id of the node.
*
* @return Error state.
*
*/
static RET_T SdlLssSetSdoCobID(uint8 sdoNr_u8, uint8 nodeId_u8)
{
uint16 cobIndex_u16;
uint32 newCobId_u32;
RET_T retVal_en = RET_INTERNAL_ERROR;
/* get od index of sdoNr */
cobIndex_u16 = SDL_SDO_CLIENT_COB + sdoNr_u8 - 1u; /* SDO starts from 1280 index */
/* set cobID for client to server direction (request) */
newCobId_u32 = SDL_SDO_CLIENT_TO_SERVER_COB + nodeId_u8;
retVal_en = coOdSetCobid(cobIndex_u16, 1u, newCobId_u32);
if (retVal_en == RET_OK)
{
/* set cobID for server to client direction (response) */
newCobId_u32 = SDL_SDO_SERVER_TO_CLIENT_COB + nodeId_u8;
retVal_en = coOdSetCobid(cobIndex_u16, 2u, newCobId_u32);
}
/* print failed setup details */
if (retVal_en != RET_OK)
{
/* ERROR */
}
return retVal_en;
}
/**
* @brief Checks if all nodes are in OPERATIONAL state.
*
* @return CO_TRUE if all nodes are OPERATIONAL, CO_FALSE otherwise.
*/
static bool SdlAreAllNodesOperational(void)
{
uint8 operationalNodeNb_u8 = 0u;
for (uint8 i_u8 = 0u; i_u8 < SDL_LSS_NODE_COUNT ; i_u8++)
{
if (nodeNMTState_gaen[i_u8] == CO_NMT_STATE_OPERATIONAL)
{
operationalNodeNb_u8++;
}
}
return (operationalNodeNb_u8 >= SDL_MIN_OPERATIONAL_NODES) ? CO_TRUE : CO_FALSE;
}
/******************************************************************************
* Callback Function Definitions
******************************************************************************/
/**
* @brief LSS indication function for handling the LSS api calls for dynamic
* setup of node ids.
*
* @param service_en LSS master services for indication functions
* errorCode_u16 Error code in the module
* errorSpec_u8 Specific error case that has occured
* pIdentity_pu32 LSS slave identity.
*
*/
static void SdlLssIndCallback(CO_LSS_MASTER_SERVICE_T service_en, uint16 errorCode_u16, uint8 errorSpec_u8, uint32 *pIdentity_pu32)
{
static uint8 matchedIndex_u8 = NMS_UINT8_MAX;
if (errorCode_u16 != 0u)
{
if (errorCode_u16 == NMS_UINT16_MAX)
{
/* ERROR */
}
else
{
/* ERROR */
}
if (service_en == CO_LSS_MASTER_SERVICE_STORE)
{
/* DEBUG INFO */
coLssSwitchGlobal(CO_LSS_STATE_WAITING);
}
return;
}
switch (service_en)
{
case CO_LSS_MASTER_SERVICE_NON_CONFIG_SLAVE:
/* DEBUG INFO */
coLssFastScan(SDL_LSS_TIMEOUT);
break;
case CO_LSS_MASTER_SERVICE_FASTSCAN:
/* Match detected node with lookup table */
for (uint8 i_u8 = 0u; i_u8 < SDL_LSS_NODE_COUNT; i_u8++)
{
if (pIdentity_pu32[0] == nodeLookupTable_gast[i_u8].vendorId_u32 &&
pIdentity_pu32[1] == nodeLookupTable_gast[i_u8].productId_u32 &&
pIdentity_pu32[2] == nodeLookupTable_gast[i_u8].versionNbr_u32 &&
pIdentity_pu32[3] == nodeLookupTable_gast[i_u8].serialNbr_u32)
{
coLssSwitchSelective(pIdentity_pu32[0], pIdentity_pu32[1],
pIdentity_pu32[2], pIdentity_pu32[3], 20);
matchedIndex_u8 = i_u8;
break;
}
}
break;
case CO_LSS_MASTER_SERVICE_SWITCH_SELECTIVE:
if (matchedIndex_u8 < SDL_LSS_NODE_COUNT)
{
coLssSetNodeId(nodeLookupTable_gast[matchedIndex_u8].nodeId_u8, SDL_LSS_TIMEOUT);
matchedIndex_u8 = NMS_UINT8_MAX;
}
else
{
/* ERROR */
}
break;
case CO_LSS_MASTER_SERVICE_SET_NODEID:
/* DEBUG INFO */
coLssInquireNodeId(SDL_LSS_TIMEOUT);
break;
case CO_LSS_MASTER_SERVICE_INQUIRE_NODEID:
/* DEBUG INFO */
coLssStoreConfig(200);
break;
case CO_LSS_MASTER_SERVICE_STORE:
/* DEBUG INFO */
coLssSwitchGlobal(CO_LSS_STATE_WAITING);
break;
default:
/* ERROR */
break;
}
}
/**
* @brief This function is called if a new NMT state of a node,
* is recordnized by the CANopen stack.
*
* @param nodeID_u8 Node id of the node that has registered a change.
* state_en Error control state
* nmtState_en NMT state of the particular node.
*
* @return void
*
*/
static void SdlLssHbMonitorCallback(uint8 nodeID_u8, CO_ERRCTRL_T state_en, CO_NMT_STATE_T nmtState_en)
{
uint8 arrIndex_u8;
/* look if node is monitored */
arrIndex_u8 = SdlLssGetNodeIndex(nodeID_u8);
/* handle monitored node */
if (arrIndex_u8 != NMS_UINT16_MAX)
{
/* save states */
nodeNMTState_gaen[arrIndex_u8] = nmtState_en;
/* indicate if monitored node lost heartbeat */
if (nmtState_en == CO_NMT_STATE_UNKNOWN)
{
/* To be transmitted via CAN */
/* ERROR */
}
/* indicate if monitored node sent a bootup message */
if (state_en == CO_ERRCTRL_BOOTUP)
{
/* INFO */
}
/* handle unmonitored node */
}
else
{
/* ERROR */
}
}
/**
* @brief This function tries to reset nodes with unknown NMT state.
*
* @param pData_pv Data.
*/
static void SdlLssResetNodesCallback(void *pData_pv)
{
uint8 arrIndex_u8;
(void)pData_pv;
/* reset defined nodes without known state */
for (arrIndex_u8 = 0u; arrIndex_u8 < SDL_LSS_NODE_COUNT; arrIndex_u8++)
{
if ((nodeNMTState_gaen[arrIndex_u8] != CO_NMT_STATE_PREOP) &&
(nodeNMTState_gaen[arrIndex_u8] != CO_NMT_STATE_OPERATIONAL))
{
/* reset node */
coNmtStateReq(nodeLookupTable_gast[arrIndex_u8].nodeId_u8, CO_NMT_STATE_RESET_NODE, CO_FALSE);
}
}
}