diff --git a/.gitignore b/.gitignore index ec36929..7b22621 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ doc/html/ .project .settings/ +#kdevelop +*.kdev4 diff --git a/README.md b/README.md index d87d833..660b779 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Documentation with [Getting started](doc/gettingStarted.md), [LSS usage](doc/LSSusage.md) and [Trace usage](doc/traceUsage.md) is in `doc` directory. Code is documented in header files. Running [doxygen](http://www.doxygen.nl/) -or `make doc` in project base directory will produce complete html documentation. +in project base directory will produce complete html documentation. Just open CANopenNode/doc/html/index.html in the browser. Report issues on https://github.com/CANopenNode/CANopenNode/issues @@ -146,9 +146,12 @@ File structure - **CO_LSSslave.h/.c** - CANopen Layer Setting Service - slave protocol. - **extra/** - **CO_trace.h/.c** - CANopen trace object for recording variables over time. - - **example/** - Directory with basic example. - - **main.c** - Mainline and other threads - example template. + - **example/** - Directory with basic example, should compile on any system. + - **CO_driver_target.h** - Example hardware definitions for CANopenNode. + - **CO_driver_blank.c** - Example blank interface for CANopenNode. - **CO_OD.h/.c** - CANopen Object dictionary. Automatically generated files. + - **main_blank.c** - Mainline and other threads - example template. + - **Makefile** - Makefile for example. - **IO.eds** - Standard CANopen EDS file, which may be used from CANopen configuration tool. Automatically generated file. - _ **project.xml** - XML file contains all data for CANopen Object dictionary. @@ -162,6 +165,7 @@ File structure - **CO_Linux_threads.h/.c** - Helper functions for implementing CANopen threads in Linux. - **CO_notify_pipe.h/.c** - Notify pipe for Linux threads. - **CO_OD_storage.h/.c** - Object Dictionary storage object for Linux SocketCAN. + - **main.c** - Mainline for socketCAN - **doc/** - Directory with documentation - **CHANGELOG.md** - Change Log file. - **deviceSupport.md** - Information about supported devices. @@ -172,7 +176,6 @@ File structure - **codingStyle** - Example of the coding style. - **.clang-format** - Definition file for the coding style. - **Doxyfile** - Configuration file for the documentation generator *doxygen*. - - **Makefile** - Basic makefile. - **LICENSE** - License. - **README.md** - This file. diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 637412a..4cdf713 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -19,6 +19,7 @@ Change Log - It is necessary to manually update CO_OD.c file - it must include: `301/CO_driver.h`, `CO_OD.h` and `301/CO_SDOserver.h`. ### Fixed - Bugfix in `CO_HBconsumer_process()`: argument `timeDifference_us` was set to 0 inside for loop, fixed now. +- BUG in CO_HBconsumer.c #168 ### Added - Documentation added to `doc` directory: CHANGELOG.md, deviceSupport.md, gettingStarted.md, LSSusage.md and traceUsage.md. - All CANopen objects calculates next timer info for OS. diff --git a/example/CO_driver.c b/example/CO_driver_blank.c similarity index 100% rename from example/CO_driver.c rename to example/CO_driver_blank.c diff --git a/Makefile b/example/Makefile similarity index 81% rename from Makefile rename to example/Makefile index 73baf71..c6cc940 100644 --- a/Makefile +++ b/example/Makefile @@ -1,9 +1,9 @@ -# Makefile for CANopenNode, basic compile with no CAN device. +# Makefile for CANopenNode, basic compile with blank CAN device. -DRV_SRC = example -CANOPEN_SRC =. -APPL_SRC = example +DRV_SRC = . +CANOPEN_SRC = .. +APPL_SRC = . LINK_TARGET = canopennode @@ -16,7 +16,7 @@ INCLUDE_DIRS = \ SOURCES = \ - $(DRV_SRC)/CO_driver.c \ + $(DRV_SRC)/CO_driver_blank.c \ $(DRV_SRC)/eeprom.c \ $(CANOPEN_SRC)/301/CO_SDOserver.c \ $(CANOPEN_SRC)/301/CO_Emergency.c \ @@ -32,7 +32,7 @@ SOURCES = \ $(CANOPEN_SRC)/extra/CO_trace.c \ $(CANOPEN_SRC)/CANopen.c \ $(APPL_SRC)/CO_OD.c \ - $(APPL_SRC)/main.c + $(APPL_SRC)/main_blank.c OBJS = $(SOURCES:%.c=%.o) @@ -41,16 +41,13 @@ CFLAGS = -Wall $(INCLUDE_DIRS) LDFLAGS = -.PHONY: all clean doc +.PHONY: all clean all: clean $(LINK_TARGET) clean: rm -f $(OBJS) $(LINK_TARGET) -doc: - doxygen - %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ diff --git a/example/main.c b/example/main_blank.c similarity index 91% rename from example/main.c rename to example/main_blank.c index ee1e4a5..5019ba8 100644 --- a/example/main.c +++ b/example/main_blank.c @@ -31,12 +31,6 @@ #define TMR_TASK_INTERVAL (1000) /* Interval of tmrTask thread in microseconds */ #define INCREMENT_1MS(var) (var++) /* Increment 1ms variable in tmrTask */ -/** - * User-defined CAN base structure, passed as argument to CO_CANinit. - */ -struct CANbase { - uintptr_t baseAddress; /**< Base address of the CAN module */ -}; /* Global variables and objects */ volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */ @@ -68,14 +62,11 @@ int main (void){ uint16_t timer1msPrevious; /* disable CAN and CAN interrupts */ - struct CANbase canBase = { - .baseAddress = 0u, /* CAN module address */ - }; /* initialize CANopen */ - err = CO_CANinit(&canBase, 125 /* bit rate */); + err = CO_CANinit(NULL /* CAN module address */, 125 /* bit rate */); if (err == CO_ERROR_NO) { - err = CO_CANopenInit(10/* NodeID */); + err = CO_CANopenInit(10 /* NodeID */); } if(err != CO_ERROR_NO){ while(1); diff --git a/socketCAN/main.c b/socketCAN/main.c new file mode 100644 index 0000000..329d678 --- /dev/null +++ b/socketCAN/main.c @@ -0,0 +1,558 @@ +/* + * CANopen main program file for Linux SocketCAN. + * + * @file main + * @author Janez Paternoster + * @copyright 2015 - 2020 Janez Paternoster + * + * This file is part of CANopenSocket, a Linux implementation of CANopen + * stack with master functionality. Project home page is + * . CANopenSocket is based + * on CANopenNode: . + * + * 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 "CANopen.h" +#include "CO_OD_storage.h" +#include "CO_Linux_tasks.h" +#include "CO_time.h" +#include "application.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CO_SINGLE_THREAD +#include "CO_command.h" +#include +#endif + + +#define NSEC_PER_SEC (1000000000) /* The number of nanoseconds per second. */ +#define NSEC_PER_MSEC (1000000) /* The number of nanoseconds per millisecond. */ +#define TMR_TASK_INTERVAL_NS (1000000) /* Interval of taskTmr in nanoseconds */ +#define TMR_TASK_OVERFLOW_US (5000) /* Overflow detect limit for taskTmr in microseconds */ +#define INCREMENT_1MS(var) (var++) /* Increment 1ms variable in taskTmr */ + + +/* Global variable increments each millisecond. */ +volatile uint16_t CO_timer1ms = 0U; + +/* Mutex is locked, when CAN is not valid (configuration state). May be used + * from other threads. RT threads may use CO->CANmodule[0]->CANnormal instead. */ +#ifndef CO_SINGLE_THREAD +pthread_mutex_t CO_CAN_VALID_mtx = PTHREAD_MUTEX_INITIALIZER; +#endif + +/* Other variables and objects */ +static int rtPriority = -1; /* Real time priority, configurable by arguments. (-1=RT disabled) */ +static int mainline_epoll_fd; /* epoll file descriptor for mainline */ +static CO_OD_storage_t odStor; /* Object Dictionary storage object for CO_OD_ROM */ +static CO_OD_storage_t odStorAuto; /* Object Dictionary storage object for CO_OD_EEPROM */ +static char *odStorFile_rom = "od_storage"; /* Name of the file */ +static char *odStorFile_eeprom = "od_storage_auto"; /* Name of the file */ +static CO_time_t CO_time; /* Object for current time */ +static in_port_t CO_command_socket_tcp_port = 60000; /* default port when used in tcp gateway mode */ + + +/* Realtime thread */ +#ifndef CO_SINGLE_THREAD +static void* rt_thread(void* arg); +static pthread_t rt_thread_id; +static int rt_thread_epoll_fd; +#endif + + +/* Signal handler */ +volatile sig_atomic_t CO_endProgram = 0; +static void sigHandler(int sig) { + CO_endProgram = 1; +} + + +/* Helper functions ***********************************************************/ +void CO_errExit(char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +/* send CANopen generic emergency message */ +void CO_error(const uint32_t info) { + CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, info); + fprintf(stderr, "canopend generic error: 0x%X\n", info); +} + + +static void printUsage(char *progName) { +fprintf(stderr, +"Usage: %s [options]\n", progName); +fprintf(stderr, +"\n" +"Options:\n" +" -i CANopen Node-id (1..127). If not specified, value from\n" +" Object dictionary (0x2101) is used.\n" +" -p Realtime priority of RT task (RT disabled by default).\n" +" -r Enable reboot on CANopen NMT reset_node command. \n" +" -s Set Filename for OD storage ('od_storage' is default).\n" +" -a Set Filename for automatic storage variables from\n" +" Object dictionary. ('od_storage_auto' is default).\n"); +#ifndef CO_SINGLE_THREAD +fprintf(stderr, +" -c Enable command interface for master functionality. \n" +" If socket path is specified as empty string \"\",\n" +" default '%s' will be used.\n" +" Note that location of socket path may affect security.\n" +" See 'canopencomm/canopencomm --help' for more info.\n" +, CO_command_socketPath); +fprintf(stderr, +" -t Enable command interface for master functionality over tcp, \n" +" listen to .\n" +" Note that using this mode may affect security.\n" +); +#endif +fprintf(stderr, +"\n" +"See also: https://github.com/CANopenNode/CANopenSocket\n" +"\n"); +} + + +/******************************************************************************/ +/** Mainline and RT thread **/ +/******************************************************************************/ +int main (int argc, char *argv[]) { + CO_NMT_reset_cmd_t reset = CO_RESET_NOT; + CO_ReturnError_t odStorStatus_rom, odStorStatus_eeprom; + int CANdevice0Index = 0; + int opt; + bool_t firstRun = true; + + char* CANdevice = NULL; /* CAN device, configurable by arguments. */ + bool_t nodeIdFromArgs = false; /* True, if program arguments are used for CANopen Node Id */ + int nodeId = -1; /* Use value from Object Dictionary or set to 1..127 by arguments */ + bool_t rebootEnable = false; /* Configurable by arguments */ +#ifndef CO_SINGLE_THREAD + typedef enum CMD_MODE {CMD_NONE, CMD_LOCAL, CMD_REMOTE} cmdMode_t; + cmdMode_t commandEnable = CMD_NONE; /* Configurable by arguments */ +#endif + + if(argc < 2 || strcmp(argv[1], "--help") == 0){ + printUsage(argv[0]); + exit(EXIT_SUCCESS); + } + + + /* Get program options */ + while((opt = getopt(argc, argv, "i:p:rc:t:s:a:")) != -1) { + switch (opt) { + case 'i': + nodeId = strtol(optarg, NULL, 0); + nodeIdFromArgs = true; + break; + case 'p': rtPriority = strtol(optarg, NULL, 0); break; + case 'r': rebootEnable = true; break; +#ifndef CO_SINGLE_THREAD + case 'c': + /* In case of empty string keep default name, just enable interface. */ + if(strlen(optarg) != 0) { + CO_command_socketPath = optarg; + } + commandEnable = CMD_LOCAL; + break; + case 't': + /* In case of empty string keep default port, just enable interface. */ + if(strlen(optarg) != 0) { + //CO_command_socket_tcp_port = optarg; + int scanResult = sscanf(optarg, "%hu", &CO_command_socket_tcp_port); + if(scanResult != 1){ //expect one argument to be extracted + printf("ERROR: -t argument \'%s\' is not a valid tcp port\n", optarg); + exit(EXIT_FAILURE); + } + } + commandEnable = CMD_REMOTE; + break; +#endif + case 's': odStorFile_rom = optarg; break; + case 'a': odStorFile_eeprom = optarg; break; + default: + printUsage(argv[0]); + exit(EXIT_FAILURE); + } + } + + if(optind < argc) { + CANdevice = argv[optind]; + CANdevice0Index = if_nametoindex(CANdevice); + } + + if(nodeIdFromArgs && (nodeId < 1 || nodeId > 127)) { + fprintf(stderr, "Wrong node ID (%d)\n", nodeId); + printUsage(argv[0]); + exit(EXIT_FAILURE); + } + + if(rtPriority != -1 && (rtPriority < sched_get_priority_min(SCHED_FIFO) + || rtPriority > sched_get_priority_max(SCHED_FIFO))) { + fprintf(stderr, "Wrong RT priority (%d)\n", rtPriority); + printUsage(argv[0]); + exit(EXIT_FAILURE); + } + + if(CANdevice0Index == 0) { + char s[120]; + snprintf(s, 120, "Can't find CAN device \"%s\"", CANdevice); + CO_errExit(s); + } + + + printf("%s - starting CANopen device with Node ID %d(0x%02X)", argv[0], nodeId, nodeId); + + + /* Verify, if OD structures have proper alignment of initial values */ + if(CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) { + fprintf(stderr, "Program init - %s - Error in CO_OD_RAM.\n", argv[0]); + exit(EXIT_FAILURE); + } + if(CO_OD_EEPROM.FirstWord != CO_OD_EEPROM.LastWord) { + fprintf(stderr, "Program init - %s - Error in CO_OD_EEPROM.\n", argv[0]); + exit(EXIT_FAILURE); + } + if(CO_OD_ROM.FirstWord != CO_OD_ROM.LastWord) { + fprintf(stderr, "Program init - %s - Error in CO_OD_ROM.\n", argv[0]); + exit(EXIT_FAILURE); + } + + + /* initialize Object Dictionary storage */ + odStorStatus_rom = CO_OD_storage_init(&odStor, (uint8_t*) &CO_OD_ROM, sizeof(CO_OD_ROM), odStorFile_rom); + odStorStatus_eeprom = CO_OD_storage_init(&odStorAuto, (uint8_t*) &CO_OD_EEPROM, sizeof(CO_OD_EEPROM), odStorFile_eeprom); + + + /* Catch signals SIGINT and SIGTERM */ + if(signal(SIGINT, sigHandler) == SIG_ERR) + CO_errExit("Program init - SIGINIT handler creation failed"); + if(signal(SIGTERM, sigHandler) == SIG_ERR) + CO_errExit("Program init - SIGTERM handler creation failed"); + + /* increase variable each startup. Variable is automatically stored in non-volatile memory. */ + printf(", count=%u ...\n", ++OD_powerOnCounter); + + + while(reset != CO_RESET_APP && reset != CO_RESET_QUIT && CO_endProgram == 0) { +/* CANopen communication reset - initialize CANopen objects *******************/ + CO_ReturnError_t err; + + printf("%s - communication reset ...\n", argv[0]); + + +#ifndef CO_SINGLE_THREAD + /* Wait other threads (command interface). */ + pthread_mutex_lock(&CO_CAN_VALID_mtx); +#endif + + /* Wait rt_thread. */ + if(!firstRun) { + CO_LOCK_OD(); + CO->CANmodule[0]->CANnormal = false; + CO_UNLOCK_OD(); + } + + + /* Enter CAN configuration. */ + CO_CANsetConfigurationMode(CANdevice0Index); + + + /* initialize CANopen */ + if(!nodeIdFromArgs) { + /* use value from Object dictionary, if not set by program arguments */ + nodeId = OD_CANNodeID; + } + err = CO_init(CANdevice0Index, nodeId, 0); + if(err != CO_ERROR_NO) { + char s[120]; + snprintf(s, 120, "Communication reset - CANopen initialization failed, err=%d", err); + CO_errExit(s); + } + + + /* initialize OD objects 1010 and 1011 and verify errors. */ + CO_OD_configure(CO->SDO[0], OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)&odStor, 0, 0U); + CO_OD_configure(CO->SDO[0], OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)&odStor, 0, 0U); + if(odStorStatus_rom != CO_ERROR_NO) { + CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_rom); + } + if(odStorStatus_eeprom != CO_ERROR_NO) { + CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_eeprom + 1000); + } + + + /* Configure callback functions for task control */ + CO_EM_initCallback(CO->em, taskMain_cbSignal); + CO_SDO_initCallback(CO->SDO[0], taskMain_cbSignal); + CO_SDOclient_initCallback(CO->SDOclient, taskMain_cbSignal); + + + /* Initialize time */ + CO_time_init(&CO_time, CO->SDO[0], &OD_time.epochTimeBaseMs, &OD_time.epochTimeOffsetMs, 0x2130); + + + /* First time only initialization. */ + if(firstRun) { + firstRun = false; + + /* Configure epoll for mainline */ + mainline_epoll_fd = epoll_create(4); + if(mainline_epoll_fd == -1) + CO_errExit("Program init - epoll_create mainline failed"); + + /* Init mainline */ + taskMain_init(mainline_epoll_fd, &OD_performance[ODA_performance_mainCycleMaxTime]); + + +#ifdef CO_SINGLE_THREAD + /* Init taskRT */ + CANrx_taskTmr_init(mainline_epoll_fd, TMR_TASK_INTERVAL_NS, &OD_performance[ODA_performance_timerCycleMaxTime]); + + OD_performance[ODA_performance_timerCycleTime] = TMR_TASK_INTERVAL_NS/1000; /* informative */ + + /* Set priority for mainline */ + if(rtPriority > 0) { + struct sched_param param; + + param.sched_priority = rtPriority; + if(sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) + CO_errExit("Program init - mainline set scheduler failed"); + } +#else + /* Configure epoll for rt_thread */ + rt_thread_epoll_fd = epoll_create(2); + if(rt_thread_epoll_fd == -1) + CO_errExit("Program init - epoll_create rt_thread failed"); + + /* Init taskRT */ + CANrx_taskTmr_init(rt_thread_epoll_fd, TMR_TASK_INTERVAL_NS, &OD_performance[ODA_performance_timerCycleMaxTime]); + + OD_performance[ODA_performance_timerCycleTime] = TMR_TASK_INTERVAL_NS/1000; /* informative */ + + /* Create rt_thread */ + if(pthread_create(&rt_thread_id, NULL, rt_thread, NULL) != 0) + CO_errExit("Program init - rt_thread creation failed"); + + /* Set priority for rt_thread */ + if(rtPriority > 0) { + struct sched_param param; + + param.sched_priority = rtPriority; + if(pthread_setschedparam(rt_thread_id, SCHED_FIFO, ¶m) != 0) + CO_errExit("Program init - rt_thread set scheduler failed"); + } +#endif + +#ifndef CO_SINGLE_THREAD + /* Initialize socket command interface */ + switch(commandEnable) { + case CMD_LOCAL: + if(CO_command_init() != 0) { + CO_errExit("Socket command interface initialization failed"); + } + printf("%s - Command interface on socket '%s' started ...\n", argv[0], CO_command_socketPath); + break; + case CMD_REMOTE: + if(CO_command_init_tcp(CO_command_socket_tcp_port) != 0) { + CO_errExit("Socket command interface initialization failed"); + } + printf("%s - Command interface on tcp port '%hu' started ...\n", argv[0], CO_command_socket_tcp_port); + break; + default: + break; + } +#endif + + /* Execute optional additional application code */ + app_programStart(); + } + + + /* Execute optional additional application code */ + app_communicationReset(); + + + /* start CAN */ + CO_CANsetNormalMode(CO->CANmodule[0]); +#ifndef CO_SINGLE_THREAD + pthread_mutex_unlock(&CO_CAN_VALID_mtx); +#endif + + + reset = CO_RESET_NOT; + + printf("%s - running ...\n", argv[0]); + + + while(reset == CO_RESET_NOT && CO_endProgram == 0) { +/* loop for normal program execution ******************************************/ + int ready; + struct epoll_event ev; + + ready = epoll_wait(mainline_epoll_fd, &ev, 1, -1); + + if(ready != 1) { + if(errno != EINTR) { + CO_error(0x11100000L + errno); + } + } + +#ifdef CO_SINGLE_THREAD + else if(CANrx_taskTmr_process(ev.data.fd)) { + /* code was processed in the above function. Additional code process below */ + INCREMENT_1MS(CO_timer1ms); + /* Detect timer large overflow */ + if(OD_performance[ODA_performance_timerCycleMaxTime] > TMR_TASK_OVERFLOW_US && rtPriority > 0) { + CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0x22400000L | OD_performance[ODA_performance_timerCycleMaxTime]); + } + } +#endif + + else if(taskMain_process(ev.data.fd, &reset, CO_timer1ms)) { + uint16_t timer1msDiff; + static uint16_t tmr1msPrev = 0; + + /* Calculate time difference */ + timer1msDiff = CO_timer1ms - tmr1msPrev; + tmr1msPrev = CO_timer1ms; + + /* code was processed in the above function. Additional code process below */ + + /* Execute optional additional application code */ + app_programAsync(timer1msDiff); + + CO_OD_storage_autoSave(&odStorAuto, CO_timer1ms, 60000); + } + + else { + /* No file descriptor was processed. */ + CO_error(0x11200000L); + } + } + } + + +/* program exit ***************************************************************/ + /* join threads */ +#ifndef CO_SINGLE_THREAD + switch (commandEnable) + { + case CMD_LOCAL: + if (CO_command_clear() != 0) { + CO_errExit("Socket command interface removal failed"); + } + break; + case CMD_REMOTE: + //nothing to do yet + break; + default: + break; + } +#endif + + CO_endProgram = 1; +#ifndef CO_SINGLE_THREAD + if(pthread_join(rt_thread_id, NULL) != 0) { + CO_errExit("Program end - pthread_join failed"); + } +#endif + + /* Execute optional additional application code */ + app_programEnd(); + + /* Store CO_OD_EEPROM */ + CO_OD_storage_autoSave(&odStorAuto, 0, 0); + CO_OD_storage_autoSaveClose(&odStorAuto); + + /* delete objects from memory */ + CANrx_taskTmr_close(); + taskMain_close(); + CO_delete(CANdevice0Index); + + printf("%s on %s (nodeId=0x%02X) - finished.\n\n", argv[0], CANdevice, nodeId); + + /* Flush all buffers (and reboot) */ + if(rebootEnable && reset == CO_RESET_APP) { + sync(); + if(reboot(LINUX_REBOOT_CMD_RESTART) != 0) { + CO_errExit("Program end - reboot failed"); + } + } + + exit(EXIT_SUCCESS); +} + + +#ifndef CO_SINGLE_THREAD +/* Realtime thread for CAN receive and taskTmr ********************************/ +static void* rt_thread(void* arg) { + + /* Endless loop */ + while(CO_endProgram == 0) { + int ready; + struct epoll_event ev; + + ready = epoll_wait(rt_thread_epoll_fd, &ev, 1, -1); + + if(ready != 1) { + if(errno != EINTR) { + CO_error(0x12100000L + errno); + } + } + + else if(CANrx_taskTmr_process(ev.data.fd)) { + int i; + + /* code was processed in the above function. Additional code process below */ + INCREMENT_1MS(CO_timer1ms); + + /* Monitor variables with trace objects */ + CO_time_process(&CO_time); +#if CO_NO_TRACE > 0 + for(i=0; itrace[i], *CO_time.epochTimeOffsetMs); + } +#endif + + /* Execute optional additional application code */ + app_program1ms(); + + /* Detect timer large overflow */ + if(OD_performance[ODA_performance_timerCycleMaxTime] > TMR_TASK_OVERFLOW_US && rtPriority > 0 && CO->CANmodule[0]->CANnormal) { + CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0x22400000L | OD_performance[ODA_performance_timerCycleMaxTime]); + } + } + + else { + /* No file descriptor was processed. */ + CO_error(0x12200000L); + } + } + + return NULL; +} +#endif