├── 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 |
--------------------------------------------------------------------------------