├── CCanOpenStack ├── .DS_Store ├── CCanOpenStack.xcodeproj │ ├── xcuserdata │ │ └── timo.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── CCanOpenStack.xcscheme │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcuserdata │ │ │ └── timo.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcshareddata │ │ │ └── CCanOpenStack.xccheckout │ └── project.pbxproj └── CCanOpenStack │ ├── heartbeat.txt │ ├── delay.h │ ├── test_util.h │ ├── test.h │ ├── test_od.h │ ├── test_nmt.h │ ├── test_rpdo.h │ ├── test_sdo.h │ ├── test_appcycle.h │ ├── test_can_bus.h │ ├── test_sdo_server.h │ ├── co_node.h │ ├── utils.h │ ├── test_tpdo.h │ ├── test_util.c │ ├── main.c │ ├── log.h │ ├── nmt.h │ ├── delay.c │ ├── log.c │ ├── can_message.h │ ├── od_object.h │ ├── test.c │ ├── can_bus.h │ ├── pdo_communication.txt │ ├── comments.txt │ ├── nmt_master.h │ ├── readme.txt │ ├── emergency.txt │ ├── co_stack.h │ ├── nmt.txt │ ├── can_bus.c │ ├── sdo_client.c │ ├── sdo_client.h │ ├── object_dictionary.txt │ ├── nmt_slave.h │ ├── nmt_master.c │ ├── pdo.h │ ├── sdo_server.h │ ├── co_stack.c │ ├── CCanOpenStack.1 │ ├── od.h │ ├── test_can_bus.c │ ├── todo.txt │ ├── test_rpdo.c │ ├── test_tpdo.c │ ├── nmt_slave.c │ ├── sdo.c │ ├── od.c │ ├── test_sdo_server.c │ ├── sdo.h │ ├── sdo_server.c │ ├── test_od.c │ ├── test_nmt.c │ ├── od_initialize.h │ ├── test_sdo.c │ └── pdo.c ├── README.md ├── .gitignore └── LICENSE /CCanOpenStack/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timojaask/C-CANopen-Stack/HEAD/CCanOpenStack/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CCanOpenStack 2 | ============= 3 | 4 | A simple CANopen stack implemented using C programming language. 5 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/xcuserdata/timo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/project.xcworkspace/xcuserdata/timo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timojaask/C-CANopen-Stack/HEAD/CCanOpenStack/CCanOpenStack.xcodeproj/project.xcworkspace/xcuserdata/timo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/heartbeat.txt: -------------------------------------------------------------------------------- 1 | The heartbeat message sent by an individual node has the CAN message identifier 700h plus the Node ID. It only contains one byte showing the current NMT state of that node. 2 | 3 | Valid values for the byte: 4 | 5 | 0 = Boot-up 6 | 4 = Stopped 7 | 5 = Operational 8 | 127 = Pre-operational -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/delay.h: -------------------------------------------------------------------------------- 1 | // 2 | // delay.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 8.12.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_delay_h 10 | #define CCanOpenStack_delay_h 11 | 12 | extern void delay_ms(uint32_t milliseconds); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_util.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_util.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_util_h 10 | #define CCanOpenStack_test_util_h 11 | 12 | #include "can_message.h" 13 | 14 | extern void print_message_data(can_message *message); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test.h: -------------------------------------------------------------------------------- 1 | // 2 | // test.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_h 10 | #define CCanOpenStack_test_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern void test_run_all_tests(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_od.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_od.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_od_h 10 | #define CCanOpenStack_test_od_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_od_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_nmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_nmt.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_nmt_h 10 | #define CCanOpenStack_test_nmt_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_nmt_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_rpdo.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_pdo.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 27.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_pdo_h 10 | #define CCanOpenStack_test_pdo_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_rpdo_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_sdo.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_sdo.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_sdo_h 10 | #define CCanOpenStack_test_sdo_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_sdo_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_appcycle.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_appcyce.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 8.12.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_appcyce_h 10 | #define CCanOpenStack_test_appcyce_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_appcycle_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_can_bus.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_can_bus.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_can_bus_h 10 | #define CCanOpenStack_test_can_bus_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_can_bus_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_sdo_server.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_sdo_server.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_sdo_server_h 10 | #define CCanOpenStack_test_sdo_server_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | extern int test_sdo_server_run(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/co_node.h: -------------------------------------------------------------------------------- 1 | // 2 | // co_node.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_co_node_h 10 | #define CCanOpenStack_co_node_h 11 | 12 | #include "od.h" 13 | #include "nmt.h" 14 | 15 | typedef struct { 16 | uint8_t node_id; 17 | nmt_state state; 18 | object_dictionary *od; 19 | } co_node; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // utils.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_utils_h 10 | #define CCanOpenStack_utils_h 11 | 12 | /***************************** Global Definitions ****************************/ 13 | 14 | /** @brief Returns the number of elements in a given array */ 15 | #define UTILS_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_tpdo.h: -------------------------------------------------------------------------------- 1 | // 2 | // test_tpdo.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 28.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_test_tpdo_h 10 | #define CCanOpenStack_test_tpdo_h 11 | 12 | /***************************** Global Definitions ****************************/ 13 | 14 | /***************************** Global Prototypes *****************************/ 15 | extern int test_tpdo_run(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_util.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_util.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_util.h" 10 | #include "log.h" 11 | 12 | extern void print_message_data(can_message *message) 13 | { 14 | log_write("["); 15 | for (int i = 0; i < message->data_len; i++) 16 | { 17 | log_write("%02Xh", message->data[i]); 18 | if (i < message->data_len - 1) 19 | { 20 | log_write(" "); 21 | } 22 | } 23 | log_write("]"); 24 | } 25 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "log.h" 10 | #include "test.h" 11 | 12 | /****************************** Local Prototypes *****************************/ 13 | 14 | /****************************** Global Functions *****************************/ 15 | int main(int argc, const char * argv[]) 16 | { 17 | test_run_all_tests(); 18 | log_write_ln("main: done"); 19 | return 0; 20 | } 21 | 22 | /****************************** Local Functions ******************************/ -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/xcuserdata/timo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CCanOpenStack.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 2578ACB0181463F50010C2D2 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/log.h: -------------------------------------------------------------------------------- 1 | // 2 | // log.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_log_h 10 | #define CCanOpenStack_log_h 11 | 12 | /***************************** Global Prototypes *****************************/ 13 | 14 | /** 15 | * @brief Writes a string to a log with an additional line break. 16 | * @param format C printf format style 17 | */ 18 | extern void log_write_ln(const char *format, ...); 19 | /** 20 | * @brief Writes a string to a log as it is (witout additional line break). 21 | * @param format C printf format style 22 | */ 23 | extern void log_write(const char *format, ...); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // nmt.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 23.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_nmt_h 10 | #define CCanOpenStack_nmt_h 11 | 12 | /***************************** Global Definitions ****************************/ 13 | typedef enum { 14 | nmt_state_boot_up = 0, 15 | nmt_state_stopped = 4, 16 | nmt_state_operational = 5, 17 | nmt_state_pre_operational = 127 18 | } nmt_state; 19 | typedef enum { 20 | nmt_command_operational = 1, 21 | nmt_command_stopped = 2, 22 | nmt_command_pre_operational = 128, 23 | nmt_command_reset_node = 129, 24 | nmt_command_reset_communication = 130 25 | } nmt_command; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/delay.c: -------------------------------------------------------------------------------- 1 | // 2 | // delay.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 8.12.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | /******************************************************************* 10 | * THIS FILE SHOULD IMPLEMENT FUNCTIONS DECLARED IN delay.h. 11 | * THIS SHOULD BE IMPLEMENTED ACCORDING TO PLATFORM THIS PROGRAM 12 | * IS INTENDED TO RUN ON. 13 | * FOR DEMONSTRATION PURPOSES AN EXAMPLE IMPLEMENTATION IS PROVIDED 14 | * FOR MAC OS X OPERATING SYSTEM. 15 | *******************************************************************/ 16 | 17 | #include 18 | #include 19 | #include "delay.h" 20 | 21 | extern void delay_ms(uint32_t milliseconds) 22 | { 23 | usleep(milliseconds * 1000); 24 | } 25 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/log.c: -------------------------------------------------------------------------------- 1 | // 2 | // log.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "log.h" 12 | 13 | /****************************** Global Functions *****************************/ 14 | extern void log_write_ln(const char *format, ...) 15 | { 16 | char buffer[256]; 17 | va_list args; 18 | va_start (args, format); 19 | vsnprintf (buffer, 256, format, args); 20 | printf("%s\n", buffer); 21 | va_end (args); 22 | } 23 | extern void log_write(const char *format, ...) 24 | { 25 | char buffer[256]; 26 | va_list args; 27 | va_start (args, format); 28 | vsnprintf (buffer, 256, format, args); 29 | printf("%s", buffer); 30 | va_end (args); 31 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/can_message.h: -------------------------------------------------------------------------------- 1 | // 2 | // can_message.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_can_message_h 10 | #define CCanOpenStack_can_message_h 11 | 12 | #include 13 | 14 | /***************************** Global Definitions ****************************/ 15 | /** 16 | * @brief A structure to represents a CAN message. This is not a part of CANopen protocol, 17 | * but underlying CAN protocol. This is the data that will be passed around the CAN network. 18 | */ 19 | typedef struct { 20 | /* @brief The CAN message identifier */ 21 | uint16_t id; 22 | /* @brief The length of the message data */ 23 | uint8_t data_len; 24 | /* @brief A pointer to CAN message data */ 25 | uint8_t *data; 26 | } can_message; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/od_object.h: -------------------------------------------------------------------------------- 1 | // 2 | // od_object.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_od_object_h 10 | #define CCanOpenStack_od_object_h 11 | 12 | #include 13 | 14 | /***************************** Global Definitions ****************************/ 15 | 16 | typedef enum { 17 | od_data_type_bool = 1, 18 | od_data_type_int8 = 2, 19 | od_data_type_int16 = 3, 20 | od_data_type_int32 = 4, 21 | od_data_type_uint8 = 5, 22 | od_data_type_uint16 = 6, 23 | od_data_type_uint32 = 7 24 | } od_data_type; 25 | 26 | typedef enum { 27 | od_access_type_rw = 0, 28 | od_access_type_wo = 1, 29 | od_access_type_ro = 2, 30 | od_access_type_const = 3 31 | } od_access_type; 32 | 33 | typedef struct { 34 | uint16_t index; 35 | uint8_t sub_index; 36 | od_data_type data_type; 37 | od_access_type access_type; 38 | uint32_t data; 39 | } od_object; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test.c: -------------------------------------------------------------------------------- 1 | // 2 | // test.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test.h" 10 | #include "log.h" 11 | #include "test_appcycle.h" 12 | #include "test_can_bus.h" 13 | #include "test_nmt.h" 14 | #include "test_od.h" 15 | #include "test_rpdo.h" 16 | #include "test_tpdo.h" 17 | #include "test_sdo.h" 18 | #include "test_sdo_server.h" 19 | 20 | /****************************** Global Functions *****************************/ 21 | extern void test_run_all_tests(void) 22 | { 23 | int error = 0; 24 | error |= test_can_bus_run(); 25 | error |= test_od_run(); 26 | error |= test_sdo_run(); 27 | error |= test_sdo_server_run(); 28 | error |= test_nmt_run(); 29 | error |= test_rpdo_run(); 30 | error |= test_tpdo_run(); 31 | error |= test_appcycle_run(); 32 | if (!error) 33 | { 34 | log_write_ln("test: ALL OK"); 35 | } 36 | else 37 | { 38 | log_write_ln("test: ERRORS"); 39 | } 40 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/can_bus.h: -------------------------------------------------------------------------------- 1 | // 2 | // can_bus.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_can_bus_h 10 | #define CCanOpenStack_can_bus_h 11 | 12 | #include "can_message.h" 13 | 14 | /***************************** Global Prototypes *****************************/ 15 | /** 16 | * @brief Sends a given message to CAN bus. 17 | * @param message A pointer to the CAN message the will be sent 18 | * @return 0: no error, greater than 0: error occurred 19 | */ 20 | extern void can_bus_send_message(can_message *message); 21 | /** 22 | * @brief Register a function that will be called whenever there is a new message on CAN bus 23 | * The message must be of a type void and take pointer to can_message as a paramter. 24 | * When a CAN message is received, this function is called with the received CAN message as a parameter. 25 | * @param handler_function A pointer to a function that will be called when a new message is received 26 | * @return 0: no error, greater than 0: error occurred 27 | */ 28 | extern int can_bus_register_message_received_handler(void (*handler_function)(can_message *message)); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/pdo_communication.txt: -------------------------------------------------------------------------------- 1 | The PDO trigger method for this project will be time driven triggering. 2 | This means that PDO messages will be transmitted after a centain period of time. 3 | Other possible trigger methods are: 4 | - Event driven (change of state) 5 | - Master polling 6 | - Synchronized (group polling) 7 | 8 | Due to time constrains and also because time driven is mostly used in our company, 9 | only time driven communication will be implemented at this time. 10 | 11 | In the time driven communication the PDO is transmitted when event timer reaches zero. So if PDO event timer is set to 100 ms, the PDO will be transmitted every 100 ms. 12 | 13 | As per CANopen standard, the RPDO communication parameters start at 0x1400 and mapping at 1600. 14 | 15 | Choosing PDO COB-ID: 16 | Typically, when the number of each PDO type (transmit/receive) does not exceed 4, the COB-ID's are allocated as follows: 17 | - TPDO1: 180h + NodeId 18 | - TPDO2: 280h + NodeId 19 | - TPDO3: 380h + NodeId 20 | - TPDO4: 480h + NodeId 21 | - RPDO1: 200h + NodeId 22 | - RPDO2: 300h + NodeId 23 | - RPDO3: 400h + NodeId 24 | - RPDO4: 500h + NodeId 25 | 26 | If the number of TPDO's or RPDO's needs to be greater than 4, then another COB-ID can be used as long as it doesn't belong to any existing node in the same CANopen network. -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/comments.txt: -------------------------------------------------------------------------------- 1 | Throuout the code, use the following comments 2 | 3 | in header files: 4 | 5 | /***************************** Global Definitions ****************************/ 6 | /***************************** Global Prototypes *****************************/ 7 | 8 | in source files: 9 | 10 | /***************************** Local Definitions *****************************/ 11 | /****************************** Local Variables ******************************/ 12 | /****************************** Local Prototypes *****************************/ 13 | /****************************** Global Functions *****************************/ 14 | /****************************** Local Functions ******************************/ 15 | 16 | 17 | 18 | Commenting a function: 19 | 20 | /** 21 | * @brief Short description 22 | * 23 | * Long description 24 | * 25 | * @param param1 Parameter description 26 | * @return Return value description 27 | */ 28 | static uint8_t do_something(int param1); 29 | 30 | Commenting a structure: 31 | 32 | /** 33 | * @brief A structure to represents a CAN message. This is not a part of CANopen protocol, 34 | * but underlying CAN protocol. This is the data that will be passed around the CAN network. 35 | */ 36 | typedef struct { ... } 37 | 38 | Commenting a variable: 39 | 40 | /* @brief The CAN message identifier */ 41 | uint16_t id; -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt_master.h: -------------------------------------------------------------------------------- 1 | // 2 | // nmt_master.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 23.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_nmt_master_h 10 | #define CCanOpenStack_nmt_master_h 11 | 12 | #include 13 | #include "can_message.h" 14 | #include "co_node.h" 15 | #include "nmt.h" 16 | 17 | /***************************** Global Prototypes *****************************/ 18 | /** 19 | * @brief Sends an NMT command to a given node 20 | * @param node_id ID of the recepient node 21 | * @param command The NMT command that will be sent 22 | */ 23 | extern void nmt_master_send_command(uint8_t node_id, nmt_command command); 24 | /** 25 | * @brief Processes a heartbeat message and updates internal node state list 26 | * @param message Raw CAN message that contains heartbeat 27 | * @return Returns the NMT state of a node 28 | */ 29 | extern nmt_state nmt_master_process_heartbeat(can_message *message); 30 | /** 31 | * @brief Returns number of nodes regiestered in the node list 32 | * @return Returns number of registered nodes 33 | */ 34 | extern int nmt_master_num_nodes(void); 35 | /** 36 | * @brief Returns the registered node list 37 | * @return Returns the registered node list 38 | */ 39 | extern co_node *nmt_master_node_list(void); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/readme.txt: -------------------------------------------------------------------------------- 1 | All the variables and functions in this project are explicitly scoped. Meaning that in every declaration there is either "extern" or "static" keyword, which defines the scope of the variable or function. 2 | The coding style is also very consistent. 3 | - All the externally accessible functions or variables must be declared in a .h file as extern. 4 | - All the externally accessible functions or variables must be prefixed with a name of a file that they belong to. (exception: od_initialize.h) 5 | - All the privat functions must be declared near the beginning of a .c file before any function definition. 6 | - In .h files, the order of items is always: 7 | + Global definitions (#define, type definitions) 8 | + Global variables 9 | + Global prototypes 10 | - In .c files, the order of items is always: 11 | + Local definitions 12 | + Local variables 13 | + Local prototypes 14 | + Global functions 15 | + Local functions 16 | - Variables should be defined with the smallest possible scope. 17 | - Functions should only be declared external if they are used outside of the file that they belong to, otherwise declare as static. 18 | - There can only be one return statement per function. 19 | - All function and variable names are all lower case and underscore ('_') is used as word separator. 20 | - Defined constants are all upper case. -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/emergency.txt: -------------------------------------------------------------------------------- 1 | The emergency message sent by an individual node has a CAN identifier of 80h plus the Node ID. 2 | 3 | Contents: 4 | 5 | bytes 0 - 1: 6 | - error code 7 | 8 | byte 2: 9 | - error register 10 | 11 | bytes 3 - 7: 12 | - manufacturer specific error code 13 | 14 | 15 | Detailed explanation: 16 | 17 | - Error Code: 2-byte error code – see Table below 18 | - Error Register: copy of the 1-byte error register at [1001h,00h] 19 | - Manufacturer Specific Error Field: Up to 5 bytes for manufacturer specific error codes 20 | 21 | Error codes: 22 | 00xx Error Reset or No Error 23 | 10xx Generic Error 24 | 20xx Current 25 | 21xx Current, device input side 26 | 22xx Current inside the device 27 | 23xx Current, device output side 28 | 30xx Voltage 29 | 31xx Mains Voltage 30 | 32xx Voltage inside the device 31 | 33xx Output Voltage 32 | 40xx Temperature 33 | 41xx Ambient Temperature 34 | 42xx Device Temperature 35 | 50xx Device Hardware 36 | 60xx Device Software 37 | 61xx Internal Software 38 | 62xx User Software 39 | 63xx Data Set 40 | 70xx Additional Modules 41 | 80xx Monitoring 42 | 81xx Communication 43 | 8110 CAN Overrun (Objects Lost) 44 | 8120 CAN in Passive Error Mode 45 | 8130 Life Guard Error or Heartbeat Error 46 | 8140 Recovered from Bus Off 47 | 8150 Transmit COB ID Collision 48 | 82xx Protocol Error 49 | 8210 PDO not processed due to length of error 50 | 8220 PDO length exceeded 51 | 90xx External Error 52 | F0xx Additional Functions 53 | FFxx Device Specific -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/co_stack.h: -------------------------------------------------------------------------------- 1 | // 2 | // co_stack.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 8.12.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_co_stack_h 10 | #define CCanOpenStack_co_stack_h 11 | 12 | #include "co_node.h" 13 | #include "can_message.h" 14 | 15 | /***************************** Global Prototypes *****************************/ 16 | /** 17 | * @brief Initializes can open node with object dictionary, SDO and PDO 18 | * @param node CANopen node which will be initialized 19 | * @param node_id 20 | * @param od Object dictionary that will be used for a given node 21 | * @param tick_count Current system tick count 22 | * @return Returns error (0 = no error, otherwise error). 23 | */ 24 | extern int co_stack_initialize(co_node *node, uint8_t node_id, object_dictionary *od, uint32_t tick_count); 25 | /** 26 | * @brief Handles normal CANopen node tasks, such as heartbeat transmission and TPDO (if enabled) 27 | * @param node CANopen node that will be used 28 | * @param tick_count Current system tick count 29 | */ 30 | extern void co_stack_do_tasks(co_node *node, uint32_t tick_count); 31 | /** 32 | * @brief Parses CAN messages and calls appropriate prosessing functions (for NMT, SDO, PDO and other CANopen messages) 33 | * @param node CANopen node that will be used 34 | * @param msg Raw CAN message 35 | */ 36 | extern void co_stack_can_message_received_handler(co_node *node, can_message *msg); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt.txt: -------------------------------------------------------------------------------- 1 | NMT: 2 | COB-ID base is 0. 3 | 4 | NMT states: 5 | - (Power-On/Reset -> Initialization) 6 | - Initialization 7 | - Pre-Operational 8 | - Operational 9 | - Stopped 10 | - (Reset Node -> Power-On/Reset) 11 | - (Reset Communication -> Initialization) 12 | 13 | How it happens: 14 | Device boots up, goes into Initialization. 15 | Initializes entire application and CAN/CANopen interfaces. 16 | Transmits boot up message. 17 | Switches to pre-operational. 18 | --- 19 | Master node can tell nodes to switch to any of three modes: pre-operational, operational and stopped. 20 | Master node can also tell nodes to "Reset communication" or "Reset node". After reset node sends boot up message and stays in pre-operational. 21 | 22 | 23 | Communication that can happen during different NMT states: 24 | - Initialization: Boot-up message 25 | - Pre-operational: SDO, EMCY, SYNC/TIME, heartbeat/nodeguard, NMT 26 | - Operational: SDO, EMCY, SYNC/TIME, heartbeat/nodeguard, NMT, PDO 27 | - Stopped: heartbeat/nodeguard, NMT 28 | 29 | Basically operational state differs from pre-operational only by PDO capability. 30 | 31 | Node auto-start: 32 | Object Dictionary entry 1F80 is implemented to report that the node is auto- starting 33 | 34 | 35 | ------------------- 36 | NMT message structure: 37 | 2 bytes 38 | byte 0: command: 39 | - 1 = Operational 40 | - 2 = Stopped 41 | - 128 = Pre-operational 42 | - 129 = Reset Node 43 | - 130 = Reset Communication 44 | byte 1: destination: 45 | - 0 (broadcast) 46 | - node ID 47 | 48 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/can_bus.c: -------------------------------------------------------------------------------- 1 | // 2 | // can_bus.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "can_bus.h" 10 | #include "log.h" 11 | /***************************** Local Definitions *****************************/ 12 | #define MAX_HANDLERS 20 13 | 14 | /****************************** Local Variables ******************************/ 15 | static int num_handlers = 0; 16 | static void (*message_received_handlers[MAX_HANDLERS])(can_message *message); 17 | 18 | /****************************** Local Prototypes *****************************/ 19 | static void message_received(can_message *message); 20 | 21 | /****************************** Global Functions *****************************/ 22 | extern void can_bus_send_message(can_message *message) 23 | { 24 | message_received(message); 25 | } 26 | extern int can_bus_register_message_received_handler(void (*handler_function)(can_message *message)) 27 | { 28 | int error = 0; 29 | if (num_handlers < MAX_HANDLERS) 30 | { 31 | message_received_handlers[num_handlers++] = handler_function; 32 | } 33 | else 34 | { 35 | error = 1; 36 | } 37 | return error; 38 | } 39 | 40 | /****************************** Local Functions ******************************/ 41 | static void message_received(can_message *message) 42 | { 43 | for (int i = 0; i < num_handlers; i++) 44 | { 45 | (*message_received_handlers[i])(message); 46 | } 47 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, timojaask 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/project.xcworkspace/xcshareddata/CCanOpenStack.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | FEA8998C-545C-412C-80F2-81DC0C0D2000 9 | IDESourceControlProjectName 10 | CCanOpenStack 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | F0EFD635-8B98-45FB-B039-F9689F6EF1BF 14 | ssh://github.com/timojaask/CCanOpenStack.git 15 | 16 | IDESourceControlProjectPath 17 | CCanOpenStack/CCanOpenStack.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | F0EFD635-8B98-45FB-B039-F9689F6EF1BF 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/timojaask/CCanOpenStack.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | F0EFD635-8B98-45FB-B039-F9689F6EF1BF 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | F0EFD635-8B98-45FB-B039-F9689F6EF1BF 36 | IDESourceControlWCCName 37 | CCanOpenStack 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo_client.c: -------------------------------------------------------------------------------- 1 | // 2 | // sdo_client.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "sdo_client.h" 10 | #include "sdo.h" 11 | 12 | /****************************** Local Prototypes *****************************/ 13 | static void sdo_set_client_command(can_message *message, sdo_client_command command); 14 | 15 | /****************************** Global Functions *****************************/ 16 | extern void sdo_message_expedited_download_request(can_message *message, uint16_t index, uint8_t sub_index, uint32_t data) 17 | { 18 | sdo_set_client_command(message, sdo_command_client_download_init); 19 | sdo_set_expedited_bit(message, 1); 20 | sdo_set_size_indicated_bit(message, 1); 21 | sdo_set_expedited_data_size(message, 4); 22 | sdo_set_index(message, index); 23 | sdo_set_sub_index(message, sub_index); 24 | sdo_set_expedited_data(message, data); 25 | } 26 | 27 | extern void sdo_message_upload_request(can_message *message, uint16_t index, uint8_t sub_index) 28 | { 29 | sdo_set_client_command(message, sdo_command_client_upload_init); 30 | sdo_set_index(message, index); 31 | sdo_set_sub_index(message, sub_index); 32 | } 33 | 34 | extern void sdo_message_client_abort_transfer(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code) 35 | { 36 | sdo_set_client_command(message, sdo_command_client_abort_transfer); 37 | sdo_set_message_abort_transfer_data(message, index, sub_index, abort_code); 38 | } 39 | 40 | /****************************** Local Functions ******************************/ 41 | static void sdo_set_client_command(can_message *message, sdo_client_command command) 42 | { 43 | set_sdo_command(message, (int)command); 44 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo_client.h: -------------------------------------------------------------------------------- 1 | // 2 | // sdo_client.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_sdo_client_h 10 | #define CCanOpenStack_sdo_client_h 11 | 12 | #include 13 | #include "can_message.h" 14 | #include "co_node.h" 15 | 16 | /***************************** Global Prototypes *****************************/ 17 | /** 18 | * @brief Initializes an expedited download request and writes it into a raw CAN message 19 | * @param message pointer to a raw CAN message where the download request will be written 20 | * @param index Object dictionary index 21 | * @param sub_index Object dictionary sub index 22 | * @param data The data to be written into the given index and sub index 23 | */ 24 | extern void sdo_message_expedited_download_request(can_message *message, uint16_t index, uint8_t sub_index, uint32_t data); 25 | /** 26 | * @brief Initializes an upload request and writes it into a raw CAN message 27 | * @param message pointer to a raw CAN message where the upload request will be written 28 | * @param index Object dictionary index 29 | * @param sub_index Object dictionary sub index 30 | */ 31 | extern void sdo_message_upload_request(can_message *message, uint16_t index, uint8_t sub_index); 32 | /** 33 | * @brief Initializes an abort transfer message and writes it into a raw CAN message 34 | * @param message pointer to a raw CAN message where the abort transfer message will be written 35 | * @param index Object dictionary index 36 | * @param sub_index Object dictionary sub index 37 | * @param abort_code SDO abort code 38 | */ 39 | extern void sdo_message_client_abort_transfer(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code); 40 | #endif 41 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/object_dictionary.txt: -------------------------------------------------------------------------------- 1 | 2 | /* Minimum OD entries: 3 | * 1000.00 : Device Type 4 | * 00 : uint32, value: 0 (no device profile) 5 | * 1001.00 : Error register 6 | * 00 : uint8, value: 0 (no errors) 7 | * 1008.00 : Device name 8 | * 00 : uint32 (4-char string), value: XXXX (use define to specify) 9 | * 1017.00 : Heartbeat producer time 10 | * 00 : uint16, value: 0 (milliseconds. It's writable and can change any time) 11 | * 1018.xx : Identity object 12 | * 00 : NumEntries: uint8, value: 1 13 | * 01 : Vendor ID: uint32, value: XXXXXX (use define to specify) 14 | * 1200.xx : SDO server parameters 15 | * 00 : NumEntries: uint8, value: 2 16 | * 01 : RSDO COB ID: uint32, value: 0x600 + NodeId 17 | * 02 : TSDO COB ID: uint32, value: 0x580 + NodeId 18 | * 1400.xx : RPDO parameters 19 | * 00 : NumEntries: uint8, value: 2 20 | * 01 : RPDO COB ID: uint32, value: XXXX 21 | * 02 : Transmission type: uint8, value: 0xFE (manufacturer specific) 22 | * 1600.xx : RPDO mapping 23 | * 00 : NumEntries: uint8, value: XX (depends on number of objects mapped into PDO) 24 | * 01 : Mapping entry, uint32, value: 0xiiiissbb (index, subindex, num bits) 25 | * .. : --- || --- 26 | * 64 : --- || --- 27 | * 1800.xx : TPDO parameters 28 | * 00 : NumEntries: uint8, value: ************ 29 | * 01 : TPDO COB ID: uint32, value: XXXX 30 | * 02 : Transmision type: uint8, value: 0xFE (manufacturer specific) 31 | * 03 : Inhibit time: uint16, value: 0 (100 micro seconds, minimum time between Tx) 32 | * 04 : Reserved: uint8, value: -- (attempt to read or write should return abort) 33 | * 05 : Event time: uint16, value: XXX (milliseconds) 34 | * 1A00.xx : TPDO mapping 35 | * 00 : NumEntries: uint8, value: XX (depends on number of objects mapped into PDO) 36 | * 01 : Mapping entry, uint32, value: 0xiiiissbb (index, subindex, num bits) 37 | * .. : --- || --- 38 | * 64 : --- || --- 39 | * 1F80.00 : NMT startup 40 | * 00 : uint32, value: xx (bit 2: 0 = auto-start, 1 = no auto start), either RO or RW 41 | * 42 | */ -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt_slave.h: -------------------------------------------------------------------------------- 1 | // 2 | // nmt_slave.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 23.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_nmt_slave_h 10 | #define CCanOpenStack_nmt_slave_h 11 | 12 | #include "can_message.h" 13 | #include "co_node.h" 14 | #include "nmt.h" 15 | 16 | /***************************** Global Prototypes *****************************/ 17 | /** 18 | * @brief Processes NMT message and calls approriate functions (such as setting node state) 19 | * @param message The raw CAN message containing the NMT command 20 | * @param node CANopen node, which will process the NMT message 21 | */ 22 | extern void nmt_slave_process_command(can_message *message, co_node *node); 23 | /** 24 | * @brief Checks if it's time to send heartbeat and if yes, sends it 25 | * @param node CANopen node, which will process the NMT message 26 | * @param tick_count Current system tick count 27 | */ 28 | extern int nmt_slave_send_heartbeat(co_node *node, uint32_t tick_count); 29 | /** 30 | * @brief Sends heartbeat immediately, even if it's not time for it yet 31 | * @param node CANopen node, which will process the NMT message 32 | */ 33 | extern void nmt_slave_send_heartbeat_immediately(co_node *node); 34 | /** 35 | * @brief Registers an event handler which will be called whenver node changes state 36 | * @param handler_function A function pointer with signature (void)function_hander(nmt_state state); 37 | */ 38 | extern int nmt_slave_register_state_changed_handler(void (*handler_function)(nmt_state state)); 39 | /** 40 | * @brief Register a function that resets node communication. It whill be called whenever NMT reset comm command is received. 41 | * @param function A function pointer with signature (void)function_hander(void); 42 | */ 43 | extern void nmt_slave_set_reset_communication_function(void (*function)(void)); 44 | /** 45 | * @brief Register a function that resets node. It whill be called whenever NMT reset node command is received. 46 | * @param function A function pointer with signature (void)function_hander(void); 47 | */ 48 | extern void nmt_slave_set_reset_node_function(void (*function)(void)); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt_master.c: -------------------------------------------------------------------------------- 1 | // 2 | // nmt_master.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "nmt_master.h" 10 | #include "can_bus.h" 11 | #include "co_node.h" 12 | 13 | /***************************** Local Definitions *****************************/ 14 | #define MAX_NODES 16 15 | 16 | /****************************** Local Variables ******************************/ 17 | static uint8_t data[2]; 18 | static co_node nodes[MAX_NODES]; 19 | static int num_nodes = 0; 20 | 21 | /****************************** Local Prototypes *****************************/ 22 | 23 | /****************************** Global Functions *****************************/ 24 | extern void nmt_master_send_command(uint8_t node_id, nmt_command command) 25 | { 26 | can_message message; 27 | // NMT master message has COB ID 0 28 | message.id = 0; 29 | message.data_len = 2; 30 | message.data = data; 31 | // Set command byte (byte 0) 32 | message.data[0] = (uint8_t)command; 33 | // Set node id byte (byte 1) 34 | message.data[1] = (uint8_t)node_id; 35 | // Send nmt command 36 | can_bus_send_message(&message); 37 | } 38 | extern nmt_state nmt_master_process_heartbeat(can_message *message) 39 | { 40 | // Get source node ID (COB ID - 700h) 41 | uint16_t node_id = message->id - 0x700; 42 | // Get node NMT state 43 | nmt_state state = (nmt_state)message->data[0]; 44 | 45 | // Look through nodes list. If node is on the list, update it's state, 46 | // If node is not on the list, add it to the list 47 | uint8_t is_on_list = 0; 48 | for (int i = 0; i < num_nodes; i++) 49 | { 50 | if (nodes[i].node_id == node_id) 51 | { 52 | // Found the node on the list. Update it's state 53 | is_on_list = 1; 54 | nodes[i].state = state; 55 | break; 56 | } 57 | } 58 | if (!is_on_list) 59 | { 60 | // Node not yet on the list - add it 61 | nodes[num_nodes].node_id = node_id; 62 | nodes[num_nodes].state = state; 63 | num_nodes++; 64 | } 65 | return state; 66 | } 67 | extern int nmt_master_num_nodes(void) 68 | { 69 | return num_nodes; 70 | } 71 | extern co_node *nmt_master_node_list(void) 72 | { 73 | return nodes; 74 | } 75 | 76 | /****************************** Local Functions ******************************/ -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/pdo.h: -------------------------------------------------------------------------------- 1 | // 2 | // pdo.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 27.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_pdo_h 10 | #define CCanOpenStack_pdo_h 11 | 12 | #include 13 | #include "can_message.h" 14 | #include "co_node.h" 15 | 16 | /***************************** Global Definitions ****************************/ 17 | typedef struct { 18 | uint16_t index; 19 | uint8_t sub_index; 20 | uint8_t length; 21 | } pdo_mapping_param; 22 | 23 | /***************************** Global Prototypes *****************************/ 24 | /** 25 | * @brief Initializes system PDO's. Call this before using PDO functions. 26 | * @param tick_count Current system tick count 27 | */ 28 | extern void pdo_initialize(uint32_t tick_count); 29 | /** 30 | * @brief Add RPDO mapping to the object dictionary in order to enable receiving the given PDO. 31 | * @param node CANopen node, to which the RPDO mapping will be added. 32 | * @param cob_id RPDO COB-ID. 33 | * @param mapping An array of RPDO mapping parameters to be written to the OD. 34 | * @param num_params Number of entries in the RPDO array. 35 | * @return Returns error (0 = no error, otherwise error). 36 | */ 37 | extern int pdo_add_rpdo(co_node *node, uint16_t cob_id, pdo_mapping_param mapping[], int num_params); 38 | /** 39 | * @brief Add TPDO mapping to the object dictionary in order to enable transmitting the given PDO. 40 | * @param node CANopen node, to which the TPDO mapping will be added. 41 | * @param cob_id TPDO COB-ID. 42 | * @param inhibit Minimum time between PDO transmissions in 100 us units. 43 | * @param event Maximum time between PDO trasmission in 1 ms units. 44 | * @param mapping An array of TPDO mapping parameters to be written to the OD. 45 | * @param num_params Number of entries in the TPDO array. 46 | * @return Returns error (0 = no error, otherwise error). 47 | */ 48 | extern int pdo_add_tpdo(co_node *node, uint16_t cob_id, uint16_t inhibit, uint16_t event, pdo_mapping_param mapping[], int num_params); 49 | /** 50 | * @brief Processes a received PDO message 51 | * @param node CANopen node, which is supposed to receive the PDO 52 | * @param msg Raw CAN message, which contains PDO data 53 | * @return Returns error (0 = no error, otherwise error). 54 | */ 55 | extern int pdo_process_rpdo(co_node *node, can_message *msg); 56 | /** 57 | * @brief Checks if any of TPDOs on a given node are ready for sending and sends them 58 | * @param node CANopen node that contains the TPDOs 59 | * @param tick_count Current system tick count 60 | * @return Returns error (0 = no error, otherwise error). 61 | */ 62 | extern int pdo_send_tpdo(co_node *node, uint32_t tick_count); 63 | #endif 64 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo_server.h: -------------------------------------------------------------------------------- 1 | // 2 | // sdo_server.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_sdo_server_h 10 | #define CCanOpenStack_sdo_server_h 11 | 12 | #include "can_message.h" 13 | #include "co_node.h" 14 | #include "od.h" 15 | 16 | /***************************** Global Definitions ****************************/ 17 | 18 | /***************************** Global Prototypes *****************************/ 19 | /** 20 | * @brief Initializes SDO server. Call this before using SDO server functionality. 21 | * @param node CANopen node, on which the SDO server will be initialized. 22 | * @return Returns error (0 = no error, otherwise error). 23 | */ 24 | extern int sdo_server_init(co_node *node); 25 | /** 26 | * @brief Returns given node's RSDO COB-ID 27 | * @param node CANopen node 28 | * @param cob_id Pointer to a place where the COB-ID will be written 29 | * @return Returns error (0 = no error, otherwise error). 30 | */ 31 | extern int sdo_server_get_rsdo_cob_id(co_node *node, uint32_t *cob_id); 32 | /** 33 | * @brief Returns given node's TSDO COB-ID 34 | * @param node CANopen node 35 | * @param cob_id Pointer to a place where the COB-ID will be written 36 | * @return Returns error (0 = no error, otherwise error). 37 | */ 38 | extern int sdo_server_get_tsdo_cob_id(co_node *node, uint32_t *cob_id); 39 | /** 40 | * @brief Processes an SDO request and sends a response 41 | * @param message Raw CAN message containing SDO request 42 | * @param node CANopen node 43 | */ 44 | extern void sdo_server_process_request(can_message *message, co_node *node); 45 | /** 46 | * @brief Initializes a download response and writes it into a raw CAN message 47 | * @param message pointer to a raw CAN message where the download response will be written 48 | * @param index Object dictionary index 49 | * @param sub_index Object dictionary sub index 50 | */ 51 | extern void sdo_message_download_response(can_message *message, uint16_t index, uint8_t sub_index); 52 | /** 53 | * @brief Initializes an expedited upload response and writes it into a raw CAN message 54 | * @param message pointer to a raw CAN message where the upload response will be written 55 | * @param index Object dictionary index 56 | * @param sub_index Object dictionary sub index 57 | * @param data The data that was read from the given index and sub index 58 | */ 59 | extern void sdo_message_expedited_upload_response(can_message *message, uint16_t index, uint8_t sub_index, uint32_t data); 60 | /** 61 | * @brief Initializes an abort transfer message and writes it into a raw CAN message 62 | * @param message pointer to a raw CAN message where the abort transfer message will be written 63 | * @param index Object dictionary index 64 | * @param sub_index Object dictionary sub index 65 | * @param abort_code SDO abort code 66 | */ 67 | extern void sdo_message_server_abort_transfer(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/co_stack.c: -------------------------------------------------------------------------------- 1 | // 2 | // co_stack.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 8.12.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "co_stack.h" 11 | #include "co_node.h" 12 | #include "log.h" 13 | #include "nmt_slave.h" 14 | #include "pdo.h" 15 | #include "sdo_server.h" 16 | 17 | /****************************** Local Variables ******************************/ 18 | 19 | /****************************** Local Prototypes *****************************/ 20 | 21 | /****************************** Global Functions *****************************/ 22 | extern int co_stack_initialize(co_node *node, uint8_t node_id, object_dictionary *od, uint32_t tick_count) 23 | { 24 | int error = 0; 25 | node->node_id = node_id; 26 | node->state = nmt_state_boot_up; 27 | node->od = od; 28 | error |= od_initialize(node->od); 29 | // Add some TPDO and RPDO mapping 30 | pdo_initialize(tick_count); 31 | // Initialize SDO server 32 | error |= sdo_server_init(node); 33 | node->state = nmt_state_pre_operational; 34 | return error; 35 | } 36 | 37 | extern void co_stack_do_tasks(co_node *node, uint32_t tick_count) 38 | { 39 | // Send heartbeat 40 | nmt_slave_send_heartbeat(node, tick_count); 41 | // Send TPDOs 42 | if (node->state == nmt_state_operational) 43 | { 44 | pdo_send_tpdo(node, tick_count); 45 | } 46 | } 47 | 48 | extern void co_stack_can_message_received_handler(co_node *node, can_message *msg) 49 | { 50 | uint32_t rsdo_cob_id = 0; 51 | int error = sdo_server_get_rsdo_cob_id(node, &rsdo_cob_id); 52 | if (error) 53 | { 54 | log_write_ln("co_stack: ERROR: unable to get SDO server RSDO COB-ID"); 55 | } 56 | else 57 | { 58 | // See if this is an NMT command 59 | if (msg->id == 0 || msg->id == node->node_id) 60 | { 61 | // This is NMT command, either broadcase or directed to this node 62 | nmt_slave_process_command(msg, node); 63 | // Send heartbeat 64 | nmt_slave_send_heartbeat_immediately(node); 65 | } 66 | else if (node->state == nmt_state_operational || node->state == nmt_state_pre_operational) 67 | { 68 | // See if this is an SDO request 69 | if (msg->id == rsdo_cob_id) 70 | { 71 | // SDO request received 72 | sdo_server_process_request(msg, node); 73 | } 74 | 75 | // See if this is an RPDO message 76 | else if (node->state == nmt_state_operational) 77 | { 78 | // pdo_process_rpdo automatically makes sure the message is RPDO before processing it, 79 | // and if not, the message will be simply ignored. 80 | error |= pdo_process_rpdo(node, msg); 81 | if (error) 82 | { 83 | log_write_ln("co_stack: ERROR: unable to process RPDO"); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | /****************************** Local Functions ******************************/ -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/CCanOpenStack.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 20.10.2013 \" DATE 7 | .Dt CCanOpenStack 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm CCanOpenStack, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/xcuserdata/timo.xcuserdatad/xcschemes/CCanOpenStack.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/od.h: -------------------------------------------------------------------------------- 1 | // 2 | // od.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_od_h 10 | #define CCanOpenStack_od_h 11 | 12 | #include 13 | #include "od_object.h" 14 | 15 | /***************************** Global Definitions ****************************/ 16 | typedef enum { 17 | OD_RESULT_OK, 18 | OD_RESULT_OBJECT_NOT_FOUND, 19 | OD_RESULT_READING_WRITE_ONLY_OBJECT, 20 | OD_RESULT_WRITING_READ_ONLY_OBJECT 21 | } od_result; 22 | 23 | typedef struct { 24 | od_object *objects; 25 | int num_objects; 26 | } object_dictionary; 27 | 28 | /***************************** Global Prototypes *****************************/ 29 | /** 30 | * @brief Initializes object dictionary. Call this before using any other OD functions. 31 | * @param od The object dictionary in use 32 | * @return Returns error (0 = no error, otherwise error). 33 | */ 34 | extern int od_initialize(object_dictionary *od); 35 | /** 36 | * @brief Read a value from a given object dictionary object 37 | * @param od The object dictionary in use 38 | * @param index Object's index 39 | * @param sub_index Object's sub-index 40 | * @param data Pointer to a place where the object's data will be put 41 | * @return Returns od_result 42 | */ 43 | extern od_result od_read(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t *data); 44 | /** 45 | * @brief Write a value to a given object dictionary object 46 | * @param od The object dictionary in use 47 | * @param index Object's index 48 | * @param sub_index Object's sub-index 49 | * @param data The data that will be writted to into the object 50 | * @return Returns od_result 51 | */ 52 | extern od_result od_write(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t data); 53 | /** 54 | * @brief Write a value to a given object dictionary object, even if it's read only 55 | * @param od The object dictionary in use 56 | * @param index Object's index 57 | * @param sub_index Object's sub-index 58 | * @param data The data that will be writted to into the object 59 | * @return Returns od_result 60 | */ 61 | extern od_result od_internal_write(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t data); 62 | /** 63 | * @brief Get object dictionary object's access type 64 | * @param od The object dictionary in use 65 | * @param index Object's index 66 | * @param sub_index Object's sub-index 67 | * @param access_type Pointer to a place where the object's access type will be written 68 | * @return Returns od_result 69 | */ 70 | extern od_result od_get_access_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_access_type *access_type); 71 | /** 72 | * @brief Get object dictionary object's data type 73 | * @param od The object dictionary in use 74 | * @param index Object's index 75 | * @param sub_index Object's sub-index 76 | * @param data_type Pointer to a place where the object's data type will be written 77 | * @return Returns od_result 78 | */ 79 | extern od_result od_get_data_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_data_type *data_type); 80 | /** 81 | * @brief Get object dictionary object's data length 82 | * @param od The object dictionary in use 83 | * @param index Object's index 84 | * @param sub_index Object's sub-index 85 | * @param num_bits Pointer to a place where the object's data length in bytes will be written 86 | * @return Returns od_result 87 | */ 88 | extern od_result od_get_data_len(object_dictionary *od, uint16_t index, uint8_t sub_index, int *num_bits); 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_can_bus.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_can_bus.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_can_bus.h" 10 | #include "can_bus.h" 11 | #include "log.h" 12 | #include "test_util.h" 13 | 14 | /****************************** Local Variables ******************************/ 15 | static uint16_t next_id = 0; 16 | static uint8_t next_len = 0; 17 | static uint8_t next_data[8]; 18 | static int error = 0; 19 | static uint8_t test_running = 0; 20 | 21 | /****************************** Local Prototypes *****************************/ 22 | static void send_message(uint16_t id, uint8_t len, uint32_t data); 23 | static void test_message_received_handler1(can_message *message); 24 | static void test_message_received_handler2(can_message *message); 25 | static void check_received_message(can_message *message, int handler_num); 26 | 27 | /****************************** Global Functions *****************************/ 28 | extern int test_can_bus_run(void) 29 | { 30 | test_running = 1; 31 | error |= can_bus_register_message_received_handler(test_message_received_handler1); 32 | error |= can_bus_register_message_received_handler(test_message_received_handler2); 33 | 34 | send_message(0x123, 4, 0x12345); 35 | send_message(0x456, 6, 0x98765); 36 | send_message(0x789, 8, 0x23452); 37 | send_message(0x123, 2, 0x3456); 38 | test_running = 0; 39 | return error; 40 | } 41 | 42 | /****************************** Local Functions ******************************/ 43 | static void send_message(uint16_t id, uint8_t len, uint32_t data) 44 | { 45 | next_id = id; 46 | next_len = len; 47 | for (int i = 0; i < len; i++) 48 | { 49 | next_data[i] = (data >> (i*8)) & 0xFF; 50 | } 51 | can_message msg = {.id = next_id, .data_len = next_len, .data = next_data}; 52 | can_bus_send_message(&msg); 53 | } 54 | static void test_message_received_handler1(can_message *message) 55 | { 56 | check_received_message(message, 1); 57 | 58 | } 59 | static void test_message_received_handler2(can_message *message) 60 | { 61 | check_received_message(message, 2); 62 | } 63 | static void check_received_message(can_message *message, int handler_num) 64 | { 65 | if (test_running) 66 | { 67 | if (message->id != next_id) 68 | { 69 | error = 1; 70 | log_write("test_can_bus: message received handler %d: wrong id, id:%Xh, len:%d, data:", handler_num, message->id, message->data_len); 71 | } 72 | if (!error) 73 | { 74 | if (message->data_len != next_len) 75 | { 76 | error = 1; 77 | log_write("test_can_bus: message received handler %d: wrong len, id:%Xh, len:%d, data:", handler_num, message->id, message->data_len); 78 | } 79 | } 80 | if (!error) 81 | { 82 | // Compare each byte in data 83 | for (int i = 0; i < message->data_len; i++) 84 | { 85 | if (message->data[i] != next_data[i]) 86 | { 87 | error = 1; 88 | log_write("test_can_bus: message received handler %d: wrong data, id:%Xh, len:%d, data:", handler_num, message->id, message->data_len); 89 | break; 90 | } 91 | } 92 | } 93 | if (!error) 94 | { 95 | log_write("test_can_bus: receive %d OK id:%Xh, len:%d, data:", handler_num, message->id, message->data_len); 96 | } 97 | print_message_data(message); 98 | log_write_ln(""); 99 | } 100 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/todo.txt: -------------------------------------------------------------------------------- 1 | TODO: 2 | - Move TPDO counters into node. At the moment the system can only have node, because TPDO counters are shared. 3 | - Revise the code and see what needs to be changed in order to support multiple nodes per system. 4 | - In co_stack.h, co_stack_initialize() add support for initializing SDO client, right now it automatically initializes SDO server. 5 | - Function that puts node into operational state if enabled in OD 0x1F80 + test 6 | - Reset communication and reset device functions. Also add them to nmt_slave.c to be called when appropriated command is received. 7 | - Implement functions that set the "current_state" variable in nmt_slave.c. 8 | - In SDO functions, on write, check for data type overflow and if so, return abort message 9 | -------------------------------------------------------------------------------------- 10 | DONE: 11 | - The heartbeat and other even based activity needs to make sure they are sending messages at a constant average interval. Meaning if interval is set to be 100, first msg is sent at 0 and second is sent at 120 (too much time passed +20), then the third message should be sent around 200 (wait for 80 instead of full 100). How to implement this probably depends on the OS running. In FreeRTOS it might be possible to make OS take care of this by calling a special delay function. 12 | - Transmit boot up message (transmitted after NMT initialization is finished, when going into NMT pre-operational) 13 | - Test how a node would function by having a function that calls NMT heartbeat and TPDO functions on every cycle with an updated tick count. 14 | - Also have a message received function that would figure out what kind if message this is and call appropriate message parser (NMT, RPDO or SDO). 15 | - Function that checks timer and sends heartbeat if enabled in OD + test 16 | - Change SDO send/receive take COB ID from object dictionary 0x1200 17 | - Function that processes RPDO if enabled in OD and uses mapping to save values in OD + test 18 | - Function that checks timer and starts TPDO transmission if enabled in OD + test 19 | - Function that prepares TPDO message using mapping + test 20 | - TPDO testing should work like NMT heartbeat test with tick count and the rest kind of like RPDO function but in reverse. 21 | - RPDO testing is straight forward. Make a can message, send to RPDO function which should first see if RPDO is enabled and if so, then parse the message and write values into OD according to mapping. Then the test function should read back these values to see that they have been written correctly. 22 | - Add od_setup_sdo(node_id) and od_setup_t/rpdo(pdo_num, cob_id, inhibit, event) functions to set initial OD parameters for SDO and PDOs. Also add od_set_r/tpdo_mapping(pdo_num, pdo_mapping_param[ ], num_params) function that takes an array if mapping parameters and writes them into OD. These PDO related functions should return error if there are not enough PDO slots available in the dictionary and that if more is needed, they should be typed into od_initialize.h list. 23 | - Put all testing OD things in the same OD. Remove small ODs from test files and put all data into od_initialize.h 24 | - NMT function that takes tick count and OD. Sends heartbeat. Test by manipulating tick count and calling the NMT function. 25 | - Create a blueprint for default object dictionary 26 | - Implement data types and access types 27 | - Test data types and access types 28 | - Create NMT tests 29 | - Create node reset tests 30 | - NMT processing 31 | - Implement heartbeat 32 | - Create functions that create and parse SDO messages 33 | + Add SDO abort codes enum 34 | + Add SDO client abort transfer message test 35 | + Add tests for server SDO messages 36 | - Somehow create and SDO server - one with OD and listening to CAN bus for SDO message and responsds 37 | - Create a CANopen node structure that holds object dictionary. 38 | - Remove objects structure array from od.c, and instead pass a pointer to each od.c function, so that they could be used with any object dictionary from any node. -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_rpdo.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_pdo.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 27.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_rpdo.h" 10 | #include "can_message.h" 11 | #include "co_node.h" 12 | #include "log.h" 13 | #include "nmt.h" 14 | #include "od.h" 15 | #include "pdo.h" 16 | #include "utils.h" 17 | 18 | /***************************** Local Definitions *****************************/ 19 | 20 | /****************************** Local Variables ******************************/ 21 | static pdo_mapping_param rpdo_params1[] = { 22 | {0x2004, 1, 8}, 23 | {0x2004, 2, 8}, 24 | {0x2004, 3, 8}, 25 | }; 26 | static uint32_t rpdo_params1_expect[] = { 27 | 11, 22, 33 28 | }; 29 | static pdo_mapping_param rpdo_params2[] = { 30 | {0x2005, 1, 3}, // 2 31 | {0x2005, 2, 16}, // 18 32 | {0x2005, 3, 7}, // 25 33 | {0x2006, 1, 9}, // 34 34 | {0x2006, 2, 21}, // 55 35 | {0x2006, 3, 5}, // 60 36 | }; 37 | static uint32_t rpdo_params2_expect[] = { 38 | 0x6, 0x4CC8, 0x6A, 0x98, 0x13444E, 0x16 39 | }; 40 | static uint8_t data[8]; 41 | /****************************** Local Prototypes *****************************/ 42 | static int test_pdo(co_node *node, int cob_id, pdo_mapping_param *params, int num_params, uint32_t *expected); 43 | 44 | /****************************** Global Functions *****************************/ 45 | extern int test_rpdo_run(void) 46 | { 47 | int error = 0; 48 | object_dictionary od; 49 | od_initialize(&od); 50 | co_node node; 51 | node.node_id = 15; 52 | node.state = nmt_state_operational; 53 | node.od = &od; 54 | 55 | // Test simple RPDO parameters 56 | data[0] = 11; 57 | data[1] = 22; 58 | data[2] = 33; 59 | error = test_pdo(&node, 0x201, rpdo_params1, UTILS_ARRAY_SIZE(rpdo_params1), rpdo_params1_expect); 60 | 61 | // Test some complex RPDO parameters // 0x169A227263526646 (6, 0x4CC8, 0x6A, 0x98, 0x13444E, 0x16) 62 | data[0] = 0x46; 63 | data[1] = 0x66; 64 | data[2] = 0x52; 65 | data[3] = 0x63; 66 | data[4] = 0x72; 67 | data[5] = 0x22; 68 | data[6] = 0x9A; 69 | data[7] = 0x16; 70 | error = test_pdo(&node, 0x301, rpdo_params2, UTILS_ARRAY_SIZE(rpdo_params2), rpdo_params2_expect); 71 | return error; 72 | } 73 | 74 | /****************************** Local Functions ******************************/ 75 | static int test_pdo(co_node *node, int cob_id, pdo_mapping_param *params, int num_params, uint32_t *expected) 76 | { 77 | int error = pdo_add_rpdo(node, cob_id, params, num_params); 78 | if (!error) 79 | { 80 | // Send some data and run process RPDO function, which should write data into object dictionary 81 | can_message msg; 82 | msg.id = cob_id; 83 | msg.data_len = 8; 84 | msg.data = data; 85 | error |= pdo_process_rpdo(node, &msg); 86 | } 87 | if (!error) 88 | { 89 | // See if data was written into object dictionary properly 90 | for (int i = 0; i < num_params; i++) 91 | { 92 | uint16_t index = params[i].index; 93 | uint8_t sub_index = params[i].sub_index; 94 | uint32_t value; 95 | od_result result = od_read(node->od, index, sub_index, &value); 96 | if (result == OD_RESULT_OK) 97 | { 98 | if (value == expected[i]) 99 | { 100 | // OK 101 | } 102 | else 103 | { 104 | log_write_ln("test_pdo: Value read from OD %04Xh:%d not what was expected (read: %X, expected: %X)", index, sub_index, value, expected[i]); 105 | error = 1; 106 | break; 107 | } 108 | } 109 | else 110 | { 111 | log_write_ln("test_pdo: Reading OD %04Xh:%d failed", index, sub_index); 112 | error = 1; 113 | break; 114 | } 115 | } 116 | } 117 | 118 | if (error) 119 | { 120 | log_write_ln("rpdo test: ERROR: pdo_add_rpdo RPDO param test"); 121 | } 122 | else 123 | { 124 | log_write_ln("rpdo test: RPDO param test OK"); 125 | } 126 | return error; 127 | } 128 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_tpdo.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_tpdo.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 28.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_tpdo.h" 10 | #include "co_node.h" 11 | #include "can_bus.h" 12 | #include "log.h" 13 | #include "pdo.h" 14 | #include "utils.h" 15 | 16 | /***************************** Local Definitions *****************************/ 17 | 18 | /****************************** Local Variables ******************************/ 19 | uint8_t test_running = 0; 20 | static pdo_mapping_param tpdo_params1[] = { 21 | {0x2007, 1, 8}, 22 | {0x2007, 2, 8}, 23 | {0x2007, 3, 8}, 24 | }; 25 | static uint32_t tpdo_params1_expect[] = { 26 | 0x47, 0x48, 0x49 27 | }; 28 | uint16_t tpdo_cob_id; 29 | uint8_t tpdo_received = 0; 30 | 31 | /****************************** Local Prototypes *****************************/ 32 | static int wait_for_tpdo(co_node *node, uint32_t *tick_count, uint16_t event_time_ms); 33 | static void can_message_received(can_message *msg); 34 | 35 | /****************************** Global Functions *****************************/ 36 | extern int test_tpdo_run(void) 37 | { 38 | int error = 0; 39 | uint32_t tick_count = 0; 40 | uint16_t event_time_ms = 90; 41 | test_running = 1; 42 | 43 | object_dictionary od; 44 | od_initialize(&od); 45 | co_node node; 46 | node.node_id = 15; 47 | node.state = nmt_state_operational; 48 | node.od = &od; 49 | 50 | can_bus_register_message_received_handler(can_message_received); 51 | 52 | int num_params = UTILS_ARRAY_SIZE(tpdo_params1); 53 | tpdo_cob_id = 0x181; 54 | error |= pdo_add_tpdo(&node, tpdo_cob_id, 10, event_time_ms, tpdo_params1, num_params); 55 | 56 | // Wait for the first TPDO (should be received when tick count is 90 ms) 57 | wait_for_tpdo(&node, &tick_count, event_time_ms); 58 | // Wait for the second TPDO (should be received when tick count is 180 ms) 59 | wait_for_tpdo(&node, &tick_count, event_time_ms); 60 | // Wait for the third TPDO (should be received when tick count is 270 ms) 61 | wait_for_tpdo(&node, &tick_count, event_time_ms); 62 | 63 | test_running = 0; 64 | return error; 65 | } 66 | /****************************** Local Functions ******************************/ 67 | static int wait_for_tpdo(co_node *node, uint32_t *tick_count, uint16_t event_time_ms) 68 | { 69 | int error = 0; 70 | int i; 71 | uint32_t event_expected_tick_count = *tick_count + event_time_ms; 72 | for (i = 0; i < 30; i++) 73 | { 74 | *tick_count += 10; 75 | error |= pdo_send_tpdo(node, *tick_count); 76 | if (!tpdo_received && *tick_count < event_expected_tick_count) 77 | { 78 | // Waiting for timed TPDO 79 | log_write_ln("test_tpdo: Waiting for TPDO %Xh, tick count: %lu ms", tpdo_cob_id, *tick_count); 80 | } 81 | else if (tpdo_received && *tick_count < event_expected_tick_count) 82 | { 83 | // TPDO received too early 84 | error = 1; 85 | log_write_ln("test_tpdo: ERROR: TPDO %Xh was received too early, tick count: %lu", tpdo_cob_id, *tick_count); 86 | } 87 | else if (!tpdo_received && *tick_count == event_expected_tick_count) 88 | { 89 | // Time has passed, but TPDO not yet received 90 | error = 1; 91 | log_write_ln("test_tpdo: ERROR: TPDO %Xh was not received, tick count: %lu", tpdo_cob_id, *tick_count); 92 | } 93 | else if (tpdo_received && *tick_count == event_expected_tick_count) 94 | { 95 | // TPDO received on time 96 | tpdo_received = 0; 97 | log_write_ln("test_tpdo: TPDO %Xh received OK, tick count: %lu ms", tpdo_cob_id, *tick_count); 98 | break; 99 | } 100 | } 101 | return error; 102 | } 103 | // When node sends TPDO, the "message received" event will be handled in this function. 104 | // This function will set 'tpdo_received' if the expected TPDO message is received. 105 | static void can_message_received(can_message *msg) 106 | { 107 | if (test_running) 108 | { 109 | // Use this for catching TPDOs from the slave 110 | if (msg->id == tpdo_cob_id) 111 | { 112 | tpdo_received = 1; 113 | // This is the TPDO that our test slave was supposed to send 114 | // Check if the data is matching with what was supposed to be sent 115 | int expected_len = UTILS_ARRAY_SIZE(tpdo_params1_expect); 116 | if (msg->data_len == expected_len) 117 | { 118 | for (int i = 0; i < msg->data_len; i++) 119 | { 120 | if (msg->data[i] != tpdo_params1_expect[i]) 121 | { 122 | tpdo_received = 0; 123 | log_write_ln("test_tpdo: ERROR: received TPDO data doesn't match with what was sent"); 124 | break; 125 | } 126 | } 127 | } 128 | else 129 | { 130 | tpdo_received = 0; 131 | log_write_ln("test_tpdo: ERROR: received TPDO length is invalid (expected: %d, got: %d)", expected_len, msg->data_len); 132 | } 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/nmt_slave.c: -------------------------------------------------------------------------------- 1 | // 2 | // nmt_slave.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "nmt_slave.h" 10 | #include "can_bus.h" 11 | #include "co_node.h" 12 | #include "log.h" 13 | #include "nmt.h" 14 | 15 | /***************************** Local Definitions *****************************/ 16 | #define MAX_HANDLERS 10 17 | 18 | /****************************** Local Variables ******************************/ 19 | static int num_handlers = 0; 20 | static void (*state_changed_handlers[MAX_HANDLERS])(nmt_state state); 21 | static void (*reset_communication_function)(void); 22 | static void (*reset_node_function)(void); 23 | static uint32_t previous_heartbeat_tick = 0; 24 | 25 | /****************************** Local Prototypes *****************************/ 26 | static void change_state(nmt_state state, co_node *node); 27 | static void state_changed(co_node *node); 28 | static void send_heartbeat(co_node *node); 29 | 30 | /****************************** Global Functions *****************************/ 31 | extern void nmt_slave_process_command(can_message *message, co_node *node) 32 | { 33 | // Get command 34 | nmt_command command = (nmt_command)message->data[0]; 35 | // Change state 36 | switch (command) 37 | { 38 | case nmt_command_operational: 39 | change_state(nmt_state_operational, node); 40 | break; 41 | case nmt_command_pre_operational: 42 | change_state(nmt_state_pre_operational, node); 43 | break; 44 | case nmt_command_reset_communication: 45 | if (reset_communication_function) 46 | { 47 | reset_communication_function(); 48 | } 49 | else 50 | { 51 | log_write_ln("nmt_slave: ERROR: reset communication function is not set"); 52 | } 53 | break; 54 | case nmt_command_reset_node: 55 | if (reset_node_function) 56 | { 57 | reset_node_function(); 58 | } 59 | else 60 | { 61 | log_write_ln("nmt_slave: ERROR: reset node function is not set"); 62 | } 63 | break; 64 | case nmt_command_stopped: 65 | change_state(nmt_state_stopped, node); 66 | break; 67 | default: 68 | log_write_ln("nmt_slave: ERROR: unknown command: %d", (int)command); 69 | break; 70 | } 71 | } 72 | /* Returns error (0 = no error, otherwise error occurred) */ 73 | extern int nmt_slave_send_heartbeat(co_node *node, uint32_t tick_count) 74 | { 75 | int error = 0; 76 | // Check object dictionary to see if heartbeat is enabled and if so what is it's frequency 77 | // The value is found in OD index 0x1017, sub index 0, data type uint16 78 | uint32_t interval; 79 | od_result result = od_read(node->od, 0x1017, 0, &interval); 80 | if (result == OD_RESULT_OK) 81 | { 82 | if (interval != 0) 83 | { 84 | if (tick_count - previous_heartbeat_tick >= interval) 85 | { 86 | send_heartbeat(node); 87 | previous_heartbeat_tick = tick_count; 88 | } 89 | } 90 | } 91 | else 92 | { 93 | log_write_ln("nmt_slave: ERROR: reading heartbeat OD entry failed"); 94 | error = 1; 95 | } 96 | return error; 97 | } 98 | extern void nmt_slave_send_heartbeat_immediately(co_node *node) 99 | { 100 | send_heartbeat(node); 101 | } 102 | extern int nmt_slave_register_state_changed_handler(void (*handler_function)(nmt_state state)){ 103 | int error = 0; 104 | if (num_handlers < MAX_HANDLERS) 105 | { 106 | state_changed_handlers[num_handlers++] = handler_function; 107 | } 108 | else 109 | { 110 | error = 1; 111 | } 112 | return error; 113 | } 114 | 115 | extern void nmt_slave_set_reset_communication_function(void (*function)(void)) 116 | { 117 | reset_communication_function = function; 118 | } 119 | extern void nmt_slave_set_reset_node_function(void (*function)(void)) 120 | { 121 | reset_node_function = function; 122 | } 123 | 124 | /****************************** Local Functions ******************************/ 125 | static void change_state(nmt_state state, co_node *node) 126 | { 127 | if (state != node->state) 128 | { 129 | switch (state) 130 | { 131 | case nmt_state_operational: 132 | node->state = nmt_state_operational; 133 | state_changed(node); 134 | break; 135 | case nmt_state_pre_operational: 136 | node->state = nmt_state_pre_operational; 137 | state_changed(node); 138 | break; 139 | case nmt_state_stopped: 140 | node->state = nmt_state_stopped; 141 | state_changed(node); 142 | break; 143 | default: 144 | log_write_ln("nmt_slave: ERROR: unknown state: %d", (int)state); 145 | break; 146 | } 147 | } 148 | } 149 | static void state_changed(co_node *node) 150 | { 151 | for (int i = 0; i < num_handlers; i++) 152 | { 153 | (*state_changed_handlers[i])(node->state); 154 | } 155 | } 156 | static void send_heartbeat(co_node *node) 157 | { 158 | can_message message; 159 | uint8_t data = (uint8_t)node->state; 160 | message.id = 0x700 + node->node_id; 161 | message.data_len = 1; 162 | message.data = &data; 163 | can_bus_send_message(&message); 164 | } 165 | 166 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo.c: -------------------------------------------------------------------------------- 1 | // 2 | // sdo.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "sdo.h" 10 | #include "log.h" 11 | 12 | /****************************** Local Prototypes *****************************/ 13 | 14 | /****************************** Global Functions *****************************/ 15 | 16 | extern sdo_server_command sdo_get_server_command(can_message *message) 17 | { 18 | sdo_server_command command = (sdo_server_command)get_sdo_command(message); 19 | return command; 20 | } 21 | 22 | extern sdo_client_command sdo_get_client_command(can_message *message) 23 | { 24 | sdo_client_command command = (sdo_client_command)get_sdo_command(message); 25 | return command; 26 | } 27 | 28 | extern uint8_t sdo_get_expedited_bit(can_message *message) 29 | { 30 | uint8_t expedited = 0; 31 | if (message->data_len >= 1) 32 | { 33 | expedited = ((message->data[0] >> 1) & 0x1); 34 | } 35 | else 36 | { 37 | log_write_ln("sdo: ERROR: data_len < 1"); 38 | } 39 | return expedited; 40 | } 41 | 42 | extern uint8_t sdo_get_size_indicated_bit(can_message *message) 43 | { 44 | uint8_t size_indicated = 0; 45 | if (message->data_len >= 1) 46 | { 47 | size_indicated = (message->data[0] & 0x1); 48 | } 49 | else 50 | { 51 | log_write_ln("sdo: ERROR: data_len < 1"); 52 | } 53 | return size_indicated; 54 | } 55 | 56 | extern int sdo_get_expedited_data_size(can_message *message) 57 | { 58 | int data_size = 0; 59 | if (message->data_len >= 4) 60 | { 61 | if (sdo_get_size_indicated_bit(message)) 62 | { 63 | int num_bytes_not_set = ((message->data[0] >> 2) & 0x3); 64 | data_size = 4 - num_bytes_not_set; 65 | } 66 | } 67 | else 68 | { 69 | log_write_ln("sdo: ERROR: data_len < 4"); 70 | } 71 | return data_size; 72 | } 73 | 74 | extern uint16_t sdo_get_index(can_message *message) 75 | { 76 | uint16_t index = 0; 77 | if (message->data_len >= 4) 78 | { 79 | index |= message->data[1]; 80 | index |= ((uint16_t)message->data[2]) << 8; 81 | } 82 | else 83 | { 84 | log_write_ln("sdo: ERROR: data_len < 4"); 85 | } 86 | return index; 87 | } 88 | 89 | extern uint8_t sdo_get_sub_index(can_message *message) 90 | { 91 | uint8_t sub_index = 0; 92 | if (message->data_len >= 4) 93 | { 94 | sub_index = message->data[3]; 95 | } 96 | else 97 | { 98 | log_write_ln("sdo: ERROR: data_len < 4"); 99 | } 100 | return sub_index; 101 | } 102 | 103 | extern uint32_t sdo_get_expedited_data(can_message *message) 104 | { 105 | uint32_t data = 0; 106 | if (message->data_len == 8) 107 | { 108 | data |= message->data[4]; 109 | data |= ((uint32_t)message->data[5]) << 8; 110 | data |= ((uint32_t)message->data[6]) << 16; 111 | data |= ((uint32_t)message->data[7]) << 24; 112 | } 113 | else 114 | { 115 | log_write_ln("sdo: ERROR: data_len < 8"); 116 | } 117 | return data; 118 | } 119 | 120 | extern void sdo_set_message_abort_transfer_data(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code) 121 | { 122 | sdo_set_index(message, index); 123 | sdo_set_sub_index(message, sub_index); 124 | sdo_set_expedited_data(message, abort_code); 125 | } 126 | 127 | extern void sdo_set_expedited_bit(can_message *message, uint8_t expedited) 128 | { 129 | if (message->data_len >= 1) 130 | { 131 | message->data[0] &= ~(0x1 << 1); 132 | message->data[0] |= ((expedited & 0x1) << 1); 133 | } 134 | else 135 | { 136 | log_write_ln("sdo: ERROR: data_len < 1"); 137 | } 138 | } 139 | 140 | extern void sdo_set_size_indicated_bit(can_message *message, uint8_t size_indicated) 141 | { 142 | if (message->data_len >= 1) 143 | { 144 | message->data[0] &= ~(0x1); 145 | message->data[0] |= (size_indicated & 0x1); 146 | } 147 | else 148 | { 149 | log_write_ln("sdo: ERROR: data_len < 1"); 150 | } 151 | } 152 | 153 | /* Note: Also sets data size indicated bit */ 154 | extern void sdo_set_expedited_data_size(can_message *message, int data_size) 155 | { 156 | if (message->data_len >= 4 && data_size <= 4 && data_size >= 0) 157 | { 158 | sdo_set_size_indicated_bit(message, 1); 159 | int num_bytes_not_set = 4 - data_size; 160 | message->data[0] &= ~(0x3 << 2); 161 | message->data[0] |= (num_bytes_not_set << 2); 162 | } 163 | else 164 | { 165 | log_write_ln("sdo: ERROR: data_len < 4"); 166 | } 167 | } 168 | 169 | extern void sdo_set_index(can_message *message, uint16_t index) 170 | { 171 | if (message->data_len >= 4) 172 | { 173 | message->data[1] = index & 0xFF; 174 | message->data[2] = (index >> 8) & 0xFF; 175 | } 176 | else 177 | { 178 | log_write_ln("sdo: ERROR: data_len < 4"); 179 | } 180 | } 181 | 182 | extern void sdo_set_sub_index(can_message *message, uint8_t sub_index) 183 | { 184 | if (message->data_len >= 4) 185 | { 186 | message->data[3] = sub_index; 187 | } 188 | else 189 | { 190 | log_write_ln("sdo: ERROR: data_len < 4"); 191 | } 192 | } 193 | 194 | extern void sdo_set_expedited_data(can_message *message, uint32_t data) 195 | { 196 | if (message->data_len == 8) 197 | { 198 | message->data[4] = data & 0xFF; 199 | message->data[5] = (data >> 8) & 0xFF; 200 | message->data[6] = (data >> 16) & 0xFF; 201 | message->data[7] = (data >> 24 ) & 0xFF; 202 | } 203 | else 204 | { 205 | log_write_ln("sdo: ERROR: data_len < 8"); 206 | } 207 | } 208 | 209 | extern int get_sdo_command(can_message *message) 210 | { 211 | int command = (int)sdo_command_server_abort_transfer; 212 | if (message->data_len >= 1) 213 | { 214 | command = (int)((message->data[0] >> 5) & 0x7); 215 | } 216 | else 217 | { 218 | log_write_ln("sdo: ERROR: data_len < 1"); 219 | } 220 | return command; 221 | } 222 | 223 | extern void set_sdo_command(can_message *message, int sdo_command) 224 | { 225 | if (message->data_len >= 1) 226 | { 227 | // first make sure the bits are empty 228 | message->data[0] &= ~(0x7 << 5); 229 | message->data[0] |= ((sdo_command & 0x7) << 5); 230 | } 231 | else 232 | { 233 | log_write_ln("sdo: ERROR: data_len < 1"); 234 | } 235 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/od.c: -------------------------------------------------------------------------------- 1 | // 2 | // od.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 20.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "od.h" 10 | #include "od_initialize.h" 11 | #include "log.h" 12 | #include "utils.h" 13 | 14 | /****************************** Local Variables ******************************/ 15 | 16 | /****************************** Local Prototypes *****************************/ 17 | static int get_data_type_num_bits(object_dictionary *od, od_data_type data_type); 18 | static void print_object_not_found_error(uint16_t index, uint8_t sub_index); 19 | 20 | /****************************** Global Functions *****************************/ 21 | extern int od_initialize(object_dictionary *od) 22 | { 23 | int error = 0; 24 | int num_objects = UTILS_ARRAY_SIZE(od_default_objects); 25 | if (num_objects >= OD_INITIALIZE_MAX_OBJECTS) 26 | { 27 | error = 1; 28 | log_write_ln("od: ERROR: failed to initialize object dictionary because maximum number of object is exceeded"); 29 | } 30 | if (!error) 31 | { 32 | od->num_objects = num_objects; 33 | od->objects = od_objects; 34 | // Copy default OD values from OD default objects array 35 | for (int i = 0; i < num_objects; i++) 36 | { 37 | od_objects[i].access_type = od_default_objects[i].access_type; 38 | od_objects[i].data = od_default_objects[i].data; 39 | od_objects[i].data_type = od_default_objects[i].data_type; 40 | od_objects[i].index = od_default_objects[i].index; 41 | od_objects[i].sub_index = od_default_objects[i].sub_index; 42 | } 43 | } 44 | return error; 45 | } 46 | extern od_result od_read(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t *data) 47 | { 48 | od_result result = OD_RESULT_OK; 49 | int object_found = 0; 50 | for (int i = 0; i < od->num_objects; i++) 51 | { 52 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 53 | { 54 | if (od->objects[i].access_type != od_access_type_wo) 55 | { 56 | *data = od->objects[i].data; 57 | } 58 | else 59 | { 60 | result = OD_RESULT_READING_WRITE_ONLY_OBJECT; 61 | log_write_ln("od: object %04Xh:%d cannot be read as it's write only", index, sub_index); 62 | } 63 | object_found = 1; 64 | break; 65 | } 66 | } 67 | if (!object_found) 68 | { 69 | result = OD_RESULT_OBJECT_NOT_FOUND; 70 | print_object_not_found_error(index, sub_index); 71 | } 72 | return result; 73 | } 74 | 75 | extern od_result od_write(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t data) 76 | { 77 | od_result result = OD_RESULT_OK; 78 | int object_found = 0; 79 | for (int i = 0; i < od->num_objects; i++) 80 | { 81 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 82 | { 83 | if (od->objects[i].access_type == od_access_type_wo || od->objects[i].access_type == od_access_type_rw) 84 | { 85 | od->objects[i].data = data; 86 | } 87 | else 88 | { 89 | result = OD_RESULT_WRITING_READ_ONLY_OBJECT; 90 | log_write_ln("od: object %04Xh:%d cannot be written as it's read only", index, sub_index); 91 | } 92 | object_found = 1; 93 | break; 94 | } 95 | } 96 | if (!object_found) 97 | { 98 | result = OD_RESULT_OBJECT_NOT_FOUND; 99 | print_object_not_found_error(index, sub_index); 100 | } 101 | return result; 102 | } 103 | 104 | // Internal write writes even read only values 105 | extern od_result od_internal_write(object_dictionary *od, uint16_t index, uint8_t sub_index, uint32_t data) 106 | { 107 | od_result result = OD_RESULT_OK; 108 | int object_found = 0; 109 | for (int i = 0; i < od->num_objects; i++) 110 | { 111 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 112 | { 113 | od->objects[i].data = data; 114 | object_found = 1; 115 | break; 116 | } 117 | } 118 | if (!object_found) 119 | { 120 | result = OD_RESULT_OBJECT_NOT_FOUND; 121 | print_object_not_found_error(index, sub_index); 122 | } 123 | return result; 124 | } 125 | 126 | extern od_result od_get_access_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_access_type *access_type) 127 | { 128 | od_result result = OD_RESULT_OK; 129 | int object_found = 0; 130 | for (int i = 0; i < od->num_objects; i++) 131 | { 132 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 133 | { 134 | *access_type = od->objects[i].access_type; 135 | object_found = 1; 136 | break; 137 | } 138 | } 139 | if (!object_found) 140 | { 141 | result = OD_RESULT_OBJECT_NOT_FOUND; 142 | print_object_not_found_error(index, sub_index); 143 | } 144 | return result; 145 | } 146 | 147 | extern od_result od_get_data_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_data_type *data_type) 148 | { 149 | od_result result = OD_RESULT_OK; 150 | int object_found = 0; 151 | for (int i = 0; i < od->num_objects; i++) 152 | { 153 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 154 | { 155 | *data_type = od->objects[i].data_type; 156 | object_found = 1; 157 | break; 158 | } 159 | } 160 | if (!object_found) 161 | { 162 | result = OD_RESULT_OBJECT_NOT_FOUND; 163 | print_object_not_found_error(index, sub_index); 164 | } 165 | return result; 166 | } 167 | 168 | extern od_result od_get_data_len(object_dictionary *od, uint16_t index, uint8_t sub_index, int *num_bits) 169 | { 170 | od_result result = OD_RESULT_OK; 171 | int object_found = 0; 172 | for (int i = 0; i < od->num_objects; i++) 173 | { 174 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 175 | { 176 | *num_bits = get_data_type_num_bits(od, od->objects[i].data_type); 177 | object_found = 1; 178 | break; 179 | } 180 | } 181 | if (!object_found) 182 | { 183 | result = OD_RESULT_OBJECT_NOT_FOUND; 184 | print_object_not_found_error(index, sub_index); 185 | } 186 | return result; 187 | } 188 | 189 | /****************************** Local Functions ******************************/ 190 | // Returns number of bits or 0 if error 191 | static int get_data_type_num_bits(object_dictionary *od, od_data_type data_type) 192 | { 193 | int num_bits = 0; 194 | for (int i = 0; i < od->num_objects; i++) 195 | { 196 | // The OD index of data type is the od_data_type enum converted to integer 197 | if (od->objects[i].index == (uint16_t)data_type && od->objects[i].sub_index == 0) 198 | { 199 | // The value of data type OD entry is number of bits 200 | num_bits = (int)od->objects[i].data; 201 | break; 202 | } 203 | } 204 | if (num_bits == 0) 205 | { 206 | log_write_ln("od: ERROR: data type length could not be found"); 207 | } 208 | return num_bits; 209 | } 210 | static void print_object_not_found_error(uint16_t index, uint8_t sub_index) 211 | { 212 | log_write_ln("od: ERROR: object %04Xh:%d not found", index, sub_index); 213 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_sdo_server.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_sdo_server.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "test_sdo_server.h" 11 | #include "can_bus.h" 12 | #include "log.h" 13 | #include "od.h" 14 | #include "sdo.h" 15 | #include "sdo_server.h" 16 | #include "sdo_client.h" 17 | #include "utils.h" 18 | 19 | /***************************** Local Definitions *****************************/ 20 | #define TSDO_BASE 0x580 21 | 22 | /****************************** Local Variables ******************************/ 23 | static int error = 0; 24 | static uint8_t test_running = 0; 25 | static uint8_t msg_data[8]; 26 | static co_node node; 27 | static can_message message; 28 | static sdo_server_command expected_response_command; 29 | static uint16_t expected_index; 30 | static uint8_t expected_sub_index; 31 | static uint32_t expected_data; 32 | 33 | /****************************** Local Prototypes *****************************/ 34 | static void send_upload_request(uint16_t index, uint8_t sub_index, uint32_t data); 35 | static void send_download_request(uint16_t index, uint8_t sub_index, uint32_t data); 36 | static void message_received_handler(can_message *message); 37 | static void parse_upload_response(can_message *message); 38 | static void parse_download_response(can_message *message); 39 | 40 | /****************************** Global Functions *****************************/ 41 | extern int test_sdo_server_run(void) 42 | { 43 | test_running = 1; 44 | error = 0; 45 | can_bus_register_message_received_handler(message_received_handler); 46 | // Prepare object dictionary and other variables 47 | object_dictionary od; 48 | od_initialize(&od); 49 | node.node_id = 123; 50 | node.od = &od; 51 | message.data = msg_data; 52 | message.data_len = 8; 53 | 54 | sdo_server_init(&node); 55 | 56 | // Send upload requests 57 | // Set error to 1 before each request. If test passes, message handler will set it to 0. 58 | send_upload_request(0x2002, 1, 20021); 59 | send_upload_request(0x2002, 2, 20022); 60 | send_upload_request(0x2002, 3, 20023); 61 | if (error) 62 | { 63 | log_write_ln("test_sdo_server: upload responses failed"); 64 | } 65 | 66 | // Send download requests 67 | send_download_request(0x2002, 1, 5); 68 | send_download_request(0x2002, 2, 6); 69 | send_download_request(0x2002, 3, 7); 70 | if (error) 71 | { 72 | log_write_ln("test_sdo_server: download responses failed"); 73 | } 74 | 75 | // Send upload requests for data that was written in download requests 76 | send_upload_request(0x2002, 1, 5); 77 | send_upload_request(0x2002, 2, 6); 78 | send_upload_request(0x2002, 3, 7); 79 | if (error) 80 | { 81 | log_write_ln("test_sdo_server: second upload responses failed"); 82 | } 83 | 84 | test_running = 0; 85 | return error; 86 | } 87 | 88 | /****************************** Local Functions ******************************/ 89 | static void send_upload_request(uint16_t index, uint8_t sub_index, uint32_t data) 90 | { 91 | expected_response_command = sdo_command_server_upload_init; 92 | expected_index = index; 93 | expected_sub_index = sub_index; 94 | expected_data = data; 95 | log_write_ln("test_sdo_server: Sending upload request %04Xh:%d", index, sub_index); 96 | sdo_message_upload_request(&message, index, sub_index); 97 | sdo_server_process_request(&message, &node); 98 | } 99 | static void send_download_request(uint16_t index, uint8_t sub_index, uint32_t data) 100 | { 101 | expected_response_command = sdo_command_server_download_init; 102 | expected_index = index; 103 | expected_sub_index = sub_index; 104 | expected_data = data; 105 | log_write_ln("test_sdo_server: Sending download request %04Xh:%d = %lu", index, sub_index, data); 106 | sdo_message_expedited_download_request(&message, index, sub_index, data); 107 | sdo_server_process_request(&message, &node); 108 | } 109 | 110 | static void message_received_handler(can_message *message) 111 | { 112 | if (test_running) 113 | { 114 | if (message->id == TSDO_BASE + node.node_id) 115 | { 116 | sdo_server_command cmd = sdo_get_server_command(message); 117 | if (cmd == expected_response_command) 118 | { 119 | switch (cmd) 120 | { 121 | case sdo_command_server_upload_init: 122 | parse_upload_response(message); 123 | break; 124 | case sdo_command_server_download_init: 125 | parse_download_response(message); 126 | break; 127 | default: 128 | log_write_ln("test_sdo_server: ERROR: test error - unsupported command, even though it matches expected"); 129 | error = 1; 130 | break; 131 | } 132 | } 133 | else 134 | { 135 | log_write_ln("test_sdo_server: ERROR: wrong response command"); 136 | error = 1; 137 | } 138 | } 139 | else 140 | { 141 | log_write_ln("test_sdo_server: ERROR: wrong message COB ID: %02Xh, expected: %02Xh", message->id, (TSDO_BASE + node.node_id)); 142 | error = 1; 143 | } 144 | } 145 | } 146 | static void parse_download_response(can_message *message) 147 | { 148 | uint16_t index = sdo_get_index(message); 149 | uint8_t sub_index = sdo_get_sub_index(message); 150 | if (index == expected_index && sub_index == expected_sub_index) 151 | { 152 | log_write_ln("test_sdo_server: SDO download response OK"); 153 | } 154 | else 155 | { 156 | if (index != expected_index) 157 | { 158 | log_write_ln("test_sdo_server: ERROR: index: got: %04Xh, expected: %04Xh", index, expected_index); 159 | } 160 | else if (sub_index != expected_sub_index) 161 | { 162 | log_write_ln("test_sdo_server: ERROR: sub_index: got: %d, expected: %d", sub_index, expected_sub_index); 163 | } 164 | error = 1; 165 | } 166 | } 167 | static void parse_upload_response(can_message *message) 168 | { 169 | uint8_t expedited = sdo_get_expedited_bit(message); 170 | uint8_t size_indicated = sdo_get_size_indicated_bit(message); 171 | if (expedited && size_indicated) 172 | { 173 | uint16_t index = sdo_get_index(message); 174 | uint8_t sub_index = sdo_get_sub_index(message); 175 | uint32_t data = sdo_get_expedited_data(message); 176 | if (index == expected_index && sub_index == expected_sub_index && data == expected_data) 177 | { 178 | log_write_ln("test_sdo_server: SDO upload response OK"); 179 | } 180 | else 181 | { 182 | if (index != expected_index) 183 | { 184 | log_write_ln("test_sdo_server: ERROR: index: got: %04Xh, expected: %04Xh", index, expected_index); 185 | } 186 | else if (sub_index != expected_sub_index) 187 | { 188 | log_write_ln("test_sdo_server: ERROR: sub_index: got: %d, expected: %d", sub_index, expected_sub_index); 189 | } 190 | else if (data != expected_data) 191 | { 192 | log_write_ln("test_sdo_server: ERROR: data: got: %lu, expected: %lu", data, expected_data); 193 | } 194 | error = 1; 195 | } 196 | } 197 | else 198 | { 199 | log_write_ln("test_sdo_server: ERROR: transfer was not expedited - expedited expected"); 200 | error = 1; 201 | } 202 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo.h: -------------------------------------------------------------------------------- 1 | // 2 | // sdo.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_sdo_h 10 | #define CCanOpenStack_sdo_h 11 | 12 | #include 13 | #include "can_message.h" 14 | 15 | /***************************** Global Definitions ****************************/ 16 | typedef enum { 17 | sdo_command_server_upload_segment = 0, 18 | sdo_command_server_download_segment = 1, 19 | sdo_command_server_upload_init = 2, 20 | sdo_command_server_download_init = 3, 21 | sdo_command_server_abort_transfer = 4, 22 | } sdo_server_command; 23 | 24 | typedef enum { 25 | sdo_command_client_download_segment = 0, 26 | sdo_command_client_download_init = 1, 27 | sdo_command_client_upload_init = 2, 28 | sdo_command_client_upload_segment = 3, 29 | sdo_command_client_abort_transfer = 4, 30 | } sdo_client_command; 31 | 32 | typedef enum { 33 | sdo_abort_toggle_bit_not_alternated = 0x05030000, 34 | sdo_abort_sdo_timed_out = 0x05040000, 35 | sdo_abort_invalid_command_specifier = 0x05040001, 36 | sdo_abort_invalid_block_size = 0x05040002, 37 | sdo_abort_invalid_sequence_number = 0x05040003, 38 | sdo_abort_crc_error = 0x05040004, 39 | sdo_abort_out_of_memory = 0x05040005, 40 | sdo_abort_unsupported_object_access = 0x06010000, 41 | sdo_abort_read_a_write_only_object = 0x06010001, 42 | sdo_abort_write_a_read_only_object = 0x06010002, 43 | sdo_abort_object_does_not_exist = 0x06020000, 44 | sdo_abort_object_cannot_be_mapped = 0x06040041, 45 | sdo_abort_object_len_bigger_than_max_pdo_len = 0x06040042, 46 | sdo_abort_general_parameter_incompatibility = 0x06040043, 47 | sdo_abort_general_internal_incompatibility = 0x06040047, 48 | sdo_abort_access_failed_due_hardware_error = 0x06060000, 49 | sdo_abort_length_of_service_parameter_does_not_match = 0x06070010, 50 | sdo_abort_length_of_service_parameter_is_too_high = 0x06070012, 51 | sdo_abort_length_of_service_parameter_is_too_low = 0x06070013, 52 | sdo_abort_subindex_does_not_exist = 0x06090011, 53 | sdo_abort_value_range_of_parameter_exceeded = 0x06090030, 54 | sdo_abort_value_of_parameter_written_is_too_high = 0x06090031, 55 | sdo_abort_value_of_parameter_written_is_too_low = 0x06090032, 56 | sdo_abort_maximum_value_less_than_minimum = 0x06090036, 57 | sdo_abort_general_error = 0x08000000, 58 | sdo_abort_data_transfer_error = 0x08000020, 59 | sdo_abort_data_transfer_error_reason_local_control = 0x08000021, 60 | sdo_abort_data_transfer_error_reason_device_state = 0x08000022, 61 | sdo_abort_od_dynamic_generation_failed = 0x08000023 62 | } sdo_abort_code; 63 | 64 | /***************************** Global Prototypes *****************************/ 65 | /** 66 | * @brief Parses a server command specifier from a raw CAN message 67 | * @param message A raw CAN message containing the SDO message 68 | * @return Returns server command specifier 69 | */ 70 | extern sdo_server_command sdo_get_server_command(can_message *message); 71 | /** 72 | * @brief Parses a client command specifier from a raw CAN message 73 | * @param message A raw CAN message containing the SDO message 74 | * @return Returns client command specifier 75 | */ 76 | extern sdo_client_command sdo_get_client_command(can_message *message); 77 | /** 78 | * @brief Parses a is_expedited bit from a raw CAN message 79 | * @param message A raw CAN message containing the SDO message 80 | * @return Returns the bit containing is_expedited information 81 | */ 82 | extern uint8_t sdo_get_expedited_bit(can_message *message); 83 | /** 84 | * @brief Parses a size_indicated bit from a raw CAN message 85 | * @param message A raw CAN message containing the SDO message 86 | * @return Returns the bit containing size_indicated information 87 | */ 88 | extern uint8_t sdo_get_size_indicated_bit(can_message *message); 89 | /** 90 | * @brief Parses data_size bits from a raw CAN message and returns actual data size in bytes 91 | * @param message A raw CAN message containing the SDO message 92 | * @return Returns actual data size in bytes 93 | */ 94 | extern int sdo_get_expedited_data_size(can_message *message); 95 | /** 96 | * @brief Parses index bits from a raw CAN message 97 | * @param message A raw CAN message containing the SDO message 98 | * @return Returns object dictionary index 99 | */ 100 | extern uint16_t sdo_get_index(can_message *message); 101 | /** 102 | * @brief Parses sub index bits from a raw CAN message 103 | * @param message A raw CAN message containing the SDO message 104 | * @return Returns object dictionary sub index 105 | */ 106 | extern uint8_t sdo_get_sub_index(can_message *message); 107 | /** 108 | * @brief Parses data bits from a raw CAN message 109 | * @param message A raw CAN message containing the SDO message 110 | * @return Returns object dictionary data 111 | */ 112 | extern uint32_t sdo_get_expedited_data(can_message *message); 113 | /** 114 | * @brief Initializes an abort transfer message and writes it into a raw CAN message 115 | * @param message pointer to a raw CAN message where the abort transfer message will be written 116 | * @param index Object dictionary index 117 | * @param sub_index Object dictionary sub index 118 | * @param abort_code SDO abort code 119 | */ 120 | extern void sdo_set_message_abort_transfer_data(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code); 121 | /** 122 | * @brief Sets the is_expedited bit and writes it into the raw CAN message 123 | * @param message pointer to a raw CAN message where the is_expedited bit will be written 124 | * @param expedited The value of is_expedited bit 125 | */ 126 | extern void sdo_set_expedited_bit(can_message *message, uint8_t expedited); 127 | /** 128 | * @brief Sets the size_indicated bit and writes it into the raw CAN message 129 | * @param message pointer to a raw CAN message where the size_indicated bit will be written 130 | * @param size_indicated The value of size_indicated bit 131 | */ 132 | extern void sdo_set_size_indicated_bit(can_message *message, uint8_t size_indicated); 133 | /** 134 | * @brief Sets the data size bits and writes it into the raw CAN message (also sets size_indicated bit to 1) 135 | * @param message pointer to a raw CAN message where the data size bits will be written 136 | * @param data_size Data size in bytes 137 | */ 138 | extern void sdo_set_expedited_data_size(can_message *message, int data_size); 139 | /** 140 | * @brief Sets the OD index bits and writes it into the raw CAN message 141 | * @param message pointer to a raw CAN message where the OD index bits will be written 142 | * @param index The object dictionary index 143 | */ 144 | extern void sdo_set_index(can_message *message, uint16_t index); 145 | /** 146 | * @brief Sets the OD sub index bits and writes it into the raw CAN message 147 | * @param message pointer to a raw CAN message where the OD sub index bits will be written 148 | * @param sub_index The object dictionary sub index 149 | */ 150 | extern void sdo_set_sub_index(can_message *message, uint8_t sub_index); 151 | /** 152 | * @brief Sets the OD object's data bits and writes it into the raw CAN message 153 | * @param message pointer to a raw CAN message where the OD object's data bits will be written 154 | * @param data The object dictionary object's data 155 | */ 156 | extern void sdo_set_expedited_data(can_message *message, uint32_t data); 157 | /** 158 | * @brief Parses the SDO command specifier bits from a raw can message 159 | * @param message A raw CAN message containing the SDO 160 | * @return Returns SDO command specifier 161 | */ 162 | extern int get_sdo_command(can_message *message); 163 | /** 164 | * @brief Sets the SDO command specifier bits and writes it into the raw CAN message 165 | * @param message pointer to a raw CAN message where the SDO command specifier bits will be written 166 | * @param sdo_command The SDO command specifier 167 | */ 168 | extern void set_sdo_command(can_message *message, int sdo_command); 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/sdo_server.c: -------------------------------------------------------------------------------- 1 | // 2 | // sdo_server.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 22.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "sdo_server.h" 10 | #include "can_bus.h" 11 | #include "log.h" 12 | #include "sdo.h" 13 | 14 | /***************************** Local Definitions *****************************/ 15 | 16 | /****************************** Local Variables ******************************/ 17 | static uint8_t message_data[8]; 18 | static can_message response_msg; 19 | static uint32_t rsdo_cob_id = 0; 20 | static uint32_t tsdo_cob_id = 0; 21 | /****************************** Local Prototypes *****************************/ 22 | static void process_download_request(can_message *message, co_node *node); 23 | static void process_upload_request(can_message *message, co_node *node); 24 | static void send_abort_message(uint16_t index, uint8_t sub_index, sdo_abort_code code, co_node *node); 25 | static void sdo_set_server_command(can_message *message, sdo_server_command command); 26 | 27 | /****************************** Global Functions *****************************/ 28 | extern int sdo_server_init(co_node *node) 29 | { 30 | int error = 0; 31 | error = sdo_server_get_tsdo_cob_id(node, &tsdo_cob_id); 32 | if (error) 33 | { 34 | log_write_ln("sdo_server: ERROR: could not read TSDO COB-ID from OD"); 35 | } 36 | if (!error) 37 | { 38 | error = sdo_server_get_rsdo_cob_id(node, &rsdo_cob_id); 39 | if (error) 40 | { 41 | log_write_ln("sdo_server: ERROR: could not read RSDO COB-ID from OD"); 42 | } 43 | } 44 | if (!error) 45 | { 46 | tsdo_cob_id += node->node_id; 47 | od_result result = od_internal_write(node->od, 0x1200, 1, tsdo_cob_id); 48 | if (result != OD_RESULT_OK) 49 | { 50 | error = 1; 51 | log_write_ln("sdo_server: ERROR: could not save TSDO COB-ID to OD"); 52 | } 53 | } 54 | if (!error) 55 | { 56 | rsdo_cob_id += node->node_id; 57 | od_result result = od_internal_write(node->od, 0x1200, 2, rsdo_cob_id); 58 | if (result != OD_RESULT_OK) 59 | { 60 | error = 1; 61 | log_write_ln("sdo_server: ERROR: could not save RSDO COB-ID to OD"); 62 | } 63 | } 64 | if (!error) 65 | { 66 | response_msg.id = tsdo_cob_id; 67 | response_msg.data = message_data; 68 | response_msg.data_len = 8; 69 | } 70 | return error; 71 | } 72 | extern int sdo_server_get_rsdo_cob_id(co_node *node, uint32_t *cob_id) 73 | { 74 | int error = 0; 75 | od_result result = od_read(node->od, 0x1200, 2, cob_id); 76 | if (result != OD_RESULT_OK) 77 | { 78 | error = 1; 79 | } 80 | return error; 81 | } 82 | extern int sdo_server_get_tsdo_cob_id(co_node *node, uint32_t *cob_id) 83 | { 84 | int error = 0; 85 | od_result result = od_read(node->od, 0x1200, 1, cob_id); 86 | if (result != OD_RESULT_OK) 87 | { 88 | error = 1; 89 | } 90 | return error; 91 | } 92 | extern void sdo_server_process_request(can_message *message, co_node *node) 93 | { 94 | sdo_client_command cmd = sdo_get_client_command(message); 95 | switch (cmd) 96 | { 97 | case sdo_command_client_download_segment: 98 | log_write_ln("sdo_server: ERROR: segment download not supported"); 99 | break; 100 | case sdo_command_client_download_init: 101 | process_download_request(message, node); 102 | break; 103 | case sdo_command_client_upload_init: 104 | process_upload_request(message, node); 105 | break; 106 | case sdo_command_client_upload_segment: 107 | log_write_ln("sdo_server: ERROR: segment upload not supported"); 108 | break; 109 | case sdo_command_client_abort_transfer: 110 | // Transfer aborted by client. Nothing to do here, as it's only relevant 111 | // for segmented transfers, which are not supported by this implementation. 112 | break; 113 | default: 114 | log_write_ln("sdo_server: ERROR: unknown client command: %d", (int)cmd); 115 | break; 116 | } 117 | } 118 | 119 | extern void sdo_message_download_response(can_message *message, uint16_t index, uint8_t sub_index) 120 | { 121 | sdo_set_server_command(message, sdo_command_server_download_init); 122 | sdo_set_index(message, index); 123 | sdo_set_sub_index(message, sub_index); 124 | } 125 | 126 | extern void sdo_message_expedited_upload_response(can_message *message, uint16_t index, uint8_t sub_index, uint32_t data) 127 | { 128 | sdo_set_server_command(message, sdo_command_server_upload_init); 129 | sdo_set_expedited_bit(message, 1); 130 | sdo_set_size_indicated_bit(message, 1); 131 | sdo_set_expedited_data_size(message, 4); 132 | sdo_set_index(message, index); 133 | sdo_set_sub_index(message, sub_index); 134 | sdo_set_expedited_data(message, data); 135 | } 136 | 137 | extern void sdo_message_server_abort_transfer(can_message *message, uint16_t index, uint8_t sub_index, uint32_t abort_code) 138 | { 139 | sdo_set_server_command(message, sdo_command_server_abort_transfer); 140 | sdo_set_message_abort_transfer_data(message, index, sub_index, abort_code); 141 | } 142 | 143 | /****************************** Local Functions ******************************/ 144 | static void process_download_request(can_message *message, co_node *node) 145 | { 146 | uint8_t is_expedited = sdo_get_expedited_bit(message); 147 | uint8_t is_size_indicated = sdo_get_size_indicated_bit(message); 148 | uint16_t index = sdo_get_index(message); 149 | uint8_t sub_index = sdo_get_sub_index(message); 150 | if (is_expedited && is_size_indicated) 151 | { 152 | uint32_t data = sdo_get_expedited_data(message); 153 | od_result result = od_write(node->od, index, sub_index, data); 154 | switch (result) 155 | { 156 | case OD_RESULT_OK: 157 | sdo_message_download_response(&response_msg, index, sub_index); 158 | can_bus_send_message(&response_msg); 159 | break; 160 | case OD_RESULT_OBJECT_NOT_FOUND: 161 | send_abort_message(index, sub_index, sdo_abort_subindex_does_not_exist, node); 162 | break; 163 | case OD_RESULT_WRITING_READ_ONLY_OBJECT: 164 | send_abort_message(index, sub_index, sdo_abort_write_a_read_only_object, node); 165 | break; 166 | default: 167 | log_write_ln("sdo_server: ERROR: unknown od_result %d", (int)result); 168 | send_abort_message(index, sub_index, sdo_abort_general_error, node); 169 | break; 170 | } 171 | } 172 | else 173 | { 174 | send_abort_message(index, sub_index, sdo_abort_unsupported_object_access, node); 175 | log_write_ln("sdo_server: ERROR: only expedited transfer supported"); 176 | } 177 | } 178 | static void process_upload_request(can_message *message, co_node *node) 179 | { 180 | uint16_t index = sdo_get_index(message); 181 | uint8_t sub_index = sdo_get_sub_index(message); 182 | uint32_t data; 183 | od_result result = od_read(node->od, index, sub_index, &data); 184 | switch (result) 185 | { 186 | case OD_RESULT_OK: 187 | sdo_message_expedited_upload_response(&response_msg, index, sub_index, data); 188 | can_bus_send_message(&response_msg); 189 | break; 190 | case OD_RESULT_OBJECT_NOT_FOUND: 191 | send_abort_message(index, sub_index, sdo_abort_subindex_does_not_exist, node); 192 | break; 193 | default: 194 | log_write_ln("sdo_server: ERROR: unknown od_result %d", (int)result); 195 | send_abort_message(index, sub_index, sdo_abort_general_error, node); 196 | break; 197 | } 198 | } 199 | static void send_abort_message(uint16_t index, uint8_t sub_index, sdo_abort_code code, co_node *node) 200 | { 201 | sdo_message_server_abort_transfer(&response_msg, index, sub_index, code); 202 | can_bus_send_message(&response_msg); 203 | } 204 | 205 | static void sdo_set_server_command(can_message *message, sdo_server_command command) 206 | { 207 | set_sdo_command(message, (int)command); 208 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_od.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_od.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_od.h" 10 | #include "log.h" 11 | #include "od.h" 12 | #include "utils.h" 13 | 14 | /***************************** Local Definitions *****************************/ 15 | 16 | /****************************** Local Variables ******************************/ 17 | static object_dictionary od; 18 | 19 | /****************************** Local Prototypes *****************************/ 20 | static int test_read(object_dictionary *od, uint16_t index, uint8_t sub_index, int expected_result, uint32_t expected_value); 21 | static int test_write(object_dictionary *od, uint16_t index, uint8_t sub_index, int expected_result, uint32_t value); 22 | static int test_access(object_dictionary *od, uint16_t index, uint8_t sub_index, od_access_type expected_access); 23 | static int test_data_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_data_type expected_type, int expected_len); 24 | 25 | /****************************** Global Functions *****************************/ 26 | extern int test_od_run(void) 27 | { 28 | int error = 0; 29 | 30 | od_initialize(&od); 31 | 32 | // Test reading 33 | error |= test_read(&od, 0x2000, 0, OD_RESULT_OK, 3); 34 | error |= test_read(&od, 0x2000, 1, OD_RESULT_OK, 20001); 35 | error |= test_read(&od, 0x2000, 2, OD_RESULT_OK, 20002); 36 | error |= test_read(&od, 0x2000, 3, OD_RESULT_OK, 20003); 37 | 38 | // Test writing 39 | error |= test_access(&od, 0x2000, 1, od_access_type_rw); 40 | error |= test_write(&od, 0x2000, 1, OD_RESULT_OK, 1234); 41 | error |= test_write(&od, 0x2000, 2, OD_RESULT_OK, 9876); 42 | error |= test_write(&od, 0x2000, 3, OD_RESULT_OK, 0); 43 | 44 | // Test read only access type 45 | error |= test_access(&od, 0x2001, 1, od_access_type_ro); 46 | error |= test_write(&od, 0x2001, 1, OD_RESULT_WRITING_READ_ONLY_OBJECT, 1234); // Try to write a read only object 47 | error |= test_read(&od, 0x2001, 1, OD_RESULT_OK, 20011); // Make sure it wasn't written (old value stayed) 48 | // Test write only access type 49 | error |= test_access(&od, 0x2001, 2, od_access_type_wo); 50 | error |= test_read(&od, 0x2001, 2, OD_RESULT_READING_WRITE_ONLY_OBJECT, 0); // Try to read a write only object 51 | error |= test_write(&od, 0x2001, 2, OD_RESULT_OK, 1234); // Write a write only object 52 | // Test constant access type 53 | error |= test_access(&od, 0x2001, 3, od_access_type_const); 54 | error |= test_write(&od, 0x2001, 3, OD_RESULT_WRITING_READ_ONLY_OBJECT, 1234); // Try to write a const object 55 | error |= test_read(&od, 0x2001, 3, OD_RESULT_OK, 20013); // Make sure it wasn't written (old value stayed) 56 | 57 | // Test data types 58 | error |= test_data_type(&od, 0x2003, 1, od_data_type_bool, 1); 59 | error |= test_data_type(&od, 0x2003, 2, od_data_type_int8, 8); 60 | error |= test_data_type(&od, 0x2003, 3, od_data_type_int16, 16); 61 | error |= test_data_type(&od, 0x2003, 4, od_data_type_int32, 32); 62 | error |= test_data_type(&od, 0x2003, 5, od_data_type_uint8, 8); 63 | error |= test_data_type(&od, 0x2003, 6, od_data_type_uint16, 16); 64 | error |= test_data_type(&od, 0x2003, 7, od_data_type_uint32, 32); 65 | 66 | return error; 67 | } 68 | 69 | /****************************** Local Functions ******************************/ 70 | static int test_read(object_dictionary *od, uint16_t index, uint8_t sub_index, int expected_result, uint32_t expected_value) 71 | { 72 | int error = 0; 73 | uint32_t data; 74 | int result = od_read(od, index, sub_index, &data); 75 | if (result != expected_result) 76 | { 77 | // Was expecting different result 78 | log_write_ln("test_od: od_read failed: wrong result @%04Xh:%d, expected:%d, got:%d", index, sub_index, expected_result, result); 79 | error = 1; 80 | } 81 | else 82 | { 83 | if (result == OD_RESULT_OK) 84 | { 85 | if (data != expected_value) 86 | { 87 | // Data is incorrect - test failed 88 | log_write_ln("test_od: od_read failed: wrong data @%04Xh:%d, expected:%lu, got:%lu", index, sub_index, expected_value, data); 89 | error = 1; 90 | } 91 | else 92 | { 93 | // Data was correct - test passed 94 | } 95 | } 96 | else 97 | { 98 | // Some other error result was expected, so no need to compare data here - test passed 99 | } 100 | } 101 | if (!error) 102 | { 103 | log_write_ln("test_od: od_read OK %04Xh:%d %lu", index, sub_index, expected_value); 104 | } 105 | return error; 106 | } 107 | static int test_write(object_dictionary *od, uint16_t index, uint8_t sub_index, int expected_result, uint32_t value) 108 | { 109 | int error = 0; 110 | int result = od_write(od, index, sub_index, value); 111 | if (result != expected_result) 112 | { 113 | // Was expecting different result 114 | log_write_ln("test_od: od_write failed: wrong result @%04Xh:%d, expected:%d, got:%d", index, sub_index, expected_result, result); 115 | error = 1; 116 | } 117 | else 118 | { 119 | if (result == OD_RESULT_OK) 120 | { 121 | od_access_type access_type; 122 | result = od_get_access_type(od, index, sub_index, &access_type); 123 | // Read back the value to make sure it was saved correctly 124 | if (result == OD_RESULT_OK) 125 | { 126 | if (access_type == od_access_type_wo) 127 | { 128 | // This is a write only object, so we can't use normal read funciton here. 129 | // Instead, check the value manually 130 | int object_found = 0; 131 | for (int i = 0; i < od->num_objects; i++) 132 | { 133 | if (od->objects[i].index == index && od->objects[i].sub_index == sub_index) 134 | { 135 | if (od->objects[i].data != value) 136 | { 137 | log_write_ln("test_od: failed: write only object wasn't written @%04Xh:%d, expected:%lu, got:%lu", index, sub_index, value, od->objects[i].data); 138 | error = 1; 139 | } 140 | else 141 | { 142 | // Data was written OK 143 | } 144 | object_found = 1; 145 | break; 146 | } 147 | } 148 | if (!object_found) 149 | { 150 | log_write_ln("test_od: od_write failed: od_write was ok, but failed to find object manually to check the value"); 151 | error = 1; 152 | } 153 | } 154 | else 155 | { 156 | error |= test_read(od, index, sub_index, OD_RESULT_OK, value); 157 | if (error) 158 | { 159 | log_write_ln("test_od: od_write failed: od_write was ok, but test_read returned error"); 160 | error = 1; 161 | } 162 | } 163 | } 164 | else 165 | { 166 | log_write_ln("test_od: od_write failed: od_write was ok, but getting access type failed"); 167 | error = 1; 168 | } 169 | } 170 | else 171 | { 172 | // Some other error result was expected, so no need to compare data here - test passed 173 | } 174 | } 175 | if (!error) 176 | { 177 | log_write_ln("test_od: od_write OK %04Xh:%d %lu", index, sub_index, value); 178 | } 179 | return error; 180 | } 181 | static int test_access(object_dictionary *od, uint16_t index, uint8_t sub_index, od_access_type expected_access) 182 | { 183 | int error = 0; 184 | od_access_type read_access; 185 | int result = od_get_access_type(od, index, sub_index, &read_access); 186 | if (result == OD_RESULT_OK) 187 | { 188 | if (read_access == expected_access) 189 | { 190 | // OK - access type matches 191 | } 192 | else 193 | { 194 | log_write_ln("test_od: test_access failed: wrong access type @%04Xh:%d, , expected:%d, got:%d", index, sub_index, expected_access, read_access); 195 | error = 1; 196 | } 197 | } 198 | else 199 | { 200 | log_write_ln("test_od: test_access failed: od_get_access_type @%04Xh:%d returned an error", index, sub_index); 201 | error = 1; 202 | } 203 | if (!error) 204 | { 205 | log_write_ln("test_od: test_access OK %04Xh:%d", index, sub_index); 206 | } 207 | return error; 208 | } 209 | static int test_data_type(object_dictionary *od, uint16_t index, uint8_t sub_index, od_data_type expected_type, int expected_len) 210 | { 211 | int error = 0; 212 | od_data_type read_type; 213 | int result = od_get_data_type(od, index, sub_index, &read_type); 214 | if (result == OD_RESULT_OK) 215 | { 216 | if (read_type == expected_type) 217 | { 218 | int read_len; 219 | result = od_get_data_len(od, index, sub_index, &read_len); 220 | if (result == OD_RESULT_OK) 221 | { 222 | if (read_len == expected_len) 223 | { 224 | // OK - length matches 225 | } 226 | else 227 | { 228 | log_write_ln("test_od: test_data_type failed: od_get_data_len @%04Xh:%d returned wrong len", index, sub_index); 229 | error = 1; 230 | } 231 | } 232 | else 233 | { 234 | log_write_ln("test_od: test_data_type failed: od_get_data_len @%04Xh:%d returned an error", index, sub_index); 235 | error = 1; 236 | } 237 | } 238 | else 239 | { 240 | log_write_ln("test_od: test_data_type failed: od_get_data_type @%04Xh:%d returned wrong type", index, sub_index); 241 | error = 1; 242 | } 243 | } 244 | else 245 | { 246 | log_write_ln("test_od: test_data_type failed: od_get_data_type @%04Xh:%d returned an error", index, sub_index); 247 | error = 1; 248 | } 249 | if (!error) 250 | { 251 | log_write_ln("test_od: test_data_type OK %04Xh:%d", index, sub_index); 252 | } 253 | return error; 254 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_nmt.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_nmt.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 24.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | // Testing NMT functions: 10 | // - Master sends NMT commands to a slave node. 11 | // - Slave node parses command, set's it's state and sends heartbeat 12 | // - Master receives heartbeat and makes sure NMT state is correct. 13 | // - repeat with another command 14 | 15 | #include 16 | #include "test_nmt.h" 17 | #include "can_bus.h" 18 | #include "co_node.h" 19 | #include "log.h" 20 | #include "nmt.h" 21 | #include "nmt_master.h" 22 | #include "nmt_slave.h" 23 | 24 | /****************************** Local Variables ******************************/ 25 | static int error = 0; 26 | static uint8_t test_running = 0; 27 | static co_node slave_node; 28 | static nmt_state next_state; 29 | static uint8_t state_changed_fired = 0; 30 | static uint8_t reset_comm_called = 0; 31 | static uint8_t reset_node_called = 0; 32 | static uint32_t tick_count = 0; 33 | static object_dictionary od; 34 | static uint8_t heartbeat_enabled = 0; 35 | /****************************** Local Prototypes *****************************/ 36 | static void set_heartbeat_interval(uint32_t interval); 37 | static void test_nmt_command(nmt_command command); 38 | static void check_node_state_from_master(co_node *node, nmt_state expected_state); 39 | static void check_node_state_from_slave(co_node *node, nmt_state expected_state); 40 | static void slave_message_received_handler(can_message *message); 41 | static void master_message_received_handler(can_message *message); 42 | static void slave_state_changed_handler(nmt_state state); 43 | static void slave_reset_communication(void); 44 | static void slave_reset_node(void); 45 | 46 | /****************************** Global Functions *****************************/ 47 | extern int test_nmt_run(void) 48 | { 49 | test_running = 1; 50 | error = 0; 51 | // Prepare test slave node 52 | slave_node.node_id = 7; 53 | slave_node.state = nmt_state_pre_operational; 54 | slave_node.od = &od; 55 | od_initialize(slave_node.od); 56 | 57 | // Register message handlers for master and slave. Also slave's state changed handler 58 | can_bus_register_message_received_handler(slave_message_received_handler); 59 | can_bus_register_message_received_handler(master_message_received_handler); 60 | nmt_slave_register_state_changed_handler(slave_state_changed_handler); 61 | nmt_slave_set_reset_communication_function(slave_reset_communication); 62 | nmt_slave_set_reset_node_function(slave_reset_node); 63 | 64 | // Enable heartbeat in OD 65 | set_heartbeat_interval(90); 66 | 67 | // Test functionality 68 | tick_count = 100; 69 | test_nmt_command(nmt_command_operational); 70 | tick_count = 200; 71 | test_nmt_command(nmt_command_stopped); 72 | tick_count = 300; 73 | test_nmt_command(nmt_command_pre_operational); 74 | tick_count = 400; 75 | test_nmt_command(nmt_command_stopped); 76 | tick_count = 500; 77 | test_nmt_command(nmt_command_operational); 78 | tick_count = 600; 79 | test_nmt_command(nmt_command_reset_communication); 80 | tick_count = 700; 81 | test_nmt_command(nmt_command_operational); 82 | tick_count = 800; 83 | test_nmt_command(nmt_command_reset_node); 84 | tick_count = 900; 85 | test_nmt_command(nmt_command_stopped); 86 | 87 | // Test heartbeat interval, enabling and disabling 88 | set_heartbeat_interval(0); 89 | test_nmt_command(nmt_command_operational); 90 | check_node_state_from_slave(&slave_node, nmt_state_operational); 91 | tick_count = 1000; 92 | set_heartbeat_interval(90); 93 | nmt_slave_send_heartbeat(&slave_node, tick_count); 94 | check_node_state_from_master(&slave_node, next_state); 95 | 96 | test_running = 0; 97 | return error; 98 | } 99 | 100 | /****************************** Local Functions ******************************/ 101 | static void set_heartbeat_interval(uint32_t interval) 102 | { 103 | od_result result = od_write(slave_node.od, 0x1017, 0, 100); 104 | if (interval == 0) 105 | { 106 | heartbeat_enabled = 0; 107 | } 108 | else 109 | { 110 | heartbeat_enabled = 1; 111 | } 112 | if (result != OD_RESULT_OK) 113 | { 114 | error = 1; 115 | log_write_ln("test_nmt: ERROR: setting heartbeat interval failed"); 116 | } 117 | } 118 | /** 119 | * @brief Test NMT functionality. 120 | * 121 | * - NMT master sends an NMT command to a slave. 122 | * - Slave receives the command, processes it and sends a heartbeat. 123 | * - NMT master receives the heartbeat, processes it and saves to slave node list. 124 | * Then check if the NMT state of the slave node is reported correctly in the 125 | * master's slave node list. 126 | * 127 | * @param command The NMT command to be tested 128 | * @note This function sets a file variable 'error' if error occurs 129 | */ 130 | static void test_nmt_command(nmt_command command) 131 | { 132 | state_changed_fired = 0; 133 | reset_comm_called = 0; 134 | reset_node_called = 0; 135 | switch (command) 136 | { 137 | case nmt_command_operational: 138 | next_state = nmt_state_operational; 139 | break; 140 | case nmt_command_pre_operational: 141 | next_state = nmt_state_pre_operational; 142 | break; 143 | case nmt_command_stopped: 144 | next_state = nmt_state_stopped; 145 | break; 146 | case nmt_command_reset_communication: 147 | next_state = nmt_state_pre_operational; 148 | break; 149 | case nmt_command_reset_node: 150 | next_state = nmt_state_pre_operational; 151 | break; 152 | default: 153 | log_write_ln("test_nmt: INTERNAL ERROR: unknown nmt_command"); 154 | error = 1; 155 | return; 156 | } 157 | nmt_master_send_command(slave_node.node_id, command); 158 | // At this point, since the test system is not multithreaded, the nmt command should be procesed and heartbeat sent by the slave node and then processed by the master node. 159 | // Check master node to see if correct heartbeat was received 160 | if (heartbeat_enabled) 161 | { 162 | check_node_state_from_master(&slave_node, next_state); 163 | if (state_changed_fired == 0 && reset_comm_called == 0 && reset_node_called == 0 && heartbeat_enabled == 1) 164 | { 165 | error = 1; 166 | log_write_ln("test_nmt: ERROR: either heartbeat not received or slave state changed didn't fire or both"); 167 | } 168 | if (reset_node_called && command != nmt_command_reset_node) 169 | { 170 | log_write_ln("test_nmt: ERROR: slave reset node was called, event thought command was %d", command); 171 | error = 1; 172 | } 173 | else if (command == nmt_command_reset_node && reset_node_called == 0) 174 | { 175 | log_write_ln("test_nmt: ERROR: command was reset node, but slate reset node function was not called", command); 176 | error = 1; 177 | } 178 | else if (reset_comm_called && command != nmt_command_reset_communication) 179 | { 180 | log_write_ln("test_nmt: ERROR: slave reset comm was called, event thought command was %d", command); 181 | error = 1; 182 | } 183 | else if (command == nmt_command_reset_communication && reset_comm_called == 0) 184 | { 185 | log_write_ln("test_nmt: ERROR: command was reset communication, but slate reset comm function was not called", command); 186 | error = 1; 187 | } 188 | } 189 | } 190 | /** 191 | * @brief See if node has reported an expected state. 192 | * 193 | * Reads a node list from NMT master and checks if the selected 194 | * node is on the list and if the node has the correct NMT state. 195 | * 196 | * @param node The CANopen node the NMT state of which is being checked. 197 | * @param expected_state The expected NMT state of the selected node. 198 | * @note This function sets a file variable 'error' if error occurs 199 | */ 200 | static void check_node_state_from_master(co_node *node, nmt_state expected_state) 201 | { 202 | if (nmt_master_num_nodes() == 1) 203 | { 204 | if (nmt_master_node_list()[0].node_id == slave_node.node_id) 205 | { 206 | // OK - the correct node ID was saved 207 | if (nmt_master_node_list()[0].state == expected_state) 208 | { 209 | // OK - the correct state was received and saved 210 | log_write_ln("test_nmt: state %d OK", expected_state); 211 | } 212 | else 213 | { 214 | error = 1; 215 | log_write_ln("test_nmt: ERROR: wrong nmt state saved to master's node list"); 216 | } 217 | } 218 | else 219 | { 220 | error = 1; 221 | log_write_ln("test_nmt: ERROR: wrong node ID saved to master's node list"); 222 | } 223 | } 224 | else 225 | { 226 | error = 1; 227 | } 228 | } 229 | 230 | static void check_node_state_from_slave(co_node *node, nmt_state expected_state) 231 | { 232 | if (node->state != expected_state) 233 | { 234 | error = 1; 235 | log_write_ln("test_nmt: ERROR: wrong nmt state saved to slave node"); 236 | } 237 | else 238 | { 239 | log_write_ln("test_nmt: state %d OK", expected_state); 240 | } 241 | } 242 | 243 | static void slave_message_received_handler(can_message *message) 244 | { 245 | if (test_running) 246 | { 247 | if (message->id == 0 || message->id == slave_node.node_id) 248 | { 249 | // This is NMT command, either broadcase or directed to this node 250 | nmt_slave_process_command(message, &slave_node); 251 | // Send heartbeat 252 | nmt_slave_send_heartbeat(&slave_node, tick_count); 253 | } 254 | } 255 | } 256 | 257 | static void slave_state_changed_handler(nmt_state state) 258 | { 259 | if (test_running) 260 | { 261 | if (state == next_state) 262 | { 263 | state_changed_fired = 1; 264 | } 265 | else 266 | { 267 | log_write_ln("test_nmt: state_changed: wrong state: got %d, expected %d", (int)state, (int)next_state); 268 | error = 1; 269 | } 270 | } 271 | } 272 | 273 | static void slave_reset_communication(void) 274 | { 275 | slave_node.state = nmt_state_pre_operational; 276 | reset_comm_called = 1; 277 | // Send heartbeat 278 | nmt_slave_send_heartbeat(&slave_node, tick_count); 279 | } 280 | 281 | static void slave_reset_node(void) 282 | { 283 | slave_node.state = nmt_state_pre_operational; 284 | reset_node_called = 1; 285 | // Send heartbeat 286 | nmt_slave_send_heartbeat(&slave_node, tick_count); 287 | } 288 | 289 | static void master_message_received_handler(can_message *message) 290 | { 291 | if (test_running) 292 | { 293 | if (message->id == 0x700 + slave_node.node_id) 294 | { 295 | // This is heard beat from the slave node 296 | nmt_master_process_heartbeat(message); 297 | } 298 | } 299 | } 300 | 301 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/od_initialize.h: -------------------------------------------------------------------------------- 1 | // 2 | // od_initialize.h 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 27.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #ifndef CCanOpenStack_od_initialize_h 10 | #define CCanOpenStack_od_initialize_h 11 | 12 | #define OD_INITIALIZE_MAX_OBJECTS 256 13 | od_object od_objects[OD_INITIALIZE_MAX_OBJECTS]; 14 | od_object od_default_objects[] = { 15 | // Data types (for reference only) 16 | {0x0001, 0, od_data_type_uint8, od_access_type_ro, 1}, // BOOL: 1 bit 17 | {0x0002, 0, od_data_type_uint8, od_access_type_ro, 8}, // INT8: 8 bits 18 | {0x0003, 0, od_data_type_uint8, od_access_type_ro, 16}, // INT16: 16 bits 19 | {0x0004, 0, od_data_type_uint8, od_access_type_ro, 32}, // INT32: 32 bits 20 | {0x0005, 0, od_data_type_uint8, od_access_type_ro, 8}, // UINT8: 8 bits 21 | {0x0006, 0, od_data_type_uint8, od_access_type_ro, 16}, // UINT16: 16 bits 22 | {0x0007, 0, od_data_type_uint8, od_access_type_ro, 32}, // UINT32: 32 bits 23 | 24 | // Device type : no device profile 25 | {0x1000, 0, od_data_type_uint32, od_access_type_ro, 0}, 26 | // Error register : no errors 27 | {0x1001, 0, od_data_type_uint8, od_access_type_ro, 0}, 28 | // Device name : 4 char string value "NODE" 29 | {0x1008, 0, od_data_type_uint32, od_access_type_ro, 0x4E4F4445}, 30 | // Heartbeat producer time : 0 ms (can be changed by master) 31 | {0x1017, 0, od_data_type_uint16, od_access_type_rw, 0}, 32 | // Identity object 33 | {0x1018, 0, od_data_type_uint8, od_access_type_ro, 1}, 34 | {0x1018, 1, od_data_type_uint32, od_access_type_ro, 12345}, // Vendor ID 35 | // SDO server parameters 36 | {0x1200, 0, od_data_type_uint8, od_access_type_ro, 2}, 37 | {0x1200, 1, od_data_type_uint32, od_access_type_ro, 0x580}, // TSDO COB ID : 0x600 base + node ID (to config) 38 | {0x1200, 2, od_data_type_uint32, od_access_type_ro, 0x600}, // RSDO COB ID : 0x580 base + node ID (to config) 39 | // RPDO 1-4 parameters 40 | {0x1400, 0, od_data_type_uint8, od_access_type_ro, 2}, 41 | {0x1400, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO1 COB ID : Not in use (to be configured) 42 | {0x1400, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : 0xFE (manufacturere specific) 43 | {0x1401, 0, od_data_type_uint8, od_access_type_ro, 2}, 44 | {0x1401, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO2 COB ID : Not in use (to be configured) 45 | {0x1401, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : 0xFE (manufacturere specific) 46 | {0x1402, 0, od_data_type_uint8, od_access_type_ro, 2}, 47 | {0x1402, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO3 COB ID : Not in use (to be configured) 48 | {0x1402, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : 0xFE (manufacturere specific) 49 | {0x1403, 0, od_data_type_uint8, od_access_type_ro, 2}, 50 | {0x1403, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO4 COB ID : Not in use (to be configured) 51 | {0x1403, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : 0xFE (manufacturere specific) 52 | // RPDO 1-4 mapping 53 | // All fillsed with dummy entries - replace as necessary 54 | {0x1600, 0, od_data_type_uint8, od_access_type_ro, 8}, 55 | {0x1600, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 56 | {0x1600, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 57 | {0x1600, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 58 | {0x1600, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 59 | {0x1600, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 60 | {0x1600, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 61 | {0x1600, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 62 | {0x1600, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 63 | {0x1601, 0, od_data_type_uint8, od_access_type_ro, 8}, 64 | {0x1601, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 65 | {0x1601, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 66 | {0x1601, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 67 | {0x1601, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 68 | {0x1601, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 69 | {0x1601, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 70 | {0x1601, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 71 | {0x1601, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 72 | {0x1602, 0, od_data_type_uint8, od_access_type_ro, 8}, 73 | {0x1602, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 74 | {0x1602, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 75 | {0x1602, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 76 | {0x1602, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 77 | {0x1602, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 78 | {0x1602, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 79 | {0x1602, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 80 | {0x1602, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 81 | {0x1603, 0, od_data_type_uint8, od_access_type_ro, 8}, 82 | {0x1603, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 83 | {0x1603, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 84 | {0x1603, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 85 | {0x1603, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 86 | {0x1603, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 87 | {0x1603, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 88 | {0x1603, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 89 | {0x1603, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 90 | // TPDO 1-4 parameters 91 | {0x1800, 0, od_data_type_uint8, od_access_type_ro, 5}, 92 | {0x1800, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO1 COB ID : uint32 : Not in use (to be configured) 93 | {0x1800, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : uint8 : 0xFE (manufacturere specific) 94 | {0x1800, 3, od_data_type_uint16, od_access_type_ro, 100}, // Inhibit time : uint16 : 100 * 100 us (microseconds) = 10 ms 95 | // {0x1800, 4, 0}, // Reserved 96 | {0x1800, 5, od_data_type_uint16, od_access_type_ro, 200}, // Event time : uint16 : 200 ms (milliseconds) 97 | {0x1801, 0, od_data_type_uint8, od_access_type_ro, 5}, 98 | {0x1801, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO2 COB ID : uint32 : Not in use (to be configured) 99 | {0x1801, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : uint8 : 0xFE (manufacturere specific) 100 | {0x1801, 3, od_data_type_uint16, od_access_type_ro, 100}, // Inhibit time : uint16 : 100 * 100 us (microseconds) = 10 ms 101 | // {0x1801, 4, 0}, // Reserved 102 | {0x1801, 5, od_data_type_uint16, od_access_type_ro, 200}, // Event time : uint16 : 200 ms (milliseconds) 103 | {0x1802, 0, od_data_type_uint8, od_access_type_ro, 5}, 104 | {0x1802, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO3 COB ID : uint32 : Not in use (to be configured) 105 | {0x1802, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : uint8 : 0xFE (manufacturere specific) 106 | {0x1802, 3, od_data_type_uint16, od_access_type_ro, 100}, // Inhibit time : uint16 : 100 * 100 us (microseconds) = 10 ms 107 | // {0x1802, 4, 0}, // Reserved 108 | {0x1802, 5, od_data_type_uint16, od_access_type_ro, 200}, // Event time : uint16 : 200 ms (milliseconds) 109 | {0x1803, 0, od_data_type_uint8, od_access_type_ro, 5}, 110 | {0x1803, 1, od_data_type_uint32, od_access_type_ro, (1 << 31)}, // RPDO4 COB ID : uint32 : Not in use (to be configured) 111 | {0x1803, 2, od_data_type_uint8, od_access_type_ro, 0xFE}, // Transmission type : uint8 : 0xFE (manufacturere specific) 112 | {0x1803, 3, od_data_type_uint16, od_access_type_ro, 100}, // Inhibit time : uint16 : 100 * 100 us (microseconds) = 10 ms 113 | // {0x1803, 4, 0}, // Reserved 114 | {0x1803, 5, od_data_type_uint16, od_access_type_ro, 200}, // Event time : uint16 : 200 ms (milliseconds) 115 | // TPDO 1-4 mapping 116 | // All fillsed with dummy entries - replace as necessary 117 | {0x1A00, 0, od_data_type_uint8, od_access_type_ro, 8}, 118 | {0x1A00, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 119 | {0x1A00, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 120 | {0x1A00, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 121 | {0x1A00, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 122 | {0x1A00, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 123 | {0x1A00, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 124 | {0x1A00, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 125 | {0x1A00, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 126 | {0x1A01, 0, od_data_type_uint8, od_access_type_ro, 8}, 127 | {0x1A01, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 128 | {0x1A01, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 129 | {0x1A01, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 130 | {0x1A01, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 131 | {0x1A01, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 132 | {0x1A01, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 133 | {0x1A01, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 134 | {0x1A01, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 135 | {0x1A02, 0, od_data_type_uint8, od_access_type_ro, 8}, 136 | {0x1A02, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 137 | {0x1A02, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 138 | {0x1A02, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 139 | {0x1A02, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 140 | {0x1A02, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 141 | {0x1A02, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 142 | {0x1A02, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 143 | {0x1A02, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 144 | {0x1A03, 0, od_data_type_uint8, od_access_type_ro, 8}, 145 | {0x1A03, 1, od_data_type_uint32, od_access_type_ro, 0x00020008}, 146 | {0x1A03, 2, od_data_type_uint32, od_access_type_ro, 0x00020008}, 147 | {0x1A03, 3, od_data_type_uint32, od_access_type_ro, 0x00020008}, 148 | {0x1A03, 4, od_data_type_uint32, od_access_type_ro, 0x00020008}, 149 | {0x1A03, 5, od_data_type_uint32, od_access_type_ro, 0x00020008}, 150 | {0x1A03, 6, od_data_type_uint32, od_access_type_ro, 0x00020008}, 151 | {0x1A03, 7, od_data_type_uint32, od_access_type_ro, 0x00020008}, 152 | {0x1A03, 8, od_data_type_uint32, od_access_type_ro, 0x00020008}, 153 | // NMT startup 154 | {0x1F80, 0, od_data_type_uint32, od_access_type_ro, (1 << 2)}, // bit 2: 0 = auto start, 1 = no auto start) 155 | 156 | // Manufacturer specific goes here 157 | 158 | // For testing read and write 159 | {0x2000, 0, od_data_type_uint8, od_access_type_ro, 3}, 160 | {0x2000, 1, od_data_type_uint32, od_access_type_rw, 20001}, 161 | {0x2000, 2, od_data_type_uint32, od_access_type_rw, 20002}, 162 | {0x2000, 3, od_data_type_uint32, od_access_type_rw, 20003}, 163 | 164 | // For testing access types 165 | {0x2001, 0, od_data_type_uint8, od_access_type_ro, 3}, 166 | {0x2001, 1, od_data_type_uint32, od_access_type_ro, 20011}, 167 | {0x2001, 2, od_data_type_uint32, od_access_type_wo, 20012}, 168 | {0x2001, 3, od_data_type_uint32, od_access_type_const, 20013}, 169 | 170 | // For testing sdo server 171 | {0x2002, 0, od_data_type_uint8, od_access_type_ro, 3}, 172 | {0x2002, 1, od_data_type_uint32, od_access_type_rw, 20021}, 173 | {0x2002, 2, od_data_type_uint32, od_access_type_rw, 20022}, 174 | {0x2002, 3, od_data_type_uint32, od_access_type_rw, 20023}, 175 | 176 | // For testing data types 177 | {0x2003, 0, od_data_type_uint8, od_access_type_ro, 7}, 178 | {0x2003, 1, od_data_type_bool, od_access_type_ro, 20031}, 179 | {0x2003, 2, od_data_type_int8, od_access_type_ro, 20032}, 180 | {0x2003, 3, od_data_type_int16, od_access_type_ro, 20033}, 181 | {0x2003, 4, od_data_type_int32, od_access_type_ro, 20034}, 182 | {0x2003, 5, od_data_type_uint8, od_access_type_ro, 20035}, 183 | {0x2003, 6, od_data_type_uint16, od_access_type_ro, 20036}, 184 | {0x2003, 7, od_data_type_uint32, od_access_type_ro, 20037}, 185 | 186 | // For rpdo mapping test 187 | {0x2004, 0, od_data_type_uint8, od_access_type_ro, 3}, 188 | {0x2004, 1, od_data_type_uint8, od_access_type_rw, 0}, 189 | {0x2004, 2, od_data_type_uint8, od_access_type_rw, 0}, 190 | {0x2004, 3, od_data_type_uint8, od_access_type_rw, 0}, 191 | 192 | {0x2005, 0, od_data_type_uint8, od_access_type_ro, 3}, 193 | {0x2005, 1, od_data_type_uint16, od_access_type_rw, 0}, 194 | {0x2005, 2, od_data_type_uint16, od_access_type_rw, 0}, 195 | {0x2005, 3, od_data_type_uint16, od_access_type_rw, 0}, 196 | 197 | {0x2006, 0, od_data_type_uint8, od_access_type_ro, 3}, 198 | {0x2006, 1, od_data_type_uint32, od_access_type_rw, 0}, 199 | {0x2006, 2, od_data_type_uint32, od_access_type_rw, 0}, 200 | {0x2006, 3, od_data_type_uint32, od_access_type_rw, 0}, 201 | 202 | // For tpdo mapping test 203 | {0x2007, 0, od_data_type_uint8, od_access_type_ro, 3}, 204 | {0x2007, 1, od_data_type_uint8, od_access_type_rw, 71}, 205 | {0x2007, 2, od_data_type_uint8, od_access_type_rw, 72}, 206 | {0x2007, 3, od_data_type_uint8, od_access_type_rw, 73}, 207 | 208 | {0x2008, 0, od_data_type_uint8, od_access_type_ro, 3}, 209 | {0x2008, 1, od_data_type_uint16, od_access_type_rw, 81}, 210 | {0x2008, 2, od_data_type_uint16, od_access_type_rw, 82}, 211 | {0x2008, 3, od_data_type_uint16, od_access_type_rw, 83}, 212 | 213 | {0x2009, 0, od_data_type_uint8, od_access_type_ro, 3}, 214 | {0x2009, 1, od_data_type_uint32, od_access_type_rw, 91}, 215 | {0x2009, 2, od_data_type_uint32, od_access_type_rw, 92}, 216 | {0x2009, 3, od_data_type_uint32, od_access_type_rw, 93}, 217 | }; 218 | 219 | #endif 220 | -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/test_sdo.c: -------------------------------------------------------------------------------- 1 | // 2 | // test_sdo.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 21.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "test_sdo.h" 10 | #include "can_bus.h" 11 | #include "log.h" 12 | #include "sdo.h" 13 | #include "sdo_client.h" 14 | #include "sdo_server.h" 15 | #include "test_util.h" 16 | 17 | /***************************** Local Definitions *****************************/ 18 | 19 | /****************************** Local Variables ******************************/ 20 | static int error = 0; 21 | static uint8_t msg_data[8]; 22 | static uint8_t next_is_from_client = 0; 23 | static uint8_t next_is_from_server = 0; 24 | static sdo_client_command next_client_command; 25 | static sdo_server_command next_server_command; 26 | static uint8_t next_is_expedited = 0; 27 | static uint8_t next_is_size_indicated = 0; 28 | static uint8_t next_data_size = 0; 29 | static uint16_t next_index = 0; 30 | static uint8_t next_sub_index = 0; 31 | static uint32_t next_expedited_data = 0; 32 | static can_message next_message; 33 | static uint8_t test_running = 0; 34 | 35 | /****************************** Local Prototypes *****************************/ 36 | static void test_download_request(uint16_t index, uint8_t sub_index, uint32_t data); 37 | static void test_download_response(uint16_t index, uint8_t sub_index); 38 | static void test_upload_request(uint16_t index, uint8_t sub_index); 39 | static void test_upload_response(uint16_t index, uint8_t sub_index, uint32_t data); 40 | static void test_client_abort(uint16_t index, uint8_t sub_index, sdo_abort_code abort_code); 41 | static void test_server_abort(uint16_t index, uint8_t sub_index, sdo_abort_code abort_code); 42 | static void message_received_handler(can_message *message); 43 | static void check_expedited_transfer(can_message *message); 44 | static void check_multiplexer_transfer(can_message *message); 45 | static void check_abort_transfer(can_message *message); 46 | static uint8_t check_multiplexer(can_message *message); 47 | /****************************** Global Functions *****************************/ 48 | extern int test_sdo_run(void) { 49 | test_running = 1; 50 | error = 0; 51 | can_bus_register_message_received_handler(message_received_handler); 52 | next_message.id = 0x123; 53 | next_message.data_len = 8; 54 | next_message.data = msg_data; 55 | // Create and send various SDO messages 56 | // Note: this test doesn't actually write to any real object dictionary 57 | test_download_request(0x2000, 1, 0x123456); 58 | test_download_request(0x2001, 9, 0x345); 59 | test_download_request(0xFF00, 0xF3, 0x9876); 60 | test_download_response(0x2000, 1); 61 | test_download_response(0x2001, 34); 62 | test_download_response(0xFF00, 44); 63 | test_upload_request(0x2000, 1); 64 | test_upload_request(0x2001, 9); 65 | test_upload_request(0xFF00, 0x56); 66 | test_upload_response(0x2000, 1, 0x123456); 67 | test_upload_response(0x2001, 9, 0x345); 68 | test_upload_response(0xFF00, 0xF3, 0x9876); 69 | test_client_abort(0x4040, 76, sdo_abort_access_failed_due_hardware_error); 70 | test_client_abort(0x5300, 54, sdo_abort_access_failed_due_hardware_error); 71 | test_client_abort(0x2031, 0x34, sdo_abort_access_failed_due_hardware_error); 72 | test_server_abort(0x4040, 76, sdo_abort_access_failed_due_hardware_error); 73 | test_server_abort(0x5300, 54, sdo_abort_access_failed_due_hardware_error); 74 | test_server_abort(0x2031, 0x34, sdo_abort_access_failed_due_hardware_error); 75 | test_running = 0; 76 | return error; 77 | } 78 | 79 | /****************************** Local Functions ******************************/ 80 | static void test_download_request(uint16_t index, uint8_t sub_index, uint32_t data) 81 | { 82 | next_is_from_client = 1; 83 | next_is_from_server = 0; 84 | next_client_command = sdo_command_client_download_init; 85 | next_is_expedited = 1; 86 | next_is_size_indicated = 1; 87 | next_data_size = 4; 88 | next_index = index; 89 | next_sub_index = sub_index; 90 | next_expedited_data = data; 91 | 92 | sdo_message_expedited_download_request(&next_message, next_index, next_sub_index, next_expedited_data); 93 | log_write_ln("test_sdo: download request: obj:%04Xh:%d data:%04Xh", index, sub_index, data); 94 | can_bus_send_message(&next_message); 95 | } 96 | static void test_download_response(uint16_t index, uint8_t sub_index) 97 | { 98 | next_is_from_client = 0; 99 | next_is_from_server = 1; 100 | next_server_command = sdo_command_server_download_init; 101 | next_is_expedited = 0; 102 | next_is_size_indicated = 0; 103 | next_data_size = 0; 104 | next_index = index; 105 | next_sub_index = sub_index; 106 | next_expedited_data = 0; 107 | 108 | sdo_message_download_response(&next_message, next_index, next_sub_index); 109 | log_write_ln("test_sdo: download response: obj:%04Xh:%d data:%04Xh", index, sub_index); 110 | can_bus_send_message(&next_message); 111 | } 112 | static void test_upload_request(uint16_t index, uint8_t sub_index) 113 | { 114 | next_is_from_client = 1; 115 | next_is_from_server = 0; 116 | next_client_command = sdo_command_client_upload_init; 117 | next_is_expedited = 0; 118 | next_is_size_indicated = 0; 119 | next_data_size = 0; 120 | next_index = index; 121 | next_sub_index = sub_index; 122 | next_expedited_data = 0; 123 | 124 | sdo_message_upload_request(&next_message, next_index, next_sub_index); 125 | log_write_ln("test_sdo: upload request: obj:%04Xh:%d data:%04Xh", index, sub_index); 126 | can_bus_send_message(&next_message); 127 | } 128 | static void test_upload_response(uint16_t index, uint8_t sub_index, uint32_t data) 129 | { 130 | next_is_from_client = 0; 131 | next_is_from_server = 1; 132 | next_server_command = sdo_command_server_upload_init; 133 | next_is_expedited = 1; 134 | next_is_size_indicated = 1; 135 | next_data_size = 4; 136 | next_index = index; 137 | next_sub_index = sub_index; 138 | next_expedited_data = data; 139 | 140 | sdo_message_expedited_upload_response(&next_message, next_index, next_sub_index, next_expedited_data); 141 | log_write_ln("test_sdo: upload response: obj:%04Xh:%d data:%04Xh", index, sub_index, data); 142 | can_bus_send_message(&next_message); 143 | } 144 | static void test_client_abort(uint16_t index, uint8_t sub_index, sdo_abort_code abort_code) 145 | { 146 | next_is_from_client = 1; 147 | next_is_from_server = 0; 148 | next_client_command = sdo_command_client_abort_transfer; 149 | next_is_expedited = 1; 150 | next_is_size_indicated = 1; 151 | next_data_size = 4; 152 | next_index = index; 153 | next_sub_index = sub_index; 154 | next_expedited_data = abort_code; 155 | 156 | sdo_message_client_abort_transfer(&next_message, index, sub_index, abort_code); 157 | log_write_ln("test_sdo: client abort transfer: obj:%04Xh:%d data:%04Xh", index, sub_index, abort_code); 158 | can_bus_send_message(&next_message); 159 | } 160 | static void test_server_abort(uint16_t index, uint8_t sub_index, sdo_abort_code abort_code) 161 | { 162 | next_is_from_client = 0; 163 | next_is_from_server = 1; 164 | next_server_command = sdo_command_server_abort_transfer; 165 | next_is_expedited = 1; 166 | next_is_size_indicated = 1; 167 | next_data_size = 4; 168 | next_index = index; 169 | next_sub_index = sub_index; 170 | next_expedited_data = abort_code; 171 | 172 | sdo_message_server_abort_transfer(&next_message, index, sub_index, abort_code); 173 | log_write_ln("test_sdo: server abort transfer: obj:%04Xh:%d data:%04Xh", index, sub_index, abort_code); 174 | can_bus_send_message(&next_message); 175 | } 176 | 177 | static void message_received_handler(can_message *message) 178 | { 179 | if (test_running) 180 | { 181 | if (next_is_from_client) 182 | { 183 | sdo_client_command r_command = sdo_get_client_command(message); 184 | if (r_command == next_client_command) 185 | { 186 | if (r_command == sdo_command_client_download_init) 187 | { 188 | // Download init command 189 | check_expedited_transfer(message); 190 | } 191 | else if (r_command == sdo_command_client_upload_init) 192 | { 193 | // Upload init command 194 | check_multiplexer_transfer(message); 195 | } 196 | else if (r_command == sdo_command_client_abort_transfer) 197 | { 198 | // Abort transfer command 199 | check_abort_transfer(message); 200 | } 201 | else 202 | { 203 | // Other client command 204 | log_write_ln("test_sdo: test not implemented"); 205 | error = 1; 206 | } 207 | } 208 | else 209 | { 210 | log_write_ln("test_sdo: wrong command"); 211 | error = 1; 212 | } 213 | } 214 | else if (next_is_from_server) 215 | { 216 | // From server 217 | sdo_server_command r_command = sdo_get_server_command(message); 218 | if (r_command == next_server_command) 219 | { 220 | if (r_command == sdo_command_server_download_init) 221 | { 222 | // Download init command 223 | check_multiplexer_transfer(message); 224 | } 225 | else if (r_command == sdo_command_server_upload_init) 226 | { 227 | // Upload init command 228 | check_expedited_transfer(message); 229 | } 230 | else if (r_command == sdo_command_server_abort_transfer) 231 | { 232 | // Abort transfer command 233 | check_abort_transfer(message); 234 | } 235 | else 236 | { 237 | // Other server command 238 | log_write_ln("test_sdo: test not implemented"); 239 | error = 1; 240 | } 241 | } 242 | else 243 | { 244 | log_write_ln("test_sdo: wrong command"); 245 | error = 1; 246 | } 247 | } 248 | else 249 | { 250 | log_write_ln("test_sdo: test setup error"); 251 | error = 1; 252 | } 253 | } 254 | } 255 | 256 | static void check_expedited_transfer(can_message *message) 257 | { 258 | uint8_t r_expedited = sdo_get_expedited_bit(message); 259 | if (r_expedited == next_is_expedited) 260 | { 261 | uint8_t r_size_indicated = sdo_get_size_indicated_bit(message); 262 | if (r_size_indicated == next_is_size_indicated) 263 | { 264 | if (r_expedited && r_size_indicated) 265 | { 266 | // Expedited 267 | uint8_t r_size = sdo_get_expedited_data_size(message); 268 | if (r_size == next_data_size) 269 | { 270 | if (check_multiplexer(message) == 0) 271 | { 272 | uint32_t r_data = sdo_get_expedited_data(message); 273 | if (r_data == next_expedited_data) 274 | { 275 | // OK 276 | log_write_ln("test_sdo: download init request OK"); 277 | } 278 | else 279 | { 280 | log_write_ln("test_sdo: wrong data"); 281 | error = 1; 282 | } 283 | } 284 | } 285 | else 286 | { 287 | log_write_ln("test_sdo: wrong data size"); 288 | error = 1; 289 | } 290 | } 291 | else if (!r_expedited && !r_size_indicated) 292 | { 293 | // Not expedited 294 | log_write_ln("test_sdo: test not implemented"); 295 | error = 1; 296 | } 297 | else 298 | { 299 | log_write_ln("test_sdo: test setup error"); 300 | error = 1; 301 | } 302 | } 303 | else 304 | { 305 | log_write_ln("test_sdo: wrong size indicated"); 306 | error = 1; 307 | } 308 | } 309 | else 310 | { 311 | log_write_ln("test_sdo: wrong expedited"); 312 | error = 1; 313 | } 314 | } 315 | 316 | static void check_multiplexer_transfer(can_message *message) 317 | { 318 | uint16_t r_index = sdo_get_index(message); 319 | if (r_index == next_index) 320 | { 321 | uint8_t r_sub_index = sdo_get_sub_index(message); 322 | if (r_sub_index == next_sub_index) 323 | { 324 | // OK 325 | log_write_ln("test_sdo: upload init request OK"); 326 | } 327 | else 328 | { 329 | log_write_ln("test_sdo: wrong sub index"); 330 | error = 1; 331 | } 332 | } 333 | else 334 | { 335 | log_write_ln("test_sdo: wrong index"); 336 | error = 1; 337 | } 338 | } 339 | 340 | static void check_abort_transfer(can_message *message) 341 | { 342 | if (check_multiplexer(message) == 0) 343 | { 344 | uint32_t r_data = sdo_get_expedited_data(message); 345 | if (r_data == next_expedited_data) 346 | { 347 | // OK 348 | log_write_ln("test_sdo: abort transfer OK"); 349 | } 350 | else 351 | { 352 | log_write_ln("test_sdo: wrong error code"); 353 | error = 1; 354 | } 355 | } 356 | } 357 | 358 | static uint8_t check_multiplexer(can_message *message) 359 | { 360 | uint16_t r_index = sdo_get_index(message); 361 | if (r_index == next_index) 362 | { 363 | uint8_t r_sub_index = sdo_get_sub_index(message); 364 | if (r_sub_index == next_sub_index) 365 | { 366 | // index and sub_index OK 367 | } 368 | else 369 | { 370 | log_write_ln("test_sdo: wrong sub index"); 371 | error = 1; 372 | } 373 | } 374 | else 375 | { 376 | log_write_ln("test_sdo: wrong index"); 377 | error = 1; 378 | } 379 | return error; 380 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack/pdo.c: -------------------------------------------------------------------------------- 1 | // 2 | // pdo.c 3 | // CCanOpenStack 4 | // 5 | // Created by Timo Jääskeläinen on 27.10.2013. 6 | // Copyright (c) 2013 Timo Jääskeläinen. All rights reserved. 7 | // 8 | 9 | #include "pdo.h" 10 | #include "can_bus.h" 11 | #include "log.h" 12 | #include "od.h" 13 | 14 | /***************************** Local Definitions *****************************/ 15 | #define MAX_TPDOS 64 16 | /****************************** Local Variables ******************************/ 17 | static int num_rpdos = 0; 18 | static int num_tpdos = 0; 19 | // tpdo_counter contains event time countdown for each registered TPDO 20 | static int32_t tpdo_counter[MAX_TPDOS]; 21 | static uint32_t prev_tick_count = 0; 22 | 23 | /****************************** Local Prototypes *****************************/ 24 | static int set_pdo_mapping(co_node *node, pdo_mapping_param mapping[], int num_params, uint16_t mapping_index); 25 | static int map_tpdo(co_node *node, int tpdo_num, uint8_t *data, uint8_t *data_num_bytes); 26 | 27 | /****************************** Global Functions *****************************/ 28 | extern void pdo_initialize(uint32_t tick_count) 29 | { 30 | num_rpdos = 0; 31 | num_tpdos = 0; 32 | for (int i = 0; i < MAX_TPDOS; i++) 33 | { 34 | tpdo_counter[i] = 0; 35 | } 36 | prev_tick_count = tick_count; 37 | } 38 | extern int pdo_add_rpdo(co_node *node, uint16_t cob_id, pdo_mapping_param mapping[], int num_params) 39 | { 40 | int error = 0; 41 | uint16_t rpdo_index = 0x1400 + num_rpdos; 42 | uint16_t mapping_index = 0x1600 + num_rpdos; 43 | od_result result = od_internal_write(node->od, rpdo_index, 1, cob_id); 44 | if (result == OD_RESULT_OK) 45 | { 46 | error = set_pdo_mapping(node, mapping, num_params, mapping_index); 47 | } 48 | else 49 | { 50 | error = 1; 51 | } 52 | if (error) 53 | { 54 | log_write_ln("pdo: ERROR add RPDO %04Xh failed", cob_id); 55 | } 56 | else 57 | { 58 | num_rpdos++; 59 | log_write_ln("pdo: add RPDO %04Xh OK", cob_id); 60 | } 61 | return error; 62 | } 63 | extern int pdo_add_tpdo(co_node *node, uint16_t cob_id, uint16_t inhibit, uint16_t event, pdo_mapping_param mapping[], int num_params) 64 | { 65 | int error = 0; 66 | uint16_t tpdo_index = 0x1800 + num_tpdos; 67 | uint16_t mapping_index = 0x1A00 + num_tpdos; 68 | od_result result = od_internal_write(node->od, tpdo_index, 1, cob_id); 69 | if (result == OD_RESULT_OK) 70 | { 71 | result = od_internal_write(node->od, tpdo_index, 3, inhibit); 72 | if (result == OD_RESULT_OK) 73 | { 74 | result = od_internal_write(node->od, tpdo_index, 5, event); 75 | if (result == OD_RESULT_OK) 76 | { 77 | error = set_pdo_mapping(node, mapping, num_params, mapping_index); 78 | } 79 | else 80 | { 81 | error = 1; 82 | } 83 | } 84 | else 85 | { 86 | error = 1; 87 | } 88 | } 89 | else 90 | { 91 | error = 1; 92 | } 93 | if (error) 94 | { 95 | log_write_ln("pdo: ERROR add TPDO %04Xh failed", cob_id); 96 | } 97 | else 98 | { 99 | num_tpdos++; 100 | tpdo_counter[num_tpdos - 1] = event; 101 | log_write_ln("pdo: add TPDO %04Xh OK", cob_id); 102 | } 103 | return error; 104 | } 105 | extern int pdo_process_rpdo(co_node *node, can_message *msg) 106 | { 107 | int error = 0; 108 | int entry_num = -1; 109 | // See if this RPDO is enabled 110 | for (int i = 0; i < num_rpdos; i++) 111 | { 112 | uint16_t index = 0x1400 + i; 113 | uint32_t data; 114 | od_read(node->od, index, 1, &data); 115 | if (data == msg->id) 116 | { 117 | entry_num = i; 118 | break; 119 | } 120 | } 121 | if (entry_num >= 0) 122 | { 123 | // RPDO entry found, so it is enabled. Now map data into OD entries 124 | uint16_t index = 0x1600 + entry_num; 125 | int num_params = 0; 126 | uint32_t data; 127 | // Find the number of mapping parameters 128 | od_result result = od_read(node->od, index, 0, &data); 129 | if (result == OD_RESULT_OK) 130 | { 131 | num_params = data; 132 | uint32_t data1 = msg->data[0] | (msg->data[1] << 8) | (msg->data[2] << 16) | (msg->data[3] << 24); 133 | uint32_t data2 = msg->data[4] | (msg->data[5] << 8) | (msg->data[6] << 16) | (msg->data[7] << 24); 134 | uint8_t current_bit = 0; 135 | for (int i = 1; i <= num_params; i++) 136 | { 137 | // Read the RPDO mapping parameter to find out where to store this piece of data 138 | result = od_read(node->od, index, i, &data); 139 | if (result != OD_RESULT_OK) 140 | { 141 | error = 1; 142 | break; 143 | } 144 | // dest_index/sub_index is where this piece of incoming data should be saved according to RPDO mapping parameter 145 | uint16_t dest_index = (data >> 16) & 0xFFFF; 146 | uint8_t dest_sub_index = (data >> 8) & 0xFF; 147 | uint8_t dest_num_bits = data & 0xFF; 148 | // Make sure this is not a dummy entry, because dummy entries are not supposed to be saved anywhere 149 | if (dest_index >= 0x1000) 150 | { 151 | // Extract n bit long value from the message data (n = dest_num_bits and offset = current_bit) 152 | uint32_t value = 0; 153 | if (current_bit < 32) 154 | { 155 | if (current_bit + dest_num_bits < 32) 156 | { 157 | uint32_t mask = (1 << dest_num_bits) - 1; 158 | value = (data1 >> current_bit) & mask; 159 | } 160 | else 161 | { 162 | int val1bits = 32 - current_bit; 163 | int val2bits = dest_num_bits - val1bits; 164 | uint32_t mask1 = (1 << val1bits) - 1; 165 | uint32_t mask2 = (1 << val2bits) - 1; 166 | value = (data1 >> current_bit) & mask1; 167 | value += (data2 & mask2) << val1bits; 168 | } 169 | } 170 | else 171 | { 172 | uint32_t mask = (1 << dest_num_bits) - 1; 173 | value = data2 >> (current_bit - 32) & mask; 174 | } 175 | // Write value to OD 176 | result = od_write(node->od, dest_index, dest_sub_index, value); 177 | if (result != OD_RESULT_OK) 178 | { 179 | error = 1; 180 | break; 181 | } 182 | } 183 | current_bit += dest_num_bits; 184 | } 185 | } 186 | } 187 | return error; 188 | } 189 | extern int pdo_send_tpdo(co_node *node, uint32_t tick_count) 190 | { 191 | int error = 0; 192 | // Get time passed since last cycle 193 | int32_t time_diff = tick_count - prev_tick_count; 194 | prev_tick_count = tick_count; 195 | if (time_diff < 0) 196 | { 197 | // The tick count overflowed - dirty fix: 198 | time_diff = tick_count; 199 | } 200 | for (int i = 0; i < num_tpdos; i++) 201 | { 202 | tpdo_counter[i] -= time_diff; 203 | if (tpdo_counter[i] <= 0) 204 | { 205 | // The counter reached zero or negative. It's time to send the TPDO. 206 | uint16_t index = 0x1800 + i; 207 | uint32_t data; 208 | uint8_t msg_data[8]; 209 | uint16_t tpdo_cob_id; 210 | uint8_t msg_num_bytes = 0; 211 | uint16_t event_time = 0; 212 | // See if this TPDO is enabled 213 | od_result result = od_read(node->od, index, 1, &data); 214 | if (result != OD_RESULT_OK) 215 | { 216 | error = 1; 217 | log_write_ln("pdo: pdo_send_tpdo: ERROR: could not read OD %04Xh:%d", index, 1); 218 | } 219 | if (!error) 220 | { 221 | // If bit 31 of sub index 1 is 1, then the PDO is diabled 222 | if ((data & (1 << 31)) > 0) 223 | { 224 | // This TPDO is diabled, move on to the next one 225 | continue; 226 | } 227 | else 228 | { 229 | // This TPDO is enabled, save the COB ID, which is 11 bits long 230 | tpdo_cob_id = data & 0x7FF; 231 | } 232 | } 233 | if (!error) 234 | { 235 | // Get this TPDO's event time 236 | result = od_read(node->od, index, 5, &data); 237 | if (result != OD_RESULT_OK) 238 | { 239 | error = 1; 240 | log_write_ln("pdo: pdo_send_tpdo: ERROR: could not read OD %04Xh:%d", index, 5); 241 | } 242 | else 243 | { 244 | event_time = (uint16_t)data; 245 | } 246 | } 247 | if (!error) 248 | { 249 | // Map TPDO to CAN message data 250 | error = map_tpdo(node, i, msg_data, &msg_num_bytes); 251 | } 252 | if (!error) 253 | { 254 | // Send the TPDO message 255 | can_message msg; 256 | msg.data = msg_data; 257 | msg.data_len = msg_num_bytes; 258 | msg.id = tpdo_cob_id; 259 | can_bus_send_message(&msg); 260 | } 261 | if (error) 262 | { 263 | log_write_ln("pdo: pdo_send_tpdo FAILED"); 264 | } 265 | 266 | // Restart the counter 267 | tpdo_counter[i] += event_time; 268 | if (tpdo_counter[i] < 0) 269 | { 270 | // If the time since last cycle was really long and TPDO counter 271 | // still shows negative - make it zero so that many TPDO's won't 272 | // be sent at once: 273 | tpdo_counter[i] = 0; 274 | } 275 | } 276 | } 277 | return error; 278 | } 279 | /****************************** Local Functions ******************************/ 280 | static int set_pdo_mapping(co_node *node, pdo_mapping_param mapping[], int num_params, uint16_t mapping_index) 281 | { 282 | int error = 0; 283 | od_result result; 284 | int total_len_bits = 0; 285 | for (int i = 0; i < num_params; i++) 286 | { 287 | // Check if mapped object exists 288 | od_access_type access_type; 289 | if (od_get_access_type(node->od, mapping[i].index, mapping[i].sub_index, &access_type) == OD_RESULT_OK) 290 | { 291 | // OK - mapped object exists 292 | total_len_bits += mapping[i].length; 293 | if (total_len_bits > 64) 294 | { 295 | error = 1; 296 | log_write_ln("pdo: ERROR: mapping failed, because mapped data exceeds 64 bit limit"); 297 | break; 298 | } 299 | uint32_t value = ((uint32_t)mapping[i].index << 16) + ((uint32_t)mapping[i].sub_index << 8) + mapping[i].length; 300 | result = od_internal_write(node->od, mapping_index, i + 1, value); 301 | if (result != OD_RESULT_OK) 302 | { 303 | error = 1; 304 | break; 305 | } 306 | } 307 | else 308 | { 309 | // Error - mapped object doesn't exist 310 | log_write_ln("pdo: ERROR: mapping failed, because mapped object doesn't exist: %04Xh:%d", mapping[i].index, mapping[i].sub_index); 311 | error = 1; 312 | break; 313 | } 314 | } 315 | if (!error) 316 | { 317 | result = od_internal_write(node->od, mapping_index, 0, num_params); 318 | } 319 | if (result != OD_RESULT_OK) 320 | { 321 | error = 1; 322 | } 323 | return error; 324 | } 325 | static int map_tpdo(co_node *node, int tpdo_num, uint8_t *msg_data, uint8_t *data_num_bytes) 326 | { 327 | int error = 0; 328 | uint32_t out_data1 = 0; 329 | uint32_t out_data2 = 0; 330 | uint8_t current_bit = 0; 331 | uint16_t index = 0x1A00 + tpdo_num; 332 | uint32_t od_value; 333 | int num_params; 334 | // See how many mapping parameters there are 335 | od_result result = od_read(node->od, index, 0, &od_value); 336 | if (result != OD_RESULT_OK) 337 | { 338 | log_write_ln("pdo: map_tpdo: ERROR: could not read OD %04Xh:%d", index, 1); 339 | error = 1; 340 | } 341 | if (!error) 342 | { 343 | num_params = od_value; 344 | // Loop through each mapping parameter and gather OD values from where they point to 345 | for (int i = 1; i <= num_params; i++) 346 | { 347 | // Get source OD entry index, sub index and number of bits 348 | result = od_read(node->od, index, i, &od_value); 349 | if (result != OD_RESULT_OK) 350 | { 351 | error = 1; 352 | log_write_ln("pdo: map_tpdo: ERROR: could not read OD %04Xh:%d", index, 1); 353 | break; 354 | } 355 | uint16_t src_index = (od_value >> 16) & 0xFFFF; 356 | uint8_t src_sub_index = (od_value >> 8) & 0xFF; 357 | uint8_t src_num_bits = od_value & 0xFF; 358 | // Get the value of the source OD entry 359 | result = od_read(node->od, src_index, src_sub_index, &od_value); 360 | // Write the value into data variables 361 | if (current_bit < 32) 362 | { 363 | if (current_bit + src_num_bits < 32) 364 | { 365 | uint32_t mask = (1 << src_num_bits) - 1; 366 | out_data1 += (od_value & mask) << current_bit; 367 | } 368 | else 369 | { 370 | int data1len = 32 - current_bit; 371 | int data2len = src_num_bits - data1len; 372 | uint32_t mask1 = (1 << data1len) - 1; 373 | uint32_t mask2 = (1 << data2len) - 1; 374 | out_data1 += (od_value & mask1) << current_bit; 375 | out_data2 = (od_value >> data1len) & mask2; 376 | } 377 | } 378 | else 379 | { 380 | uint32_t mask = (1 << src_num_bits) - 1; 381 | out_data2 += (od_value & mask) << (current_bit - 32); 382 | } 383 | current_bit += src_num_bits; 384 | } 385 | } 386 | // Copy data from out_data to *msg_data array 387 | for (int i = 0; i < 4; i++) 388 | { 389 | msg_data[i] = (out_data1 >> (8 * i)) & 0xFF; 390 | msg_data[i + 4] = (out_data2 >> (8 * i)) & 0xFF; 391 | } 392 | *data_num_bytes = (current_bit / 8); 393 | return error; 394 | } -------------------------------------------------------------------------------- /CCanOpenStack/CCanOpenStack.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 25140D621818864A009F2F4E /* sdo_client.c in Sources */ = {isa = PBXBuildFile; fileRef = 25140D611818864A009F2F4E /* sdo_client.c */; }; 11 | 25140D6418188C0F009F2F4E /* nmt_master.c in Sources */ = {isa = PBXBuildFile; fileRef = 25140D6318188C0F009F2F4E /* nmt_master.c */; }; 12 | 25140D6618188C1C009F2F4E /* nmt_slave.c in Sources */ = {isa = PBXBuildFile; fileRef = 25140D6518188C1C009F2F4E /* nmt_slave.c */; }; 13 | 25140D691819713A009F2F4E /* test_nmt.c in Sources */ = {isa = PBXBuildFile; fileRef = 25140D681819713A009F2F4E /* test_nmt.c */; }; 14 | 253643FC181DB76200B9D035 /* pdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 253643FB181DB76200B9D035 /* pdo.c */; }; 15 | 253643FE181DBCC000B9D035 /* test_rpdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 253643FD181DBCC000B9D035 /* test_rpdo.c */; }; 16 | 25364402181EFDE100B9D035 /* test_tpdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 25364401181EFDE100B9D035 /* test_tpdo.c */; }; 17 | 25760ACC1816FCFD0013E412 /* test_sdo_server.c in Sources */ = {isa = PBXBuildFile; fileRef = 25760ACB1816FCFD0013E412 /* test_sdo_server.c */; }; 18 | 2578ACB5181463F50010C2D2 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACB4181463F50010C2D2 /* main.c */; }; 19 | 2578ACB7181463F50010C2D2 /* CCanOpenStack.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2578ACB6181463F50010C2D2 /* CCanOpenStack.1 */; }; 20 | 2578ACC11814657F0010C2D2 /* can_bus.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACC01814657F0010C2D2 /* can_bus.c */; }; 21 | 2578ACC518146B0B0010C2D2 /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACC418146B0B0010C2D2 /* log.c */; }; 22 | 2578ACC9181470560010C2D2 /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACC8181470560010C2D2 /* test.c */; }; 23 | 2578ACCC181470D00010C2D2 /* test_can_bus.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACCB181470D00010C2D2 /* test_can_bus.c */; }; 24 | 2578ACD6181479A10010C2D2 /* od.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACD5181479A10010C2D2 /* od.c */; }; 25 | 2578ACD918147D620010C2D2 /* test_od.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACD818147D620010C2D2 /* test_od.c */; }; 26 | 2578ACE31815B2710010C2D2 /* sdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACE21815B2710010C2D2 /* sdo.c */; }; 27 | 2578ACE61815C2430010C2D2 /* test_sdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACE51815C2430010C2D2 /* test_sdo.c */; }; 28 | 2578ACE91815D38F0010C2D2 /* test_util.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACE81815D38F0010C2D2 /* test_util.c */; }; 29 | 2578ACEC1816E31B0010C2D2 /* sdo_server.c in Sources */ = {isa = PBXBuildFile; fileRef = 2578ACEB1816E31B0010C2D2 /* sdo_server.c */; }; 30 | 25C1FF5E1855087100887766 /* delay.c in Sources */ = {isa = PBXBuildFile; fileRef = 25C1FF5D1855087100887766 /* delay.c */; }; 31 | 25C1FF60185509B000887766 /* test_appcycle.c in Sources */ = {isa = PBXBuildFile; fileRef = 25C1FF5F185509B000887766 /* test_appcycle.c */; }; 32 | 25C1FF641855125500887766 /* co_stack.c in Sources */ = {isa = PBXBuildFile; fileRef = 25C1FF631855125500887766 /* co_stack.c */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXCopyFilesBuildPhase section */ 36 | 2578ACAF181463F50010C2D2 /* CopyFiles */ = { 37 | isa = PBXCopyFilesBuildPhase; 38 | buildActionMask = 2147483647; 39 | dstPath = /usr/share/man/man1/; 40 | dstSubfolderSpec = 0; 41 | files = ( 42 | 2578ACB7181463F50010C2D2 /* CCanOpenStack.1 in CopyFiles */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 1; 45 | }; 46 | /* End PBXCopyFilesBuildPhase section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | 25140D5D181799E4009F2F4E /* nmt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nmt.h; sourceTree = ""; }; 50 | 25140D5E181799EE009F2F4E /* nmt_master.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nmt_master.h; sourceTree = ""; }; 51 | 25140D5F181799F6009F2F4E /* nmt_slave.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nmt_slave.h; sourceTree = ""; }; 52 | 25140D6018188641009F2F4E /* sdo_client.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sdo_client.h; sourceTree = ""; }; 53 | 25140D611818864A009F2F4E /* sdo_client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdo_client.c; sourceTree = ""; }; 54 | 25140D6318188C0F009F2F4E /* nmt_master.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nmt_master.c; sourceTree = ""; }; 55 | 25140D6518188C1C009F2F4E /* nmt_slave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nmt_slave.c; sourceTree = ""; }; 56 | 25140D6718197132009F2F4E /* test_nmt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_nmt.h; sourceTree = ""; }; 57 | 25140D681819713A009F2F4E /* test_nmt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_nmt.c; sourceTree = ""; }; 58 | 25140D6A1819AE1C009F2F4E /* pdo_communication.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = pdo_communication.txt; sourceTree = ""; }; 59 | 253643FA181DB6E400B9D035 /* pdo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pdo.h; sourceTree = ""; }; 60 | 253643FB181DB76200B9D035 /* pdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pdo.c; sourceTree = ""; }; 61 | 253643FD181DBCC000B9D035 /* test_rpdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_rpdo.c; sourceTree = ""; }; 62 | 253643FF181DBCCB00B9D035 /* test_rpdo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_rpdo.h; sourceTree = ""; }; 63 | 25364400181EFDD900B9D035 /* test_tpdo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_tpdo.h; sourceTree = ""; }; 64 | 25364401181EFDE100B9D035 /* test_tpdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_tpdo.c; sourceTree = ""; }; 65 | 254140E91819CD73008A75C0 /* object_dictionary.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = object_dictionary.txt; sourceTree = ""; }; 66 | 256C9BD918A01D6800D6BC75 /* readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; 67 | 25760ACA1816FCD80013E412 /* test_sdo_server.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_sdo_server.h; sourceTree = ""; }; 68 | 25760ACB1816FCFD0013E412 /* test_sdo_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_sdo_server.c; sourceTree = ""; }; 69 | 25760ACD18170FB50013E412 /* nmt.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nmt.txt; sourceTree = ""; }; 70 | 25760ACE181713590013E412 /* heartbeat.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = heartbeat.txt; sourceTree = ""; }; 71 | 25760ACF181713C00013E412 /* emergency.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = emergency.txt; sourceTree = ""; }; 72 | 2578ACB1181463F50010C2D2 /* CCanOpenStack */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CCanOpenStack; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | 2578ACB4181463F50010C2D2 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 74 | 2578ACB6181463F50010C2D2 /* CCanOpenStack.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = CCanOpenStack.1; sourceTree = ""; }; 75 | 2578ACBE1814646F0010C2D2 /* can_message.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = can_message.h; sourceTree = ""; }; 76 | 2578ACBF181465490010C2D2 /* can_bus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = can_bus.h; sourceTree = ""; }; 77 | 2578ACC01814657F0010C2D2 /* can_bus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = can_bus.c; sourceTree = ""; }; 78 | 2578ACC3181469C30010C2D2 /* log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; 79 | 2578ACC418146B0B0010C2D2 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = ""; }; 80 | 2578ACC71814702D0010C2D2 /* test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test.h; sourceTree = ""; }; 81 | 2578ACC8181470560010C2D2 /* test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test.c; sourceTree = ""; }; 82 | 2578ACCA1814707A0010C2D2 /* test_can_bus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_can_bus.h; sourceTree = ""; }; 83 | 2578ACCB181470D00010C2D2 /* test_can_bus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_can_bus.c; sourceTree = ""; }; 84 | 2578ACD0181477500010C2D2 /* od_object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = od_object.h; sourceTree = ""; }; 85 | 2578ACD41814799B0010C2D2 /* od.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = od.h; sourceTree = ""; }; 86 | 2578ACD5181479A10010C2D2 /* od.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = od.c; sourceTree = ""; }; 87 | 2578ACD718147D5A0010C2D2 /* test_od.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_od.h; sourceTree = ""; }; 88 | 2578ACD818147D620010C2D2 /* test_od.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_od.c; sourceTree = ""; }; 89 | 2578ACDD1814805B0010C2D2 /* comments.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = comments.txt; sourceTree = ""; }; 90 | 2578ACDE181482C20010C2D2 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = ""; }; 91 | 2578ACDF18148AE30010C2D2 /* todo.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = todo.txt; sourceTree = ""; }; 92 | 2578ACE018148B410010C2D2 /* co_node.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = co_node.h; sourceTree = ""; }; 93 | 2578ACE11815B0C30010C2D2 /* sdo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sdo.h; sourceTree = ""; }; 94 | 2578ACE21815B2710010C2D2 /* sdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdo.c; sourceTree = ""; }; 95 | 2578ACE41815C23B0010C2D2 /* test_sdo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_sdo.h; sourceTree = ""; }; 96 | 2578ACE51815C2430010C2D2 /* test_sdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_sdo.c; sourceTree = ""; }; 97 | 2578ACE71815D3840010C2D2 /* test_util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_util.h; sourceTree = ""; }; 98 | 2578ACE81815D38F0010C2D2 /* test_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_util.c; sourceTree = ""; }; 99 | 2578ACEA1816E2A70010C2D2 /* sdo_server.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sdo_server.h; sourceTree = ""; }; 100 | 2578ACEB1816E31B0010C2D2 /* sdo_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdo_server.c; sourceTree = ""; }; 101 | 25C1FF5C1855086A00887766 /* delay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = delay.h; sourceTree = ""; }; 102 | 25C1FF5D1855087100887766 /* delay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = delay.c; sourceTree = ""; }; 103 | 25C1FF5F185509B000887766 /* test_appcycle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_appcycle.c; sourceTree = ""; }; 104 | 25C1FF61185509BB00887766 /* test_appcycle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test_appcycle.h; sourceTree = ""; }; 105 | 25C1FF621855124D00887766 /* co_stack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = co_stack.h; sourceTree = ""; }; 106 | 25C1FF631855125500887766 /* co_stack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = co_stack.c; sourceTree = ""; }; 107 | 25FB1A11181C704700C5C3C9 /* od_initialize.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = od_initialize.h; sourceTree = ""; }; 108 | /* End PBXFileReference section */ 109 | 110 | /* Begin PBXFrameworksBuildPhase section */ 111 | 2578ACAE181463F50010C2D2 /* Frameworks */ = { 112 | isa = PBXFrameworksBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXFrameworksBuildPhase section */ 119 | 120 | /* Begin PBXGroup section */ 121 | 2578ACA8181463F50010C2D2 = { 122 | isa = PBXGroup; 123 | children = ( 124 | 2578ACB3181463F50010C2D2 /* CCanOpenStack */, 125 | 2578ACB2181463F50010C2D2 /* Products */, 126 | ); 127 | sourceTree = ""; 128 | }; 129 | 2578ACB2181463F50010C2D2 /* Products */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 2578ACB1181463F50010C2D2 /* CCanOpenStack */, 133 | ); 134 | name = Products; 135 | sourceTree = ""; 136 | }; 137 | 2578ACB3181463F50010C2D2 /* CCanOpenStack */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 25C1FF5B1854E1A900887766 /* portable */, 141 | 2578ACC61814701C0010C2D2 /* tests */, 142 | 2578ACCD181476EB0010C2D2 /* can_open */, 143 | 2578ACC2181469B20010C2D2 /* log */, 144 | 2578ACBD181464650010C2D2 /* can */, 145 | 2578ACB4181463F50010C2D2 /* main.c */, 146 | 2578ACB6181463F50010C2D2 /* CCanOpenStack.1 */, 147 | 2578ACDD1814805B0010C2D2 /* comments.txt */, 148 | 2578ACDE181482C20010C2D2 /* utils.h */, 149 | 2578ACDF18148AE30010C2D2 /* todo.txt */, 150 | 25760ACD18170FB50013E412 /* nmt.txt */, 151 | 256C9BD918A01D6800D6BC75 /* readme.txt */, 152 | 25760ACE181713590013E412 /* heartbeat.txt */, 153 | 25760ACF181713C00013E412 /* emergency.txt */, 154 | 25140D6A1819AE1C009F2F4E /* pdo_communication.txt */, 155 | 254140E91819CD73008A75C0 /* object_dictionary.txt */, 156 | ); 157 | path = CCanOpenStack; 158 | sourceTree = ""; 159 | }; 160 | 2578ACBD181464650010C2D2 /* can */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 2578ACBE1814646F0010C2D2 /* can_message.h */, 164 | 2578ACBF181465490010C2D2 /* can_bus.h */, 165 | 2578ACC01814657F0010C2D2 /* can_bus.c */, 166 | ); 167 | name = can; 168 | sourceTree = ""; 169 | }; 170 | 2578ACC2181469B20010C2D2 /* log */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 2578ACC3181469C30010C2D2 /* log.h */, 174 | 2578ACC418146B0B0010C2D2 /* log.c */, 175 | ); 176 | name = log; 177 | sourceTree = ""; 178 | }; 179 | 2578ACC61814701C0010C2D2 /* tests */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 2578ACC71814702D0010C2D2 /* test.h */, 183 | 2578ACC8181470560010C2D2 /* test.c */, 184 | 2578ACCA1814707A0010C2D2 /* test_can_bus.h */, 185 | 2578ACCB181470D00010C2D2 /* test_can_bus.c */, 186 | 2578ACD718147D5A0010C2D2 /* test_od.h */, 187 | 2578ACD818147D620010C2D2 /* test_od.c */, 188 | 2578ACE41815C23B0010C2D2 /* test_sdo.h */, 189 | 2578ACE51815C2430010C2D2 /* test_sdo.c */, 190 | 2578ACE71815D3840010C2D2 /* test_util.h */, 191 | 2578ACE81815D38F0010C2D2 /* test_util.c */, 192 | 25760ACA1816FCD80013E412 /* test_sdo_server.h */, 193 | 25760ACB1816FCFD0013E412 /* test_sdo_server.c */, 194 | 25140D6718197132009F2F4E /* test_nmt.h */, 195 | 25140D681819713A009F2F4E /* test_nmt.c */, 196 | 253643FF181DBCCB00B9D035 /* test_rpdo.h */, 197 | 253643FD181DBCC000B9D035 /* test_rpdo.c */, 198 | 25364400181EFDD900B9D035 /* test_tpdo.h */, 199 | 25364401181EFDE100B9D035 /* test_tpdo.c */, 200 | 25C1FF5F185509B000887766 /* test_appcycle.c */, 201 | 25C1FF61185509BB00887766 /* test_appcycle.h */, 202 | ); 203 | name = tests; 204 | sourceTree = ""; 205 | }; 206 | 2578ACCD181476EB0010C2D2 /* can_open */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | 2578ACD0181477500010C2D2 /* od_object.h */, 210 | 2578ACD41814799B0010C2D2 /* od.h */, 211 | 2578ACD5181479A10010C2D2 /* od.c */, 212 | 25FB1A11181C704700C5C3C9 /* od_initialize.h */, 213 | 2578ACE018148B410010C2D2 /* co_node.h */, 214 | 2578ACE11815B0C30010C2D2 /* sdo.h */, 215 | 2578ACE21815B2710010C2D2 /* sdo.c */, 216 | 2578ACEA1816E2A70010C2D2 /* sdo_server.h */, 217 | 2578ACEB1816E31B0010C2D2 /* sdo_server.c */, 218 | 25140D6018188641009F2F4E /* sdo_client.h */, 219 | 25140D611818864A009F2F4E /* sdo_client.c */, 220 | 25140D5D181799E4009F2F4E /* nmt.h */, 221 | 25140D5E181799EE009F2F4E /* nmt_master.h */, 222 | 25140D6318188C0F009F2F4E /* nmt_master.c */, 223 | 25140D5F181799F6009F2F4E /* nmt_slave.h */, 224 | 25140D6518188C1C009F2F4E /* nmt_slave.c */, 225 | 253643FA181DB6E400B9D035 /* pdo.h */, 226 | 253643FB181DB76200B9D035 /* pdo.c */, 227 | 25C1FF621855124D00887766 /* co_stack.h */, 228 | 25C1FF631855125500887766 /* co_stack.c */, 229 | ); 230 | name = can_open; 231 | sourceTree = ""; 232 | }; 233 | 25C1FF5B1854E1A900887766 /* portable */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | 25C1FF5C1855086A00887766 /* delay.h */, 237 | 25C1FF5D1855087100887766 /* delay.c */, 238 | ); 239 | name = portable; 240 | sourceTree = ""; 241 | }; 242 | /* End PBXGroup section */ 243 | 244 | /* Begin PBXNativeTarget section */ 245 | 2578ACB0181463F50010C2D2 /* CCanOpenStack */ = { 246 | isa = PBXNativeTarget; 247 | buildConfigurationList = 2578ACBA181463F50010C2D2 /* Build configuration list for PBXNativeTarget "CCanOpenStack" */; 248 | buildPhases = ( 249 | 2578ACAD181463F50010C2D2 /* Sources */, 250 | 2578ACAE181463F50010C2D2 /* Frameworks */, 251 | 2578ACAF181463F50010C2D2 /* CopyFiles */, 252 | ); 253 | buildRules = ( 254 | ); 255 | dependencies = ( 256 | ); 257 | name = CCanOpenStack; 258 | productName = CCanOpenStack; 259 | productReference = 2578ACB1181463F50010C2D2 /* CCanOpenStack */; 260 | productType = "com.apple.product-type.tool"; 261 | }; 262 | /* End PBXNativeTarget section */ 263 | 264 | /* Begin PBXProject section */ 265 | 2578ACA9181463F50010C2D2 /* Project object */ = { 266 | isa = PBXProject; 267 | attributes = { 268 | LastUpgradeCheck = 0500; 269 | ORGANIZATIONNAME = "Timo Jääskeläinen"; 270 | }; 271 | buildConfigurationList = 2578ACAC181463F50010C2D2 /* Build configuration list for PBXProject "CCanOpenStack" */; 272 | compatibilityVersion = "Xcode 3.2"; 273 | developmentRegion = English; 274 | hasScannedForEncodings = 0; 275 | knownRegions = ( 276 | en, 277 | ); 278 | mainGroup = 2578ACA8181463F50010C2D2; 279 | productRefGroup = 2578ACB2181463F50010C2D2 /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | 2578ACB0181463F50010C2D2 /* CCanOpenStack */, 284 | ); 285 | }; 286 | /* End PBXProject section */ 287 | 288 | /* Begin PBXSourcesBuildPhase section */ 289 | 2578ACAD181463F50010C2D2 /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 2578ACD6181479A10010C2D2 /* od.c in Sources */, 294 | 253643FE181DBCC000B9D035 /* test_rpdo.c in Sources */, 295 | 25140D6618188C1C009F2F4E /* nmt_slave.c in Sources */, 296 | 2578ACEC1816E31B0010C2D2 /* sdo_server.c in Sources */, 297 | 2578ACD918147D620010C2D2 /* test_od.c in Sources */, 298 | 2578ACCC181470D00010C2D2 /* test_can_bus.c in Sources */, 299 | 2578ACC518146B0B0010C2D2 /* log.c in Sources */, 300 | 25C1FF60185509B000887766 /* test_appcycle.c in Sources */, 301 | 25C1FF641855125500887766 /* co_stack.c in Sources */, 302 | 253643FC181DB76200B9D035 /* pdo.c in Sources */, 303 | 25760ACC1816FCFD0013E412 /* test_sdo_server.c in Sources */, 304 | 2578ACE31815B2710010C2D2 /* sdo.c in Sources */, 305 | 25364402181EFDE100B9D035 /* test_tpdo.c in Sources */, 306 | 25140D6418188C0F009F2F4E /* nmt_master.c in Sources */, 307 | 25140D691819713A009F2F4E /* test_nmt.c in Sources */, 308 | 2578ACB5181463F50010C2D2 /* main.c in Sources */, 309 | 2578ACC11814657F0010C2D2 /* can_bus.c in Sources */, 310 | 25140D621818864A009F2F4E /* sdo_client.c in Sources */, 311 | 2578ACE91815D38F0010C2D2 /* test_util.c in Sources */, 312 | 2578ACE61815C2430010C2D2 /* test_sdo.c in Sources */, 313 | 2578ACC9181470560010C2D2 /* test.c in Sources */, 314 | 25C1FF5E1855087100887766 /* delay.c in Sources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | /* End PBXSourcesBuildPhase section */ 319 | 320 | /* Begin XCBuildConfiguration section */ 321 | 2578ACB8181463F50010C2D2 /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 326 | CLANG_CXX_LIBRARY = "libc++"; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | COPY_PHASE_STRIP = NO; 337 | GCC_C_LANGUAGE_STANDARD = gnu99; 338 | GCC_DYNAMIC_NO_PIC = NO; 339 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 340 | GCC_OPTIMIZATION_LEVEL = 0; 341 | GCC_PREPROCESSOR_DEFINITIONS = ( 342 | "DEBUG=1", 343 | "$(inherited)", 344 | ); 345 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | MACOSX_DEPLOYMENT_TARGET = 10.8; 353 | ONLY_ACTIVE_ARCH = YES; 354 | SDKROOT = macosx; 355 | }; 356 | name = Debug; 357 | }; 358 | 2578ACB9181463F50010C2D2 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ALWAYS_SEARCH_USER_PATHS = NO; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_OBJC_ARC = YES; 365 | CLANG_WARN_BOOL_CONVERSION = YES; 366 | CLANG_WARN_CONSTANT_CONVERSION = YES; 367 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INT_CONVERSION = YES; 371 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 372 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 373 | COPY_PHASE_STRIP = YES; 374 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 375 | ENABLE_NS_ASSERTIONS = NO; 376 | GCC_C_LANGUAGE_STANDARD = gnu99; 377 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 378 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 379 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 380 | GCC_WARN_UNDECLARED_SELECTOR = YES; 381 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 382 | GCC_WARN_UNUSED_FUNCTION = YES; 383 | GCC_WARN_UNUSED_VARIABLE = YES; 384 | MACOSX_DEPLOYMENT_TARGET = 10.8; 385 | SDKROOT = macosx; 386 | }; 387 | name = Release; 388 | }; 389 | 2578ACBB181463F50010C2D2 /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | }; 394 | name = Debug; 395 | }; 396 | 2578ACBC181463F50010C2D2 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | 2578ACAC181463F50010C2D2 /* Build configuration list for PBXProject "CCanOpenStack" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 2578ACB8181463F50010C2D2 /* Debug */, 410 | 2578ACB9181463F50010C2D2 /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | 2578ACBA181463F50010C2D2 /* Build configuration list for PBXNativeTarget "CCanOpenStack" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 2578ACBB181463F50010C2D2 /* Debug */, 419 | 2578ACBC181463F50010C2D2 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | /* End XCConfigurationList section */ 425 | }; 426 | rootObject = 2578ACA9181463F50010C2D2 /* Project object */; 427 | } 428 | --------------------------------------------------------------------------------