📄 mco.c
字号:
gMCOConfig.Node_ID = Node_ID;
gMCOConfig.error_code = 0;
gMCOConfig.Baudrate = Baudrate;
gMCOConfig.heartbeat_time = Heartbeat;
gMCOConfig.heartbeat_msg.ID = 0x700+Node_ID;
gMCOConfig.heartbeat_msg.LEN = 1;
gMCOConfig.heartbeat_msg.BUF[0] = 0; // Current NMT state of this node = bootup
gMCOConfig.error_register = 0;
// Init SDO Response/Abort message
gTxSDO.ID = 0x580+gMCOConfig.Node_ID;
gTxSDO.LEN = 8;
#if NR_OF_TPDOS > 0
i = 0;
while (i < NR_OF_TPDOS)
{ // Init TPDOs
gTPDOConfig[i].CAN.ID = 0;
i++;
}
#endif
#if NR_OF_RPDOS > 0
i = 0;
while (i < NR_OF_RPDOS)
{ // Init RPDOs
gRPDOConfig[i].CANID = 0;
i++;
}
#endif
// Init the CAN interface
if (!MCOHW_Init(Baudrate))
{
MCOUSER_FatalError(0x8802);
}
if (!MCOHW_SetCANFilter(0)) // For NMT master message
{
MCOUSER_FatalError(0x8803);
}
if (!MCOHW_SetCANFilter(0x600+Node_ID)) // For SDO Requests
{
MCOUSER_FatalError(0x8803);
}
// Signal to MCO_ProcessStack: we just initialized
gTPDONr = 0xFF;
}
#if NR_OF_RPDOS > 0
/**************************************************************************
DOES: This function initializes a receive PDO. Once initialized, the
MicroCANopen stack automatically updates the data at offset.
NOTE: For data consistency, the application should not read the data
while function MCO_ProcessStack executes.
**************************************************************************/
void MCO_InitRPDO
(
BYTE PDO_NR, // RPDO number (1-4)
WORD CAN_ID, // CAN identifier to be used (set to 0 to use default)
BYTE len, // Number of data bytes in RPDO
BYTE offset // Offset to data location in process image
)
{
#ifdef CHECK_PARAMETERS
if ( ((PDO_NR < 1) || (PDO_NR > NR_OF_RPDOS)) || // Check PDO range
((gMCOConfig.Node_ID < 1) || (gMCOConfig.Node_ID > 127)) )
// Check Node ID range 1-127
{
MCOUSER_FatalError(0x8804);
}
if (offset >= PROCIMG_SIZE)
{ // Size of process image exceeded
MCOUSER_FatalError(0x8904);
}
#endif
PDO_NR--;
gRPDOConfig[PDO_NR].len = len;
gRPDOConfig[PDO_NR].offset = offset;
if (CAN_ID == 0)
{
gRPDOConfig[PDO_NR].CANID = 0x200 + (0x100 * ((WORD)(PDO_NR))) + gMCOConfig.Node_ID;
}
else
{
gRPDOConfig[PDO_NR].CANID = CAN_ID;
}
if (!MCOHW_SetCANFilter(gRPDOConfig[PDO_NR].CANID))
{
MCOUSER_FatalError(0x8805);
}
}
#endif // NR_OF_RPDOS > 0
#if NR_OF_TPDOS > 0
/**************************************************************************
DOES: This function initializes a transmit PDO. Once initialized, the
MicroCANopen stack automatically handles transmitting the PDO.
The application can directly change the data at any time.
NOTE: For data consistency, the application should not write to the data
while function MCO_ProcessStack executes.
**************************************************************************/
void MCO_InitTPDO
(
BYTE PDO_NR, // TPDO number (1-4)
WORD CAN_ID, // CAN identifier to be used (set to 0 to use default)
WORD event_time, // Transmitted every event_tim ms
WORD inhibit_time, // Inhibit time in ms for change-of-state transmit
// (set to 0 if ONLY event_tim should be used)
BYTE len, // Number of data bytes in TPDO
BYTE offset // Offset to data location in process image
)
{
#ifdef CHECK_PARAMETERS
if ( ((PDO_NR < 1) || (PDO_NR > NR_OF_TPDOS)) || // Check PDO range
((gMCOConfig.Node_ID < 1) || (gMCOConfig.Node_ID > 127)) || // Check Node ID
((len < 1) || (len > 8)) || // Check len range 1-8
((event_time == 0) && (inhibit_time == 0)) ) // One of these needs to be set
{
MCOUSER_FatalError(0x8806);
}
if (offset >= PROCIMG_SIZE)
{ // Size of process image exceeded
MCOUSER_FatalError(0x8906);
}
#endif
PDO_NR--;
if (CAN_ID == 0)
{
gTPDOConfig[PDO_NR].CAN.ID = 0x180 + (0x100 * ((WORD)(PDO_NR))) + gMCOConfig.Node_ID;
}
else
{
gTPDOConfig[PDO_NR].CAN.ID = CAN_ID;
}
gTPDOConfig[PDO_NR].CAN.LEN = len;
gTPDOConfig[PDO_NR].offset = offset;
gTPDOConfig[PDO_NR].event_time = event_time;
}
#endif // NR_OF_TPDOS > 0
/**************************************************************************
DOES: This function implements the main MicroCANopen protocol stack.
It must be called frequently to ensure proper operation of the
communication stack.
Typically it is called from the while(1) loop in main.
**************************************************************************/
BYTE MCO_ProcessStack
( // Returns 0 if nothing was done
// Returns 1 if a CAN message was received or sent
void
)
{
BYTE i;
BYTE x;
BYTE ret_val = 0;
// Check if this is right after boot-up
if (gTPDONr == 0xFF) // Was set by MCO_Init
{
// Init heartbeat time
gMCOConfig.heartbeat_timestamp = MCOHW_GetTime() + gMCOConfig.heartbeat_time;
// Send Boot-up message
if (!MCOHW_PushMessage(&gMCOConfig.heartbeat_msg))
{
MCOUSER_FatalError(0x8801);
}
gMCOConfig.heartbeat_msg.BUF[0] = 0x05; // going into operational state
#if NR_OF_TPDOS > 0
MCO_Prepare_TPDOs();
#endif
gTPDONr = NR_OF_TPDOS; // Retun value to default
return 1;
}
// Work on next incoming messages
if (MCOHW_PullMessage(&gRxCAN))
{ // Message received
gRxCAN.ID++;
MCOHW_PushMessage(&gRxCAN);
gRxCAN.ID--;
// Check if it is NMT master message
if (gRxCAN.ID == 0)
{ // NMT Master Message
if ((gRxCAN.BUF[1] == gMCOConfig.Node_ID) || (gRxCAN.BUF[1] == 0))
{ // NMT message is for this node or all nodes
switch (gRxCAN.BUF[0])
{
case 1: // Start node
gMCOConfig.heartbeat_msg.BUF[0] = 5;
#if NR_OF_TPDOS > 0
MCO_Prepare_TPDOs();
#endif
break;
case 2: // Stop node
gMCOConfig.heartbeat_msg.BUF[0] = 4;
break;
case 128:
gMCOConfig.heartbeat_msg.BUF[0] = 127;
break;
case 129:
// Node Reset
MCOUSER_ResetApplication();
break;
case 130:
// Reset Communication
MCOUSER_ResetCommunication();
break;
default:
break;
} // switch
return 1;
} // NMT message addressed to this node
} // NMT master message received
// Check if it is SDO request message
if (gMCOConfig.heartbeat_msg.BUF[0] != 4)
{ // Node is NOT stopped
if (gRxCAN.ID == gMCOConfig.Node_ID+0x600)
{ // SDO Request
i = MCO_Handle_SDO_Request(&gRxCAN.BUF[0]); // Return value not used in this version
return 1;
}
}
#if NR_OF_RPDOS > 0
// Check if it is RPDO message
if (gMCOConfig.heartbeat_msg.BUF[0] == 5)
{ // Node is in operational
i = 0;
while (i < NR_OF_RPDOS)
{
if (gRxCAN.ID == gRPDOConfig[i].CANID)
{ // RPDO match
for (x=0;x<gRPDOConfig[i].len;x++)
{
gProcImg[gRPDOConfig[i].offset+x] = gRxCAN.BUF[x];
}
i = NR_OF_RPDOS;
ret_val = 1;
}
i++;
} // for all RPDOs
} // Node is operational
#endif // NR_OF_RPDOS > 0
} // Message received
#if NR_OF_TPDOS > 0
// Check Next TPDO for transmit
if (gMCOConfig.heartbeat_msg.BUF[0] == 0x05)
{ // Node is in operational
gTPDONr++;
if (gTPDONr >= NR_OF_TPDOS)
{
gTPDONr = 0;
}
if (gTPDOConfig[gTPDONr].CAN.ID != 0)
{ // TPDO 'gTPDONr' is used
if ((gTPDOConfig[gTPDONr].event_time != 0) &&
(MCOHW_IsTimeExpired(gTPDOConfig[gTPDONr].event_timestamp)) )
{ // TPDO 'i' uses event timer and event timer is expired
// Get application data
for (x=0;x<gTPDOConfig[gTPDONr].CAN.LEN;x++)
{
gTPDOConfig[gTPDONr].CAN.BUF[x] = gProcImg[gTPDOConfig[gTPDONr].offset+x];
}
MCO_TransmitPDO(gTPDONr);
return 1;
}
} // PDO active (CAN_ID != 0)
} // if node is operational
#endif // NR_OF_TPDOS > 0
// Produce Heartbeat
if (gMCOConfig.heartbeat_time != 0)
{
if (MCOHW_IsTimeExpired(gMCOConfig.heartbeat_timestamp))
{
if (!MCOHW_PushMessage(&gMCOConfig.heartbeat_msg))
{
MCOUSER_FatalError(0x8801);
}
gMCOConfig.heartbeat_timestamp = MCOHW_GetTime() + gMCOConfig.heartbeat_time;
ret_val = 1;
}
}
return ret_val;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -