library for C++ CANOpen implementation. mbed independant, but is easy to attach into with mbed.
Dependents: ppCANOpen_Example DISCO-F746NG_rtos_test
Example:
Import programppCANOpen_Example
I am no longer actively working on the ppCANOpen library, however, I want to publish this project so that anyone who wants to pick up any of the pieces can have a good example. This is a a project I was working on using the ppCANOpen library. It has a pretty in deep use of the object dictionary structure. And a number of functions to control high voltage pinball drivers, if you're into that sort of thing.
source/Node.cpp
- Committer:
- ptpaterson
- Date:
- 2016-02-13
- Revision:
- 5:22a337cdc0e3
- Parent:
- 4:2034b04c86d2
File content as of revision 5:22a337cdc0e3:
/**
******************************************************************************
* @file
* @author Paul Paterson
* @version
* @date 2015-12-14
* @brief CANOpen implementation library
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT(c) 2015 Paul Paterson
*
* All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Node.h"
#include "ServiceProvider.h"
#include "ObjectDictionary.h"
#include "CanOpenMessage.h"
#include <stdio.h>
namespace ppCANOpen
{
Node::Node (int id, ServiceProvider * pProvider, int bLoop)
{
nodeId = id;
state.nmtState = State::INITIALIZED;
bLoopbackOn = bLoop;
pMyProvider = pProvider;
pProvider->AddNode(this);
}
/*=============================================================================
* Methods to handle message indication and confirmation
*=============================================================================
*/
int Node::DispatchMessage(CanOpenMessage *canOpenMsg)
{
int command = MESSAGE_GET_COMMAND(canOpenMsg->id);
switch (command) {
case CANOPEN_FUNCTION_CODE_NMT:
printf(" NMT Control: \r\n");
HandleNodeControl(canOpenMsg);
break;
case CANOPEN_FUNCTION_CODE_PDO1T:
case CANOPEN_FUNCTION_CODE_PDO1R:
case CANOPEN_FUNCTION_CODE_PDO2T:
case CANOPEN_FUNCTION_CODE_PDO2R:
case CANOPEN_FUNCTION_CODE_PDO3T:
case CANOPEN_FUNCTION_CODE_PDO3R:
case CANOPEN_FUNCTION_CODE_PDO4T:
case CANOPEN_FUNCTION_CODE_PDO4R:
if (state.bPDO) {
HandlePdo(canOpenMsg);
}
break;
case CANOPEN_FUNCTION_CODE_SDOT:
case CANOPEN_FUNCTION_CODE_SDOR:
if (state.bSDO) {
HandleSdo(canOpenMsg);
}
break;
default:
printf(" some random message\r\n");
break;
}
return 1;
}
/*=============================================================================
* Methods to handle message indication and confirmation
*=============================================================================
*/
static void CopyBits(uint8_t *sourceData,
uint8_t *destData,
uint8_t mappedBits,
uint8_t &sourceBitNum,
uint8_t &destBitNum);
int Node::HandlePdo (CanOpenMessage *canOpenMsg)
{
printf(" RPDO:\r\n");
int result = 0;
if (CANOPEN_TYPE_REMOTE == canOpenMsg->type) {
} else {
int pdoNum = 0;
int bSearching = 1;
ObjectData * parameterObject;
/* search through PDO parameters until we find a match */
while (bSearching) {
//printf(" PDO %d?", pdoNum);
parameterObject = ScanIndex (0x1400 + pdoNum);
if (parameterObject) {
uint32_t *cobId = (uint32_t*) parameterObject->entries[1].pData;
if (canOpenMsg->id == *cobId) {
bSearching = 0;
//printf(" ... match!!!\r\n");
} else {
pdoNum++;
//printf(" ... nope\r\n");
}
} else {
bSearching = 0;
printf("\r\n ERROR: No PDO Parameter match found\r\n");
}
}
/* if a matching parameter object was found then get mapping */
if (parameterObject) {
ObjectData *mappingObject = ScanIndex (0x1600 + pdoNum);
if (mappingObject) {
SubIndexSize *numMappedVariables = (SubIndexSize*) mappingObject->entries[0].pData;
/* variable to track position in data copy */
uint8_t sourceBitNum = 0;
int bError = 0;
/* for each mapped variable, write in data from message */
for (SubIndexSize subIndexIterator = 1; subIndexIterator <= *numMappedVariables && !bError; subIndexIterator++) {
uint32_t *map = (uint32_t*)mappingObject->entries[subIndexIterator].pData;
uint8_t mappedBits = (uint8_t) (*map);
uint8_t mappedBytes = mappedBits / 8;
SubIndexSize mappedSubIndex = (SubIndexSize) (*map >> 8);
IndexSize mappedIndex = (IndexSize) (*map >> 16);
printf(" mapped: %#06x, %#04x, %d\r\n",
mappedIndex, mappedSubIndex, mappedBits);
/* if less than 0x1000 then it is a dummy value */
if (mappedIndex < 0x1000) {
sourceBitNum += mappedBits;
} else {
/* get the index object */
ObjectData *mappedObject = ScanIndex (mappedIndex);
if (mappedObject) {
/* get the subindex object */
if (mappedSubIndex <= *(SubIndexSize*) mappedObject->entries[0].pData) {
EntryData *destinationEntry = &mappedObject->entries[mappedSubIndex];
/* can we write to it? */
if (!(destinationEntry->properties & EntryData::PROPERTY_WRITEABLE)) {
printf(" ERROR: Mapped SubIndex is not writeable!\r\n");
bError = 1;
}
uint8_t sourceByteNum = sourceBitNum / 8;
if (sourceByteNum + mappedBytes > canOpenMsg->dataCount) {
printf(" ERROR: Insufficient mapped data remaining!\r\n");
bError = 1;
}
if (destinationEntry->size < mappedBytes) {
printf(" ERROR: Too much data to pack into destination!\r\n");
bError = 1;
}
if (!bError) {
//printf(" No Errors, copying data...\r\n");
uint8_t* destData = (uint8_t*) destinationEntry->pData;
uint8_t* sourceData = canOpenMsg->data;
uint8_t destBitNum = 0;
CopyBits(sourceData, destData, mappedBits, sourceBitNum, destBitNum);
}
} else {
printf(" ERROR: Mapped SubIndex does not exist!\r\n");
bError = 1;
}
} else {
printf(" ERROR: Mapped Index does not exist!\r\n");
bError = 1;
}
} /* if mappedIndex < 0x1000 */
} /* for each mapping */
if (!bError) {
result = 1;
}
} else {
printf(" ERROR: No PDO Mapping match found\r\n");
}
} /* if parameter exists */
} /* if remote message */
return result;
}
int Node::HandleSdo (CanOpenMessage *canOpenMsg)
{
printf(" SDO:\r\n");
int result = 0;
int bSearching = 1;
int sdoNum = 0;
int sdoType = 0; /* 0=unknown, 1=receive/server, 2=transmit/client */
ObjectData * sdoObject;
/* search through PDO parameters until we find a match */
while (bSearching) {
sdoObject = ScanIndex (0x1200 + sdoNum);
if (sdoObject) {
uint32_t *receiveCobId = (uint32_t*) sdoObject->entries[1].pData;
uint32_t *transmitCobId = (uint32_t*) sdoObject->entries[2].pData;
if (canOpenMsg->id == *receiveCobId) {
bSearching = 0;
sdoType = 1;
printf(" Receive SDO\r\n");
} else if (canOpenMsg->id == *transmitCobId) {
bSearching = 0;
sdoType = 2;
printf(" Transmit SDO\r\n");
}
} else {
printf(" ERROR: No SDO parameters found");
bSearching = 0;
}
sdoNum++;
}
return result;
}
int Node::ConsumeEmergency (void)
{
return 0;
}
int Node::HandleNodeControl (CanOpenMessage *canOpenMsg)
{
int result = 0;
if (canOpenMsg->data[0] == nodeId) {
int commandSpecifier = (int)canOpenMsg->data[1];
switch (commandSpecifier) {
case NMT_CS_START:
printf(" NMT_CS_START\r\n");
if ((State::INITIALIZED == state.nmtState) ||
(State::PREOPERATIONAL == state.nmtState) ||
(State::STOPPED == state.nmtState))
{
state.nmtState = State::OPERATIONAL;
state.bBoot = 0;
state.bSDO = 1;
state.bEmergency = 1;
state.bSYNC = 1;
state.bLifeGuard = 1;
state.bPDO = 1;
state.bLSS = 0;
OnOperational();
result = 1;
}
break;
case NMT_CS_STOP:
if (State::INITIALIZED != state.nmtState) {
state.nmtState = State::STOPPED;
state.bBoot = 0;
state.bSDO = 0;
state.bEmergency = 0;
state.bSYNC = 0;
state.bLifeGuard = 1;
state.bPDO = 0;
state.bLSS = 1;
}
OnStopped();
result = 1;
break;
case NMT_CS_ENTER_PREOP:
state.nmtState = State::PREOPERATIONAL;
state.bBoot = 0;
state.bSDO = 1;
state.bEmergency = 1;
state.bSYNC = 1;
state.bLifeGuard = 1;
state.bPDO = 0;
state.bLSS = 1;
OnPreoperational();
result = 1;
break;
case NMT_CS_RESET_NODE:
case NMT_CS_RESET_COM:
printf(" NMT_CS_RESET\r\n");
state.nmtState = State::INITIALIZED;
state.bBoot = 1;
state.bSDO = 0;
state.bEmergency = 0;
state.bSYNC = 0;
state.bLifeGuard = 0;
state.bPDO = 0;
state.bLSS = 0;
state.bLifeGuardToggle = 0;
/* boot message is actually just the first node guard/ heart beat message */
// TODO: wrap up into heartbeat/lifeguard message
CanOpenMessage msgBoot;
msgBoot.id = CANOPEN_FUNCTION_CODE_NODE_GUARD | 5;
msgBoot.format = CANOPEN_FORMAT_STANDARD;
msgBoot.type = CANOPEN_TYPE_DATA;
msgBoot.dataCount = 1;
msgBoot.data[0] = 0;
pMyProvider->PostMessage(nodeId, &msgBoot);
OnInitialize();
state.nmtState = State::PREOPERATIONAL;
state.bBoot = 0;
state.bSDO = 1;
state.bEmergency = 1;
state.bSYNC = 1;
state.bLifeGuard = 1;
state.bPDO = 0;
state.bLSS = 1;
OnPreoperational();
result = 1;
break;
default:
break;
}
}
return result;
}
int Node::HandleNodeGuardRequest (const int masterId)
{
return 0;
}
int Node::ConsumeHeartbeat (const int producerId)
{
return 0;
}
/*=============================================================================
* Methods to handle operation of node device
*=============================================================================
*/
void Node::FixedUpdate (uint32_t time)
{
timeSinceLastTick = time - timeCurrentTick;
timeCurrentTick = time;
if (State::OPERATIONAL == state.nmtState) {
OnFixedUpdate();
}
}
void Node::Update (void)
{
if (State::OPERATIONAL == state.nmtState) {
OnUpdate();
}
}
/*=============================================================================
* Other Member Functions
*=============================================================================
*/
int Node::PostTPDO (int specifiedCobId)
{
printf(" TPDO:\r\n");
int result = 0;
/* initialize a blank message -------------------------------------------*/
CanOpenMessage msg;
msg.id = specifiedCobId;
msg.dataCount = 0;
msg.type = CANOPEN_TYPE_DATA;
msg.format = CANOPEN_FORMAT_STANDARD;
msg.data[0] = 0;
msg.data[1] = 0;
msg.data[2] = 0;
msg.data[3] = 0;
msg.data[4] = 0;
msg.data[5] = 0;
msg.data[6] = 0;
msg.data[7] = 0;
int pdoNum = 0;
int bSearching = 1;
/* Find the mapped data and send ----------------------------------------*/
ObjectData * parameterObject;
/* search through PDO parameters until we find a match */
while (bSearching) {
//printf(" PDO %d?", pdoNum);
parameterObject = ScanIndex (0x1800 + pdoNum);
if (parameterObject) {
uint32_t *cobId = (uint32_t*) parameterObject->entries[1].pData;
if (specifiedCobId == *cobId) {
bSearching = 0;
//printf(" ... match!!!\r\n");
} else {
pdoNum++;
//printf(" ... nope\r\n");
}
} else {
bSearching = 0;
printf("\r\n ERROR: No PDO Parameter match found\r\n");
}
}
/* if a matching parameter object was found then get mapping */
if (parameterObject) {
ObjectData *mappingObject = ScanIndex (0x1A00 + pdoNum);
if (mappingObject) {
SubIndexSize *numMappedVariables = (SubIndexSize*) mappingObject->entries[0].pData;
//printf(" numMappedVariables: %d\r\n", *numMappedVariables);
/* variable to track position in data copy */
uint8_t destBitNum = 0;
int bError = 0;
/* for each mapped variable, write in data from message */
for (SubIndexSize subIndexIterator = 1; subIndexIterator <= *numMappedVariables && !bError; subIndexIterator++) {
uint32_t *map = (uint32_t*)mappingObject->entries[subIndexIterator].pData;
uint8_t mappedBits = (uint8_t) (*map);
uint8_t mappedBytes = mappedBits / 8;
SubIndexSize mappedSubIndex = (SubIndexSize) (*map >> 8);
IndexSize mappedIndex = (IndexSize) (*map >> 16);
msg.dataCount += mappedBytes;
//printf(" mapped: %#06x, %#04x, %d\r\n",
// mappedIndex, mappedSubIndex, mappedBits);
/* if less than 0x1000 then it is a dummy value */
if (mappedIndex < 0x1000) {
destBitNum += mappedBits;
//printf(" No Errors, skipping VOID data...\r\n");
} else {
/* push into the TPDO data */
ObjectData *mappedObject = ScanIndex (mappedIndex);
if (mappedObject) {
/* get the subindex object */
if (mappedSubIndex <= *(SubIndexSize*) mappedObject->entries[0].pData) {
EntryData *sourceEntry = &mappedObject->entries[mappedSubIndex];
/* can we write to it? */
if (!(sourceEntry->properties & EntryData::PROPERTY_READABLE)) {
printf(" ERROR: Mapped SubIndex is not readable!\r\n");
bError = 1;
}
uint8_t destByteNum = destBitNum / 8;
if (destByteNum + mappedBytes > 8) {
printf(" ERROR: Too much data to pack into destination!\r\n");
bError = 1;
}
if (sourceEntry->size < mappedBytes) {
printf(" ERROR: trying to grab too much information!\r\n");
bError = 1;
}
if (!bError) {
//printf(" No Errors, copying data...\r\n");
uint8_t *destData = msg.data;
uint8_t *sourceData = (uint8_t*)sourceEntry->pData;
uint8_t sourceBitNum = 0;
CopyBits(sourceData, destData, mappedBits, sourceBitNum, destBitNum);
}
} else {
printf(" ERROR: Mapped SubIndex does not exist!\r\n");
bError = 1;
}
} else {
printf(" ERROR: Mapped Index does not exist!\r\n");
bError = 1;
}
} /* if mappedIndex < 0x1000 */
} /* for each mapping */
if (!bError) {
result = 1;
/* Send the message we built up */
pMyProvider->PostMessage(nodeId, &msg);
}
} else {
printf(" ERROR: No PDO Mapping match found\r\n");
} /* if mappingObject exists */
} /* if parameter exists */
return result;
}
/*=============================================================================
* Private functions
*=============================================================================
*/
void ChangeState(int newState) {
}
/*=============================================================================
* Local functions
*=============================================================================
*/
void CopyBits(uint8_t *sourceData,
uint8_t *destData,
uint8_t mappedBits,
uint8_t &sourceBitNum,
uint8_t &destBitNum)
{
uint8_t sourceByteNum;
uint8_t destByteNum;
uint8_t bitCounter = 0;
if ((mappedBits % 8) == 0 && (sourceBitNum % 8) == 0 && (destBitNum % 8) == 0) {
//printf(" Loading BYTEwise...\r\n");
/* load in by bytes */
uint8_t destByteNum = 0;
while (bitCounter < mappedBits) {
destByteNum = destBitNum / 8;
sourceByteNum = sourceBitNum / 8;
destData[destByteNum] = sourceData[sourceByteNum];
destBitNum += 8;
sourceBitNum += 8;
bitCounter += 8;
}
} else {
//printf(" Loading BITwise...");
/* not a multiple of 8, so do bit by bit */
while (bitCounter < mappedBits) {
destByteNum = destBitNum / 8;
sourceByteNum = sourceBitNum / 8;
/* clear the destination bit */
destData[destByteNum] &= ~(1 << destBitNum);
/* get source bit value */
uint8_t destValue = (sourceData[sourceByteNum] & (1 << (sourceBitNum % 8))) >> (sourceBitNum % 8) << destBitNum;
/* set dest bit */
destData[destByteNum] |= destValue;
destBitNum++;
sourceBitNum++;
bitCounter++;
}
}
}
/*=============================================================================
*=============================================================================
* Methods to handle message requests and responses
* Called by the node, usually during Update() or during handling of
* incoming messages.
*
* Removed from Service ProviderClass. Not sure if we will need these in the future
*=============================================================================
*=============================================================================
*/
/* PDO (7.2.2), MPDO (7.2.3) --------------------------------------------*/
/** Build and send a PDO request
* @note
* @param
*/
void RequestPdo (int pdoNum) {}
/** Build and send a PDO
* @note
* @param
*/
void ProducePdo (int pdoNum, char * data) {}
/* SDO (7.2.4) ----------------------------------------------------------*/
/** initiate SDO download
* @note Handles automatically whether it will be a expedited transfer or
* or if message will be split into
* @param
*
* Node will create a big data array and Service provide will have to
* iterate through and send all of the data. ServiceProvider will pass
* the confirmation to the node, and the node will free up it's buffer.
*/
void DownloadSdo (int sdoNum, int index, int subindex, int size, char * data) {}
/** initiate SDO upload
* @note
* @param
*/
void UploadSdo (int sdoNum, int index, int subindex) {}
/** Acknowledge that SDO was recieved properly
* @note
* @param
*/
void ConfirmSdo (int sdoNum, int bSuccess) {}
/** Abort current SDO transfer
* @note
* @param
*/
void AbortSdo (int sdoNum) {}
/* Emergency object (7.2.7) ---------------------------------------------*/
// TODO: emergency producer
/* Network Management (7.2.8) -------------------------------------------*/
/* ---- Node Control (7.2.8.2.1) ----------------------------------------*/
/** Build a CANOpen nmt control message to a node
* @note
* @param
*/
int SendNodeControl (NmtCommandSpecifier cs, unsigned int nodeId)
{
return 0;
}
/* ---- Error Control (7.2.8.2.2) ---------------------------------------*/
/** Build a CANOpen error control request to a node
* @note
* @param
*/
int RequestErrorControl (NmtCommandSpecifier cs, unsigned int nodeId)
{
return 0;
}
/** Build a CANOpen error control response
* @note
* @param
*/
int RespondErrorControl (NmtCommandSpecifier cs, unsigned int nodeId)
{
return 0;
}
} /* namspace ppCANOpen */
