/* * CANopen main program file. * * This file is a template for other microcontrollers. * * @file main_generic.c * @author Janez Paternoster * @copyright 2021 Janez Paternoster * * This file is part of , a CANopen Stack. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ #include #include "CANopen.h" #include "OD.h" #include "CO_storageBlank.h" #define log_printf(macropar_message, ...) printf(macropar_message, ##__VA_ARGS__) /* default values for CO_CANopenInit() */ #define NMT_CONTROL \ CO_NMT_STARTUP_TO_OPERATIONAL \ | CO_NMT_ERR_ON_ERR_REG | CO_ERR_REG_GENERIC_ERR | CO_ERR_REG_COMMUNICATION #define FIRST_HB_TIME 500 #define SDO_SRV_TIMEOUT_TIME 1000 #define SDO_CLI_TIMEOUT_TIME 500 #define SDO_CLI_BLOCK false #define OD_STATUS_BITS NULL /* Global variables and objects */ CO_t* CO = NULL; /* CANopen object */ uint8_t LED_red, LED_green; /* main ***********************************************************************/ int main(void) { CO_ReturnError_t err; CO_NMT_reset_cmd_t reset = CO_RESET_NOT; uint32_t heapMemoryUsed; void* CANptr = NULL; /* CAN module address */ uint8_t pendingNodeId = 10; /* read from dip switches or nonvolatile memory, configurable by LSS slave */ uint8_t activeNodeId = 10; /* Copied from CO_pendingNodeId in the communication reset section */ uint16_t pendingBitRate = 125; /* read from dip switches or nonvolatile memory, configurable by LSS slave */ #if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE CO_storage_t storage; CO_storage_entry_t storageEntries[] = {{.addr = &OD_PERSIST_COMM, .len = sizeof(OD_PERSIST_COMM), .subIndexOD = 2, .attr = CO_storage_cmd | CO_storage_restore, .addrNV = NULL}}; uint8_t storageEntriesCount = sizeof(storageEntries) / sizeof(storageEntries[0]); uint32_t storageInitError = 0; #endif /* Configure microcontroller. */ /* Allocate memory */ CO_config_t* config_ptr = NULL; #ifdef CO_MULTIPLE_OD /* example usage of CO_MULTIPLE_OD (but still single OD here) */ CO_config_t co_config = {0}; OD_INIT_CONFIG(co_config); /* helper macro from OD.h */ co_config.CNT_LEDS = 1; co_config.CNT_LSS_SLV = 1; config_ptr = &co_config; #endif /* CO_MULTIPLE_OD */ CO = CO_new(config_ptr, &heapMemoryUsed); if (CO == NULL) { log_printf("Error: Can't allocate memory\n"); return 0; } else { log_printf("Allocated %u bytes for CANopen objects\n", heapMemoryUsed); } #if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE err = CO_storageBlank_init(&storage, CO->CANmodule, OD_ENTRY_H1010_storeParameters, OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, storageEntriesCount, &storageInitError); if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT) { log_printf("Error: Storage %d\n", storageInitError); return 0; } #endif while (reset != CO_RESET_APP) { /* CANopen communication reset - initialize CANopen objects *******************/ log_printf("CANopenNode - Reset communication...\n"); /* Wait rt_thread. */ CO->CANmodule->CANnormal = false; /* Enter CAN configuration. */ CO_CANsetConfigurationMode((void*)&CANptr); CO_CANmodule_disable(CO->CANmodule); /* initialize CANopen */ err = CO_CANinit(CO, CANptr, pendingBitRate); if (err != CO_ERROR_NO) { log_printf("Error: CAN initialization failed: %d\n", err); return 0; } CO_LSS_address_t lssAddress = {.identity = {.vendorID = OD_PERSIST_COMM.x1018_identity.vendor_ID, .productCode = OD_PERSIST_COMM.x1018_identity.productCode, .revisionNumber = OD_PERSIST_COMM.x1018_identity.revisionNumber, .serialNumber = OD_PERSIST_COMM.x1018_identity.serialNumber}}; err = CO_LSSinit(CO, &lssAddress, &pendingNodeId, &pendingBitRate); if (err != CO_ERROR_NO) { log_printf("Error: LSS slave initialization failed: %d\n", err); return 0; } activeNodeId = pendingNodeId; uint32_t errInfo = 0; err = CO_CANopenInit(CO, /* CANopen object */ NULL, /* alternate NMT */ NULL, /* alternate em */ OD, /* Object dictionary */ OD_STATUS_BITS, /* Optional OD_statusBits */ NMT_CONTROL, /* CO_NMT_control_t */ FIRST_HB_TIME, /* firstHBTime_ms */ SDO_SRV_TIMEOUT_TIME, /* SDOserverTimeoutTime_ms */ SDO_CLI_TIMEOUT_TIME, /* SDOclientTimeoutTime_ms */ SDO_CLI_BLOCK, /* SDOclientBlockTransfer */ activeNodeId, &errInfo); if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { if (err == CO_ERROR_OD_PARAMETERS) { log_printf("Error: Object Dictionary entry 0x%X\n", errInfo); } else { log_printf("Error: CANopen initialization failed: %d\n", err); } return 0; } err = CO_CANopenInitPDO(CO, CO->em, OD, activeNodeId, &errInfo); if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { if (err == CO_ERROR_OD_PARAMETERS) { log_printf("Error: Object Dictionary entry 0x%X\n", errInfo); } else { log_printf("Error: PDO initialization failed: %d\n", err); } return 0; } /* Configure Timer interrupt function for execution every 1 millisecond */ /* Configure CAN transmit and receive interrupt */ /* Configure CANopen callbacks, etc */ if (!CO->nodeIdUnconfigured) { #if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE if (storageInitError != 0) { CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, storageInitError); } #endif } else { log_printf("CANopenNode - Node-id not initialized\n"); } /* start CAN */ CO_CANsetNormalMode(CO->CANmodule); reset = CO_RESET_NOT; log_printf("CANopenNode - Running...\n"); fflush(stdout); while (reset == CO_RESET_NOT) { /* loop for normal program execution ******************************************/ /* get time difference since last function call */ uint32_t timeDifference_us = 500; /* CANopen process */ reset = CO_process(CO, false, timeDifference_us, NULL); LED_red = CO_LED_RED(CO->LEDs, CO_LED_CANopen); LED_green = CO_LED_GREEN(CO->LEDs, CO_LED_CANopen); /* Nonblocking application code may go here. */ /* Process automatic storage */ /* optional sleep for short time */ } } /* program exit ***************************************************************/ /* stop threads */ /* delete objects from memory */ CO_CANsetConfigurationMode((void*)&CANptr); CO_delete(CO); log_printf("CANopenNode finished\n"); /* reset */ return 0; } /* timer thread executes in constant intervals ********************************/ void tmrTask_thread(void) { for (;;) { CO_LOCK_OD(CO->CANmodule); if (!CO->nodeIdUnconfigured && CO->CANmodule->CANnormal) { bool_t syncWas = false; /* get time difference since last function call */ uint32_t timeDifference_us = 1000; #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE syncWas = CO_process_SYNC(CO, timeDifference_us, NULL); #endif #if (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE CO_process_RPDO(CO, syncWas, timeDifference_us, NULL); #endif #if (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE CO_process_TPDO(CO, syncWas, timeDifference_us, NULL); #endif /* Further I/O or nonblocking application code may go here. */ } CO_UNLOCK_OD(CO->CANmodule); } } /* CAN interrupt function executes on received CAN message ********************/ void /* interrupt */ CO_CAN1InterruptHandler(void) { /* clear interrupt flag */ }