CANFestival-3 source code explanation I: important structure
There are several points to note:
1. Use the canfestival-3 source code downloaded from the official website. The downloaded compressed package file name is Mongo-canfestival-3-asc-1a25f5151a8d.zip. Use the object dictionary editor built into the source code to generate the object dictionary file;
2. It mainly analyzes the code related to DS 301 protocol in the source code. The reference document of DS 301 protocol is 301_v04000201.pdf;
3. Recommend a document called CANopen easy introduction, which introduces the basic knowledge of CANopen. It is produced by Guangzhou Zhiyuan electronics. It is very well written. A document CANopen high level protocol for CAN bus is also recommended.
4. This article assumes that the reader knows something about CANopen and does not involve the basic knowledge of CANopen.
1.CANOpen node structure
The core structure in canfestival is the node data structure, which contains all the data information needed by a CANOpen node. This structure is defined in the data.h file. The source code is as follows:
struct struct_CO_Data { /* Object dictionary */ UNS8 *bDeviceNodeId; const indextable *objdict; s_PDO_status *PDO_status; TIMER_HANDLE *RxPDO_EventTimers; void (*RxPDO_EventTimers_Handler)(CO_Data*, UNS32); const quick_index *firstIndex; const quick_index *lastIndex; const UNS16 *ObjdictSize; const UNS8 *iam_a_slave; valueRangeTest_t valueRangeTest; /* SDO */ s_transfer transfers[SDO_MAX_SIMULTANEOUS_TRANSFERS]; /* s_sdo_parameter *sdo_parameters; */ /* State machine */ e_nodeState nodeState; s_state_communication CurrentCommunicationState; initialisation_t initialisation; preOperational_t preOperational; operational_t operational; stopped_t stopped; void (*NMT_Slave_Node_Reset_Callback)(CO_Data*); void (*NMT_Slave_Communications_Reset_Callback)(CO_Data*); /* NMT-heartbeat */ UNS8 *ConsumerHeartbeatCount; UNS32 *ConsumerHeartbeatEntries; TIMER_HANDLE *ConsumerHeartBeatTimers; UNS16 *ProducerHeartBeatTime; TIMER_HANDLE ProducerHeartBeatTimer; heartbeatError_t heartbeatError; e_nodeState NMTable[NMT_MAX_NODE_ID]; /* NMT-nodeguarding */ TIMER_HANDLE GuardTimeTimer; TIMER_HANDLE LifeTimeTimer; nodeguardError_t nodeguardError; UNS16 *GuardTime; UNS8 *LifeTimeFactor; UNS8 nodeGuardStatus[NMT_MAX_NODE_ID]; /* SYNC */ TIMER_HANDLE syncTimer; UNS32 *COB_ID_Sync; UNS32 *Sync_Cycle_Period; /*UNS32 *Sync_window_length;;*/ post_sync_t post_sync; post_TPDO_t post_TPDO; post_SlaveBootup_t post_SlaveBootup; post_SlaveStateChange_t post_SlaveStateChange; /* General */ UNS8 toggle; CAN_PORT canHandle; scanIndexOD_t scanIndexOD; storeODSubIndex_t storeODSubIndex; /* DCF concise */ const indextable* dcf_odentry; UNS8* dcf_cursor; UNS32 dcf_entries_count; UNS8 dcf_status; UNS32 dcf_size; UNS8* dcf_data; /* EMCY */ e_errorState error_state; UNS8 error_history_size; UNS8* error_number; UNS32* error_first_element; UNS8* error_register; UNS32* error_cobid; s_errors error_data[EMCY_MAX_ERRORS]; post_emcy_t post_emcy; #ifdef CO_ENABLE_LSS /* LSS */ lss_transfer_t lss_transfer; lss_StoreConfiguration_t lss_StoreConfiguration; #endif }; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
Let's take a look at the contents of this structure.
1.1 Object dictionary
1.1.1 Node-ID
UNS8 *bDeviceNodeId;
Each node has a unique ID called node ID. this variable is used to record node ID.
According to the source code, 301 official documents and CANopen high level protocol for CAN bus, the node ID occupies 7 bits, and the value range is: [1, 127].
The source code for judging whether the node ID is valid is as follows. You can see that the node ID range is [1, 127].
if(!(nodeId>0 && nodeId<=127)){ MSG_WAR(0x2D01, "Invalid NodeID",nodeId); return; } 1234
Some online tutorial documents write [0, 127], and even some write a maximum of 128, which is wrong. It should be noted that 0 cannot be used as a node ID.
1.1.2 Index table
const indextable *objdict;
This variable is used to store the index of each object. According to the index, we can find the real location where the object data is stored. It is a structure array type. The source code of this structure is as follows:
struct td_indextable { subindex* pSubindex; /* Pointer to the subindex */ UNS8 bSubCount; /* the count of valid entries for this subindex * This count here defines how many memory has been * allocated. this memory does not have to be used. */ UNS16 index; }; 123456789
among
subindex* pSubindex; Save information about sub indexes. In CANopen, each object has 16 bit indexes and 8-bit sub indexes.
UNS8 bSubCount; Represents the number of sub indexes. An 8-bit sub index can represent up to 255 sub indexes.
UNS16 index; Represents an index.
The length of the array depends on the number of variables defined. For example, in the source code of the object dictionary generated by the object dictionary editor, the source code of the initialization of the array is as follows:
/**************************************************************************/ /* Declaration of pointed variables */ /**************************************************************************/ const indextable TestMaster_objdict[] = { { (subindex*)TestMaster_Index1000,sizeof(TestMaster_Index1000)/sizeof(TestMaster_Index1000[0]), 0x1000}, { (subindex*)TestMaster_Index1001,sizeof(TestMaster_Index1001)/sizeof(TestMaster_Index1001[0]), 0x1001}, { (subindex*)TestMaster_Index1017,sizeof(TestMaster_Index1017)/sizeof(TestMaster_Index1017[0]), 0x1017}, { (subindex*)TestMaster_Index1018,sizeof(TestMaster_Index1018)/sizeof(TestMaster_Index1018[0]), 0x1018}, { (subindex*)TestMaster_Index1200,sizeof(TestMaster_Index1200)/sizeof(TestMaster_Index1200[0]), 0x1200}, { (subindex*)TestMaster_Index1201,sizeof(TestMaster_Index1201)/sizeof(TestMaster_Index1201[0]), 0x1201}, { (subindex*)TestMaster_Index1280,sizeof(TestMaster_Index1280)/sizeof(TestMaster_Index1280[0]), 0x1280}, { (subindex*)TestMaster_Index1281,sizeof(TestMaster_Index1281)/sizeof(TestMaster_Index1281[0]), 0x1281}, { (subindex*)TestMaster_Index1400,sizeof(TestMaster_Index1400)/sizeof(TestMaster_Index1400[0]), 0x1400}, { (subindex*)TestMaster_Index1401,sizeof(TestMaster_Index1401)/sizeof(TestMaster_Index1401[0]), 0x1401}, { (subindex*)TestMaster_Index1600,sizeof(TestMaster_Index1600)/sizeof(TestMaster_Index1600[0]), 0x1600}, { (subindex*)TestMaster_Index1601,sizeof(TestMaster_Index1601)/sizeof(TestMaster_Index1601[0]), 0x1601}, { (subindex*)TestMaster_Index1800,sizeof(TestMaster_Index1800)/sizeof(TestMaster_Index1800[0]), 0x1800}, { (subindex*)TestMaster_Index1801,sizeof(TestMaster_Index1801)/sizeof(TestMaster_Index1801[0]), 0x1801}, { (subindex*)TestMaster_Index1A00,sizeof(TestMaster_Index1A00)/sizeof(TestMaster_Index1A00[0]), 0x1A00}, { (subindex*)TestMaster_Index1A01,sizeof(TestMaster_Index1A01)/sizeof(TestMaster_Index1A01[0]), 0x1A01}, }; 1234567891011121314151617181920212223
There are 16 objects defined. The meaning of these objects will be discussed later.
The source code of subindex of the above subindex structure is as follows:
typedef struct td_subindex { UNS8 bAccessType; UNS8 bDataType; /* Defines of what datatype the entry is */ UNS32 size; /* The size (in Byte) of the variable */ void* pObject; /* This is the pointer of the Variable */ ODCallback_t callback; /* Callback function on write event */ } subindex; 12345678
among
UNS8 bAccessType; There are three types of permissions: RW, wo and ro.
UNS8 bDataType; Represents the data type of data stored in this object. The source code of the data type is as follows, which is completely consistent with the definition in 301 document:
/** this are static defined datatypes taken fCODE the canopen standard. They * are located at index 0x0001 to 0x001B. As described in the standard, they * are in the object dictionary for definition purpose only. a device does not * to support all of this datatypes. */ #define boolean 0x01 #define int8 0x02 #define int16 0x03 #define int32 0x04 #define uint8 0x05 #define uint16 0x06 #define uint32 0x07 #define real32 0x08 #define visible_string 0x09 #define octet_string 0x0A #define unicode_string 0x0B #define time_of_day 0x0C #define time_difference 0x0D #define domain 0x0F #define int24 0x10 #define real64 0x11 #define int40 0x12 #define int48 0x13 #define int56 0x14 #define int64 0x15 #define uint24 0x16 #define uint40 0x18 #define uint48 0x19 #define uint56 0x1A #define uint64 0x1B #define pdo_communication_parameter 0x20 #define pdo_mapping 0x21 #define sdo_parameter 0x22 #define identity 0x23 /* CanFestival is using 0x24 to 0xFF to define some types containing a value range (See how it works in objdict.c) */ 1234567891011121314151617181920212223242526272829303132333435363738394041
UNS32 size; Indicates the size of the data, that is, the number of bytes.
void* pObject; A pointer indicating that the object holds the data variable, that is, the location where the data is really stored.
ODCallback_t callback; The callback function when writing events. The function prototype of the callback function is as follows:
typedef UNS32 (*ODCallback_t)(CO_Data* d, const indextable *, UNS8 bSubindex); 1
1.1.3 PDO structure
s_PDO_status *PDO_status;
PDO_status is used to save the array of PDO communication parameters sent, and an s will be generated for each TPDO_ PDO_ For the status structure, even if TPDO is not defined, in order to avoid compilation errors, the length of this array is at least 1, which will be discussed later when the structure is initialized.
s_ PDO_ The source code of status is as follows:
/** The PDO structure */ struct struct_s_PDO_status { UNS8 transmit_type_parameter; TIMER_HANDLE event_timer; TIMER_HANDLE inhibit_timer; Message last_message; }; 1234567
It can be seen that this structure actually saves the communication parameters of PDO, only TPDO parameters, not RPDO parameters. In fact, the communication parameters of PDO are saved in the object index in 1.1.2. It is unclear why a structure should be saved separately in this place.
First, review the communication parameters of PDO
Both TPDO and RPDO have communication parameters and mapping parameters. There are six communication parameters as follows:
subindex | name | type |
---|---|---|
0x01 | COB ID | UNSIGNED32 |
0x02 | Transmission Type | UNSIGNED8 |
0x03 | Inhibit Time | UNSIGNED16 |
0x04 | Compatibility Entry | UNSIGNED8 |
0x05 | Event Time | UNSIGNED16 |
0x06 | SYNC start value | UNSIGNED8 |
Let's explain what these six parameters mean first.
1. There is no need to explain the COB ID too much. It is simply understood that the CAN node can know whether this frame message belongs to PDO, SDO, NMT, SYNC or others through the COB ID.
2.Transmission Type refers to the communication type of PDO, including synchronous, asynchronous, periodic, aperiodic, etc. see Table 3 of CANopen high level protocol for CAN bus for details. The value range is 0 ~ 255. The source code of value definition is as follows:
/** definitions of the different types of PDOs' transmission * * SYNCHRO(n) means that the PDO will be transmited every n SYNC signal. */ #define TRANS_EVERY_N_SYNC(n) (n) /*n = 1 to 240 */ #define TRANS_SYNC_ACYCLIC 0 /* Trans after reception of n SYNC. n = 1 to 240 */ #define TRANS_SYNC_MIN 1 /* Trans after reception of n SYNC. n = 1 to 240 */ #define TRANS_SYNC_MAX 240 /* Trans after reception of n SYNC. n = 1 to 240 */ #define TRANS_RTR_SYNC 252 /* Transmission on request */ #define TRANS_RTR 253 /* Transmission on request */ #define TRANS_EVENT_SPECIFIC 254 /* Transmission on event */ #define TRANS_EVENT_PROFILE 255 /* Transmission on event */ 123456789101112
3.Inhibit Time refers to the minimum time interval of PDO transmission to avoid too fast transmission frequency and too large bus load. The unit is 100us.
4. I don't know what the compatibility entry is. It's not mentioned in the document. Let's ignore it first.
5.Event Time: if the PDO is sent regularly, this parameter indicates the timing time. If this parameter is 0, it indicates that the PDO is triggered by the event, and the unit is ms.
6.SYNC start value synchronization start value. When the PDO is sent synchronously, such as Transmission Type=10, the PDO is sent after receiving 10 synchronization packets. If SYNC start value=2, the PDO is sent after receiving 2 synchronization packets at the beginning, and then it is sent according to 10 synchronization packets.
struct_ s_ PDO_ The status structure saves the 2, 3 and 5 parameters in the TPDO parameters separately. The purpose of this is unknown for the time being. Another parameter is Message last_message; Save the latest TPDO message. You also don't know the purpose of doing so.
1.1.4 RxPDO_EventTimers
TIMER_HANDLE *RxPDO_EventTimers;
void (*RxPDO_EventTimers_Handler)(CO_Data*, UNS32);
When these two are put together, they are literally related to the receiving PDO time timer.
1.1.5 First and Last Index
const quick_index *firstIndex;
const quick_index *lastIndex;
These two variables are mainly used to store the index values of SDO and PDO in the object dictionary described in 1.1.2, mainly to make it easier to use SDO and PDO objects later. Specifically, the structure quick_ The source code of index is as follows:
typedef struct s_quick_index{ UNS16 SDO_SVR; UNS16 SDO_CLT; UNS16 PDO_RCV; UNS16 PDO_RCV_MAP; UNS16 PDO_TRS; UNS16 PDO_TRS_MAP; }quick_index; 12345678
SDO_SVR represents the index of SDO server; SDO_CLT represents the index of SDO client; PDO_RCV represents the index of RPDO; PDO_RCV_MAP represents the index of RPDO mapping object; PDO_TRS represents the index of TPDO; PDO_TRS_MAP represents the index of the TPDO mapping object.
The values of these two variables are with testmaster in 1.1.2_ Objdict [] is automatically generated by the object dictionary editor. Take testmaster in 1.1.2_ For objdict [], the corresponding firstIndex and lastIndex are as follows:
const quick_index TestMaster_firstIndex = { 4, /* SDO_SVR */ 6, /* SDO_CLT */ 8, /* PDO_RCV */ 10, /* PDO_RCV_MAP */ 12, /* PDO_TRS */ 14 /* PDO_TRS_MAP */ }; const quick_index TestMaster_lastIndex = { 5, /* SDO_SVR */ 7, /* SDO_CLT */ 9, /* PDO_RCV */ 11, /* PDO_RCV_MAP */ 13, /* PDO_TRS */ 15 /* PDO_TRS_MAP */ }; 1234567891011121314151617
Indicates testmaster_ In objdict [], indexes 4-5 belong to SDO server objects; 6-7 belong to SDO client object; 8-9 belong to RPDO object; 10-11 belong to RPDO mapping object; 12-13 belong to TPDO object; 14-15 belong to TPDO mapping object.
1.1.6 ObjdictSize
const UNS16 *ObjdictSize;
Represents the number of objects in the object dictionary. The initialized value is also automatically generated by the object dictionary editor:
const UNS16 TestMaster_ObjdictSize = sizeof(TestMaster_objdict)/sizeof(TestMaster_objdict[0]); 1
1.1.7 Slave or Master
const UNS8 *iam_a_slave;
The master or slave flag is also initialized in the file automatically generated by the object dictionary editor.
const UNS8 TestMaster_iam_a_slave = 0; 1
0 represents the master and 1 represents the slave.
1.1.8 valueRangeTest
valueRangeTest_t valueRangeTest;
This is a function pointer that detects whether the value is out of range. This function is also initialized in the file automatically generated by the dictionary editor:
/**************************************************************************/ /* Declaration of value range types */ /**************************************************************************/ #define valueRange_EMC 0x9F /* Type for index 0x1003 subindex 0x00 (only set of value 0 is possible) */ UNS32 TestMaster_valueRangeTest (UNS8 typeValue, void * value) { switch (typeValue) { case valueRange_EMC: if (*(UNS8*)value != (UNS8)0) return OD_VALUE_RANGE_EXCEEDED; break; } return 0; } 1234567891011121314
As for what this function does, we'll talk about it later.
1.2 SDO
s_transfer transfers[SDO_MAX_SIMULTANEOUS_TRANSFERS];
This structure holds all the information about SDO. SDO_ MAX_ SIMULTANEOUS_ The macro definition of transfers represents the maximum number of SDOs transmitted simultaneously. It is defined in config.h, and the default value is 1. The source code of this structure is as follows:
/* The Transfer structure Used to store the different segments of - a SDO received before writing in the dictionary - the reading of the dictionary to put on a SDO to transmit WARNING : after a change in this structure check the macro s_transfer_Initializer in data.h */ struct struct_s_transfer { UNS8 CliServNbr; /**< The index of the SDO client / server in our OD minus 0x1280 / 0x1200 */ UNS8 whoami; /**< Takes the values SDO_CLIENT or SDO_SERVER */ UNS8 state; /**< state of the transmission : Takes the values SDO_... */ UNS8 toggle; UNS32 abortCode; /**< Sent or received */ /**< index and subindex of the dictionary where to store */ /**< (for a received SDO) or to read (for a transmit SDO) */ UNS16 index; UNS8 subIndex; UNS32 count; /**< Number of data received or to be sent. */ UNS32 offset; /**< stack pointer of data[] * Used only to tranfer part of a line to or from a SDO. * offset is always pointing on the next free cell of data[]. * WARNING s_transfer.data is subject to ENDIANISATION * (with respect to CANOPEN_BIG_ENDIAN) */ UNS8 data [SDO_MAX_LENGTH_TRANSFER]; #ifdef SDO_DYNAMIC_BUFFER_ALLOCATION UNS8 *dynamicData; UNS32 dynamicDataSize; #endif //SDO_DYNAMIC_BUFFER_ALLOCATION UNS8 peerCRCsupport; /**< True if peer supports CRC */ UNS8 blksize; /**< Number of segments per block with 0 < blksize < 128 */ UNS8 ackseq; /**< sequence number of last segment that was received successfully */ UNS32 objsize; /**< Size in bytes of the object provided by data producer */ UNS32 lastblockoffset; /**< Value of offset before last block */ UNS8 seqno; /**< Last sequence number received OK or transmitted */ UNS8 endfield; /**< nbr of bytes in last segment of last block that do not contain data */ rxStep_t rxstep; /**< data consumer receive step - set to true when last segment of a block received */ UNS8 tmpData[8]; /**< temporary segment storage */ UNS8 dataType; /**< Defined in objdictdef.h Value is visible_string * if it is a string, any other value if it is not a string, * like 0. In fact, it is used only if client. */ TIMER_HANDLE timer; /**< Time counter to implement a timeout in milliseconds. * It is automatically incremented whenever * the line state is in SDO_DOWNLOAD_IN_PROGRESS or * SDO_UPLOAD_IN_PROGRESS, and reseted to 0 * when the response SDO have been received. */ SDOCallback_t Callback; /**< The user callback func to be called at SDO transaction end */ }; typedef struct struct_s_transfer s_transfer; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
As can be seen from the annotation of this structure, this structure is mainly used to save the SDO received before writing to the object dictionary; Data read from the object dictionary before SDO is sent. I won't go into the details. I'll talk about it later.
1.3 State machine
/* State machine */ e_nodeState nodeState; s_state_communication CurrentCommunicationState; initialisation_t initialisation; preOperational_t preOperational; operational_t operational; stopped_t stopped; void (*NMT_Slave_Node_Reset_Callback)(CO_Data*); void (*NMT_Slave_Communications_Reset_Callback)(CO_Data*); 123456789
Save state machine related information. See the document CANopen high level protocol for CAN bus for the state machine defined by CANopen Node.
1.3.1 nodeState
nodeState indicates the node state. The source code is defined as follows:
/* The nodes states * ----------------- * values are choosen so, that they can be sent directly * for heartbeat messages... * Must be coded on 7 bits only * */ /* Should not be modified */ enum enum_nodeState { Initialisation = 0x00, Disconnected = 0x01, Connecting = 0x02, Preparing = 0x02, Stopped = 0x04, Operational = 0x05, Pre_operational = 0x7F, Unknown_state = 0x0F }; typedef enum enum_nodeState e_nodeState; 12345678910111213141516171819
The node status in CANopen high level protocol for CAN bus is described as follows:
Value | State |
---|---|
0 | Initialising |
1 | Disconnected * |
2 | Connecting * |
3 | Preparing * |
4 | Stopped |
5 | Operational |
127 | Pre-operational |
The status with * is only available on the extended boot up node; State 0 cannot appear in the Node Guard reply, because the node will not reply to the Node Guard message in the initializing state.
1.3.2 CurrentCommunicationState
Save the flag of whether to open the corresponding function. The source code of this structure:
typedef struct { INTEGER8 csBoot_Up; INTEGER8 csSDO; INTEGER8 csEmergency; INTEGER8 csSYNC; INTEGER8 csLifeGuard; INTEGER8 csPDO; INTEGER8 csLSS; } s_state_communication; 12345678910
1 means to turn on the corresponding function, and 0 means to turn off the corresponding function. For example, in the initialization state, s_state_communication newCommunicationState = {1, 0, 0, 0, 0, 0, 0}; Indicates that only boot is enabled_ Up function, other functions are turned off. In the pre operational state, s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 0, 1}; Indicates that SDO, Emergency, SYNC, LifeGuard and LSS functions are enabled.
1.3.3 status calling function
1.3.3.1 initialisation
Initialization function, which will be called when the node is in the initialization state. This function has an implementation version that does nothing in state.c:
void _initialisation(CO_Data* d){(void)d;} 1
Users can also reconstruct this function in the application and do some initialization in the user application.
1.3.3.2 preOperational
The function called by pre operational state also has an implementation in state.c:
void _preOperational(CO_Data* d){ if (!(*(d->iam_a_slave))) { masterSendNMTstateChange (d, 0, NMT_Reset_Node); } } 123456
The implementation of this function indicates that if the current node is master, then masterSendNMTstateChange (d, 0, NMT_Reset_Node) will be executed;, This function is a function for the master node to send NMT messages. The function is defined in nmtMaster.c as follows:
/*! ** ** ** @param d ** @param nodeId ** @param cs ** ** @return **/ UNS8 masterSendNMTstateChange(CO_Data* d, UNS8 nodeId, UNS8 cs) { Message m; MSG_WAR(0x3501, "Send_NMT cs : ", cs); MSG_WAR(0x3502, " to node : ", nodeId); /* message configuration */ m.cob_id = 0x0000; /*(NMT) << 7*/ m.rtr = NOT_A_REQUEST; m.len = 2; m.data[0] = cs; m.data[1] = nodeId; return canSend(d->canHandle,&m); } 123456789101112131415161718192021222324
Where, #define NMT_Reset_Node 0x81, nodeId=0x00 indicates control over all nodes, which indicates that when the master node enters pre operational, it will send a frame of such data to all nodes:
000 | 0 | 81 | nodeId |
---|---|---|---|
In another blog, it is said that this frame data 0x81 indicates that the node is placed in reset application mode.
1.3.3.3 operational
The function used to call the Operational state also has an implementation in state.c:
void _operational(CO_Data* d){(void)d;} 1
I didn't do anything.
1.3.3.4 stopped
The function called for Stopped status also has an implementation in state.c:
void _stopped(CO_Data* d){(void)d;} 1
1.3.4 callback function
void (*NMT_Slave_Node_Reset_Callback)(CO_Data*); void (*NMT_Slave_Communications_Reset_Callback)(CO_Data*); 12
These two callback functions are called respectively when the node is reset and the communication is reset.
1.4 NMT-heartbeat
Data structure related to node heartbeat.
/* NMT-heartbeat */ UNS8 *ConsumerHeartbeatCount; UNS32 *ConsumerHeartbeatEntries; TIMER_HANDLE *ConsumerHeartBeatTimers; UNS16 *ProducerHeartBeatTime; TIMER_HANDLE ProducerHeartBeatTimer; heartbeatError_t heartbeatError; e_nodeState NMTable[NMT_MAX_NODE_ID]; 12345678
The slave station can monitor the heartbeat from other creators (master station or other slave stations). The first three are variables related to the consumer monitoring the creator's heartbeat, which are set in the variable dictionary 0x1016.
UNS16 *ProducerHeartBeatTime; Indicates the time interval of heartbeat generation, which is set in 0x1017. For example, if you want to generate a heartbeat every second, set 0x3E8 in the sub index of 0x1017 to generate such a code:
/* index 0x1017 : Producer Heartbeat Time. */ UNS16 TestMaster_obj1017 = 0x3E8; /* 1000 */ subindex TestMaster_Index1017[] = { { RW, uint16, sizeof (UNS16), (void*)&TestMaster_obj1017, NULL } }; 1234567
TIMER_HANDLE ProducerHeartBeatTimer; It is the producer's heartbeat clock. When the structure is initialized, it is a null value #define TIMER_NONE -1, which will be assigned in the initialization function.
heartbeatError_t heartbeatError; Is the error handling function pointer, which is initialized to an empty function:
void _heartbeatError(CO_Data* d, UNS8 heartbeatID){(void)d;(void)heartbeatID;} 1
e_nodeState NMTable[NMT_MAX_NODE_ID]; Is an array of all node states, holding the maximum #define NMT_MAX_NODE_ID 128128 node status, initialized as Unknown_state = 0x0F.
struct_ CO_ The other parts of the data structure, NMT nodeguarding, sync, general, DCF, reason, emcy, will be discussed in the next article.