├── docs └── README.md ├── examples ├── master402 │ ├── README_zh.md │ ├── README.md │ ├── master402_canopen.h │ ├── canopen_callback.h │ ├── master402_od.h │ ├── canopen_callback.c │ ├── motor_control.c │ └── master402_canopen.c └── SConscript ├── SConscript ├── src ├── SConscript ├── nmtMaster.c ├── timer_rtthread.c ├── can_rtthread.c ├── symbols.c ├── nmtSlave.c ├── timer.c ├── sync.c ├── emcy.c ├── objacces.c ├── dcf.c ├── lifegrd.c └── states.c ├── inc ├── sysdep.h ├── timerscfg.h ├── canfestival.h ├── can.h ├── dcf.h ├── nmtSlave.h ├── timers_driver.h ├── config.h ├── sync.h ├── applicfg.h ├── timer.h ├── nmtMaster.h ├── emcy.h ├── can_driver.h ├── states.h ├── objdictdef.h ├── lifegrd.h ├── pdo.h ├── def.h ├── lss.h ├── data.h ├── objacces.h └── sdo.h └── README.md /docs/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/master402/README_zh.md: -------------------------------------------------------------------------------- 1 | 1. 本示例是一个Cia402主站示例,可以用来控制伺服驱动器 2 | 2. 伺服 ID 应设置为2, 默认伺服被设置为位置(Profile Position)模式 3 | 3. 可以使用 finsh 测试 servo on及相对移动功能 -------------------------------------------------------------------------------- /examples/master402/README.md: -------------------------------------------------------------------------------- 1 | 1. this is a cia 402 master demo, used to control servo drivers. 2 | 2. servo driver id should be 2, servo driver is configured in profile position mode 3 | 3. use finsh to servo on the driver, move to relative position. -------------------------------------------------------------------------------- /examples/master402/master402_canopen.h: -------------------------------------------------------------------------------- 1 | #ifndef __AGV_CANOPEN_H__ 2 | #define __AGV_CANOPEN_H__ 3 | 4 | #define CONTROLLER_NODEID 1 5 | #define SERVO_NODEID 2 6 | #define PDO_TRANSMISSION_TYPE 1 7 | 8 | extern CO_Data *OD_Data; 9 | void canopen_start_thread_entry(void *parameter); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /examples/SConscript: -------------------------------------------------------------------------------- 1 | Import('rtconfig') 2 | import os 3 | from building import * 4 | 5 | cwd = GetCurrentDir() 6 | src = Glob('master402/*.c') 7 | CPPPATH = [GetCurrentDir() + '/master402'] 8 | 9 | group = DefineGroup('Master402', src, depend = ['CANFESTIVAL_USING_EG_MASTER402'], CPPPATH = CPPPATH) 10 | 11 | Return('group') 12 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | Import('rtconfig') 2 | import os 3 | from building import * 4 | 5 | 6 | objs = [] 7 | cwd = GetCurrentDir() 8 | list = os.listdir(cwd) 9 | 10 | for item in list: 11 | if os.path.isfile(os.path.join(cwd, item, 'SConscript')): 12 | objs = objs + SConscript(os.path.join(item, 'SConscript')) 13 | 14 | Return('objs') 15 | -------------------------------------------------------------------------------- /src/SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | 3 | cwd = GetCurrentDir() 4 | src = Split(""" 5 | emcy.c 6 | lifegrd.c 7 | nmtMaster.c 8 | nmtSlave.c 9 | objacces.c 10 | pdo.c 11 | sdo.c 12 | states.c 13 | sync.c 14 | timer.c 15 | can_rtthread.c 16 | timer_rtthread.c 17 | """) 18 | CPPPATH = [GetCurrentDir() + '/../inc'] 19 | 20 | group = DefineGroup('CanFestival', src, depend = ['RT_USING_CAN', 'RT_USING_HWTIMER'], CPPPATH = CPPPATH) 21 | 22 | Return('group') 23 | -------------------------------------------------------------------------------- /examples/master402/canopen_callback.h: -------------------------------------------------------------------------------- 1 | #include "data.h" 2 | 3 | void master402_heartbeatError(CO_Data* d, UNS8 heartbeatID); 4 | void master402_initialisation(CO_Data* d); 5 | void master402_preOperational(CO_Data* d); 6 | void master402_operational(CO_Data* d); 7 | void master402_stopped(CO_Data* d); 8 | 9 | void master402_post_sync(CO_Data* d); 10 | void master402_post_TPDO(CO_Data* d); 11 | void master402_storeODSubIndex(CO_Data* d, UNS16 wIndex, UNS8 bSubindex); 12 | void master402_post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]); 13 | -------------------------------------------------------------------------------- /inc/sysdep.h: -------------------------------------------------------------------------------- 1 | #ifndef __sysdep_h__ 2 | #define __sysdep_h__ 3 | 4 | #include "config.h" 5 | 6 | #ifdef CANOPEN_BIG_ENDIAN 7 | 8 | /* Warning: the argument must not update pointers, e.g. *p++ */ 9 | 10 | #define UNS16_LE(v) ((((UNS16)(v) & 0xff00) >> 8) | \ 11 | (((UNS16)(v) & 0x00ff) << 8)) 12 | 13 | #define UNS32_LE(v) ((((UNS32)(v) & 0xff000000) >> 24) | \ 14 | (((UNS32)(v) & 0x00ff0000) >> 8) | \ 15 | (((UNS32)(v) & 0x0000ff00) << 8) | \ 16 | (((UNS32)(v) & 0x000000ff) << 24)) 17 | 18 | #else 19 | 20 | #define UNS16_LE(v) (v) 21 | 22 | #define UNS32_LE(v) (v) 23 | 24 | #endif 25 | 26 | #endif /* __sysdep_h__ */ 27 | 28 | -------------------------------------------------------------------------------- /examples/master402/master402_od.h: -------------------------------------------------------------------------------- 1 | 2 | /* File generated by gen_cfile.py. Should not be modified. */ 3 | 4 | #ifndef MASTER402_H 5 | #define MASTER402_H 6 | 7 | #include "data.h" 8 | 9 | /* Prototypes of function provided by object dictionnary */ 10 | UNS32 master402_valueRangeTest (UNS8 typeValue, void * value); 11 | const indextable * master402_scanIndexOD (CO_Data *d, UNS16 wIndex, UNS32 * errorCode); 12 | 13 | /* Master node data struct */ 14 | extern CO_Data master402_Data; 15 | extern UNS16 control_word_6040; /* Mapped at index 0x2000, subindex 0x00*/ 16 | extern INTEGER8 modes_of_operation_6060; /* Mapped at index 0x2001, subindex 0x00*/ 17 | extern INTEGER32 target_position_607a; /* Mapped at index 0x2002, subindex 0x00*/ 18 | extern UNS32 profile_velocity_6081; /* Mapped at index 0x2003, subindex 0x00*/ 19 | extern UNS16 status_word_6041; /* Mapped at index 0x2004, subindex 0x00*/ 20 | extern INTEGER32 position_actual_value_6063; /* Mapped at index 0x2005, subindex 0x00*/ 21 | extern INTEGER32 velocity_actual_value_606c; /* Mapped at index 0x2006, subindex 0x00*/ 22 | 23 | #endif // MASTER402_H 24 | -------------------------------------------------------------------------------- /inc/timerscfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | AT91 Port: Peter CHRISTEN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | */ 23 | 24 | #ifndef __TIMERSCFG_H__ 25 | #define __TIMERSCFG_H__ 26 | 27 | // Whatever your microcontroller, the timer wont work if 28 | // TIMEVAL is not at least on 32 bits 29 | #define TIMEVAL UNS32 30 | 31 | // The timer of the STM32 counts from 0000 to 0xFFFF 32 | #define TIMEVAL_MAX 0xFFFFFFFF 33 | 34 | // The timer is incrementing every 1 us. 35 | #define MS_TO_TIMEVAL(ms) ((ms) * 1000) 36 | #define US_TO_TIMEVAL(us) ((us) * 1) 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /inc/canfestival.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | AT91 Port: Peter CHRISTEN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | */ 23 | 24 | 25 | #ifndef __CAN_CANFESTIVAL__ 26 | #define __CAN_CANFESTIVAL__ 27 | 28 | #include "applicfg.h" 29 | #include "can_driver.h" 30 | #include "data.h" 31 | //#include "objdict.h" 32 | 33 | // --------- to be called by user app --------- 34 | void initTimer(void); 35 | CAN_PORT canOpen(s_BOARD *board, CO_Data * d); 36 | void StartTimerLoop(TimerCallback_t _init_callback); 37 | UNS8 canSend(CAN_PORT notused, Message *m); 38 | UNS8 canReceive(Message *m); 39 | #endif 40 | -------------------------------------------------------------------------------- /inc/can.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #ifndef __can_h__ 24 | #define __can_h__ 25 | 26 | #include "applicfg.h" 27 | 28 | /** 29 | * @brief The CAN message structure 30 | * @ingroup can 31 | */ 32 | typedef struct { 33 | UNS16 cob_id; /**< message's ID */ 34 | UNS8 rtr; /**< remote transmission request. (0 if not rtr message, 1 if rtr message) */ 35 | UNS8 len; /**< message's length (0 to 8) */ 36 | UNS8 data[8]; /**< message's datas */ 37 | } Message; 38 | 39 | #define Message_Initializer {0,0,0,{0,0,0,0,0,0,0,0}} 40 | 41 | typedef UNS8 (*canSend_t)(Message *); 42 | 43 | #endif /* __can_h__ */ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a package for RT-Thread operating system. 2 | 3 | Forked from the CanFestival-3 project https://bitbucket.org/Mongo/canfestival-3-asc 4 | 5 | # CanFestival 6 | 7 | ## 1、介绍 8 | 9 | 此 package 是 Canfestival (一个开源的 CANopen 协议栈)在 RT-Thread 系统上的移植。使用了 10 | RT-Thread 的 CAN 驱动和 hwtimer 驱动,从而可以运行于所有提供了这两个驱动的平台。 11 | 同时提供了 CANopen 的一些示例,力图做到开箱即用。 12 | 13 | ### 1.1 目录结构 14 | 15 | | 名称 | 说明 | 16 | | ---- | ---- | 17 | | docs | 文档目录 | 18 | | examples | 例子目录,Master402 为 DS402 主站示例,用于控制伺服电机| 19 | | inc | 头文件目录 | 20 | | src | 源代码目录 | 21 | 22 | ### 1.2 许可证 23 | 24 | Canfestival package 遵循 LGPLv2.1 许可,详见 `LICENSE` 文件。 25 | 26 | ### 1.3 依赖 27 | 28 | - RT-Thread 3.0+ 29 | - CAN 驱动 30 | - hwtimer 驱动 31 | 32 | ## 2、如何打开 CanFestival 33 | 34 | 使用 CanFestival package 需要在 RT-Thread 的包管理器中选择它,具体路径如下: 35 | 36 | ``` 37 | RT-Thread online packages 38 | miscellaneous packages ---> 39 | [*] CanFestival: A free software CANopen framework 40 | ``` 41 | 42 | 然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新包到 BSP 中。 43 | 44 | ## 3、使用 Canfestival 45 | 46 | 在 menuconfig 中打开 CAN 驱动和 hwtimer驱动 47 | 并且在 CanFestival config 中配置好 CAN 驱动的 device name, 以及 hwtimer 驱动的 device name 48 | ``` 49 | (can1) CAN device name for CanFestival 50 | (timer1) hwtimer device name for CanFestival 51 | (9) The priority level value of can receive thread 52 | (10) The priority level value of timer thread 53 | [*] Enable Cia402 Master example 54 | ``` 55 | 根据需要配置 can 接收线程,和时钟线程的优先级。 56 | 选择需要使用的例子。 57 | 58 | 在打开 Canfestival package 后,当进行 bsp 编译时,它会被加入到 bsp 工程中进行编译。 59 | 60 | ## 5、联系方式 & 感谢 61 | 62 | * 维护:gbcwbz 63 | * 主页:https://github.com/gbcwbz/canfestival-rtt 64 | -------------------------------------------------------------------------------- /inc/dcf.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | #include "data.h" 23 | 24 | #define DCF_STATUS_INIT 0 25 | #define DCF_STATUS_READ_CHECK 1 26 | #define DCF_STATUS_WRITE 2 27 | #define DCF_STATUS_SAVED 3 28 | #define DCF_STATUS_VERIF_OK 4 29 | 30 | /** 31 | * @brief Init the consise dcf in CO_Data for nodeId 32 | * 33 | * @param *d Pointer on a CAN object data structure 34 | * @param nodeId Id of the slave node 35 | * @return 1: dcf check started 36 | * 0: nothing to do 37 | */ 38 | UNS8 init_consise_dcf(CO_Data* d, UNS8 nodeId); 39 | 40 | /** 41 | * @brief Function to be called from post_SlaveBootup 42 | * for starting the configuration manager 43 | * 44 | * @param *d Pointer on a CAN object data structure 45 | * @param nodeId Id of the slave node 46 | * @return 0: configuration manager busy 47 | * 1: nothing to check, node started 48 | * 2: dcf check started 49 | */ 50 | UNS8 check_and_start_node(CO_Data* d, UNS8 nodeId); 51 | 52 | -------------------------------------------------------------------------------- /inc/nmtSlave.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup nmtslave NMT Slave 24 | * @brief The NMT Slave methods are called automatically when a NMT message from Master are received. 25 | * @ingroup networkmanagement 26 | */ 27 | 28 | #ifndef __nmtSlave_h__ 29 | #define __nmtSlave_h__ 30 | 31 | #include 32 | #include "data.h" 33 | 34 | /** 35 | * @brief Threat the reception of a NMT message from the master. 36 | * @param *d Pointer to the CAN data structure 37 | * @param *m Pointer to the message received 38 | * @return 39 | * - 0 if OK 40 | * - -1 if the slave is not allowed, by its state, to receive the message 41 | */ 42 | void proceedNMTstateChange (CO_Data* d, Message * m); 43 | 44 | /** 45 | * @brief Transmit the boot-Up frame when the slave is moving from initialization 46 | * state to pre_operational state. 47 | * @param *d Pointer on the CAN data structure 48 | * @return canSend(bus_id,&m) 49 | */ 50 | UNS8 slaveSendBootUp (CO_Data* d); 51 | 52 | 53 | #endif /* __nmtSlave_h__ */ 54 | -------------------------------------------------------------------------------- /inc/timers_driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup timer Timer Management 24 | * @ingroup userapi 25 | */ 26 | 27 | #ifndef __timer_driver_h__ 28 | #define __timer_driver_h__ 29 | 30 | #include "timerscfg.h" 31 | #include "timer.h" 32 | 33 | // For use from CAN driver 34 | 35 | 36 | /** 37 | * @ingroup timer 38 | * @brief Acquire mutex 39 | */ 40 | void EnterMutex(void); 41 | 42 | /** 43 | * @ingroup timer 44 | * @brief Release mutex 45 | */ 46 | void LeaveMutex(void); 47 | 48 | void WaitReceiveTaskEnd(TASK_HANDLE*); 49 | 50 | /** 51 | * @ingroup timer 52 | * @brief Initialize Timer 53 | */ 54 | void TimerInit(void); 55 | 56 | /** 57 | * @ingroup timer 58 | * @brief Cleanup Timer 59 | */ 60 | void TimerCleanup(void); 61 | 62 | /** 63 | * @ingroup timer 64 | * @brief Start the timer task 65 | * @param Callback A callback function 66 | */ 67 | void StartTimerLoop(TimerCallback_t Callback); 68 | 69 | /** 70 | * @ingroup timer 71 | * @brief Stop the timer task 72 | * @param Callback A callback function 73 | */ 74 | void StopTimerLoop(TimerCallback_t Callback); 75 | 76 | /** 77 | * @brief Stop the timer task 78 | * @param port CanFestival file descriptor 79 | * @param *handle handle of receive loop thread 80 | * @param *ReceiveLoopPtr Pointer on the receive loop function 81 | */ 82 | void CreateReceiveTask(CAN_PORT port, TASK_HANDLE* handle, void* ReceiveLoopPtr); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /inc/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | AVR Port: Andreas GLAUSER and Peter CHRISTEN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | */ 23 | 24 | #ifndef _CONFIG_H_ 25 | #define _CONFIG_H_ 26 | 27 | #define CAN_BAUDRATE 1000 28 | 29 | // Needed defines by Canfestival lib 30 | #define MAX_CAN_BUS_ID 1 31 | #define SDO_MAX_LENGTH_TRANSFER 32 32 | #define SDO_BLOCK_SIZE 16 33 | #define SDO_MAX_SIMULTANEOUS_TRANSFERS 4 34 | #define NMT_MAX_NODE_ID 128 35 | #define SDO_TIMEOUT_MS 3000U 36 | #define MAX_NB_TIMER 8 37 | #define MIN_TIMER_TIMEOUT_US 5U 38 | 39 | //#define SDO_DYNAMIC_BUFFER_ALLOCATION 40 | //#define SDO_DYNAMIC_BUFFER_ALLOCATION_SIZE (1024) 41 | //#define SDO_MAX_LENGTH_TRANSFERT 32 42 | //#define SDO_MAX_SIMULTANEOUS_TRANSFERTS 5 43 | 44 | // CANOPEN_BIG_ENDIAN is not defined 45 | #define CANOPEN_LITTLE_ENDIAN 1 46 | 47 | #define US_TO_TIMEVAL_FACTOR 8 48 | 49 | #define REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERS_TIMES(repeat)\ 50 | repeat 51 | #define REPEAT_NMT_MAX_NODE_ID_TIMES(repeat)\ 52 | repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat 53 | 54 | #define EMCY_MAX_ERRORS 8 55 | #define REPEAT_EMCY_MAX_ERRORS_TIMES(repeat)\ 56 | repeat repeat repeat repeat repeat repeat repeat repeat 57 | 58 | 59 | #endif /* _CONFIG_H_ */ 60 | -------------------------------------------------------------------------------- /inc/sync.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup synco Synchronisation Object 24 | * SYNC object is a CANopen message forcing the receiving nodes to sample the inputs mapped into synchronous TPDOS. 25 | * Receiving this message cause the node to set the outputs to values received in the previous synchronous RPDO. 26 | * @ingroup comobj 27 | */ 28 | 29 | #ifndef __SYNC_h__ 30 | #define __SYNC_h__ 31 | 32 | void startSYNC(CO_Data* d); 33 | 34 | void stopSYNC(CO_Data* d); 35 | 36 | typedef void (*post_sync_t)(CO_Data*); 37 | void _post_sync(CO_Data* d); 38 | 39 | typedef void (*post_TPDO_t)(CO_Data*); 40 | void _post_TPDO(CO_Data* d); 41 | 42 | /** 43 | * @brief Transmit a SYNC message and trigger sync TPDOs 44 | * @param *d Pointer on a CAN object data structure 45 | * @return 46 | */ 47 | UNS8 sendSYNC (CO_Data* d); 48 | 49 | /** 50 | * @brief Transmit a SYNC message on CAN bus 51 | * @param *d Pointer on a CAN object data structure 52 | * @return 53 | */ 54 | UNS8 sendSYNCMessage(CO_Data* d); 55 | 56 | /** 57 | * @brief This function is called when the node is receiving a SYNC message (cob-id = 0x80). 58 | * - Check if the node is in OERATIONAL mode. (other mode : return 0 but does nothing). 59 | * - Get the SYNC cobId by reading the dictionary index 1005, check it does correspond to the received cobId 60 | * - Trigger sync TPDO emission 61 | * @param *d Pointer on a CAN object data structure 62 | * @return 0 if OK, 0xFF if error 63 | */ 64 | UNS8 proceedSYNC (CO_Data* d); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /inc/applicfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | AT91 Port: Peter CHRISTEN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | */ 23 | 24 | #ifndef __APPLICFG_H__ 25 | #define __APPLICFG_H__ 26 | 27 | #include 28 | #include 29 | 30 | // Integers 31 | #define INTEGER8 signed char 32 | #define INTEGER16 short 33 | #define INTEGER24 long 34 | #define INTEGER32 long 35 | #define INTEGER40 long long 36 | #define INTEGER48 long long 37 | #define INTEGER56 long long 38 | #define INTEGER64 long long 39 | 40 | // Unsigned integers 41 | #define UNS8 unsigned char 42 | #define UNS16 unsigned short 43 | #define UNS32 unsigned long 44 | #define UNS24 unsigned long 45 | #define UNS40 unsigned long long 46 | #define UNS48 unsigned long long 47 | #define UNS56 unsigned long long 48 | #define UNS64 unsigned long long 49 | 50 | 51 | 52 | // Reals 53 | #define REAL32 float 54 | #define REAL64 double 55 | 56 | #include "can.h" 57 | 58 | 59 | // MSG functions 60 | // not finished, the strings have to be placed to the flash and printed out 61 | // using the printf_P function 62 | /// Definition of MSG_ERR 63 | // --------------------- 64 | #ifdef DEBUG_ERR_CONSOLE_ON 65 | #define MSG_ERR(num, str, val) LOG_E("0x%04X %s %d", num, str, val) 66 | #else 67 | # define MSG_ERR(num, str, val) 68 | #endif 69 | 70 | /// Definition of MSG_WAR 71 | // --------------------- 72 | #ifdef DEBUG_WAR_CONSOLE_ON 73 | #define MSG_WAR(num, str, val) LOG_W("0x%04X %s %d", num, str, val) 74 | #else 75 | # define MSG_WAR(num, str, val) 76 | #endif 77 | 78 | typedef void* CAN_HANDLE; 79 | 80 | typedef void* CAN_PORT; 81 | 82 | typedef struct rt_thread TASK_HANDLE; 83 | 84 | #endif 85 | 86 | 87 | -------------------------------------------------------------------------------- /examples/master402/canopen_callback.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #include 24 | #include "canfestival.h" 25 | #include "master402_od.h" 26 | #include "canopen_callback.h" 27 | #include "master402_canopen.h" 28 | 29 | /*****************************************************************************/ 30 | void master402_heartbeatError(CO_Data* d, UNS8 heartbeatID) 31 | { 32 | rt_kprintf("heartbeatError %d\n", heartbeatID); 33 | } 34 | 35 | void master402_initialisation(CO_Data* d) 36 | { 37 | rt_kprintf("canfestival enter initialisation state\n"); 38 | } 39 | 40 | void master402_preOperational(CO_Data* d) 41 | { 42 | rt_thread_t tid; 43 | rt_kprintf("canfestival enter preOperational state\n"); 44 | tid = rt_thread_create("co_cfg", canopen_start_thread_entry, RT_NULL, 1024, 12, 2); 45 | if(tid == RT_NULL) 46 | { 47 | rt_kprintf("canfestival config thread start failed!\n"); 48 | } 49 | else 50 | { 51 | rt_thread_startup(tid); 52 | } 53 | } 54 | 55 | void master402_operational(CO_Data* d) 56 | { 57 | rt_kprintf("canfestival enter operational state\n"); 58 | } 59 | 60 | void master402_stopped(CO_Data* d) 61 | { 62 | rt_kprintf("canfestival enter stop state\n"); 63 | } 64 | 65 | void master402_post_sync(CO_Data* d) 66 | { 67 | 68 | } 69 | 70 | void master402_post_TPDO(CO_Data* d) 71 | { 72 | 73 | } 74 | 75 | void master402_storeODSubIndex(CO_Data* d, UNS16 wIndex, UNS8 bSubindex) 76 | { 77 | /*TODO : 78 | * - call getODEntry for index and subindex, 79 | * - save content to file, database, flash, nvram, ... 80 | * 81 | * To ease flash organisation, index of variable to store 82 | * can be established by scanning d->objdict[d->ObjdictSize] 83 | * for variables to store. 84 | * 85 | * */ 86 | rt_kprintf("storeODSubIndex : %4.4x %2.2x\n", wIndex, bSubindex); 87 | } 88 | 89 | void master402_post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]) 90 | { 91 | rt_kprintf("received EMCY message. Node: %2.2x ErrorCode: %4.4x ErrorRegister: %2.2x\n", nodeID, errCode, errReg); 92 | } 93 | -------------------------------------------------------------------------------- /src/nmtMaster.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22 | USA 23 | */ 24 | /*! 25 | ** @file nmtMaster.c 26 | ** @author Edouard TISSERANT and Francis DUPIN 27 | ** @date Tue Jun 5 08:47:18 2007 28 | ** 29 | ** @brief 30 | ** 31 | ** 32 | */ 33 | #include "nmtMaster.h" 34 | #include "canfestival.h" 35 | #include "sysdep.h" 36 | 37 | /*! 38 | ** 39 | ** 40 | ** @param d 41 | ** @param nodeId 42 | ** @param cs 43 | ** 44 | ** @return 45 | **/ 46 | UNS8 masterSendNMTstateChange(CO_Data* d, UNS8 nodeId, UNS8 cs) 47 | { 48 | Message m; 49 | 50 | MSG_WAR(0x3501, "Send_NMT cs : ", cs); 51 | MSG_WAR(0x3502, " to node : ", nodeId); 52 | /* message configuration */ 53 | m.cob_id = 0x0000; /*(NMT) << 7*/ 54 | m.rtr = NOT_A_REQUEST; 55 | m.len = 2; 56 | m.data[0] = cs; 57 | m.data[1] = nodeId; 58 | 59 | return canSend(d->canHandle,&m); 60 | } 61 | 62 | 63 | /*! 64 | ** 65 | ** 66 | ** @param d 67 | ** @param nodeId 68 | ** 69 | ** @return 70 | **/ 71 | UNS8 masterSendNMTnodeguard(CO_Data* d, UNS8 nodeId) 72 | { 73 | Message m; 74 | 75 | /* message configuration */ 76 | UNS16 tmp = nodeId | (NODE_GUARD << 7); 77 | m.cob_id = UNS16_LE(tmp); 78 | m.rtr = REQUEST; 79 | m.len = 0; 80 | 81 | MSG_WAR(0x3503, "Send_NODE_GUARD to node : ", nodeId); 82 | 83 | return canSend(d->canHandle,&m); 84 | } 85 | 86 | /*! 87 | ** 88 | ** 89 | ** @param d 90 | ** @param nodeId 91 | ** 92 | ** @return 93 | **/ 94 | UNS8 masterRequestNodeState(CO_Data* d, UNS8 nodeId) 95 | { 96 | /* FIXME: should warn for bad toggle bit. */ 97 | 98 | /* NMTable configuration to indicate that the master is waiting 99 | for a Node_Guard frame from the slave whose node_id is ID 100 | */ 101 | d->NMTable[nodeId] = Unknown_state; /* A state that does not exist 102 | */ 103 | 104 | if (nodeId == 0) { /* NMT broadcast */ 105 | UNS8 i = 0; 106 | for (i = 0 ; i < NMT_MAX_NODE_ID ; i++) { 107 | d->NMTable[i] = Unknown_state; 108 | } 109 | } 110 | return masterSendNMTnodeguard(d,nodeId); 111 | } 112 | 113 | -------------------------------------------------------------------------------- /inc/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #ifndef __timer_h__ 24 | #define __timer_h__ 25 | 26 | #include 27 | #include 28 | 29 | #define TIMER_HANDLE INTEGER16 30 | 31 | #include "data.h" 32 | 33 | /* --------- types and constants definitions --------- */ 34 | #define TIMER_FREE 0 35 | #define TIMER_ARMED 1 36 | #define TIMER_TRIG 2 37 | #define TIMER_TRIG_PERIOD 3 38 | 39 | #define TIMER_NONE -1 40 | 41 | typedef void (*TimerCallback_t)(CO_Data* d, UNS32 id); 42 | 43 | struct struct_s_timer_entry { 44 | UNS8 state; 45 | CO_Data* d; 46 | TimerCallback_t callback; /* The callback func. */ 47 | UNS32 id; /* The callback func. */ 48 | TIMEVAL val; 49 | TIMEVAL interval; /* Periodicity */ 50 | }; 51 | 52 | typedef struct struct_s_timer_entry s_timer_entry; 53 | 54 | /* --------- prototypes --------- */ 55 | /*#define SetAlarm(d, id, callback, value, period) printf("%s, %d, SetAlarm(%s, %s, %s, %s, %s)\n",__FILE__, __LINE__, #d, #id, #callback, #value, #period); _SetAlarm(d, id, callback, value, period)*/ 56 | /** 57 | * @ingroup timer 58 | * @brief Set an alarm to execute a callback function when expired. 59 | * @param *d Pointer to a CAN object data structure 60 | * @param id The alarm Id 61 | * @param callback A callback function 62 | * @param value Call the callback function at current time + value 63 | * @param period Call periodically the callback function 64 | * @return handle The timer handle 65 | */ 66 | TIMER_HANDLE SetAlarm(CO_Data* d, UNS32 id, TimerCallback_t callback, TIMEVAL value, TIMEVAL period); 67 | 68 | /** 69 | * @ingroup timer 70 | * @brief Delete an alarm before expiring. 71 | * @param handle A timer handle 72 | * @return The timer handle 73 | */ 74 | TIMER_HANDLE DelAlarm(TIMER_HANDLE handle); 75 | 76 | void TimeDispatch(void); 77 | 78 | /** 79 | * @ingroup timer 80 | * @brief Set a timerfor a given time. 81 | * @param value The time value. 82 | */ 83 | void setTimer(TIMEVAL value); 84 | 85 | /** 86 | * @ingroup timer 87 | * @brief Get the time elapsed since latest timer occurence. 88 | * @return time elapsed since latest timer occurence 89 | */ 90 | TIMEVAL getElapsedTime(void); 91 | 92 | #endif /* #define __timer_h__ */ 93 | -------------------------------------------------------------------------------- /examples/master402/motor_control.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef RT_USING_FINSH 9 | #include 10 | #endif 11 | 12 | #include "canfestival.h" 13 | #include "timers_driver.h" 14 | #include "master402_od.h" 15 | #include "master402_canopen.h" 16 | 17 | #define SYNC_DELAY rt_thread_delay(RT_TICK_PER_SECOND/50) 18 | #define PROFILE_POSITION_MODE 1 19 | #define ENCODER_RES (2500 * 4) 20 | 21 | #define CONTROL_WORD_DISABLE_VOLTAGE 0x00 22 | #define CONTROL_WORD_SHUTDOWN 0x06 23 | #define CONTROL_WORD_SWITCH_ON 0x07 24 | #define CONTROL_WORD_ENABLE_OPERATION 0x0F 25 | 26 | #define STATUS_WORD_TARGET_REACHED_BIT (1 << 10) 27 | #define STATUS_WORD_FOLLOWING_ERROR_BIT (1 << 12) 28 | 29 | // bits for Profile Position mode 30 | #define CONTROL_WORD_NEW_SETPOINT_BIT (1 << 4) 31 | #define CONTROL_WORD_CHANGE_SET_IMMEDIATELY_BIT (1 << 5) 32 | #define CONTROL_WORD_CHANGE_RELATIVE_BIT (1 << 6) 33 | #define STATUS_WORD_SETPOINT_ACKNOWLEDGE_BIT (1 << 12) 34 | 35 | #ifdef RT_USING_MSH 36 | static void cmd_motor_on(int argc, char* argv[]) 37 | { 38 | int nodeId = 1; 39 | if(argc > 1) { 40 | nodeId = atoi(argv[1]); 41 | } 42 | (void)nodeId; 43 | 44 | modes_of_operation_6060 = PROFILE_POSITION_MODE; 45 | profile_velocity_6081 = 0; 46 | target_position_607a = 0; 47 | 48 | control_word_6040 = CONTROL_WORD_SHUTDOWN; 49 | SYNC_DELAY; 50 | control_word_6040 = CONTROL_WORD_SWITCH_ON; 51 | SYNC_DELAY; 52 | control_word_6040 = CONTROL_WORD_ENABLE_OPERATION; 53 | } 54 | MSH_CMD_EXPORT_ALIAS(cmd_motor_on, motor_on, power on motor driver); 55 | 56 | static void cmd_motor_off(int argc, char* argv[]) 57 | { 58 | int nodeId = 1; 59 | if(argc > 1) { 60 | nodeId = atoi(argv[1]); 61 | } 62 | (void)nodeId; 63 | 64 | 65 | control_word_6040 = CONTROL_WORD_SHUTDOWN; 66 | SYNC_DELAY; 67 | control_word_6040 = CONTROL_WORD_DISABLE_VOLTAGE; 68 | } 69 | MSH_CMD_EXPORT_ALIAS(cmd_motor_off, motor_off, power off motor driver); 70 | 71 | 72 | static void cmd_motor_relmove(int argc, char* argv[]) 73 | { 74 | int32_t position = 0; 75 | int32_t speed = 20; 76 | 77 | if(argc < 2) { 78 | rt_kprintf("Usage: motor_relmove [position] \n"); 79 | } 80 | 81 | position = atoi(argv[1]); 82 | if(argc > 2) { 83 | speed = atoi(argv[2]); 84 | } 85 | 86 | rt_kprintf("move to position: %d, speed: %d\n", position, speed); 87 | 88 | 89 | target_position_607a = position; 90 | profile_velocity_6081 = speed; 91 | 92 | SYNC_DELAY; 93 | control_word_6040 = (CONTROL_WORD_ENABLE_OPERATION); 94 | SYNC_DELAY; 95 | control_word_6040 = 0x6f; 96 | } 97 | MSH_CMD_EXPORT_ALIAS(cmd_motor_relmove, motor_relmove, move motor to relative position); 98 | 99 | static void cmd_motor_state(void) 100 | { 101 | rt_kprintf("ControlWord 0x%0X\n", control_word_6040); 102 | rt_kprintf("StatusWord 0x%0X\n", status_word_6041); 103 | rt_kprintf("current position %d\n", position_actual_value_6063); 104 | rt_kprintf("current speed %d\n", velocity_actual_value_606c); 105 | } 106 | MSH_CMD_EXPORT_ALIAS(cmd_motor_state, motor_state, print states of motors); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /inc/nmtMaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup networkmanagement Network Management 24 | * @ingroup userapi 25 | */ 26 | /** @defgroup nmtmaster NMT Master 27 | * @brief NMT master provides mechanisms that control and monitor the state of nodes and their behavior in the network. 28 | * @ingroup networkmanagement 29 | */ 30 | 31 | #ifndef __nmtMaster_h__ 32 | #define __nmtMaster_h__ 33 | 34 | #include "data.h" 35 | 36 | /** 37 | * @ingroup nmtmaster 38 | * @brief Transmit a NMT message on the network to the slave whose nodeId is node ID. 39 | * 40 | * @param *d Pointer to a CAN object data structure 41 | * @param nodeId Id of the slave node 42 | * @param cs The order of state changement \n\n 43 | * 44 | * Allowed states : 45 | * - cs = NMT_Start_Node // Put the node in operational mode 46 | * - cs = NMT_Stop_Node // Put the node in stopped mode 47 | * - cs = NMT_Enter_PreOperational // Put the node in pre_operational mode 48 | * - cs = NMT_Reset_Node // Put the node in initialization mode 49 | * - cs = NMT_Reset_Comunication // Put the node in initialization mode 50 | * 51 | * The mode is changed according to the slave state machine mode : 52 | * - initialisation ---> pre-operational (Automatic transition) 53 | * - pre-operational <--> operational 54 | * - pre-operational <--> stopped 55 | * - pre-operational, operational, stopped -> initialisation 56 | * \n\n 57 | * @return errorcode 58 | * - 0 if the NMT message was send 59 | * - 1 if an error occurs 60 | */ 61 | UNS8 masterSendNMTstateChange (CO_Data* d, UNS8 nodeId, UNS8 cs); 62 | 63 | /** 64 | * @ingroup nmtmaster 65 | * @brief Transmit a NodeGuard message on the network to the slave whose nodeId is node ID 66 | * 67 | * @param *d Pointer to a CAN object data structure 68 | * @param nodeId Id of the slave node 69 | * @return 70 | * - 0 is returned if the NodeGuard message was send. 71 | * - 1 is returned if an error occurs. 72 | */ 73 | UNS8 masterSendNMTnodeguard (CO_Data* d, UNS8 nodeId); 74 | 75 | /** 76 | * @ingroup nmtmaster 77 | * @brief Ask the state of the slave node whose nodeId is node Id. 78 | * 79 | * To ask states of all nodes on the network (NMT broadcast), nodeId must be equal to 0 80 | * @param *d Pointer to a CAN object data structure 81 | * @param nodeId Id of the slave node 82 | */ 83 | UNS8 masterRequestNodeState (CO_Data* d, UNS8 nodeId); 84 | 85 | 86 | #endif /* __nmtMaster_h__ */ 87 | -------------------------------------------------------------------------------- /inc/emcy.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /*! 24 | ** @file emcy.h 25 | ** @author Luis Jimenez 26 | ** @date Wed Sep 26 2007 27 | ** 28 | ** @brief Declarations of the functions that manage EMCY (emergency) messages 29 | ** 30 | ** 31 | */ 32 | 33 | /** @defgroup emcyo Emergency Object 34 | * Emergency Object is used to communicate device and application failures. 35 | * @ingroup comobj 36 | */ 37 | 38 | #ifndef __emcy_h__ 39 | #define __emcy_h__ 40 | 41 | 42 | #include 43 | 44 | /* The error states 45 | * ----------------- */ 46 | typedef enum enum_errorState { 47 | Error_free = 0x00, 48 | Error_occurred = 0x01 49 | } e_errorState; 50 | 51 | typedef struct { 52 | UNS16 errCode; 53 | UNS8 errRegMask; 54 | UNS8 active; 55 | } s_errors; 56 | 57 | #include "data.h" 58 | 59 | 60 | typedef void (*post_emcy_t)(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]); 61 | void _post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]); 62 | 63 | /************************************************************************* 64 | * Functions 65 | *************************************************************************/ 66 | 67 | /** 68 | * @ingroup emcy 69 | * @brief Sets a new error with code errCode. Also sets corresponding bits in Error register (1001h) 70 | * @param *d Pointer on a CAN object data structure 71 | * @param errCode The error code 72 | * @param errRegMask 73 | * @param addInfo 74 | * @return 75 | */ 76 | UNS8 EMCY_setError(CO_Data* d, UNS16 errCode, UNS8 errRegMask, UNS16 addInfo); 77 | 78 | /** 79 | * @ingroup emcy 80 | * @brief Indicates it has recovered from error errCode. Also clears corresponding bits in Error register (1001h) 81 | * @param *d Pointer on a CAN object data structure 82 | * @param errCode The error code 83 | */ 84 | void EMCY_errorRecovered(CO_Data* d, UNS16 errCode); 85 | 86 | /** 87 | * @ingroup emcy 88 | * @brief Start EMCY consumer and producer 89 | * @param *d Pointer on a CAN object data structure 90 | */ 91 | void emergencyInit(CO_Data* d); 92 | 93 | /** 94 | * @ingroup emcy 95 | * @brief Stop EMCY producer and consumer 96 | * @param *d Pointer on a CAN object data structure 97 | */ 98 | void emergencyStop(CO_Data* d); 99 | 100 | /** 101 | * @ingroup emcy 102 | * @brief This function is responsible to process an EMCY canopen-message 103 | * @param *d Pointer on a CAN object data structure 104 | * @param *m Pointer on the CAN-message which has to be analysed. 105 | */ 106 | void proceedEMCY(CO_Data* d, Message* m); 107 | 108 | #endif /*__emcy_h__ */ 109 | -------------------------------------------------------------------------------- /src/timer_rtthread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "canfestival.h" 5 | #include "timer.h" 6 | #include "timers_driver.h" 7 | 8 | #define DBG_TAG "app.CANopen" 9 | #define DBG_LVL DBG_LOG 10 | #include 11 | 12 | 13 | /************************** Module variables **********************************/ 14 | static rt_sem_t canfstvl_timer_sem = RT_NULL; 15 | static rt_device_t canfstvl_timer_dev=RT_NULL; 16 | static rt_hwtimerval_t last_timer_val; 17 | 18 | 19 | void setTimer(TIMEVAL value) 20 | { 21 | rt_hwtimerval_t val; 22 | 23 | val.sec = value / 1000000; 24 | val.usec = value % 1000000; 25 | /// Avoid invalid 0 timeout. 26 | /// See here for detailed reference: https://club.rt-thread.org/ask/question/11194.html . 27 | if(val.usec < MIN_TIMER_TIMEOUT_US){ 28 | val.usec = MIN_TIMER_TIMEOUT_US; 29 | } 30 | 31 | last_timer_val.sec = 0; 32 | last_timer_val.usec = 0; 33 | if(rt_device_write(canfstvl_timer_dev, 0, &val, sizeof(val)) == 0) { 34 | LOG_E("CANopen set timer failed, err = %d", rt_get_errno()); 35 | } 36 | } 37 | 38 | TIMEVAL getElapsedTime(void) 39 | { 40 | rt_hwtimerval_t val; 41 | 42 | rt_device_read(canfstvl_timer_dev, 0, &val, sizeof(val)); 43 | 44 | return (val.sec - last_timer_val.sec) * 1000000 + (val.usec - last_timer_val.usec); 45 | } 46 | 47 | static void canopen_timer_thread_entry(void* parameter) 48 | { 49 | while(1) 50 | { 51 | if(rt_sem_take(canfstvl_timer_sem, RT_WAITING_FOREVER) != RT_EOK) { 52 | LOG_E("canfestival take timer sem failed"); 53 | return; 54 | } 55 | 56 | EnterMutex(); 57 | rt_size_t read_size = rt_device_read(canfstvl_timer_dev, 0, &last_timer_val, sizeof(last_timer_val)); 58 | if( read_size == 0) { 59 | LOG_E("canfestival read timer failed, err = %d", rt_get_errno()); 60 | } else { 61 | TimeDispatch(); 62 | } 63 | LeaveMutex(); 64 | } 65 | } 66 | 67 | 68 | static rt_err_t timer_timeout_cb(rt_device_t dev, rt_size_t size) 69 | { 70 | rt_sem_release(canfstvl_timer_sem); 71 | 72 | return RT_EOK; 73 | } 74 | 75 | 76 | void initTimer(void) 77 | { 78 | rt_thread_t tid; 79 | rt_err_t err; 80 | rt_hwtimer_mode_t mode; 81 | int freq = 1000000; 82 | 83 | canfstvl_timer_sem = rt_sem_create("canfstvl", 0, RT_IPC_FLAG_PRIO); 84 | 85 | canfstvl_timer_dev = rt_device_find(CANFESTIVAL_TIMER_DEVICE_NAME); 86 | RT_ASSERT(canfstvl_timer_dev != RT_NULL); 87 | err = rt_device_open(canfstvl_timer_dev, RT_DEVICE_OFLAG_RDWR); 88 | if (err != RT_EOK) 89 | { 90 | rt_kprintf("CanFestival open timer Failed! err=%d\n", err); 91 | return; 92 | } 93 | rt_device_set_rx_indicate(canfstvl_timer_dev, timer_timeout_cb); 94 | err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_FREQ_SET, &freq); 95 | if (err != RT_EOK) 96 | { 97 | rt_kprintf("Set Freq=%dhz Failed\n", freq); 98 | } 99 | 100 | mode = HWTIMER_MODE_ONESHOT; 101 | err = rt_device_control(canfstvl_timer_dev, HWTIMER_CTRL_MODE_SET, &mode); 102 | rt_device_read(canfstvl_timer_dev, 0, &last_timer_val, sizeof(last_timer_val)); 103 | 104 | tid = rt_thread_create("cf_timer", 105 | canopen_timer_thread_entry, RT_NULL, 106 | 1024, CANFESTIVAL_TIMER_THREAD_PRIO, 20); 107 | if (tid != RT_NULL) rt_thread_startup(tid); 108 | 109 | } 110 | 111 | static TimerCallback_t init_callback; 112 | void StartTimerLoop(TimerCallback_t _init_callback) 113 | { 114 | init_callback = _init_callback; 115 | EnterMutex(); 116 | SetAlarm(NULL, 0, init_callback, 5, 0); 117 | LeaveMutex(); 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/can_rtthread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "canfestival.h" 6 | #include "timers_driver.h" 7 | 8 | #define DBG_TAG "app.CANopen" 9 | #define DBG_LVL DBG_LOG 10 | #include 11 | 12 | #define MAX_MUTEX_WAIT_TIME 5000 13 | #define MAX_SEM_WAIT_TIME 5000 14 | 15 | struct can_app_struct 16 | { 17 | const char *name; 18 | struct rt_semaphore sem; 19 | }; 20 | 21 | static rt_device_t candev = RT_NULL; 22 | static CO_Data * OD_Data = RT_NULL; 23 | static rt_mutex_t canfstvl_mutex = RT_NULL; 24 | 25 | static struct can_app_struct can_data = 26 | { 27 | CANFESTIVAL_CAN_DEVICE_NAME 28 | }; 29 | 30 | void EnterMutex(void) 31 | { 32 | if(rt_mutex_take(canfstvl_mutex, MAX_MUTEX_WAIT_TIME) != RT_EOK) { 33 | LOG_E("canfestival take mutex failed!"); 34 | } 35 | } 36 | 37 | void LeaveMutex(void) 38 | { 39 | if(rt_mutex_release(canfstvl_mutex) != RT_EOK) { 40 | LOG_E("canfestival release mutex failed!"); 41 | } 42 | } 43 | 44 | static rt_err_t can1ind(rt_device_t dev, rt_size_t size) 45 | { 46 | rt_err_t err = rt_sem_release(&can_data.sem); 47 | if(err != RT_EOK) { 48 | LOG_E("canfestival release receive semaphore failed!"); 49 | } 50 | return err; 51 | } 52 | 53 | unsigned char canSend(CAN_PORT notused, Message *m) 54 | { 55 | static int err_cnt = 0; 56 | 57 | struct rt_can_msg msg; 58 | 59 | msg.id = m->cob_id; 60 | msg.ide = 0; 61 | msg.rtr = m->rtr; 62 | msg.len = m->len; 63 | memcpy(msg.data, m->data, m->len); 64 | RT_ASSERT(candev != RT_NULL); 65 | 66 | rt_size_t write_size = rt_device_write(candev, 0, &msg, sizeof(msg)); 67 | if(write_size != sizeof(msg)) { 68 | LOG_W("canfestival send failed, err = %d", rt_get_errno()); 69 | if(++err_cnt >= 100) { 70 | setState(OD_Data, Stopped); 71 | } 72 | return 0xFF; 73 | } 74 | 75 | err_cnt = 0; 76 | return 0; 77 | } 78 | 79 | void canopen_recv_thread_entry(void* parameter) 80 | { 81 | struct can_app_struct *canpara = (struct can_app_struct *) parameter; 82 | struct rt_can_msg msg; 83 | Message co_msg; 84 | 85 | candev = rt_device_find(canpara->name); 86 | RT_ASSERT(candev); 87 | rt_sem_init(&can_data.sem, "co-rx", 0, RT_IPC_FLAG_PRIO); 88 | rt_err_t err = rt_device_open(candev, (RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX)); 89 | if( err != RT_EOK) { 90 | LOG_E("canfestival open device %s failed, err = %d", canpara->name, err); 91 | return; 92 | } 93 | 94 | err = rt_device_set_rx_indicate(candev, can1ind); 95 | if( err != RT_EOK) { 96 | LOG_E("canfestival set rx indicate failed, err = %d", err); 97 | return; 98 | } 99 | 100 | rt_size_t read_size = 0; 101 | while (1) 102 | { 103 | err = rt_sem_take(&can_data.sem, MAX_SEM_WAIT_TIME); 104 | if ( err != RT_EOK) 105 | { 106 | if(getState(OD_Data) == Operational) { 107 | LOG_W("canfestival wait receive timeout, err = %d", err); 108 | } 109 | } else { 110 | read_size = rt_device_read(candev, 0, &msg, sizeof(msg)); 111 | if( read_size == sizeof(msg)) 112 | { 113 | co_msg.cob_id = msg.id; 114 | co_msg.len = msg.len; 115 | co_msg.rtr = msg.rtr; 116 | memcpy(co_msg.data, msg.data, msg.len); 117 | EnterMutex(); 118 | canDispatch(OD_Data, &co_msg); 119 | LeaveMutex(); 120 | } else if (read_size == 0){ 121 | LOG_W("canfestival receive faild, err = %d", rt_get_errno()); 122 | } else { 123 | LOG_W("canfestival receive size wrong, size = %u", read_size); 124 | } 125 | } 126 | } 127 | } 128 | 129 | CAN_PORT canOpen(s_BOARD *board, CO_Data * d) 130 | { 131 | rt_thread_t tid; 132 | canfstvl_mutex = rt_mutex_create("canfstvl",RT_IPC_FLAG_PRIO); 133 | 134 | OD_Data = d; 135 | tid = rt_thread_create("cf_recv", 136 | canopen_recv_thread_entry, &can_data, 137 | 1024, CANFESTIVAL_RECV_THREAD_PRIO, 20); 138 | if (tid != RT_NULL) rt_thread_startup(tid); 139 | 140 | return 0; 141 | } 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/symbols.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "data.h" 3 | #include "can_driver.h" 4 | #include "dcf.h" 5 | #include "nmtSlave.h" 6 | #include "timers_driver.h" 7 | 8 | // CanFestival symbols available to other kernel modules 9 | 10 | // dcf.h 11 | EXPORT_SYMBOL (send_consise_dcf); 12 | 13 | // emcy.h 14 | EXPORT_SYMBOL (_post_emcy); 15 | EXPORT_SYMBOL (EMCY_setError); 16 | EXPORT_SYMBOL (EMCY_errorRecovered); 17 | EXPORT_SYMBOL (emergencyInit); 18 | EXPORT_SYMBOL (emergencyStop); 19 | EXPORT_SYMBOL (proceedEMCY); 20 | 21 | // lifegrd.h 22 | EXPORT_SYMBOL (_heartbeatError); 23 | EXPORT_SYMBOL (_post_SlaveBootup); 24 | EXPORT_SYMBOL (getNodeState); 25 | EXPORT_SYMBOL (heartbeatInit); 26 | EXPORT_SYMBOL (heartbeatStop); 27 | EXPORT_SYMBOL (proceedNODE_GUARD); 28 | 29 | // lss.h 30 | #ifdef CO_ENABLE_LSS 31 | EXPORT_SYMBOL (startLSS); 32 | EXPORT_SYMBOL (stopLSS); 33 | EXPORT_SYMBOL (sendLSS); 34 | EXPORT_SYMBOL (sendLSSMessage); 35 | EXPORT_SYMBOL (proceedLSS_Master); 36 | EXPORT_SYMBOL (proceedLSS_Slave); 37 | EXPORT_SYMBOL (configNetworkNode); 38 | EXPORT_SYMBOL (configNetworkNodeCallBack); 39 | EXPORT_SYMBOL (getConfigResultNetworkNode); 40 | #endif 41 | 42 | // nmtMaster.h 43 | EXPORT_SYMBOL (masterSendNMTstateChange); 44 | EXPORT_SYMBOL (masterSendNMTnodeguard); 45 | EXPORT_SYMBOL (masterRequestNodeState); 46 | 47 | // nmtSlave.h 48 | EXPORT_SYMBOL (proceedNMTstateChange); 49 | EXPORT_SYMBOL (slaveSendBootUp); 50 | 51 | // objacces.h 52 | EXPORT_SYMBOL (_storeODSubIndex); 53 | #ifdef DEBUG_WAR_CONSOLE_ON 54 | EXPORT_SYMBOL (accessDictionaryError); 55 | #endif 56 | EXPORT_SYMBOL (_getODentry); 57 | EXPORT_SYMBOL (_setODentry); 58 | //EXPORT_SYMBOL (writeLocalDict); 59 | //EXPORT_SYMBOL (scanIndexOD); 60 | EXPORT_SYMBOL (RegisterSetODentryCallBack); 61 | 62 | // pdo.h 63 | EXPORT_SYMBOL (buildPDO); 64 | EXPORT_SYMBOL (sendPDOrequest); 65 | EXPORT_SYMBOL (proceedPDO); 66 | EXPORT_SYMBOL (sendPDOevent); 67 | EXPORT_SYMBOL (sendOnePDOevent); 68 | EXPORT_SYMBOL (_sendPDOevent); 69 | EXPORT_SYMBOL (PDOInit); 70 | EXPORT_SYMBOL (PDOStop); 71 | EXPORT_SYMBOL (PDOEventTimerAlarm); 72 | EXPORT_SYMBOL (PDOInhibitTimerAlarm); 73 | EXPORT_SYMBOL (CopyBits); 74 | 75 | // sdo.h 76 | EXPORT_SYMBOL (SDOTimeoutAlarm); 77 | EXPORT_SYMBOL (resetSDO); 78 | EXPORT_SYMBOL (SDOlineToObjdict); 79 | EXPORT_SYMBOL (objdictToSDOline); 80 | EXPORT_SYMBOL (lineToSDO); 81 | EXPORT_SYMBOL (SDOtoLine); 82 | EXPORT_SYMBOL (failedSDO); 83 | EXPORT_SYMBOL (resetSDOline); 84 | EXPORT_SYMBOL (initSDOline); 85 | EXPORT_SYMBOL (getSDOfreeLine); 86 | EXPORT_SYMBOL (getSDOlineOnUse); 87 | EXPORT_SYMBOL (closeSDOtransfer); 88 | EXPORT_SYMBOL (getSDOlineRestBytes); 89 | EXPORT_SYMBOL (setSDOlineRestBytes); 90 | EXPORT_SYMBOL (sendSDO); 91 | EXPORT_SYMBOL (sendSDOabort); 92 | EXPORT_SYMBOL (proceedSDO); 93 | EXPORT_SYMBOL (writeNetworkDict); 94 | EXPORT_SYMBOL (writeNetworkDictCallBack); 95 | EXPORT_SYMBOL (readNetworkDict); 96 | EXPORT_SYMBOL (readNetworkDictCallback); 97 | EXPORT_SYMBOL (getReadResultNetworkDict); 98 | EXPORT_SYMBOL (getWriteResultNetworkDict); 99 | 100 | // states.h 101 | EXPORT_SYMBOL (_initialisation); 102 | EXPORT_SYMBOL (_preOperational); 103 | EXPORT_SYMBOL (_operational); 104 | EXPORT_SYMBOL (_stopped); 105 | EXPORT_SYMBOL (canDispatch); 106 | EXPORT_SYMBOL (getState); 107 | EXPORT_SYMBOL (setState); 108 | EXPORT_SYMBOL (getNodeId); 109 | EXPORT_SYMBOL (setNodeId); 110 | 111 | // sync.h 112 | EXPORT_SYMBOL (startSYNC); 113 | EXPORT_SYMBOL (stopSYNC); 114 | EXPORT_SYMBOL (_post_sync); 115 | EXPORT_SYMBOL (_post_TPDO); 116 | EXPORT_SYMBOL (sendSYNC); 117 | EXPORT_SYMBOL (sendSYNCMessage); 118 | EXPORT_SYMBOL (proceedSYNC); 119 | 120 | // timer.h 121 | EXPORT_SYMBOL (SetAlarm); 122 | EXPORT_SYMBOL (DelAlarm); 123 | EXPORT_SYMBOL (TimeDispatch); 124 | EXPORT_SYMBOL (setTimer); 125 | EXPORT_SYMBOL (getElapsedTime); 126 | 127 | // timers_driver.h 128 | EXPORT_SYMBOL (EnterMutex); 129 | EXPORT_SYMBOL (LeaveMutex); 130 | EXPORT_SYMBOL (TimerInit); 131 | EXPORT_SYMBOL (TimerCleanup); 132 | EXPORT_SYMBOL (StartTimerLoop); 133 | EXPORT_SYMBOL (StopTimerLoop); 134 | EXPORT_SYMBOL (CreateReceiveTask); 135 | EXPORT_SYMBOL (WaitReceiveTaskEnd); 136 | -------------------------------------------------------------------------------- /inc/can_driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #ifndef __can_driver_h__ 24 | #define __can_driver_h__ 25 | 26 | #include "applicfg.h" 27 | #include "can.h" 28 | 29 | /** 30 | * @brief The CAN board configuration 31 | * @ingroup can 32 | */ 33 | 34 | typedef struct { 35 | char * busname; /**< The bus name on which the CAN board is connected */ 36 | char * baudrate; /**< The board baudrate */ 37 | } s_BOARD; 38 | 39 | #ifndef DLL_CALL 40 | #if !defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) 41 | #define LIBAPI 42 | #define DLL_CALL(funcname) funcname##_driver 43 | #else 44 | #define LIBAPI __stdcall 45 | //Windows was missing the definition of the calling convention 46 | #define DLL_CALL(funcname) LIBAPI funcname##_driver 47 | #endif 48 | #endif //DLL_CALL 49 | 50 | #define LIBPUBLIC 51 | 52 | #ifndef FCT_PTR_INIT 53 | #define FCT_PTR_INIT 54 | #endif 55 | 56 | 57 | UNS8 DLL_CALL(canReceive)(CAN_HANDLE, Message *)FCT_PTR_INIT; 58 | UNS8 DLL_CALL(canSend)(CAN_HANDLE, Message const *)FCT_PTR_INIT; 59 | CAN_HANDLE DLL_CALL(canOpen)(s_BOARD *)FCT_PTR_INIT; 60 | int DLL_CALL(canClose)(CAN_HANDLE)FCT_PTR_INIT; 61 | UNS8 DLL_CALL(canChangeBaudRate)(CAN_HANDLE, char *)FCT_PTR_INIT; 62 | 63 | #if defined DEBUG_MSG_CONSOLE_ON || defined NEED_PRINT_MESSAGE 64 | #include "def.h" 65 | 66 | #define _P(fc) case fc: MSG(#fc" ");break; 67 | 68 | static inline void print_message(Message const *m) 69 | { 70 | int i; 71 | UNS8 fc; 72 | MSG("id:%02x ", m->cob_id & 0x7F); 73 | fc = m->cob_id >> 7; 74 | switch(fc) 75 | { 76 | case SYNC: 77 | if(m->cob_id == 0x080) 78 | MSG("SYNC "); 79 | else 80 | MSG("EMCY "); 81 | break; 82 | #ifdef CO_ENABLE_LSS 83 | case LSS: 84 | if(m->cob_id == 0x7E5) 85 | MSG("MLSS "); 86 | else 87 | MSG("SLSS "); 88 | break; 89 | #endif 90 | _P(TIME_STAMP) 91 | _P(PDO1tx) 92 | _P(PDO1rx) 93 | _P(PDO2tx) 94 | _P(PDO2rx) 95 | _P(PDO3tx) 96 | _P(PDO3rx) 97 | _P(PDO4tx) 98 | _P(PDO4rx) 99 | _P(SDOtx) 100 | _P(SDOrx) 101 | _P(NODE_GUARD) 102 | _P(NMT) 103 | } 104 | if( fc == SDOtx) 105 | { 106 | switch(m->data[0] >> 5) 107 | { 108 | /* scs: server command specifier */ 109 | _P(UPLOAD_SEGMENT_RESPONSE) 110 | _P(DOWNLOAD_SEGMENT_RESPONSE) 111 | _P(INITIATE_DOWNLOAD_RESPONSE) 112 | _P(INITIATE_UPLOAD_RESPONSE) 113 | _P(ABORT_TRANSFER_REQUEST) 114 | } 115 | }else if( fc == SDOrx) 116 | { 117 | switch(m->data[0] >> 5) 118 | { 119 | /* ccs: client command specifier */ 120 | _P(DOWNLOAD_SEGMENT_REQUEST) 121 | _P(INITIATE_DOWNLOAD_REQUEST) 122 | _P(INITIATE_UPLOAD_REQUEST) 123 | _P(UPLOAD_SEGMENT_REQUEST) 124 | _P(ABORT_TRANSFER_REQUEST) 125 | } 126 | } 127 | MSG(" rtr:%d", m->rtr); 128 | MSG(" len:%d", m->len); 129 | for (i = 0 ; i < m->len ; i++) 130 | MSG(" %02x", m->data[i]); 131 | MSG("\n"); 132 | } 133 | 134 | #endif 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /src/nmtSlave.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22 | USA 23 | */ 24 | /*! 25 | ** @file nmtSlave.c 26 | ** @author Edouard TISSERANT and Francis DUPIN 27 | ** @date Tue Jun 5 08:50:53 2007 28 | ** 29 | ** @brief 30 | ** 31 | ** 32 | */ 33 | #include "nmtSlave.h" 34 | #include "states.h" 35 | #include "canfestival.h" 36 | #include "sysdep.h" 37 | 38 | /*! 39 | ** put the slave in the state wanted by the master 40 | ** 41 | ** @param d 42 | ** @param m 43 | **/ 44 | void proceedNMTstateChange(CO_Data* d, Message *m) 45 | { 46 | if( d->nodeState == Pre_operational || 47 | d->nodeState == Operational || 48 | d->nodeState == Stopped ) { 49 | 50 | MSG_WAR(0x3400, "NMT received. for node : ", (*m).data[1]); 51 | 52 | /* Check if this NMT-message is for this node */ 53 | /* byte 1 = 0 : all the nodes are concerned (broadcast) */ 54 | 55 | if( ( (*m).data[1] == 0 ) || ( (*m).data[1] == *d->bDeviceNodeId ) ){ 56 | 57 | switch( (*m).data[0]){ /* command specifier (cs) */ 58 | case NMT_Start_Node: 59 | if ( (d->nodeState == Pre_operational) || (d->nodeState == Stopped) ) 60 | setState(d,Operational); 61 | break; 62 | 63 | case NMT_Stop_Node: 64 | if ( d->nodeState == Pre_operational || 65 | d->nodeState == Operational ) 66 | setState(d,Stopped); 67 | break; 68 | 69 | case NMT_Enter_PreOperational: 70 | if ( d->nodeState == Operational || 71 | d->nodeState == Stopped ) 72 | setState(d,Pre_operational); 73 | break; 74 | 75 | case NMT_Reset_Node: 76 | if(d->NMT_Slave_Node_Reset_Callback != NULL) 77 | d->NMT_Slave_Node_Reset_Callback(d); 78 | setState(d,Initialisation); 79 | break; 80 | 81 | case NMT_Reset_Comunication: 82 | { 83 | UNS8 currentNodeId = getNodeId(d); 84 | 85 | if(d->NMT_Slave_Communications_Reset_Callback != NULL) 86 | d->NMT_Slave_Communications_Reset_Callback(d); 87 | #ifdef CO_ENABLE_LSS 88 | // LSS changes NodeId here in case lss_transfer.nodeID doesn't 89 | // match current getNodeId() 90 | if(currentNodeId!=d->lss_transfer.nodeID) 91 | currentNodeId = d->lss_transfer.nodeID; 92 | #endif 93 | 94 | // clear old NodeId to make SetNodeId reinitializing 95 | // SDO, EMCY and other COB Ids 96 | *d->bDeviceNodeId = 0xFF; 97 | 98 | setNodeId(d, currentNodeId); 99 | } 100 | setState(d,Initialisation); 101 | break; 102 | 103 | }/* end switch */ 104 | 105 | }/* end if( ( (*m).data[1] == 0 ) || ( (*m).data[1] == 106 | bDeviceNodeId ) ) */ 107 | } 108 | } 109 | 110 | 111 | /*! 112 | ** 113 | ** 114 | ** @param d 115 | ** 116 | ** @return 117 | **/ 118 | UNS8 slaveSendBootUp(CO_Data* d) 119 | { 120 | Message m; 121 | 122 | #ifdef CO_ENABLE_LSS 123 | if(*d->bDeviceNodeId==0xFF)return 0; 124 | #endif 125 | 126 | MSG_WAR(0x3407, "Send a Boot-Up msg ", 0); 127 | 128 | /* message configuration */ 129 | { 130 | UNS16 tmp = NODE_GUARD << 7 | *d->bDeviceNodeId; 131 | m.cob_id = UNS16_LE(tmp); 132 | } 133 | m.rtr = NOT_A_REQUEST; 134 | m.len = 1; 135 | m.data[0] = 0x00; 136 | 137 | return canSend(d->canHandle,&m); 138 | } 139 | 140 | -------------------------------------------------------------------------------- /inc/states.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup statemachine State Machine 24 | * @ingroup userapi 25 | */ 26 | 27 | #ifndef __states_h__ 28 | #define __states_h__ 29 | 30 | #include 31 | 32 | /* The nodes states 33 | * ----------------- 34 | * values are choosen so, that they can be sent directly 35 | * for heartbeat messages... 36 | * Must be coded on 7 bits only 37 | * */ 38 | /* Should not be modified */ 39 | enum enum_nodeState { 40 | Initialisation = 0x00, 41 | Disconnected = 0x01, 42 | Connecting = 0x02, 43 | Preparing = 0x02, 44 | Stopped = 0x04, 45 | Operational = 0x05, 46 | Pre_operational = 0x7F, 47 | Unknown_state = 0x0F 48 | }; 49 | 50 | typedef enum enum_nodeState e_nodeState; 51 | 52 | typedef struct 53 | { 54 | INTEGER8 csBoot_Up; 55 | INTEGER8 csSDO; 56 | INTEGER8 csEmergency; 57 | INTEGER8 csSYNC; 58 | INTEGER8 csLifeGuard; 59 | INTEGER8 csPDO; 60 | INTEGER8 csLSS; 61 | } s_state_communication; 62 | 63 | /** 64 | * @brief Function that user app can overload 65 | * @ingroup statemachine 66 | */ 67 | typedef void (*initialisation_t)(CO_Data*); 68 | typedef void (*preOperational_t)(CO_Data*); 69 | typedef void (*operational_t)(CO_Data*); 70 | typedef void (*stopped_t)(CO_Data*); 71 | 72 | /** 73 | * @ingroup statemachine 74 | * @brief Function that user app can overload 75 | * @param *d Pointer on a CAN object data structure 76 | */ 77 | void _initialisation(CO_Data* d); 78 | 79 | /** 80 | * @ingroup statemachine 81 | * @brief Function that user app can overload 82 | * @param *d Pointer on a CAN object data structure 83 | */ 84 | void _preOperational(CO_Data* d); 85 | 86 | /** 87 | * @ingroup statemachine 88 | * @brief Function that user app can overload 89 | * @param *d Pointer on a CAN object data structure 90 | */ 91 | void _operational(CO_Data* d); 92 | 93 | /** 94 | * @ingroup statemachine 95 | * @brief Function that user app can overload 96 | * @param *d Pointer on a CAN object data structure 97 | */ 98 | void _stopped(CO_Data* d); 99 | 100 | #include "data.h" 101 | 102 | /************************* prototypes ******************************/ 103 | 104 | /** 105 | * @brief Called by driver/app when receiving messages 106 | * @param *d Pointer on a CAN object data structure 107 | * @param *m Pointer on a CAN message structure 108 | */ 109 | void canDispatch(CO_Data* d, Message *m); 110 | 111 | /** 112 | * @ingroup statemachine 113 | * @brief Returns the state of the node 114 | * @param *d Pointer on a CAN object data structure 115 | * @return The node state 116 | */ 117 | e_nodeState getState (CO_Data* d); 118 | 119 | /** 120 | * @ingroup statemachine 121 | * @brief Change the state of the node 122 | * @param *d Pointer on a CAN object data structure 123 | * @param newState The state to assign 124 | * @return 125 | */ 126 | UNS8 setState (CO_Data* d, e_nodeState newState); 127 | 128 | /** 129 | * @ingroup statemachine 130 | * @brief Returns the nodId 131 | * @param *d Pointer on a CAN object data structure 132 | * @return 133 | */ 134 | UNS8 getNodeId (CO_Data* d); 135 | 136 | /** 137 | * @ingroup statemachine 138 | * @brief Define the node ID. Initialize the object dictionary 139 | * @param *d Pointer on a CAN object data structure 140 | * @param nodeId The node ID to assign 141 | */ 142 | void setNodeId (CO_Data* d, UNS8 nodeId); 143 | 144 | /** 145 | * @brief Some stuff to do when the node enter in pre-operational mode 146 | * @param *d Pointer on a CAN object data structure 147 | */ 148 | void initPreOperationalMode (CO_Data* d); 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /inc/objdictdef.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #ifndef __objdictdef_h__ 24 | #define __objdictdef_h__ 25 | 26 | /************************* CONSTANTES **********************************/ 27 | /** this are static defined datatypes taken fCODE the canopen standard. They 28 | * are located at index 0x0001 to 0x001B. As described in the standard, they 29 | * are in the object dictionary for definition purpose only. a device does not 30 | * to support all of this datatypes. 31 | */ 32 | #define boolean 0x01 33 | #define int8 0x02 34 | #define int16 0x03 35 | #define int32 0x04 36 | #define uint8 0x05 37 | #define uint16 0x06 38 | #define uint32 0x07 39 | #define real32 0x08 40 | #define visible_string 0x09 41 | #define octet_string 0x0A 42 | #define unicode_string 0x0B 43 | #define time_of_day 0x0C 44 | #define time_difference 0x0D 45 | 46 | #define domain 0x0F 47 | #define int24 0x10 48 | #define real64 0x11 49 | #define int40 0x12 50 | #define int48 0x13 51 | #define int56 0x14 52 | #define int64 0x15 53 | #define uint24 0x16 54 | 55 | #define uint40 0x18 56 | #define uint48 0x19 57 | #define uint56 0x1A 58 | #define uint64 0x1B 59 | 60 | #define pdo_communication_parameter 0x20 61 | #define pdo_mapping 0x21 62 | #define sdo_parameter 0x22 63 | #define identity 0x23 64 | 65 | /* CanFestival is using 0x24 to 0xFF to define some types containing a 66 | value range (See how it works in objdict.c) 67 | */ 68 | 69 | 70 | /** Each entry of the object dictionary can be READONLY (RO), READ/WRITE (RW), 71 | * WRITE-ONLY (WO) 72 | */ 73 | #define RW 0x00 74 | #define WO 0x01 75 | #define RO 0x02 76 | 77 | #define TO_BE_SAVE 0x04 78 | #define DCF_TO_SEND 0x08 79 | 80 | /************************ STRUCTURES ****************************/ 81 | /** This are some structs which are neccessary for creating the entries 82 | * of the object dictionary. 83 | */ 84 | typedef struct td_indextable indextable; 85 | 86 | typedef UNS32 (*ODCallback_t)(CO_Data* d, const indextable *, UNS8 bSubindex); 87 | 88 | typedef struct td_subindex 89 | { 90 | UNS8 bAccessType; 91 | UNS8 bDataType; /* Defines of what datatype the entry is */ 92 | UNS32 size; /* The size (in Byte) of the variable */ 93 | void* pObject; /* This is the pointer of the Variable */ 94 | ODCallback_t callback; /* Callback function on write event */ 95 | } subindex; 96 | 97 | /** Struct for creating entries in the communictaion profile 98 | */ 99 | struct td_indextable 100 | { 101 | subindex* pSubindex; /* Pointer to the subindex */ 102 | UNS8 bSubCount; /* the count of valid entries for this subindex 103 | * This count here defines how many memory has been 104 | * allocated. this memory does not have to be used. 105 | */ 106 | UNS16 index; 107 | }; 108 | 109 | typedef struct s_quick_index{ 110 | UNS16 SDO_SVR; 111 | UNS16 SDO_CLT; 112 | UNS16 PDO_RCV; 113 | UNS16 PDO_RCV_MAP; 114 | UNS16 PDO_TRS; 115 | UNS16 PDO_TRS_MAP; 116 | }quick_index; 117 | 118 | 119 | typedef const indextable * (*scanIndexOD_t)(CO_Data* d, UNS16 wIndex, UNS32 * errorCode); 120 | 121 | /************************** MACROS *********************************/ 122 | 123 | /* CANopen usefull helpers */ 124 | #define GET_NODE_ID(m) (UNS16_LE(m.cob_id) & 0x7f) 125 | #define GET_FUNCTION_CODE(m) (UNS16_LE(m.cob_id) >> 7) 126 | 127 | #endif /* __objdictdef_h__ */ 128 | -------------------------------------------------------------------------------- /inc/lifegrd.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup heartbeato Heartbeat Object 24 | * The heartbeat mechanism for a device is established through cyclically transmitting a message by a 25 | * heartbeat producer. One or more devices in the network are aware of this heartbeat message. If the 26 | * heartbeat cycle fails for the heartbeat producer the local application on the heartbeat consumer will be 27 | * informed about that event. 28 | * @ingroup comobj 29 | */ 30 | 31 | /** @defgroup nodeguardo Node-guarding Object 32 | * The node-guarding mechanism for a device is established through cyclically polling all slaves by the NMT 33 | * master. If one polled slave does not respond during a specified time (LifeTime), the local application 34 | * will be informed about that event.
35 | * It is also possible for the slaves to monitor the node-guarding requests coming from the master to 36 | * determine, if the master operates in a right way 37 | * @ingroup comobj 38 | * 39 | * @todo The implementation is very basic. The toggle bit of the nodes confirmation is not checked at the moment 40 | */ 41 | 42 | /** 43 | ** @file lifegrd.h 44 | ** @author Markus WILDBOLZ 45 | ** @date Mon Oct 01 14:44:36 CEST 2012 46 | ** 47 | ** @brief 48 | ** 49 | ** 50 | */ 51 | 52 | #ifndef __lifegrd_h__ 53 | #define __lifegrd_h__ 54 | 55 | 56 | #include 57 | 58 | typedef void (*heartbeatError_t)(CO_Data*, UNS8); 59 | void _heartbeatError(CO_Data* d, UNS8 heartbeatID); 60 | 61 | typedef void (*post_SlaveBootup_t)(CO_Data*, UNS8); 62 | void _post_SlaveBootup(CO_Data* d, UNS8 SlaveID); 63 | 64 | typedef void (*post_SlaveStateChange_t)(CO_Data*, UNS8, e_nodeState); 65 | void _post_SlaveStateChange(CO_Data* d, UNS8 nodeId, e_nodeState newNodeState); 66 | 67 | typedef void (*nodeguardError_t)(CO_Data*, UNS8); 68 | void _nodeguardError(CO_Data* d, UNS8 id); 69 | 70 | #include "data.h" 71 | 72 | /************************************************************************* 73 | * Functions 74 | *************************************************************************/ 75 | /** 76 | * @brief Start node guarding with respect to 0x100C and 0x100D 77 | * in the object dictionary 78 | * 79 | * @param *d Pointer on a CAN object data structure 80 | * @ingroup nodeguardo 81 | */ 82 | void nodeguardInit(CO_Data* d); 83 | 84 | /** 85 | * @brief Stop producing node guarding messages 86 | * 87 | * @param *d Pointer on a CAN object data structure 88 | * @ingroup nodeguardo 89 | */ 90 | void nodeguardStop(CO_Data* d); 91 | 92 | /** 93 | * @brief Start the life guarding service (heartbeat/node guarding). 94 | * This service handles NMT error control messages either by using 95 | * heartbeats and/or by using node guarding messages (defined via the 96 | * object dictionary) 97 | * 98 | * @param *d Pointer on a CAN object data structure 99 | */ 100 | void lifeGuardInit(CO_Data* d); 101 | 102 | /** 103 | * @brief Stop the life guarding service (heartbeat/node guarding). 104 | * 105 | * @param *d Pointer on a CAN object data structure 106 | */ 107 | void lifeGuardStop(CO_Data* d); 108 | 109 | /** 110 | * @ingroup statemachine 111 | * @brief To read the state of a node 112 | * This can be used by the master after having sent a life guard request, 113 | * of by any node if it is waiting for heartbeat. 114 | * @param *d Pointer on a CAN object data structure 115 | * @param nodeId Id of a node 116 | * @return e_nodeState State of the node corresponding to the nodeId 117 | */ 118 | e_nodeState getNodeState (CO_Data* d, UNS8 nodeId); 119 | 120 | /** 121 | * @brief Start heartbeat consumer and producer 122 | * with respect to 0x1016 and 0x1017 123 | * object dictionary entries 124 | * @param *d Pointer on a CAN object data structure 125 | * @ingroup heartbeato 126 | */ 127 | void heartbeatInit(CO_Data* d); 128 | 129 | /** 130 | * @brief Stop heartbeat consumer and producer 131 | * @param *d Pointer on a CAN object data structure 132 | * @ingroup heartbeato 133 | */ 134 | void heartbeatStop(CO_Data* d); 135 | 136 | /** 137 | * @brief This function is responsible to process a canopen-message which seams to be an NMT Error Control 138 | * Messages. 139 | * If a BootUp message is detected, it will return the nodeId of the Slave who booted up 140 | * @param *d Pointer on a CAN object data structure 141 | * @param *m Pointer on the CAN-message which has to be analysed. 142 | * @ingroup nodeguardo 143 | */ 144 | void proceedNODE_GUARD (CO_Data* d, Message* m); 145 | 146 | 147 | #endif /*__lifegrd_h__ */ 148 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | /*! 23 | ** @file timer.c 24 | ** @author Edouard TISSERANT and Francis DUPIN 25 | ** @date Tue Jun 5 09:32:32 2007 26 | ** 27 | ** @brief 28 | ** 29 | ** 30 | */ 31 | 32 | /* #define DEBUG_WAR_CONSOLE_ON */ 33 | /* #define DEBUG_ERR_CONSOLE_ON */ 34 | 35 | #include 36 | #include "timer.h" 37 | 38 | /* --------- The timer table --------- */ 39 | s_timer_entry timers[MAX_NB_TIMER] = {{TIMER_FREE, NULL, NULL, 0, 0, 0},}; 40 | 41 | TIMEVAL total_sleep_time = TIMEVAL_MAX; 42 | TIMER_HANDLE last_timer_raw = -1; 43 | 44 | #define min_val(a,b) ((astate == TIMER_FREE) /* and empty row */ 67 | { /* just store */ 68 | TIMEVAL real_timer_value; 69 | TIMEVAL elapsed_time; 70 | 71 | if (row_number == last_timer_raw + 1) last_timer_raw++; 72 | 73 | elapsed_time = getElapsedTime(); 74 | /* set next wakeup alarm if new entry is sooner than others, or if it is alone */ 75 | real_timer_value = value; 76 | real_timer_value = min_val(real_timer_value, TIMEVAL_MAX); 77 | 78 | if (total_sleep_time > elapsed_time && total_sleep_time - elapsed_time > real_timer_value) 79 | { 80 | total_sleep_time = elapsed_time + real_timer_value; 81 | setTimer(real_timer_value); 82 | } 83 | row->callback = callback; 84 | row->d = d; 85 | row->id = id; 86 | row->val = value + elapsed_time; 87 | row->interval = period; 88 | row->state = TIMER_ARMED; 89 | return row_number; 90 | } 91 | } 92 | 93 | return TIMER_NONE; 94 | } 95 | 96 | /*! 97 | ** ----- Use this to remove an alarm ---- 98 | ** 99 | ** @param handle 100 | ** 101 | ** @return 102 | **/ 103 | TIMER_HANDLE DelAlarm(TIMER_HANDLE handle) 104 | { 105 | /* Quick and dirty. system timer will continue to be trigged, but no action will be preformed. */ 106 | MSG_WAR(0x3320, "DelAlarm. handle = ", handle); 107 | if(handle != TIMER_NONE) 108 | { 109 | if(handle == last_timer_raw) 110 | last_timer_raw--; 111 | timers[handle].state = TIMER_FREE; 112 | } 113 | return TIMER_NONE; 114 | } 115 | 116 | /*! 117 | ** ------ TimeDispatch is called on each timer expiration ---- 118 | ** 119 | **/ 120 | int tdcount=0; 121 | void TimeDispatch(void) 122 | { 123 | TIMER_HANDLE i; 124 | TIMEVAL next_wakeup = TIMEVAL_MAX; /* used to compute when should normaly occur next wakeup */ 125 | /* First run : change timer state depending on time */ 126 | /* Get time since timer signal */ 127 | UNS32 overrun = (UNS32)getElapsedTime(); 128 | 129 | TIMEVAL real_total_sleep_time = total_sleep_time + overrun; 130 | 131 | s_timer_entry *row; 132 | 133 | for(i=0, row = timers; i <= last_timer_raw; i++, row++) 134 | { 135 | if (row->state & TIMER_ARMED) /* if row is active */ 136 | { 137 | if (row->val <= real_total_sleep_time) /* to be trigged */ 138 | { 139 | if (!row->interval) /* if simply outdated */ 140 | { 141 | row->state = TIMER_TRIG; /* ask for trig */ 142 | } 143 | else /* or period have expired */ 144 | { 145 | /* set val as interval, with 32 bit overrun correction, */ 146 | /* modulo for 64 bit not available on all platforms */ 147 | row->val = row->interval - (overrun % (UNS32)row->interval); 148 | row->state = TIMER_TRIG_PERIOD; /* ask for trig, periodic */ 149 | /* Check if this new timer value is the soonest */ 150 | if(row->val < next_wakeup) 151 | next_wakeup = row->val; 152 | } 153 | } 154 | else 155 | { 156 | /* Each armed timer value in decremented. */ 157 | row->val -= real_total_sleep_time; 158 | 159 | /* Check if this new timer value is the soonest */ 160 | if(row->val < next_wakeup) 161 | next_wakeup = row->val; 162 | } 163 | } 164 | } 165 | 166 | /* Remember how much time we should sleep. */ 167 | total_sleep_time = next_wakeup; 168 | 169 | /* Set timer to soonest occurence */ 170 | setTimer(next_wakeup); 171 | 172 | /* Then trig them or not. */ 173 | for(i=0, row = timers; i<=last_timer_raw; i++, row++) 174 | { 175 | if (row->state & TIMER_TRIG) 176 | { 177 | row->state &= ~TIMER_TRIG; /* reset trig state (will be free if not periodic) */ 178 | if(row->callback) 179 | (*row->callback)(row->d, row->id); /* trig ! */ 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /inc/pdo.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup pdo Process Data Object (PDO) 24 | * PDO is a communication object defined by the DPO communication parameter and PDA mapping parameter objects. 25 | * It is an uncomfirmed communication service without protocol overhead. 26 | * @ingroup comobj 27 | */ 28 | 29 | #ifndef __pdo_h__ 30 | #define __pdo_h__ 31 | 32 | #include 33 | #include 34 | 35 | #include "can.h" 36 | 37 | typedef struct struct_s_PDO_status s_PDO_status; 38 | 39 | #include "data.h" 40 | 41 | /* Handler for RxPDO event timers : empty function that user can overload */ 42 | void _RxPDO_EventTimers_Handler(CO_Data *d, UNS32 pdoNum); 43 | 44 | /* Status of the TPDO : */ 45 | #define PDO_INHIBITED 0x01 46 | #define PDO_RTR_SYNC_READY 0x01 47 | 48 | /** The PDO structure */ 49 | struct struct_s_PDO_status { 50 | UNS8 transmit_type_parameter; 51 | TIMER_HANDLE event_timer; 52 | TIMER_HANDLE inhibit_timer; 53 | Message last_message; 54 | }; 55 | 56 | #define s_PDO_status_Initializer {0, TIMER_NONE, TIMER_NONE, Message_Initializer} 57 | 58 | /** definitions of the different types of PDOs' transmission 59 | * 60 | * SYNCHRO(n) means that the PDO will be transmited every n SYNC signal. 61 | */ 62 | #define TRANS_EVERY_N_SYNC(n) (n) /*n = 1 to 240 */ 63 | #define TRANS_SYNC_ACYCLIC 0 /* Trans after reception of n SYNC. n = 1 to 240 */ 64 | #define TRANS_SYNC_MIN 1 /* Trans after reception of n SYNC. n = 1 to 240 */ 65 | #define TRANS_SYNC_MAX 240 /* Trans after reception of n SYNC. n = 1 to 240 */ 66 | #define TRANS_RTR_SYNC 252 /* Transmission on request */ 67 | #define TRANS_RTR 253 /* Transmission on request */ 68 | #define TRANS_EVENT_SPECIFIC 254 /* Transmission on event */ 69 | #define TRANS_EVENT_PROFILE 255 /* Transmission on event */ 70 | 71 | /** 72 | * @brief Copy all the data to transmit in process_var 73 | * Prepare the PDO defined at index to be sent 74 | * *pwCobId : returns the value of the cobid. (subindex 1) 75 | * @param *d Pointer on a CAN object data structure 76 | * @param numPdo The PDO number 77 | * @param *pdo Pointer on a CAN message structure 78 | * @return 0 or 0xFF if error. 79 | */ 80 | UNS8 buildPDO(CO_Data* d, UNS8 numPdo, Message *pdo); 81 | 82 | /** 83 | * @ingroup pdo 84 | * @brief Transmit a PDO request frame on the network to the slave. 85 | * @param *d Pointer on a CAN object data structure 86 | * @param RPDOIndex Index of the receive PDO 87 | * @return 88 | * - CanFestival file descriptor is returned upon success. 89 | * - 0xFF is returned if RPDO Index is not found. 90 | 91 | * @return 0xFF if error, other in success. 92 | */ 93 | UNS8 sendPDOrequest( CO_Data* d, UNS16 RPDOIndex ); 94 | 95 | /** 96 | * @brief Compute a PDO frame reception 97 | * bus_id is hardware dependant 98 | * @param *d Pointer on a CAN object data structure 99 | * @param *m Pointer on a CAN message structure 100 | * @return 0xFF if error, else return 0 101 | */ 102 | UNS8 proceedPDO (CO_Data* d, Message *m); 103 | 104 | /** 105 | * @brief Used by the application to signal changes in process data 106 | * that could be mapped to some TPDO. 107 | * This do not necessarily imply PDO emission. 108 | * Function iterates on all TPDO and look TPDO transmit 109 | * type and content change before sending it. 110 | * @param *d Pointer on a CAN object data structure 111 | */ 112 | UNS8 sendPDOevent (CO_Data* d); 113 | UNS8 sendOnePDOevent (CO_Data* d, UNS8 pdoNum); 114 | 115 | /** 116 | * @brief Enable a PDO by setting to 0 the bit 32 of the COB-ID parameter 117 | * @param *d Pointer on a CAN object data structure 118 | * @param pdoNum The PDO number 119 | */ 120 | void PDOEnable (CO_Data * d, UNS8 pdoNum); 121 | 122 | /** 123 | * @brief Disable a PDO by setting to 1 the bit 32 of the COB-ID parameter 124 | * @param *d Pointer on a CAN object data structure 125 | * @param pdoNum The PDO number 126 | */ 127 | void PDODisable (CO_Data * d, UNS8 pdoNum); 128 | 129 | /** 130 | * @ingroup pdo 131 | * @brief Function iterates on all TPDO and look TPDO transmit 132 | * type and content change before sending it. 133 | * @param *d Pointer on a CAN object data structure 134 | * @param isSyncEvent 135 | */ 136 | UNS8 _sendPDOevent(CO_Data* d, UNS8 isSyncEvent); 137 | 138 | /** 139 | * @brief Initialize PDO feature 140 | * @param *d Pointer on a CAN object data structure 141 | */ 142 | void PDOInit(CO_Data* d); 143 | 144 | /** 145 | * @brief Stop PDO feature 146 | * @param *d Pointer on a CAN object data structure 147 | */ 148 | void PDOStop(CO_Data* d); 149 | 150 | /** 151 | * @ingroup pdo 152 | * @brief Set timer for PDO event 153 | * @param *d Pointer on a CAN object data structure 154 | * @param pdoNum The PDO number 155 | */ 156 | void PDOEventTimerAlarm(CO_Data* d, UNS32 pdoNum); 157 | 158 | /** 159 | * @ingroup pdo 160 | * @brief Inhibit timer for PDO event 161 | * @param *d Pointer on a CAN object data structure 162 | * @param pdoNum The PDO number 163 | */ 164 | void PDOInhibitTimerAlarm(CO_Data* d, UNS32 pdoNum); 165 | 166 | /* copy bit per bit in little endian */ 167 | void CopyBits(UNS8 NbBits, UNS8* SrcByteIndex, UNS8 SrcBitIndex, UNS8 SrcBigEndian, UNS8* DestByteIndex, UNS8 DestBitIndex, UNS8 DestBigEndian); 168 | #endif 169 | -------------------------------------------------------------------------------- /inc/def.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | 24 | #ifndef __def_h__ 25 | #define __def_h__ 26 | 27 | #include 28 | 29 | /** definitions used for object dictionary access. ie SDO Abort codes . (See DS 301 v.4.02 p.48) 30 | */ 31 | #define OD_SUCCESSFUL 0x00000000 32 | #define OD_READ_NOT_ALLOWED 0x06010001 33 | #define OD_WRITE_NOT_ALLOWED 0x06010002 34 | #define OD_NO_SUCH_OBJECT 0x06020000 35 | #define OD_NOT_MAPPABLE 0x06040041 36 | #define OD_ACCES_FAILED 0x06060000 37 | #define OD_LENGTH_DATA_INVALID 0x06070010 38 | #define OD_NO_SUCH_SUBINDEX 0x06090011 39 | #define OD_VALUE_RANGE_EXCEEDED 0x06090030 /* Value range test result */ 40 | #define OD_VALUE_TOO_LOW 0x06090031 /* Value range test result */ 41 | #define OD_VALUE_TOO_HIGH 0x06090032 /* Value range test result */ 42 | /* Others SDO abort codes 43 | */ 44 | #define SDOABT_TOGGLE_NOT_ALTERNED 0x05030000 45 | #define SDOABT_TIMED_OUT 0x05040000 46 | #define SDOABT_CS_NOT_VALID 0x05040001 47 | #define SDOABT_INVALID_BLOCK_SIZE 0x05040002 48 | #define SDOABT_OUT_OF_MEMORY 0x05040005 /* Size data exceed SDO_MAX_LENGTH_TRANSFER */ 49 | #define SDOABT_GENERAL_ERROR 0x08000000 /* Error size of SDO message */ 50 | #define SDOABT_LOCAL_CTRL_ERROR 0x08000021 51 | 52 | /******************** CONSTANTS ****************/ 53 | 54 | /** Constantes which permit to define if a PDO frame 55 | is a request one or a data one 56 | */ 57 | /* Should not be modified */ 58 | #define REQUEST 1 59 | #define NOT_A_REQUEST 0 60 | 61 | /* Misc constants */ 62 | /* -------------- */ 63 | /* Should not be modified */ 64 | #define Rx 0 65 | #define Tx 1 66 | #ifndef TRUE 67 | #define TRUE 1 68 | #endif 69 | #ifndef FALSE 70 | #define FALSE 0 71 | #endif 72 | 73 | /** Status of the SDO transmission 74 | */ 75 | #define SDO_RESET 0x0 /* Transmission not started. Init state. */ 76 | #define SDO_FINISHED 0x1 /* data are available */ 77 | #define SDO_ABORTED_RCV 0x80 /* Received an abort message. Data not available */ 78 | #define SDO_ABORTED_INTERNAL 0x85 /* Aborted but not because of an abort message. */ 79 | #define SDO_DOWNLOAD_IN_PROGRESS 0x2 80 | #define SDO_UPLOAD_IN_PROGRESS 0x3 81 | #define SDO_BLOCK_DOWNLOAD_IN_PROGRESS 0x4 82 | #define SDO_BLOCK_UPLOAD_IN_PROGRESS 0x5 83 | 84 | /** getReadResultNetworkDict may return any of above status value or this one 85 | */ 86 | #define SDO_PROVIDED_BUFFER_TOO_SMALL 0x8A 87 | 88 | /* Status of the node during the SDO transfer : */ 89 | #define SDO_SERVER 0x1 90 | #define SDO_CLIENT 0x2 91 | #define SDO_UNKNOWN 0x3 92 | 93 | /* SDOrx ccs: client command specifier */ 94 | #define DOWNLOAD_SEGMENT_REQUEST 0 95 | #define INITIATE_DOWNLOAD_REQUEST 1 96 | #define INITIATE_UPLOAD_REQUEST 2 97 | #define UPLOAD_SEGMENT_REQUEST 3 98 | #define ABORT_TRANSFER_REQUEST 4 99 | #define BLOCK_UPLOAD_REQUEST 5 100 | #define BLOCK_DOWNLOAD_REQUEST 6 101 | 102 | /* SDOtx scs: server command specifier */ 103 | #define UPLOAD_SEGMENT_RESPONSE 0 104 | #define DOWNLOAD_SEGMENT_RESPONSE 1 105 | #define INITIATE_DOWNLOAD_RESPONSE 3 106 | #define INITIATE_UPLOAD_RESPONSE 2 107 | #define ABORT_TRANSFER_REQUEST 4 108 | #define BLOCK_DOWNLOAD_RESPONSE 5 109 | #define BLOCK_UPLOAD_RESPONSE 6 110 | 111 | /* SDO block upload client subcommand */ 112 | #define SDO_BCS_INITIATE_UPLOAD_REQUEST 0 113 | #define SDO_BCS_END_UPLOAD_REQUEST 1 114 | #define SDO_BCS_UPLOAD_RESPONSE 2 115 | #define SDO_BCS_START_UPLOAD 3 116 | 117 | /* SDO block upload server subcommand */ 118 | #define SDO_BSS_INITIATE_UPLOAD_RESPONSE 0 119 | #define SDO_BSS_END_UPLOAD_RESPONSE 1 120 | 121 | /* SDO block download client subcommand */ 122 | #define SDO_BCS_INITIATE_DOWNLOAD_REQUEST 0 123 | #define SDO_BCS_END_DOWNLOAD_REQUEST 1 124 | 125 | /* SDO block download server subcommand */ 126 | #define SDO_BSS_INITIATE_DOWNLOAD_RESPONSE 0 127 | #define SDO_BSS_END_DOWNLOAD_RESPONSE 1 128 | #define SDO_BSS_DOWNLOAD_RESPONSE 2 129 | 130 | /* Function Codes 131 | --------------- 132 | defined in the canopen DS301 133 | */ 134 | #define NMT 0x0 135 | #define SYNC 0x1 136 | #define TIME_STAMP 0x2 137 | #define PDO1tx 0x3 138 | #define PDO1rx 0x4 139 | #define PDO2tx 0x5 140 | #define PDO2rx 0x6 141 | #define PDO3tx 0x7 142 | #define PDO3rx 0x8 143 | #define PDO4tx 0x9 144 | #define PDO4rx 0xA 145 | #define SDOtx 0xB 146 | #define SDOrx 0xC 147 | #define NODE_GUARD 0xE 148 | #define LSS 0xF 149 | 150 | /* NMT Command Specifier, sent by master to change a slave state */ 151 | /* ------------------------------------------------------------- */ 152 | /* Should not be modified */ 153 | #define NMT_Start_Node 0x01 154 | #define NMT_Stop_Node 0x02 155 | #define NMT_Enter_PreOperational 0x80 156 | #define NMT_Reset_Node 0x81 157 | #define NMT_Reset_Comunication 0x82 158 | 159 | /** Status of the LSS transmission 160 | */ 161 | #define LSS_RESET 0x0 /* Transmission not started. Init state. */ 162 | #define LSS_FINISHED 0x1 /* data are available */ 163 | #define LSS_ABORTED_INTERNAL 0x2 /* Aborted but not because of an abort message. */ 164 | #define LSS_TRANS_IN_PROGRESS 0x3 165 | 166 | /* constantes used in the different state machines */ 167 | /* ----------------------------------------------- */ 168 | /* Must not be modified */ 169 | #define state1 0x01 170 | #define state2 0x02 171 | #define state3 0x03 172 | #define state4 0x04 173 | #define state5 0x05 174 | #define state6 0x06 175 | #define state7 0x07 176 | #define state8 0x08 177 | #define state9 0x09 178 | #define state10 0x0A 179 | #define state11 0x0B 180 | 181 | #endif /* __def_h__ */ 182 | 183 | -------------------------------------------------------------------------------- /src/sync.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | 8 | See COPYING file for copyrights details. 9 | 10 | 11 | This library is free software; you can redistribute it and/or 12 | modify it under the terms of the GNU Lesser General Public 13 | License as published by the Free Software Foundation; either 14 | version 2.1 of the License, or (at your option) any later version. 15 | 16 | 17 | This library is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | Lesser General Public License for more details. 21 | 22 | 23 | You should have received a copy of the GNU Lesser General Public 24 | License along with this library; if not, write to the Free Software 25 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 | */ 27 | 28 | 29 | /*! 30 | ** @file sync.c 31 | ** @author Edouard TISSERANT and Francis DUPIN 32 | ** @date Tue Jun 5 09:32:32 2007 33 | ** 34 | ** @brief 35 | ** 36 | ** 37 | */ 38 | 39 | #include "data.h" 40 | #include "sync.h" 41 | #include "canfestival.h" 42 | #include "sysdep.h" 43 | 44 | /* Prototypes for internals functions */ 45 | 46 | /*! 47 | ** 48 | ** 49 | ** @param d 50 | ** @param id 51 | **/ 52 | void SyncAlarm(CO_Data* d, UNS32 id); 53 | UNS32 OnCOB_ID_SyncUpdate(CO_Data* d, const indextable * unsused_indextable, 54 | UNS8 unsused_bSubindex); 55 | 56 | /*! 57 | ** 58 | ** 59 | ** @param d 60 | ** @param id 61 | **/ 62 | void SyncAlarm(CO_Data* d, UNS32 id) 63 | { 64 | sendSYNC(d) ; 65 | } 66 | 67 | /*! 68 | ** This is called when Index 0x1005 is updated. 69 | ** 70 | ** @param d 71 | ** @param unsused_indextable 72 | ** @param unsused_bSubindex 73 | ** 74 | ** @return 75 | **/ 76 | UNS32 OnCOB_ID_SyncUpdate(CO_Data* d, const indextable * unsused_indextable, UNS8 unsused_bSubindex) 77 | { 78 | startSYNC(d); 79 | return 0; 80 | } 81 | 82 | /*! 83 | ** 84 | ** 85 | ** @param d 86 | **/ 87 | void startSYNC(CO_Data* d) 88 | { 89 | if(d->syncTimer != TIMER_NONE){ 90 | stopSYNC(d); 91 | } 92 | 93 | RegisterSetODentryCallBack(d, 0x1005, 0, &OnCOB_ID_SyncUpdate); 94 | RegisterSetODentryCallBack(d, 0x1006, 0, &OnCOB_ID_SyncUpdate); 95 | 96 | if(*d->COB_ID_Sync & 0x40000000ul && *d->Sync_Cycle_Period) 97 | { 98 | d->syncTimer = SetAlarm( 99 | d, 100 | 0 /*No id needed*/, 101 | &SyncAlarm, 102 | US_TO_TIMEVAL(*d->Sync_Cycle_Period), 103 | US_TO_TIMEVAL(*d->Sync_Cycle_Period)); 104 | } 105 | } 106 | 107 | /*! 108 | ** 109 | ** 110 | ** @param d 111 | **/ 112 | void stopSYNC(CO_Data* d) 113 | { 114 | RegisterSetODentryCallBack(d, 0x1005, 0, NULL); 115 | RegisterSetODentryCallBack(d, 0x1006, 0, NULL); 116 | d->syncTimer = DelAlarm(d->syncTimer); 117 | } 118 | 119 | 120 | /*! 121 | ** 122 | ** 123 | ** @param d 124 | ** 125 | ** @return 126 | **/ 127 | UNS8 sendSYNCMessage(CO_Data* d) 128 | { 129 | Message m; 130 | 131 | MSG_WAR(0x3001, "sendSYNC ", 0); 132 | 133 | m.cob_id = (UNS16)UNS16_LE(*d->COB_ID_Sync); 134 | m.rtr = NOT_A_REQUEST; 135 | m.len = 0; 136 | 137 | return canSend(d->canHandle,&m); 138 | } 139 | 140 | 141 | /*! 142 | ** 143 | ** 144 | ** @param d 145 | ** 146 | ** @return 147 | **/ 148 | UNS8 sendSYNC(CO_Data* d) 149 | { 150 | UNS8 res; 151 | res = sendSYNCMessage(d); 152 | proceedSYNC(d) ; 153 | return res ; 154 | } 155 | 156 | /*! 157 | ** 158 | ** 159 | ** @param d 160 | ** 161 | ** @return 162 | **/ 163 | UNS8 proceedSYNC(CO_Data* d) 164 | { 165 | 166 | UNS8 res; 167 | 168 | MSG_WAR(0x3002, "SYNC received. Proceed. ", 0); 169 | 170 | (*d->post_sync)(d); 171 | 172 | /* only operational state allows PDO transmission */ 173 | if(! d->CurrentCommunicationState.csPDO) 174 | return 0; 175 | 176 | res = _sendPDOevent(d, 1 /*isSyncEvent*/ ); 177 | 178 | /*Call user app callback*/ 179 | (*d->post_TPDO)(d); 180 | 181 | return res; 182 | 183 | } 184 | 185 | 186 | void _post_sync(CO_Data* d){} 187 | void _post_TPDO(CO_Data* d){} 188 | -------------------------------------------------------------------------------- /src/emcy.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | Modified by: Jaroslav Fojtik 7 | 8 | See COPYING file for copyrights details. 9 | 10 | This library is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU Lesser General Public 12 | License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public 21 | License along with this library; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 23 | USA 24 | */ 25 | 26 | /*! 27 | ** @file emcy.c 28 | ** @author Luis Jimenez 29 | ** @date Wed Sep 26 2007 30 | ** 31 | ** @brief Definitions of the functions that manage EMCY (emergency) messages 32 | ** 33 | ** 34 | */ 35 | 36 | #include 37 | #include "emcy.h" 38 | #include "canfestival.h" 39 | #include "sysdep.h" 40 | 41 | 42 | 43 | UNS32 OnNumberOfErrorsUpdate(CO_Data* d, const indextable * unsused_indextable, UNS8 unsused_bSubindex); 44 | 45 | #define Data data /* temporary fix */ 46 | 47 | 48 | /*! This is called when Index 0x1003 is updated. 49 | ** 50 | ** 51 | ** @param d 52 | ** @param unsused_indextable 53 | ** @param unsused_bSubindex 54 | ** 55 | ** @return 56 | **/ 57 | UNS32 OnNumberOfErrorsUpdate(CO_Data* d, const indextable * unsused_indextable, UNS8 unsused_bSubindex) 58 | { 59 | UNS8 index; 60 | // if 0, reset Pre-defined Error Field 61 | // else, don't change and give an abort message (eeror code: 0609 0030h) 62 | if (*d->error_number == 0) 63 | for (index = 0; index < d->error_history_size; ++index) 64 | *(d->error_first_element + index) = 0; /* clear all the fields in Pre-defined Error Field (1003h) */ 65 | else 66 | { 67 | ;// abort message 68 | } 69 | return 0; 70 | } 71 | 72 | /*! start the EMCY mangagement. 73 | ** 74 | ** 75 | ** @param d 76 | **/ 77 | void emergencyInit(CO_Data* d) 78 | { 79 | RegisterSetODentryCallBack(d, 0x1003, 0x00, &OnNumberOfErrorsUpdate); 80 | 81 | *d->error_number = 0; 82 | } 83 | 84 | /*! 85 | ** 86 | ** 87 | ** @param d 88 | **/ 89 | void emergencyStop(CO_Data* d) 90 | { 91 | 92 | } 93 | 94 | 95 | /*! 96 | ** 97 | ** @param d 98 | ** @param errCode Error Code 99 | ** @param errRegister Error Register 100 | ** @param errSpecific Pointer to char[5] containing Manufacturer Specific Error Field or NULL 101 | ** 102 | ** @return 103 | **/ 104 | UNS8 sendEMCY(CO_Data* d, UNS16 errCode, UNS8 errRegister, const UNS8 errSpecific[5]) 105 | { 106 | Message m; 107 | 108 | MSG_WAR(0x3051, "sendEMCY", 0); 109 | 110 | m.cob_id = (UNS16)(*(UNS32*)d->error_cobid); 111 | m.rtr = NOT_A_REQUEST; 112 | m.len = 8; 113 | m.Data[0] = errCode & 0xFF; /* LSB */ 114 | m.Data[1] = (errCode >> 8) & 0xFF; /* MSB */ 115 | m.Data[2] = errRegister; 116 | 117 | if (errSpecific == NULL) /* Manufacturer Specific Error Field */ 118 | memset(&m.Data[3], 0, 5); 119 | else 120 | memcpy(&m.Data[3], errSpecific, 5); 121 | 122 | return canSend(d->canHandle,&m); 123 | } 124 | 125 | /*! Sets a new error with code errCode. Also sets corresponding bits in Error register (1001h) 126 | ** 127 | ** 128 | ** @param d 129 | ** @param errCode Code of the error 130 | ** @param errRegMask 131 | ** @param addInfo 132 | ** @return 1 if error, 0 if successful 133 | */ 134 | UNS8 EMCY_setError(CO_Data* d, UNS16 errCode, UNS8 errRegMask, UNS16 addInfo) 135 | { 136 | UNS8 index; 137 | UNS8 errRegister_tmp; 138 | 139 | for (index = 0; index < EMCY_MAX_ERRORS; ++index) 140 | { 141 | if (d->error_data[index].errCode == errCode) /* error already registered */ 142 | { 143 | if (d->error_data[index].active) 144 | { 145 | MSG_WAR(0x3052, "EMCY message already sent", 0); 146 | return 0; 147 | } else d->error_data[index].active = 1; /* set as active error */ 148 | break; 149 | } 150 | } 151 | 152 | if (index == EMCY_MAX_ERRORS) /* if errCode not already registered */ 153 | for (index = 0; index < EMCY_MAX_ERRORS; ++index) if (d->error_data[index].active == 0) break; /* find first inactive error */ 154 | 155 | if (index == EMCY_MAX_ERRORS) /* error_data full */ 156 | { 157 | MSG_ERR(0x3053, "error_data full", 0); 158 | return 1; 159 | } 160 | 161 | d->error_data[index].errCode = errCode; 162 | d->error_data[index].errRegMask = errRegMask; 163 | d->error_data[index].active = 1; 164 | 165 | /* set the new state in the error state machine */ 166 | d->error_state = Error_occurred; 167 | 168 | /* set Error Register (1001h) */ 169 | for (index = 0, errRegister_tmp = 0; index < EMCY_MAX_ERRORS; ++index) 170 | if (d->error_data[index].active == 1) errRegister_tmp |= d->error_data[index].errRegMask; 171 | *d->error_register = errRegister_tmp; 172 | 173 | /* set Pre-defined Error Field (1003h) */ 174 | for (index = d->error_history_size - 1; index > 0; --index) 175 | *(d->error_first_element + index) = *(d->error_first_element + index - 1); 176 | *(d->error_first_element) = errCode | ((UNS32)addInfo << 16); 177 | if(*d->error_number < d->error_history_size) ++(*d->error_number); 178 | 179 | /* send EMCY message */ 180 | if (d->CurrentCommunicationState.csEmergency) 181 | return sendEMCY(d, errCode, *d->error_register, NULL); 182 | else return 1; 183 | } 184 | 185 | /*! Deletes error errCode. Also clears corresponding bits in Error register (1001h) 186 | ** 187 | ** 188 | ** @param d 189 | ** @param errCode Code of the error 190 | ** @return 1 if error, 0 if successful 191 | */ 192 | void EMCY_errorRecovered(CO_Data* d, UNS16 errCode) 193 | { 194 | UNS8 index; 195 | UNS8 errRegister_tmp; 196 | UNS8 anyActiveError = 0; 197 | 198 | for (index = 0; index < EMCY_MAX_ERRORS; ++index) 199 | if (d->error_data[index].errCode == errCode) break; /* find the position of the error */ 200 | 201 | 202 | if ((index != EMCY_MAX_ERRORS) && (d->error_data[index].active == 1)) 203 | { 204 | d->error_data[index].active = 0; 205 | 206 | /* set Error Register (1001h) and check error state machine */ 207 | for (index = 0, errRegister_tmp = 0; index < EMCY_MAX_ERRORS; ++index) 208 | if (d->error_data[index].active == 1) 209 | { 210 | anyActiveError = 1; 211 | errRegister_tmp |= d->error_data[index].errRegMask; 212 | } 213 | if(anyActiveError == 0) 214 | { 215 | d->error_state = Error_free; 216 | /* send a EMCY message with code "Error Reset or No Error" */ 217 | if (d->CurrentCommunicationState.csEmergency) 218 | sendEMCY(d, 0x0000, 0x00, NULL); 219 | } 220 | *d->error_register = errRegister_tmp; 221 | } 222 | else 223 | { 224 | MSG_WAR(0x3054, "recovered error was not active", 0); 225 | } 226 | 227 | } 228 | 229 | /*! This function is responsible to process an EMCY canopen-message. 230 | ** 231 | ** 232 | ** @param d 233 | ** @param m The CAN-message which has to be analysed. 234 | ** 235 | **/ 236 | void proceedEMCY(CO_Data* d, Message* m) 237 | { 238 | UNS8 nodeID; 239 | UNS16 errCode; 240 | UNS8 errReg; 241 | 242 | MSG_WAR(0x3055, "EMCY received. Proceed. ", 0); 243 | 244 | /* Test if the size of the EMCY is ok */ 245 | if ( m->len != 8) { 246 | MSG_ERR(0x1056, "Error size EMCY. CobId : ", m->cob_id); 247 | return; 248 | } 249 | 250 | /* post the received EMCY */ 251 | nodeID = m->cob_id & 0x7F; 252 | errCode = m->Data[0] | ((UNS16)m->Data[1] << 8); 253 | errReg = m->Data[2]; 254 | (*d->post_emcy)(d, nodeID, errCode, errReg, (const UNS8*)&m->Data[3]); 255 | } 256 | 257 | void _post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]){} 258 | -------------------------------------------------------------------------------- /inc/lss.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Jorge Berzosa 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup lss Layer Setting Services Object 24 | * @brief LSS offers the possibility to inquire and change the settings of certain parameters of the local layers on 25 | * a CANopen module with LSS Slave capabilities by a CANopen module with LSS Master capabilities via the 26 | * CAN Network. 27 | * The following parameters can be inquired and/or changed by the use of LSS: 28 | * - Node-ID of the CANopen Slave 29 | * - Bit timing parameters of the physical layer (baud rate) 30 | * - LSS address (/2/ Identity Object, Index 1018H) 31 | * @ingroup comobj 32 | */ 33 | 34 | #ifndef __LSS_h__ 35 | #define __LSS_h__ 36 | 37 | #define SLSS_ADRESS 0x7E4 38 | #define MLSS_ADRESS 0x7E5 39 | 40 | #define SDELAY_OFF 0 41 | #define SDELAY_FIRST 1 42 | #define SDELAY_SECOND 2 43 | 44 | #define LSS_WAITING_MODE 0 45 | #define LSS_CONFIGURATION_MODE 1 46 | 47 | /* Switch mode services */ 48 | #define LSS_SM_GLOBAL 4 49 | #define LSS_SM_SELECTIVE_VENDOR 64 50 | #define LSS_SM_SELECTIVE_PRODUCT 65 51 | #define LSS_SM_SELECTIVE_REVISION 66 52 | #define LSS_SM_SELECTIVE_SERIAL 67 53 | #define LSS_SM_SELECTIVE_RESP 68 54 | /* Configuration services */ 55 | #define LSS_CONF_NODE_ID 17 56 | #define LSS_CONF_BIT_TIMING 19 57 | #define LSS_CONF_ACT_BIT_TIMING 21 58 | #define LSS_CONF_STORE 23 59 | /* Inquire services */ 60 | #define LSS_INQ_VENDOR_ID 90 61 | #define LSS_INQ_PRODUCT_CODE 91 62 | #define LSS_INQ_REV_NUMBER 92 63 | #define LSS_INQ_SERIAL_NUMBER 93 64 | #define LSS_INQ_NODE_ID 94 65 | /* Identification services */ 66 | #define LSS_IDENT_REMOTE_VENDOR 70 67 | #define LSS_IDENT_REMOTE_PRODUCT 71 68 | #define LSS_IDENT_REMOTE_REV_LOW 72 69 | #define LSS_IDENT_REMOTE_REV_HIGH 73 70 | #define LSS_IDENT_REMOTE_SERIAL_LOW 74 71 | #define LSS_IDENT_REMOTE_SERIAL_HIGH 75 72 | #define LSS_IDENT_REMOTE_NON_CONF 76 73 | #define LSS_IDENT_SLAVE 79 74 | #define LSS_IDENT_NON_CONF_SLAVE 80 75 | #define LSS_IDENT_FASTSCAN 81 76 | 77 | /*FastScan State Machine*/ 78 | #define LSS_FS_RESET 0 79 | #define LSS_FS_PROCESSING 1 80 | #define LSS_FS_CONFIRMATION 2 81 | 82 | 83 | typedef void (*LSSCallback_t)(CO_Data* d, UNS8 command); 84 | 85 | typedef void (*lss_StoreConfiguration_t)(CO_Data* d,UNS8*,UNS8*); 86 | //void _lss_StoreConfiguration(UNS8 *error, UNS8 *spec_error); 87 | 88 | //typedef void (*lss_ChangeBaudRate_t)(CO_Data* d,char*); 89 | //void _lss_ChangeBaudRate(char *BaudRate); 90 | 91 | 92 | struct struct_lss_transfer; 93 | 94 | //#include "timer.h" 95 | 96 | #ifdef CO_ENABLE_LSS_FS 97 | struct struct_lss_fs_transfer { 98 | UNS32 FS_LSS_ID[4]; 99 | UNS8 FS_BitChecked[4]; 100 | }; 101 | 102 | typedef struct struct_lss_fs_transfer lss_fs_transfer_t; 103 | #endif 104 | 105 | /* The Transfer structure 106 | * Used to store the different fields of the internal state of the LSS 107 | */ 108 | 109 | struct struct_lss_transfer { 110 | UNS8 state; /* state of the transmission : Takes the values LSS_... */ 111 | UNS8 command; /* the LSS command of the transmision */ 112 | UNS8 mode; /* LSS mode */ 113 | 114 | UNS32 dat1; /* the data from the last msg received */ 115 | UNS8 dat2; 116 | 117 | UNS8 nodeID; /* the new nodeid stored to update the nodeid when switching to LSS operational*/ 118 | UNS8 addr_sel_match; /* the matching mask for the LSS Switch Mode Selective service */ 119 | UNS8 addr_ident_match; /* the matching mask for the LSS Identify Remote Slaves service*/ 120 | 121 | char *baudRate; /* the new baudrate stored to update the node baudrate when a Activate Bit 122 | * Timing Parameters is received*/ 123 | UNS16 switchDelay; /* the period of the two delay */ 124 | UNS8 switchDelayState; /* the state machine for the switchDelay */ 125 | CAN_PORT canHandle_t; 126 | 127 | /* Time counters to implement a timeout in milliseconds.*/ 128 | TIMER_HANDLE timerMSG; /* timerMSG is automatically incremented whenever 129 | * the lss state is in LSS_TRANS_IN_PROGRESS, and reseted to 0 130 | * when the response LSS have been received. 131 | */ 132 | 133 | TIMER_HANDLE timerSDELAY; /* timerSDELAY is automatically incremented whenever 134 | * the lss switchDelayState is in SDELAY_FIRST or SDELAY_SECOND, and reseted to 0 135 | * when the two periods have been expired. 136 | */ 137 | 138 | LSSCallback_t Callback; /* The user callback func to be called at LSS transaction end */ 139 | 140 | UNS8 LSSanswer; /* stores if a message has been received during a timer period */ 141 | 142 | #ifdef CO_ENABLE_LSS_FS 143 | UNS32 IDNumber; /* in the master, the LSS address parameter which it currently tries to identify. 144 | * in the slave, the LSS address parameter which is being checked (LSS-ID[sub]). */ 145 | UNS8 BitChecked; /* bits of the current IDNumber that are currently checked */ 146 | UNS8 LSSSub; /* which part of the LSS-ID is currently checked in IDNumber */ 147 | UNS8 LSSNext; /* which LSSSub value will be used in the next request */ 148 | UNS8 LSSPos; /* in the slave, which part of the LSS-ID is currently processed*/ 149 | UNS8 FastScan_SM; /* the state machine for the FastScan protocol */ 150 | TIMER_HANDLE timerFS; /* timerFS is automatically incremented when the FastScan service 151 | * has been requested and reseted to 0 when the protocol ends. 152 | */ 153 | #ifdef CO_ENABLE_LSS_FS 154 | lss_fs_transfer_t lss_fs_transfer; 155 | #endif 156 | 157 | #endif 158 | }; 159 | 160 | #ifdef CO_ENABLE_LSS 161 | typedef struct struct_lss_transfer lss_transfer_t; 162 | #else 163 | typedef UNS8 lss_transfer_t; 164 | #endif 165 | 166 | 167 | 168 | void startLSS(CO_Data* d); 169 | void stopLSS(CO_Data* d); 170 | 171 | 172 | /** transmit a LSS message 173 | * command is the LSS command specifier 174 | * dat1 and dat2 are pointers to optional data (depend on command) 175 | * return sendLSSMessage(d,command,dat1,dat2) 176 | */ 177 | UNS8 sendLSS (CO_Data* d, UNS8 command,void *dat1, void *dat2); 178 | 179 | /** transmit a LSS message on CAN bus 180 | * comamnd is the LSS command specifier 181 | * bus_id is MLSS_ADRESS or SLSS_ADRESS depending in d->iam_a_slave. 182 | * dat1 and dat2 are pointers to optional data (depend on command). 183 | * return canSend(bus_id,&m) 184 | */ 185 | 186 | UNS8 sendLSSMessage(CO_Data* d, UNS8 command, void *dat1, void *dat2); 187 | 188 | /** This function is called when the node is receiving a Master LSS message (cob-id = 0x7E5). 189 | * - Check if there is a callback which will take care of the response. If not return 0 but does nothing. 190 | * - Stops the timer so the alarm wont raise an error. 191 | * - return 0 if OK 192 | */ 193 | UNS8 proceedLSS_Master (CO_Data* d, Message* m ); 194 | 195 | /** This function is called when the node is receiving a Slave LSS message (cob-id = 0x7E4). 196 | * - Call the callback function or send the response message depending on the LSS comand within m. 197 | * - return 0 if OK 198 | */ 199 | UNS8 proceedLSS_Slave (CO_Data* d, Message* m ); 200 | 201 | /** Used by the Master application to send a LSS command, WITHOUT response, to the slave. 202 | * command: the LSS command. LSS_... 203 | * dat1 and dat2: pointers to optional data (depend on command). 204 | * return sendLSS(d,command,dat1,dat2) 205 | */ 206 | //UNS8 configNetworkNode(CO_Data* d, UNS8 command, void *dat1, void* dat2); 207 | 208 | /** 209 | * @ingroup lss 210 | * @brief Used by the Master application to send a LSS command, WITH response, to the slave. 211 | * @param *d Pointer on a CAN object data structure 212 | * @param command 213 | * @param *dat1 214 | * @param *dat2 215 | * @param Callback The function Callback, which must be defined in the user code, is called at the 216 | * end of the exchange (on succes or abort) and can be NULL. 217 | * @return sendLSS(d,command,dat1,dat2) 218 | * The LSS_MSG_TIMER timer is started to control the timeout 219 | */ 220 | UNS8 configNetworkNode (CO_Data* d, UNS8 command, void *dat1, void* dat2, LSSCallback_t Callback); 221 | 222 | /** 223 | * @ingroup lss 224 | * @brief Use this function after a configNetworkNode or configNetworkNodeCallBack to get the result. 225 | * @param *d Pointer on a CAN object data structure 226 | * @param command The LSS command (unused). 227 | * @param *dat1 228 | * @param *dat2 229 | * @return : 230 | * - LSS_RESET // Transmission not started. Init state. 231 | * - LSS_FINISHED // data are available 232 | * - LSS_ABORTED_INTERNAL // Aborted but not because of an abort message. 233 | * - LSS_TRANS_IN_PROGRESS // Data not yet available 234 | * @code 235 | * example: 236 | * UNS32 dat1; 237 | * UNS8 dat2; 238 | * res=configNetworkNodeCallBack(&_Data,LSS_INQ_NODE_ID,0,0,NULL); // inquire the nodeID 239 | * while (getConfigResultNetworkNode (&_Data, LSS_INQ_NODE_ID, &dat1, &dat2) != LSS_TRANS_IN_PROGRESS); 240 | * @endcode 241 | */ 242 | UNS8 getConfigResultNetworkNode (CO_Data* d, UNS8 command, UNS32* dat1, UNS8* dat2); 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /src/objacces.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22 | USA 23 | */ 24 | /*! 25 | ** @file objacces.c 26 | ** @author Edouard TISSERANT and Francis DUPIN 27 | ** @date Tue Jun 5 08:55:23 2007 28 | ** 29 | ** @brief 30 | ** 31 | ** 32 | */ 33 | 34 | 35 | 36 | 37 | /* #define DEBUG_WAR_CONSOLE_ON */ 38 | /* #define DEBUG_ERR_CONSOLE_ON */ 39 | 40 | 41 | #include "data.h" 42 | 43 | //We need the function implementation for linking 44 | //Only a placeholder with a define isnt enough! 45 | UNS8 accessDictionaryError(UNS16 index, UNS8 subIndex, 46 | UNS32 sizeDataDict, UNS32 sizeDataGiven, UNS32 code) 47 | { 48 | #ifdef DEBUG_WAR_CONSOLE_ON 49 | MSG_WAR(0x2B09,"Dictionary index : ", index); 50 | MSG_WAR(0X2B10," subindex : ", subIndex); 51 | switch (code) { 52 | case OD_NO_SUCH_OBJECT: 53 | MSG_WAR(0x2B11,"Index not found ", index); 54 | break; 55 | case OD_NO_SUCH_SUBINDEX : 56 | MSG_WAR(0x2B12,"SubIndex not found ", subIndex); 57 | break; 58 | case OD_WRITE_NOT_ALLOWED : 59 | MSG_WAR(0x2B13,"Write not allowed, data is read only ", index); 60 | break; 61 | case OD_LENGTH_DATA_INVALID : 62 | MSG_WAR(0x2B14,"Conflict size data. Should be (bytes) : ", sizeDataDict); 63 | MSG_WAR(0x2B15,"But you have given the size : ", sizeDataGiven); 64 | break; 65 | case OD_NOT_MAPPABLE : 66 | MSG_WAR(0x2B16,"Not mappable data in a PDO at index : ", index); 67 | break; 68 | case OD_VALUE_TOO_LOW : 69 | MSG_WAR(0x2B17,"Value range error : value too low. SDOabort : ", code); 70 | break; 71 | case OD_VALUE_TOO_HIGH : 72 | MSG_WAR(0x2B18,"Value range error : value too high. SDOabort : ", code); 73 | break; 74 | default : 75 | MSG_WAR(0x2B20, "Unknown error code : ", code); 76 | } 77 | #endif 78 | 79 | return 0; 80 | } 81 | 82 | UNS32 _getODentry( CO_Data* d, 83 | UNS16 wIndex, 84 | UNS8 bSubindex, 85 | void * pDestData, 86 | UNS32 * pExpectedSize, 87 | UNS8 * pDataType, 88 | UNS8 checkAccess, 89 | UNS8 endianize) 90 | { /* DO NOT USE MSG_ERR because the macro may send a PDO -> infinite 91 | loop if it fails. */ 92 | UNS32 errorCode; 93 | UNS32 szData; 94 | const indextable *ptrTable; 95 | 96 | ptrTable = (*d->scanIndexOD)(d, wIndex, &errorCode); 97 | 98 | if (errorCode != OD_SUCCESSFUL) 99 | return errorCode; 100 | if( ptrTable->bSubCount <= bSubindex ) { 101 | /* Subindex not found */ 102 | accessDictionaryError(wIndex, bSubindex, 0, 0, OD_NO_SUCH_SUBINDEX); 103 | return OD_NO_SUCH_SUBINDEX; 104 | } 105 | 106 | if (checkAccess && (ptrTable->pSubindex[bSubindex].bAccessType & WO)) { 107 | MSG_WAR(0x2B30, "Access Type : ", ptrTable->pSubindex[bSubindex].bAccessType); 108 | accessDictionaryError(wIndex, bSubindex, 0, 0, OD_READ_NOT_ALLOWED); 109 | return OD_READ_NOT_ALLOWED; 110 | } 111 | 112 | if (pDestData == 0) { 113 | return SDOABT_GENERAL_ERROR; 114 | } 115 | 116 | if (ptrTable->pSubindex[bSubindex].size > *pExpectedSize) { 117 | /* Requested variable is too large to fit into a transfer line, inform * 118 | * the caller about the real size of the requested variable. */ 119 | *pExpectedSize = ptrTable->pSubindex[bSubindex].size; 120 | return SDOABT_OUT_OF_MEMORY; 121 | } 122 | 123 | *pDataType = ptrTable->pSubindex[bSubindex].bDataType; 124 | szData = ptrTable->pSubindex[bSubindex].size; 125 | 126 | # ifdef CANOPEN_BIG_ENDIAN 127 | if(endianize && *pDataType > boolean && !( 128 | *pDataType >= visible_string && 129 | *pDataType <= domain)) { 130 | /* data must be transmited with low byte first */ 131 | UNS8 i, j = 0; 132 | MSG_WAR(boolean, "data type ", *pDataType); 133 | MSG_WAR(visible_string, "data type ", *pDataType); 134 | for ( i = szData ; i > 0 ; i--) { 135 | MSG_WAR(i," ", j); 136 | ((UNS8*)pDestData)[j++] = 137 | ((UNS8*)ptrTable->pSubindex[bSubindex].pObject)[i-1]; 138 | } 139 | *pExpectedSize = szData; 140 | } 141 | else /* no endianisation change */ 142 | # endif 143 | memcpy(pDestData, ptrTable->pSubindex[bSubindex].pObject,szData); 144 | 145 | if(*pDataType != visible_string) 146 | *pExpectedSize = szData; 147 | else { 148 | /* VISIBLE_STRING objects are returned with \0 termination, if the user * 149 | * provided enough space. * 150 | * Note: If the parameter "Default String Size" of the Object Dictionary * 151 | * Editor is larger than the string, then the \0 byte will be * 152 | * appended anyways! */ 153 | if(*pExpectedSize > ptrTable->pSubindex[bSubindex].size) { 154 | *((UNS8*)pDestData + szData) = '\0'; 155 | *pExpectedSize = szData + 1; 156 | } 157 | else 158 | *pExpectedSize = szData; 159 | } 160 | return OD_SUCCESSFUL; 161 | } 162 | 163 | UNS32 _setODentry( CO_Data* d, 164 | UNS16 wIndex, 165 | UNS8 bSubindex, 166 | void * pSourceData, 167 | UNS32 * pExpectedSize, 168 | UNS8 checkAccess, 169 | UNS8 endianize) 170 | { 171 | UNS32 szData; 172 | UNS8 dataType; 173 | UNS32 errorCode; 174 | const indextable *ptrTable; 175 | ODCallback_t Callback; 176 | 177 | ptrTable =(*d->scanIndexOD)(d, wIndex, &errorCode); 178 | if (errorCode != OD_SUCCESSFUL) 179 | return errorCode; 180 | 181 | if( ptrTable->bSubCount <= bSubindex ) { 182 | /* Subindex not found */ 183 | accessDictionaryError(wIndex, bSubindex, 0, *pExpectedSize, OD_NO_SUCH_SUBINDEX); 184 | return OD_NO_SUCH_SUBINDEX; 185 | } 186 | if (checkAccess && (ptrTable->pSubindex[bSubindex].bAccessType == RO)) { 187 | MSG_WAR(0x2B25, "Access Type : ", ptrTable->pSubindex[bSubindex].bAccessType); 188 | accessDictionaryError(wIndex, bSubindex, 0, *pExpectedSize, OD_WRITE_NOT_ALLOWED); 189 | return OD_WRITE_NOT_ALLOWED; 190 | } 191 | 192 | 193 | dataType = ptrTable->pSubindex[bSubindex].bDataType; 194 | szData = ptrTable->pSubindex[bSubindex].size; 195 | Callback = ptrTable->pSubindex[bSubindex].callback; 196 | 197 | /* check the size, we must allow to store less bytes than data size, even for intergers 198 | (e.g. UNS40 : objdictedit will store it in a uint64_t, setting the size to 8 but PDO comes 199 | with 5 bytes so ExpectedSize is 5 */ 200 | if( *pExpectedSize == 0 || *pExpectedSize <= szData ) 201 | { 202 | #ifdef CANOPEN_BIG_ENDIAN 203 | /* re-endianize do not occur for bool, strings time and domains */ 204 | if(endianize && dataType > boolean && !( 205 | dataType >= visible_string && 206 | dataType <= domain)) 207 | { 208 | /* we invert the data source directly. This let us do range 209 | testing without */ 210 | /* additional temp variable */ 211 | UNS8 i; 212 | for ( i = 0 ; i < ( ptrTable->pSubindex[bSubindex].size >> 1) ; i++) 213 | { 214 | UNS8 tmp =((UNS8 *)pSourceData) [(ptrTable->pSubindex[bSubindex].size - 1) - i]; 215 | ((UNS8 *)pSourceData) [(ptrTable->pSubindex[bSubindex].size - 1) - i] = ((UNS8 *)pSourceData)[i]; 216 | ((UNS8 *)pSourceData)[i] = tmp; 217 | } 218 | } 219 | #endif 220 | errorCode = (*d->valueRangeTest)(dataType, pSourceData); 221 | if (errorCode) { 222 | accessDictionaryError(wIndex, bSubindex, szData, *pExpectedSize, errorCode); 223 | return errorCode; 224 | } 225 | memcpy(ptrTable->pSubindex[bSubindex].pObject,pSourceData, *pExpectedSize); 226 | /* TODO : CONFORM TO DS-301 : 227 | * - stop using NULL terminated strings 228 | * - store string size in td_subindex 229 | * */ 230 | /* terminate visible_string with '\0' */ 231 | if(dataType == visible_string && *pExpectedSize < szData) 232 | ((UNS8*)ptrTable->pSubindex[bSubindex].pObject)[*pExpectedSize] = 0; 233 | 234 | *pExpectedSize = szData; 235 | 236 | /* Callbacks */ 237 | if(Callback){ 238 | errorCode = (Callback)(d, ptrTable, bSubindex); 239 | if(errorCode != OD_SUCCESSFUL) 240 | { 241 | return errorCode; 242 | } 243 | } 244 | 245 | /* Store value if requested with user defined function 246 | Function should return OD_ACCES_FAILED in case of store error */ 247 | if (ptrTable->pSubindex[bSubindex].bAccessType & TO_BE_SAVE){ 248 | return (*d->storeODSubIndex)(d, wIndex, bSubindex); 249 | } 250 | return OD_SUCCESSFUL; 251 | }else{ 252 | *pExpectedSize = szData; 253 | accessDictionaryError(wIndex, bSubindex, szData, *pExpectedSize, OD_LENGTH_DATA_INVALID); 254 | return OD_LENGTH_DATA_INVALID; 255 | } 256 | } 257 | 258 | UNS32 RegisterSetODentryCallBack(CO_Data* d, UNS16 wIndex, UNS8 bSubindex, ODCallback_t Callback) 259 | { 260 | UNS32 errorCode; 261 | const indextable *odentry; 262 | 263 | odentry = d->scanIndexOD (d, wIndex, &errorCode); 264 | if(errorCode == OD_SUCCESSFUL && bSubindex < odentry->bSubCount) 265 | odentry->pSubindex[bSubindex].callback = Callback; 266 | return errorCode; 267 | } 268 | 269 | UNS32 _storeODSubIndex (CO_Data* d, UNS16 wIndex, UNS8 bSubindex) 270 | { 271 | return OD_SUCCESSFUL; 272 | } 273 | -------------------------------------------------------------------------------- /src/dcf.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22 | USA 23 | */ 24 | 25 | 26 | /** 27 | ** @file dcf.c 28 | ** @author Edouard TISSERANT and Francis DUPIN 29 | ** @date Mon Jun 4 17:06:12 2007 30 | ** 31 | ** @brief EXEMPLE OF SOMMARY 32 | ** 33 | ** 34 | */ 35 | 36 | 37 | #include "data.h" 38 | #include "sysdep.h" 39 | #include "dcf.h" 40 | 41 | typedef struct { 42 | UNS16 Index; 43 | UNS8 Subindex; 44 | UNS32 Size; 45 | UNS8 *Data; 46 | } dcf_entry_t; 47 | 48 | void SaveNode(CO_Data* d, UNS8 nodeId); 49 | static UNS8 read_consise_dcf_next_entry(CO_Data* d, UNS8 nodeId); 50 | static UNS8 write_consise_dcf_next_entry(CO_Data* d, UNS8 nodeId); 51 | UNS8 init_consise_dcf(CO_Data* d,UNS8 nodeId); 52 | 53 | 54 | #ifdef _MSC_VER 55 | #define inline _inline 56 | #endif /* _MSC_VER */ 57 | 58 | 59 | inline void start_node(CO_Data* d, UNS8 nodeId){ 60 | /* Ask slave node to go in operational mode */ 61 | masterSendNMTstateChange (d, nodeId, NMT_Start_Node); 62 | d->NMTable[nodeId] = Connecting; 63 | } 64 | 65 | /** 66 | ** @brief Function to be called from post_SlaveBootup 67 | ** 68 | ** @param d 69 | ** @param nodeId 70 | */ 71 | UNS8 check_and_start_node(CO_Data* d, UNS8 nodeId) 72 | { 73 | if(d->dcf_status != DCF_STATUS_INIT) 74 | return 0; 75 | /* Set the first SDO client as available */ 76 | if(d->firstIndex->SDO_CLT) 77 | *(UNS8*) d->objdict[d->firstIndex->SDO_CLT].pSubindex[3].pObject = 0; 78 | else 79 | return 3; 80 | if((init_consise_dcf(d, nodeId) == 0) || (read_consise_dcf_next_entry(d, nodeId) == 0)){ 81 | start_node(d, nodeId); 82 | return 1; 83 | } 84 | d->dcf_status = DCF_STATUS_READ_CHECK; 85 | return 2; 86 | } 87 | 88 | /** 89 | ** @brief Start the nodeId slave and look for other nodes waiting to be started 90 | ** If nodeId is 0 the start node is not done 91 | ** @param d 92 | ** @param nodeId 93 | */ 94 | inline void start_and_seek_node(CO_Data* d, UNS8 nodeId){ 95 | UNS8 node; 96 | if(nodeId) 97 | start_node(d,nodeId); 98 | for(node = 0 ; nodeNMTable[node] != Initialisation) 100 | continue; 101 | if(check_and_start_node(d, node) == 2) 102 | return; 103 | } 104 | d->dcf_status = DCF_STATUS_INIT; 105 | } 106 | 107 | /** 108 | ** 109 | ** 110 | ** @param d 111 | ** @param nodeId 112 | */ 113 | static void CheckSDOAndContinue(CO_Data* d, UNS8 nodeId) 114 | { 115 | UNS32 abortCode = 0; 116 | UNS8 buf[4], match = 0, node; 117 | UNS32 size=4; 118 | if(d->dcf_status == DCF_STATUS_READ_CHECK){ 119 | if(getReadResultNetworkDict (d, nodeId, buf, &size, &abortCode) != SDO_FINISHED) 120 | goto dcferror; 121 | /* Check if data received match the DCF */ 122 | if(size == d->dcf_size){ 123 | match = 1; 124 | while(size--) 125 | if(buf[size] != d->dcf_data[size]) 126 | match = 0; 127 | } 128 | if(match) { 129 | if(read_consise_dcf_next_entry(d, nodeId) == 0){ 130 | start_and_seek_node(d, nodeId); 131 | } 132 | } 133 | else { /* Data received does not match : start rewriting all */ 134 | if((init_consise_dcf(d, nodeId) == 0) || (write_consise_dcf_next_entry(d, nodeId) == 0)) 135 | goto dcferror; 136 | d->dcf_status = DCF_STATUS_WRITE; 137 | } 138 | } 139 | else if(d->dcf_status == DCF_STATUS_WRITE){ 140 | if(getWriteResultNetworkDict (d, nodeId, &abortCode) != SDO_FINISHED) 141 | goto dcferror; 142 | if(write_consise_dcf_next_entry(d, nodeId) == 0){ 143 | #ifdef DCF_SAVE_NODE 144 | SaveNode(d, nodeId); 145 | d->dcf_status = DCF_STATUS_SAVED; 146 | #else //DCF_SAVE_NODE 147 | d->dcf_status = DCF_STATUS_INIT; 148 | start_and_seek_node(d,nodeId); 149 | #endif //DCF_SAVE_NODE 150 | } 151 | } 152 | else if(d->dcf_status == DCF_STATUS_SAVED){ 153 | if(getWriteResultNetworkDict (d, nodeId, &abortCode) != SDO_FINISHED) 154 | goto dcferror; 155 | masterSendNMTstateChange (d, nodeId, NMT_Reset_Node); 156 | d->dcf_status = DCF_STATUS_INIT; 157 | d->NMTable[nodeId] = Unknown_state; 158 | } 159 | return; 160 | dcferror: 161 | MSG_ERR(0x1A01, "SDO error in consise DCF", abortCode); 162 | MSG_WAR(0x2A02, "slave node : ", nodeId); 163 | resetClientSDOLineFromNodeId(d, nodeId); 164 | d->NMTable[nodeId] = Unknown_state; 165 | d->dcf_status = DCF_STATUS_INIT; 166 | start_and_seek_node(d,0); 167 | } 168 | 169 | /** 170 | ** 171 | ** 172 | ** @param d 173 | ** @param nodeId 174 | ** 175 | ** @return 176 | */ 177 | UNS8 init_consise_dcf(CO_Data* d,UNS8 nodeId) 178 | { 179 | /* Fetch DCF OD entry */ 180 | UNS32 errorCode; 181 | UNS8* dcf; 182 | d->dcf_odentry = (*d->scanIndexOD)(d, 0x1F22, &errorCode); 183 | /* If DCF entry do not exist... Nothing to do.*/ 184 | if (errorCode != OD_SUCCESSFUL) goto DCF_finish; 185 | /* Fix DCF table overflow */ 186 | if(nodeId > d->dcf_odentry->bSubCount) goto DCF_finish; 187 | /* If DCF empty... Nothing to do */ 188 | if(! d->dcf_odentry->pSubindex[nodeId].size) goto DCF_finish; 189 | //dcf = *(UNS8**)d->dcf_odentry->pSubindex[nodeId].pObject; 190 | dcf = (UNS8*)d->dcf_odentry->pSubindex[nodeId].pObject; 191 | d->dcf_cursor = dcf + 4; 192 | d->dcf_entries_count = 0; 193 | d->dcf_status = DCF_STATUS_INIT; 194 | return 1; 195 | DCF_finish: 196 | return 0; 197 | } 198 | 199 | UNS8 get_next_DCF_data(CO_Data* d, dcf_entry_t *dcf_entry, UNS8 nodeId) 200 | { 201 | UNS8* dcfend; 202 | UNS32 nb_entries; 203 | UNS32 szData; 204 | UNS8* dcf; 205 | if(!d->dcf_odentry) 206 | return 0; 207 | if(nodeId > d->dcf_odentry->bSubCount) 208 | return 0; 209 | szData = d->dcf_odentry->pSubindex[nodeId].size; 210 | //dcf = *(UNS8**)d->dcf_odentry->pSubindex[nodeId].pObject; 211 | dcf = (UNS8*)d->dcf_odentry->pSubindex[nodeId].pObject; 212 | nb_entries = UNS32_LE(*((UNS32*)dcf)); 213 | dcfend = dcf + szData; 214 | if((UNS8*)d->dcf_cursor + 7 < (UNS8*)dcfend && d->dcf_entries_count < nb_entries){ 215 | /* DCF data may not be 32/16b aligned, 216 | * we cannot directly dereference d->dcf_cursor 217 | * as UNS16 or UNS32 218 | * Do it byte per byte taking care on endianess*/ 219 | #ifdef CANOPEN_BIG_ENDIAN 220 | dcf_entry->Index = *(d->dcf_cursor++) << 8 | 221 | *(d->dcf_cursor++); 222 | #else 223 | memcpy(&dcf_entry->Index, d->dcf_cursor,2); 224 | d->dcf_cursor+=2; 225 | #endif 226 | dcf_entry->Subindex = *(d->dcf_cursor++); 227 | #ifdef CANOPEN_BIG_ENDIAN 228 | dcf_entry->Size = *(d->dcf_cursor++) << 24 | 229 | *(d->dcf_cursor++) << 16 | 230 | *(d->dcf_cursor++) << 8 | 231 | *(d->dcf_cursor++); 232 | #else 233 | memcpy(&dcf_entry->Size, d->dcf_cursor,4); 234 | d->dcf_cursor+=4; 235 | #endif 236 | d->dcf_data = dcf_entry->Data = d->dcf_cursor; 237 | d->dcf_size = dcf_entry->Size; 238 | d->dcf_cursor += dcf_entry->Size; 239 | d->dcf_entries_count++; 240 | return 1; 241 | } 242 | return 0; 243 | } 244 | 245 | static UNS8 write_consise_dcf_next_entry(CO_Data* d, UNS8 nodeId) 246 | { 247 | UNS8 Ret; 248 | dcf_entry_t dcf_entry; 249 | if(!get_next_DCF_data(d, &dcf_entry, nodeId)) 250 | return 0; 251 | Ret = writeNetworkDictCallBackAI(d, /* CO_Data* d*/ 252 | nodeId, /* UNS8 nodeId*/ 253 | dcf_entry.Index, /* UNS16 index*/ 254 | dcf_entry.Subindex, /* UNS8 subindex*/ 255 | (UNS8)dcf_entry.Size, /* UNS8 count*/ 256 | 0, /* UNS8 dataType*/ 257 | dcf_entry.Data,/* void *data*/ 258 | CheckSDOAndContinue,/* Callback*/ 259 | 0, /* no endianize*/ 260 | 0); /* no block mode */ 261 | if(Ret) { 262 | MSG_ERR(0x1A02,"Error writeNetworkDictCallBackAI",Ret); 263 | } 264 | return 1; 265 | } 266 | 267 | static UNS8 read_consise_dcf_next_entry(CO_Data* d, UNS8 nodeId) 268 | { 269 | UNS8 Ret; 270 | dcf_entry_t dcf_entry; 271 | if(!get_next_DCF_data(d, &dcf_entry, nodeId)) 272 | return 0; 273 | Ret = readNetworkDictCallbackAI(d, /* CO_Data* d*/ 274 | nodeId, /* UNS8 nodeId*/ 275 | dcf_entry.Index, /* UNS16 index*/ 276 | dcf_entry.Subindex, /* UNS8 subindex*/ 277 | 0, /* UNS8 dataType*/ 278 | CheckSDOAndContinue,/* Callback*/ 279 | 0); /* no block mode */ 280 | if(Ret) { 281 | MSG_ERR(0x1A03,"Error readNetworkDictCallbackAI",Ret); 282 | } 283 | return 1; 284 | } 285 | 286 | void SaveNode(CO_Data* d, UNS8 nodeId) 287 | { 288 | UNS8 Ret; 289 | UNS32 data=0x65766173; 290 | Ret = writeNetworkDictCallBackAI(d, /* CO_Data* d*/ 291 | nodeId, /* UNS8 nodeId*/ 292 | 0x1010, /* UNS16 index*/ 293 | 1, /* UNS8 subindex*/ 294 | 4, /* UNS8 count*/ 295 | 0, /* UNS8 dataType*/ 296 | (void *)&data,/* void *data*/ 297 | CheckSDOAndContinue,/* Callback*/ 298 | 0, /* no endianize*/ 299 | 0); /* no block mode */ 300 | if(Ret) { 301 | MSG_ERR(0x1A04,"Error writeNetworkDictCallBackAI",Ret); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /examples/master402/master402_canopen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "canfestival.h" 5 | #include "canopen_callback.h" 6 | #include "timers_driver.h" 7 | #include "master402_canopen.h" 8 | #include "master402_od.h" 9 | 10 | #define PRODUCER_HEARTBEAT_TIME 500 11 | #define CONSUMER_HEARTBEAT_TIME 1000 12 | 13 | struct servo_config_state 14 | { 15 | uint8_t state; 16 | uint8_t try_cnt; 17 | struct rt_semaphore finish_sem; 18 | }; 19 | 20 | 21 | void InitNodes(CO_Data* d, UNS32 id); 22 | 23 | static void config_servo_param(uint8_t nodeId, struct servo_config_state *conf); 24 | static struct servo_config_state servo_conf[4]; 25 | static void config_servo(uint8_t nodeId); 26 | static void config_single_servo(void *parameter); 27 | 28 | CO_Data *OD_Data = &master402_Data; 29 | s_BOARD agv_board = {"0", "1M"}; 30 | 31 | int canopen_init(void) 32 | { 33 | OD_Data->heartbeatError = master402_heartbeatError; 34 | OD_Data->initialisation = master402_initialisation; 35 | OD_Data->preOperational = master402_preOperational; 36 | OD_Data->operational = master402_operational; 37 | OD_Data->stopped = master402_stopped; 38 | OD_Data->post_sync = master402_post_sync; 39 | OD_Data->post_TPDO = master402_post_TPDO; 40 | OD_Data->storeODSubIndex = (storeODSubIndex_t)master402_storeODSubIndex; 41 | OD_Data->post_emcy = (post_emcy_t)master402_post_emcy; 42 | 43 | canOpen(&agv_board, OD_Data); 44 | initTimer(); 45 | 46 | // Start timer thread 47 | StartTimerLoop(&InitNodes); 48 | 49 | return 0; 50 | } 51 | INIT_APP_EXPORT(canopen_init); 52 | 53 | void InitNodes(CO_Data* d, UNS32 id) 54 | { 55 | setNodeId(OD_Data, 0x01); 56 | setState(OD_Data, Initialisation); 57 | } 58 | 59 | void Exit(CO_Data* d, UNS32 id) 60 | { 61 | 62 | } 63 | 64 | static void slaveBootupHdl(CO_Data* d, UNS8 nodeId) 65 | { 66 | rt_thread_t tid; 67 | 68 | tid = rt_thread_create("co_cfg", config_single_servo, (void *)(int)nodeId, 1024, 12 + nodeId, 2); 69 | if(tid == RT_NULL) 70 | { 71 | rt_kprintf("canopen config thread start failed!\n"); 72 | } 73 | else 74 | { 75 | rt_thread_startup(tid); 76 | } 77 | } 78 | 79 | void canopen_start_thread_entry(void *parameter) 80 | { 81 | UNS32 sync_id, size; 82 | UNS8 data_type, sub_cnt; 83 | UNS32 consumer_heartbeat_time; 84 | 85 | rt_thread_delay(200); 86 | config_servo(SERVO_NODEID); 87 | OD_Data->post_SlaveBootup = slaveBootupHdl; 88 | consumer_heartbeat_time = (2 << 16) | CONSUMER_HEARTBEAT_TIME; 89 | size = 4; 90 | writeLocalDict(OD_Data, 0x1016, 1, &consumer_heartbeat_time, &size, 0); 91 | consumer_heartbeat_time = (3 << 16) | CONSUMER_HEARTBEAT_TIME; 92 | writeLocalDict(OD_Data, 0x1016, 2, &consumer_heartbeat_time, &size, 0); 93 | sub_cnt = 2; 94 | size = 1; 95 | writeLocalDict(OD_Data, 0x1016, 0, &sub_cnt, &size, 0); 96 | data_type = uint32; 97 | setState(OD_Data, Operational); 98 | masterSendNMTstateChange(OD_Data, SERVO_NODEID, NMT_Start_Node); 99 | size = 4; 100 | readLocalDict(OD_Data, 0x1005, 0, &sync_id, &size, &data_type, 0); 101 | sync_id |= (1 << 30); 102 | writeLocalDict(OD_Data, 0x1005, 0, &sync_id, &size, 0); 103 | } 104 | 105 | static void config_servo(uint8_t nodeId) 106 | { 107 | servo_conf[nodeId - 2].state = 0; 108 | servo_conf[nodeId - 2].try_cnt = 0; 109 | rt_sem_init(&(servo_conf[nodeId - 2].finish_sem), "servocnf", 0, RT_IPC_FLAG_FIFO); 110 | 111 | EnterMutex(); 112 | config_servo_param(nodeId, &servo_conf[nodeId - 2]); 113 | LeaveMutex(); 114 | rt_sem_take(&(servo_conf[nodeId - 2].finish_sem), RT_WAITING_FOREVER); 115 | rt_sem_detach(&(servo_conf[nodeId - 2].finish_sem)); 116 | } 117 | 118 | static void config_single_servo(void *parameter) 119 | { 120 | uint32_t nodeId; 121 | nodeId = (uint32_t)parameter; 122 | config_servo(nodeId); 123 | masterSendNMTstateChange(OD_Data, nodeId, NMT_Start_Node); 124 | } 125 | 126 | 127 | static void config_servo_param_cb(CO_Data* d, UNS8 nodeId) 128 | { 129 | UNS32 abortCode; 130 | UNS8 res; 131 | struct servo_config_state *conf; 132 | 133 | conf = &servo_conf[nodeId - 2]; 134 | res = getWriteResultNetworkDict(OD_Data, nodeId, &abortCode); 135 | closeSDOtransfer(OD_Data, nodeId, SDO_CLIENT); 136 | if(res != SDO_FINISHED) 137 | { 138 | conf->try_cnt++; 139 | rt_kprintf("write SDO failed! nodeId = %d, abortCode = 0x%08X\n", nodeId, abortCode); 140 | if(conf->try_cnt < 3) 141 | { 142 | config_servo_param(nodeId, conf); 143 | } 144 | else 145 | { 146 | rt_sem_release(&(conf->finish_sem)); 147 | conf->state = 0; 148 | conf->try_cnt = 0; 149 | rt_kprintf("SDO config try count > 3, config failed!\n"); 150 | } 151 | } 152 | else 153 | { 154 | conf->state++; 155 | conf->try_cnt = 0; 156 | config_servo_param(nodeId, conf); 157 | } 158 | } 159 | 160 | static void config_servo_param(uint8_t nodeId, struct servo_config_state *conf) 161 | { 162 | switch(conf->state) 163 | { 164 | case 0: 165 | { // disable Slave's TPDO 166 | UNS32 TPDO_COBId = 0x80000180 + nodeId; 167 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1800, 1, 168 | 4, uint32, &TPDO_COBId, config_servo_param_cb, 0); 169 | } 170 | break; 171 | case 1: 172 | { 173 | UNS8 trans_type = PDO_TRANSMISSION_TYPE; 174 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1800, 2, 175 | 1, uint8, &trans_type, config_servo_param_cb, 0); 176 | } 177 | break; 178 | case 2: 179 | { 180 | UNS8 pdo_map_cnt = 0; 181 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A00, 0, 182 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 183 | } 184 | break; 185 | case 3: 186 | { 187 | UNS32 pdo_map_val = 0x60410010; 188 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A00, 1, 189 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 190 | } 191 | break; 192 | case 4: 193 | { 194 | UNS32 pdo_map_val = 0x60630020; 195 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A00, 2, 196 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 197 | } 198 | break; 199 | case 5: 200 | { 201 | UNS8 pdo_map_cnt = 2; 202 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A00, 0, 203 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 204 | } 205 | break; 206 | case 6: 207 | { // enable Slave's TPDO 208 | UNS32 TPDO_COBId = 0x00000180 + nodeId; 209 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1800, 1, 210 | 4, uint32, &TPDO_COBId, config_servo_param_cb, 0); 211 | } 212 | break; 213 | case 7: 214 | { // disable Slave's TPDO 215 | UNS32 TPDO_COBId = 0x80000280 + nodeId; 216 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1801, 1, 217 | 4, uint32, &TPDO_COBId, config_servo_param_cb, 0); 218 | } 219 | break; 220 | case 8: 221 | { 222 | UNS8 trans_type = PDO_TRANSMISSION_TYPE; 223 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1801, 2, 224 | 1, uint8, &trans_type, config_servo_param_cb, 0); 225 | } 226 | break; 227 | case 9: 228 | { 229 | UNS8 pdo_map_cnt = 0; 230 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A01, 0, 231 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 232 | } 233 | break; 234 | case 10: 235 | { 236 | UNS32 pdo_map_val = 0x606c0020; 237 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A01, 1, 238 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 239 | } 240 | break; 241 | case 11: 242 | { 243 | UNS8 pdo_map_cnt = 1; 244 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1A01, 0, 245 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 246 | } 247 | break; 248 | case 12: 249 | { // enable Slave's TPDO 250 | UNS32 TPDO_COBId = 0x00000280 + nodeId; 251 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1801, 1, 252 | 4, uint32, &TPDO_COBId, config_servo_param_cb, 0); 253 | } 254 | break; 255 | case 13: 256 | { // disable Slave's RPDO 257 | UNS32 RPDO_COBId = 0x80000200 + nodeId; 258 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1400, 1, 259 | 4, uint32, &RPDO_COBId, config_servo_param_cb, 0); 260 | } 261 | break; 262 | case 14: 263 | { 264 | UNS8 trans_type = PDO_TRANSMISSION_TYPE; 265 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1400, 2, 266 | 1, uint8, &trans_type, config_servo_param_cb, 0); 267 | } 268 | break; 269 | case 15: 270 | { 271 | UNS8 pdo_map_cnt = 0; 272 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1600, 0, 273 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 274 | } 275 | break; 276 | case 16: 277 | { 278 | UNS32 pdo_map_val = 0x60400010; 279 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1600, 1, 280 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 281 | } 282 | break; 283 | case 17: 284 | { 285 | UNS32 pdo_map_val = 0x60600008; 286 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1600, 2, 287 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 288 | } 289 | break; 290 | case 18: 291 | { 292 | UNS8 pdo_map_cnt = 2; 293 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1600, 0, 294 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 295 | } 296 | break; 297 | case 19: 298 | { // enable Slave's RPDO 299 | UNS32 RPDO_COBId = 0x00000200 + nodeId; 300 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1400, 1, 301 | 4, uint32, &RPDO_COBId, config_servo_param_cb, 0); 302 | } 303 | break; 304 | case 20: 305 | { // disable Slave's RPDO 306 | UNS32 RPDO_COBId = 0x80000300 + nodeId; 307 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1401, 1, 308 | 4, uint32, &RPDO_COBId, config_servo_param_cb, 0); 309 | } 310 | break; 311 | case 21: 312 | { 313 | UNS8 trans_type = PDO_TRANSMISSION_TYPE; 314 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1401, 2, 315 | 1, uint8, &trans_type, config_servo_param_cb, 0); 316 | } 317 | break; 318 | case 22: 319 | { 320 | UNS8 pdo_map_cnt = 0; 321 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1601, 0, 322 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 323 | } 324 | break; 325 | case 23: 326 | { 327 | UNS32 pdo_map_val = 0x607a0020; 328 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1601, 1, 329 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 330 | } 331 | break; 332 | case 24: 333 | { 334 | UNS32 pdo_map_val = 0x60810020; 335 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1601, 2, 336 | 4, uint32, &pdo_map_val, config_servo_param_cb, 0); 337 | } 338 | break; 339 | case 25: 340 | { 341 | UNS8 pdo_map_cnt = 2; 342 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1601, 0, 343 | 1, uint8, &pdo_map_cnt, config_servo_param_cb, 0); 344 | } 345 | break; 346 | case 26: 347 | { // enable Slave's RPDO 348 | UNS32 TPDO_COBId = 0x00000300 + nodeId; 349 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1401, 1, 350 | 4, uint32, &TPDO_COBId, config_servo_param_cb, 0); 351 | } 352 | break; 353 | case 27: 354 | { 355 | UNS16 producer_heartbeat_time = PRODUCER_HEARTBEAT_TIME; 356 | writeNetworkDictCallBack(OD_Data, nodeId, 0x1017, 0, 357 | 2, uint16, &producer_heartbeat_time, config_servo_param_cb, 0); 358 | } 359 | break; 360 | case 28: 361 | rt_sem_release(&(conf->finish_sem)); 362 | break; 363 | default: 364 | break; 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /inc/data.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | #ifndef __data_h__ 24 | #define __data_h__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | /* declaration of CO_Data type let us include all necessary headers 31 | struct struct_CO_Data can then be defined later 32 | */ 33 | typedef struct struct_CO_Data CO_Data; 34 | 35 | #include "applicfg.h" 36 | #include "def.h" 37 | #include "can.h" 38 | #include "objdictdef.h" 39 | #include "objacces.h" 40 | #include "sdo.h" 41 | #include "pdo.h" 42 | #include "states.h" 43 | #include "lifegrd.h" 44 | #include "sync.h" 45 | #include "nmtSlave.h" 46 | #include "nmtMaster.h" 47 | #include "emcy.h" 48 | #ifdef CO_ENABLE_LSS 49 | #include "lss.h" 50 | #endif 51 | 52 | /** 53 | * @ingroup od 54 | * @brief This structure contains all necessary informations to define a CANOpen node 55 | */ 56 | struct struct_CO_Data { 57 | /* Object dictionary */ 58 | UNS8 *bDeviceNodeId; 59 | const indextable *objdict; 60 | s_PDO_status *PDO_status; 61 | TIMER_HANDLE *RxPDO_EventTimers; 62 | void (*RxPDO_EventTimers_Handler)(CO_Data*, UNS32); 63 | const quick_index *firstIndex; 64 | const quick_index *lastIndex; 65 | const UNS16 *ObjdictSize; 66 | const UNS8 *iam_a_slave; 67 | valueRangeTest_t valueRangeTest; 68 | 69 | /* SDO */ 70 | s_transfer transfers[SDO_MAX_SIMULTANEOUS_TRANSFERS]; 71 | /* s_sdo_parameter *sdo_parameters; */ 72 | 73 | /* State machine */ 74 | e_nodeState nodeState; 75 | s_state_communication CurrentCommunicationState; 76 | initialisation_t initialisation; 77 | preOperational_t preOperational; 78 | operational_t operational; 79 | stopped_t stopped; 80 | void (*NMT_Slave_Node_Reset_Callback)(CO_Data*); 81 | void (*NMT_Slave_Communications_Reset_Callback)(CO_Data*); 82 | 83 | /* NMT-heartbeat */ 84 | UNS8 *ConsumerHeartbeatCount; 85 | UNS32 *ConsumerHeartbeatEntries; 86 | TIMER_HANDLE *ConsumerHeartBeatTimers; 87 | UNS16 *ProducerHeartBeatTime; 88 | TIMER_HANDLE ProducerHeartBeatTimer; 89 | heartbeatError_t heartbeatError; 90 | e_nodeState NMTable[NMT_MAX_NODE_ID]; 91 | 92 | /* NMT-nodeguarding */ 93 | TIMER_HANDLE GuardTimeTimer; 94 | TIMER_HANDLE LifeTimeTimer; 95 | nodeguardError_t nodeguardError; 96 | UNS16 *GuardTime; 97 | UNS8 *LifeTimeFactor; 98 | UNS8 nodeGuardStatus[NMT_MAX_NODE_ID]; 99 | 100 | /* SYNC */ 101 | TIMER_HANDLE syncTimer; 102 | UNS32 *COB_ID_Sync; 103 | UNS32 *Sync_Cycle_Period; 104 | /*UNS32 *Sync_window_length;;*/ 105 | post_sync_t post_sync; 106 | post_TPDO_t post_TPDO; 107 | post_SlaveBootup_t post_SlaveBootup; 108 | post_SlaveStateChange_t post_SlaveStateChange; 109 | 110 | /* General */ 111 | UNS8 toggle; 112 | CAN_PORT canHandle; 113 | scanIndexOD_t scanIndexOD; 114 | storeODSubIndex_t storeODSubIndex; 115 | 116 | /* DCF concise */ 117 | const indextable* dcf_odentry; 118 | UNS8* dcf_cursor; 119 | UNS32 dcf_entries_count; 120 | UNS8 dcf_status; 121 | UNS32 dcf_size; 122 | UNS8* dcf_data; 123 | 124 | /* EMCY */ 125 | e_errorState error_state; 126 | UNS8 error_history_size; 127 | UNS8* error_number; 128 | UNS32* error_first_element; 129 | UNS8* error_register; 130 | UNS32* error_cobid; 131 | s_errors error_data[EMCY_MAX_ERRORS]; 132 | post_emcy_t post_emcy; 133 | 134 | #ifdef CO_ENABLE_LSS 135 | /* LSS */ 136 | lss_transfer_t lss_transfer; 137 | lss_StoreConfiguration_t lss_StoreConfiguration; 138 | #endif 139 | }; 140 | 141 | #define NMTable_Initializer Unknown_state, 142 | #define nodeGuardStatus_Initializer 0x00, 143 | 144 | #ifdef SDO_DYNAMIC_BUFFER_ALLOCATION 145 | #define s_transfer_Initializer {\ 146 | 0, /* CliServNbr */\ 147 | 0, /* wohami */\ 148 | SDO_RESET, /* state */\ 149 | 0, /* toggle */\ 150 | 0, /* abortCode */\ 151 | 0, /* index */\ 152 | 0, /* subIndex */\ 153 | 0, /* count */\ 154 | 0, /* offset */\ 155 | {0}, /* data (static use, so that all the table is initialize at 0)*/\ 156 | NULL, /* dynamicData */ \ 157 | 0, /* dynamicDataSize */ \ 158 | 0, /* peerCRCsupport */\ 159 | 0, /* blksize */\ 160 | 0, /* ackseq */\ 161 | 0, /* objsize */\ 162 | 0, /* lastblockoffset */\ 163 | 0, /* seqno */\ 164 | 0, /* endfield */\ 165 | RXSTEP_INIT,/* rxstep */\ 166 | {0}, /* tmpData */\ 167 | 0, /* dataType */\ 168 | -1, /* timer */\ 169 | NULL /* Callback */\ 170 | }, 171 | #else 172 | #define s_transfer_Initializer {\ 173 | 0, /* CliServNbr */\ 174 | 0, /* wohami */\ 175 | SDO_RESET, /* state */\ 176 | 0, /* toggle */\ 177 | 0, /* abortCode */\ 178 | 0, /* index */\ 179 | 0, /* subIndex */\ 180 | 0, /* count */\ 181 | 0, /* offset */\ 182 | {0}, /* data (static use, so that all the table is initialize at 0)*/\ 183 | 0, /* peerCRCsupport */\ 184 | 0, /* blksize */\ 185 | 0, /* ackseq */\ 186 | 0, /* objsize */\ 187 | 0, /* lastblockoffset */\ 188 | 0, /* seqno */\ 189 | 0, /* endfield */\ 190 | RXSTEP_INIT,/* rxstep */\ 191 | {0}, /* tmpData */\ 192 | 0, /* */\ 193 | -1, /* */\ 194 | NULL /* */\ 195 | }, 196 | #endif //SDO_DYNAMIC_BUFFER_ALLOCATION 197 | 198 | #define ERROR_DATA_INITIALIZER \ 199 | {\ 200 | 0, /* errCode */\ 201 | 0, /* errRegMask */\ 202 | 0 /* active */\ 203 | }, 204 | 205 | #ifdef CO_ENABLE_LSS 206 | 207 | #ifdef CO_ENABLE_LSS_FS 208 | #define lss_fs_Initializer \ 209 | ,0, /* IDNumber */\ 210 | 128, /* BitChecked */\ 211 | 0, /* LSSSub */\ 212 | 0, /* LSSNext */\ 213 | 0, /* LSSPos */\ 214 | LSS_FS_RESET, /* FastScan_SM */\ 215 | -1, /* timerFS */\ 216 | {{0,0,0,0},{0,0,0,0}} /* lss_fs_transfer */ 217 | #else 218 | #define lss_fs_Initializer 219 | #endif 220 | 221 | #define lss_Initializer {\ 222 | LSS_RESET, /* state */\ 223 | 0, /* command */\ 224 | LSS_WAITING_MODE, /* mode */\ 225 | 0, /* dat1 */\ 226 | 0, /* dat2 */\ 227 | 0, /* NodeID */\ 228 | 0, /* addr_sel_match */\ 229 | 0, /* addr_ident_match */\ 230 | "none", /* BaudRate */\ 231 | 0, /* SwitchDelay */\ 232 | SDELAY_OFF, /* SwitchDelayState */\ 233 | NULL, /* canHandle_t */\ 234 | -1, /* TimerMSG */\ 235 | -1, /* TimerSDELAY */\ 236 | NULL, /* Callback */\ 237 | 0 /* LSSanswer */\ 238 | lss_fs_Initializer /*FastScan service initialization */\ 239 | },\ 240 | NULL /* _lss_StoreConfiguration*/ 241 | #else 242 | #define lss_Initializer 243 | #endif 244 | 245 | 246 | /* A macro to initialize the data in client app.*/ 247 | /* CO_Data structure */ 248 | #define CANOPEN_NODE_DATA_INITIALIZER(NODE_PREFIX) {\ 249 | /* Object dictionary*/\ 250 | & NODE_PREFIX ## _bDeviceNodeId, /* bDeviceNodeId */\ 251 | NODE_PREFIX ## _objdict, /* objdict */\ 252 | NODE_PREFIX ## _PDO_status, /* PDO_status */\ 253 | NULL, /* RxPDO_EventTimers */\ 254 | _RxPDO_EventTimers_Handler, /* RxPDO_EventTimers_Handler */\ 255 | & NODE_PREFIX ## _firstIndex, /* firstIndex */\ 256 | & NODE_PREFIX ## _lastIndex, /* lastIndex */\ 257 | & NODE_PREFIX ## _ObjdictSize, /* ObjdictSize */\ 258 | & NODE_PREFIX ## _iam_a_slave, /* iam_a_slave */\ 259 | NODE_PREFIX ## _valueRangeTest, /* valueRangeTest */\ 260 | \ 261 | /* SDO, structure s_transfer */\ 262 | {\ 263 | REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERS_TIMES(s_transfer_Initializer)\ 264 | },\ 265 | \ 266 | /* State machine*/\ 267 | Unknown_state, /* nodeState */\ 268 | /* structure s_state_communication */\ 269 | {\ 270 | 0, /* csBoot_Up */\ 271 | 0, /* csSDO */\ 272 | 0, /* csEmergency */\ 273 | 0, /* csSYNC */\ 274 | 0, /* csHeartbeat */\ 275 | 0, /* csPDO */\ 276 | 0 /* csLSS */\ 277 | },\ 278 | _initialisation, /* initialisation */\ 279 | _preOperational, /* preOperational */\ 280 | _operational, /* operational */\ 281 | _stopped, /* stopped */\ 282 | NULL, /* NMT node reset callback */\ 283 | NULL, /* NMT communications reset callback */\ 284 | \ 285 | /* NMT-heartbeat */\ 286 | & NODE_PREFIX ## _highestSubIndex_obj1016, /* ConsumerHeartbeatCount */\ 287 | NODE_PREFIX ## _obj1016, /* ConsumerHeartbeatEntries */\ 288 | NODE_PREFIX ## _heartBeatTimers, /* ConsumerHeartBeatTimers */\ 289 | & NODE_PREFIX ## _obj1017, /* ProducerHeartBeatTime */\ 290 | TIMER_NONE, /* ProducerHeartBeatTimer */\ 291 | _heartbeatError, /* heartbeatError */\ 292 | \ 293 | {REPEAT_NMT_MAX_NODE_ID_TIMES(NMTable_Initializer)},\ 294 | /* is well initialized at "Unknown_state". Is it ok ? (FD)*/\ 295 | \ 296 | /* NMT-nodeguarding */\ 297 | TIMER_NONE, /* GuardTimeTimer */\ 298 | TIMER_NONE, /* LifeTimeTimer */\ 299 | _nodeguardError, /* nodeguardError */\ 300 | & NODE_PREFIX ## _obj100C, /* GuardTime */\ 301 | & NODE_PREFIX ## _obj100D, /* LifeTimeFactor */\ 302 | {REPEAT_NMT_MAX_NODE_ID_TIMES(nodeGuardStatus_Initializer)},\ 303 | \ 304 | /* SYNC */\ 305 | TIMER_NONE, /* syncTimer */\ 306 | & NODE_PREFIX ## _obj1005, /* COB_ID_Sync */\ 307 | & NODE_PREFIX ## _obj1006, /* Sync_Cycle_Period */\ 308 | /*& NODE_PREFIX ## _obj1007, */ /* Sync_window_length */\ 309 | _post_sync, /* post_sync */\ 310 | _post_TPDO, /* post_TPDO */\ 311 | _post_SlaveBootup, /* post_SlaveBootup */\ 312 | _post_SlaveStateChange, /* post_SlaveStateChange */\ 313 | \ 314 | /* General */\ 315 | 0, /* toggle */\ 316 | NULL, /* canSend */\ 317 | NODE_PREFIX ## _scanIndexOD, /* scanIndexOD */\ 318 | _storeODSubIndex, /* storeODSubIndex */\ 319 | /* DCF concise */\ 320 | NULL, /*dcf_odentry*/\ 321 | NULL, /*dcf_cursor*/\ 322 | 1, /*dcf_entries_count*/\ 323 | 0, /* dcf_status*/\ 324 | 0, /* dcf_size */\ 325 | NULL, /* dcf_data */\ 326 | \ 327 | /* EMCY */\ 328 | Error_free, /* error_state */\ 329 | sizeof(NODE_PREFIX ## _obj1003) / sizeof(NODE_PREFIX ## _obj1003[0]), /* error_history_size */\ 330 | & NODE_PREFIX ## _highestSubIndex_obj1003, /* error_number */\ 331 | & NODE_PREFIX ## _obj1003[0], /* error_first_element */\ 332 | & NODE_PREFIX ## _obj1001, /* error_register */\ 333 | & NODE_PREFIX ## _obj1014, /* error_cobid */\ 334 | /* error_data: structure s_errors */\ 335 | {\ 336 | REPEAT_EMCY_MAX_ERRORS_TIMES(ERROR_DATA_INITIALIZER)\ 337 | },\ 338 | _post_emcy, /* post_emcy */\ 339 | /* LSS */\ 340 | lss_Initializer\ 341 | } 342 | 343 | #ifdef __cplusplus 344 | }; 345 | #endif 346 | 347 | #endif /* __data_h__ */ 348 | 349 | 350 | -------------------------------------------------------------------------------- /src/lifegrd.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen 3 | Stack. 4 | 5 | Copyright (C): Edouard TISSERANT and Francis DUPIN 6 | 7 | See COPYING file for copyrights details. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22 | USA 23 | */ 24 | 25 | /*! 26 | ** @file lifegrd.c 27 | ** @author Edouard TISSERANT 28 | ** @date Mon Jun 4 17:19:24 2007 29 | ** 30 | ** @brief 31 | ** 32 | ** 33 | */ 34 | 35 | #include 36 | #include "lifegrd.h" 37 | #include "canfestival.h" 38 | #include "dcf.h" 39 | #include "sysdep.h" 40 | 41 | 42 | void ConsumerHeartbeatAlarm(CO_Data* d, UNS32 id); 43 | void ProducerHeartbeatAlarm(CO_Data* d, UNS32 id); 44 | UNS32 OnHearbeatProducerUpdate(CO_Data* d, const indextable * unused_indextable, UNS8 unused_bSubindex); 45 | 46 | void GuardTimeAlarm(CO_Data* d, UNS32 id); 47 | UNS32 OnNodeGuardUpdate(CO_Data* d, const indextable * unused_indextable, UNS8 unused_bSubindex); 48 | 49 | 50 | e_nodeState getNodeState (CO_Data* d, UNS8 nodeId) 51 | { 52 | e_nodeState networkNodeState = Unknown_state; 53 | #if NMT_MAX_NODE_ID>0 54 | if(nodeId < NMT_MAX_NODE_ID) 55 | networkNodeState = d->NMTable[nodeId]; 56 | #endif 57 | return networkNodeState; 58 | } 59 | 60 | /*! 61 | ** The Consumer Timer Callback 62 | ** 63 | ** @param d 64 | ** @param id 65 | * @ingroup heartbeato 66 | **/ 67 | void ConsumerHeartbeatAlarm(CO_Data* d, UNS32 id) 68 | { 69 | UNS8 nodeId = (UNS8)(((d->ConsumerHeartbeatEntries[id]) & (UNS32)0x00FF0000) >> (UNS8)16); 70 | /*MSG_WAR(0x00, "ConsumerHearbeatAlarm", 0x00);*/ 71 | 72 | /* timer have been notified and is now free (non periodic)*/ 73 | /* -> avoid deleting re-assigned timer if message is received too late*/ 74 | d->ConsumerHeartBeatTimers[id]=TIMER_NONE; 75 | 76 | /* set node state */ 77 | d->NMTable[nodeId] = Disconnected; 78 | /*! call heartbeat error with NodeId */ 79 | (*d->heartbeatError)(d, nodeId); 80 | } 81 | 82 | void proceedNODE_GUARD(CO_Data* d, Message* m ) 83 | { 84 | UNS8 nodeId = (UNS8) GET_NODE_ID((*m)); 85 | 86 | if((m->rtr == 1) ) 87 | /*! 88 | ** Notice that only the master can have sent this 89 | ** node guarding request 90 | */ 91 | { 92 | /*! 93 | ** Receiving a NMT NodeGuarding (request of the state by the 94 | ** master) 95 | ** Only answer to the NMT NodeGuarding request, the master is 96 | ** not checked (not implemented) 97 | */ 98 | if (nodeId == *d->bDeviceNodeId ) 99 | { 100 | Message msg; 101 | UNS16 tmp = *d->bDeviceNodeId + 0x700; 102 | msg.cob_id = UNS16_LE(tmp); 103 | msg.len = (UNS8)0x01; 104 | msg.rtr = 0; 105 | msg.data[0] = d->nodeState; 106 | if (d->toggle) 107 | { 108 | msg.data[0] |= 0x80 ; 109 | d->toggle = 0 ; 110 | } 111 | else 112 | d->toggle = 1 ; 113 | /* send the nodeguard response. */ 114 | MSG_WAR(0x3130, "Sending NMT Nodeguard to master, state: ", d->nodeState); 115 | canSend(d->canHandle,&msg ); 116 | } 117 | 118 | }else{ /* Not a request CAN */ 119 | /* The state is stored on 7 bit */ 120 | e_nodeState newNodeState = (e_nodeState) ((*m).data[0] & 0x7F); 121 | 122 | MSG_WAR(0x3110, "Received NMT nodeId : ", nodeId); 123 | 124 | /*! 125 | ** Record node response for node guarding service 126 | */ 127 | d->nodeGuardStatus[nodeId] = *d->LifeTimeFactor; 128 | 129 | if (d->NMTable[nodeId] != newNodeState) 130 | { 131 | (*d->post_SlaveStateChange)(d, nodeId, newNodeState); 132 | /* the slave's state receievd is stored in the NMTable */ 133 | d->NMTable[nodeId] = newNodeState; 134 | } 135 | 136 | /* Boot-Up frame reception */ 137 | if ( d->NMTable[nodeId] == Initialisation) 138 | { 139 | /* 140 | ** The device send the boot-up message (Initialisation) 141 | ** to indicate the master that it is entered in 142 | ** pre_operational mode 143 | */ 144 | MSG_WAR(0x3100, "The NMT is a bootup from node : ", nodeId); 145 | /* call post SlaveBootup with NodeId */ 146 | (*d->post_SlaveBootup)(d, nodeId); 147 | } 148 | 149 | if( d->NMTable[nodeId] != Unknown_state ) { 150 | UNS8 index, ConsumerHeartBeat_nodeId ; 151 | for( index = (UNS8)0x00; index < *d->ConsumerHeartbeatCount; index++ ) 152 | { 153 | ConsumerHeartBeat_nodeId = (UNS8)( ((d->ConsumerHeartbeatEntries[index]) & (UNS32)0x00FF0000) >> (UNS8)16 ); 154 | if ( nodeId == ConsumerHeartBeat_nodeId ) 155 | { 156 | TIMEVAL time = ( (d->ConsumerHeartbeatEntries[index]) & (UNS32)0x0000FFFF ) ; 157 | /* Renew alarm for next heartbeat. */ 158 | DelAlarm(d->ConsumerHeartBeatTimers[index]); 159 | d->ConsumerHeartBeatTimers[index] = SetAlarm(d, index, &ConsumerHeartbeatAlarm, MS_TO_TIMEVAL(time), 0); 160 | } 161 | } 162 | } 163 | } 164 | } 165 | 166 | /*! The Producer Timer Callback 167 | ** 168 | ** 169 | ** @param d 170 | ** @param id 171 | * @ingroup heartbeato 172 | **/ 173 | void ProducerHeartbeatAlarm(CO_Data* d, UNS32 id) 174 | { 175 | if(*d->ProducerHeartBeatTime) 176 | { 177 | Message msg; 178 | /* Time expired, the heartbeat must be sent immediately 179 | ** generate the correct node-id: this is done by the offset 1792 180 | ** (decimal) and additionaly 181 | ** the node-id of this device. 182 | */ 183 | UNS16 tmp = *d->bDeviceNodeId + 0x700; 184 | msg.cob_id = UNS16_LE(tmp); 185 | msg.len = (UNS8)0x01; 186 | msg.rtr = 0; 187 | msg.data[0] = d->nodeState; /* No toggle for heartbeat !*/ 188 | /* send the heartbeat */ 189 | MSG_WAR(0x3130, "Producing heartbeat: ", d->nodeState); 190 | canSend(d->canHandle,&msg ); 191 | 192 | }else{ 193 | d->ProducerHeartBeatTimer = DelAlarm(d->ProducerHeartBeatTimer); 194 | } 195 | } 196 | 197 | /** 198 | * @brief The guardTime - Timer Callback. 199 | * 200 | * This function is called every GuardTime (OD 0x100C) ms
201 | * On every call, a NodeGuard-Request is sent to all nodes which have a 202 | * node-state not equal to "Unknown" (according to NMTable). If the node has 203 | * not responded within the lifetime, the nodeguardError function is called and 204 | * the status of this node is set to "Disconnected" 205 | * 206 | * @param d Pointer on a CAN object data structure 207 | * @param id 208 | * @ingroup nodeguardo 209 | */ 210 | void GuardTimeAlarm(CO_Data* d, UNS32 id) 211 | { 212 | if (*d->GuardTime) { 213 | UNS8 i; 214 | 215 | MSG_WAR(0x00, "Producing nodeguard-requests: ", 0); 216 | 217 | for (i = 0; i < NMT_MAX_NODE_ID; i++) { 218 | /** Send node guard request to all nodes except this node, if the 219 | * node state is not "Unknown_state" 220 | */ 221 | if (d->NMTable[i] != Unknown_state && i != *d->bDeviceNodeId) { 222 | 223 | /** Check if the node has confirmed the guarding request within 224 | * the LifeTime (GuardTime x LifeTimeFactor) 225 | */ 226 | if (d->nodeGuardStatus[i] <= 0) { 227 | 228 | MSG_WAR(0x00, "Node Guard alarm for nodeId : ", i); 229 | 230 | // Call error-callback function 231 | if (*d->nodeguardError) { 232 | (*d->nodeguardError)(d, i); 233 | } 234 | 235 | // Mark node as disconnected 236 | d->NMTable[i] = Disconnected; 237 | 238 | } 239 | 240 | d->nodeGuardStatus[i]--; 241 | 242 | masterSendNMTnodeguard(d, i); 243 | 244 | } 245 | } 246 | } else { 247 | d->GuardTimeTimer = DelAlarm(d->GuardTimeTimer); 248 | } 249 | 250 | 251 | 252 | } 253 | 254 | /** 255 | * This function is called, if index 0x100C or 0x100D is updated to 256 | * restart the node-guarding service with the new parameters 257 | * 258 | * @param d Pointer on a CAN object data structure 259 | * @param unused_indextable 260 | * @param unused_bSubindex 261 | * @ingroup nodeguardo 262 | */ 263 | UNS32 OnNodeGuardUpdate(CO_Data* d, const indextable * unused_indextable, UNS8 unused_bSubindex) 264 | { 265 | nodeguardStop(d); 266 | nodeguardInit(d); 267 | return 0; 268 | } 269 | 270 | 271 | /*! This is called when Index 0x1017 is updated. 272 | ** 273 | ** 274 | ** @param d 275 | ** @param unused_indextable 276 | ** @param unused_bSubindex 277 | ** 278 | ** @return 279 | * @ingroup heartbeato 280 | **/ 281 | UNS32 OnHeartbeatProducerUpdate(CO_Data* d, const indextable * unused_indextable, UNS8 unused_bSubindex) 282 | { 283 | d->ProducerHeartBeatTimer = DelAlarm(d->ProducerHeartBeatTimer); 284 | if ( *d->ProducerHeartBeatTime ) 285 | { 286 | TIMEVAL time = *d->ProducerHeartBeatTime; 287 | d->ProducerHeartBeatTimer = SetAlarm(d, 0, &ProducerHeartbeatAlarm, 0, MS_TO_TIMEVAL(time)); 288 | } 289 | return 0; 290 | } 291 | 292 | void heartbeatInit(CO_Data* d) 293 | { 294 | 295 | UNS8 index; /* Index to scan the table of heartbeat consumers */ 296 | RegisterSetODentryCallBack(d, 0x1017, 0x00, &OnHeartbeatProducerUpdate); 297 | 298 | d->toggle = 0; 299 | 300 | for( index = (UNS8)0x00; index < *d->ConsumerHeartbeatCount; index++ ) 301 | { 302 | TIMEVAL time = (UNS16) ( (d->ConsumerHeartbeatEntries[index]) & (UNS32)0x0000FFFF ) ; 303 | if ( time ) 304 | { 305 | d->ConsumerHeartBeatTimers[index] = SetAlarm(d, index, &ConsumerHeartbeatAlarm, MS_TO_TIMEVAL(time), 0); 306 | } 307 | } 308 | 309 | if ( *d->ProducerHeartBeatTime ) 310 | { 311 | TIMEVAL time = *d->ProducerHeartBeatTime; 312 | d->ProducerHeartBeatTimer = SetAlarm(d, 0, &ProducerHeartbeatAlarm, MS_TO_TIMEVAL(time), MS_TO_TIMEVAL(time)); 313 | } 314 | } 315 | 316 | 317 | void nodeguardInit(CO_Data* d) 318 | { 319 | 320 | RegisterSetODentryCallBack(d, 0x100C, 0x00, &OnNodeGuardUpdate); 321 | RegisterSetODentryCallBack(d, 0x100D, 0x00, &OnNodeGuardUpdate); 322 | 323 | if (*d->GuardTime && *d->LifeTimeFactor) { 324 | UNS8 i; 325 | 326 | TIMEVAL time = *d->GuardTime; 327 | d->GuardTimeTimer = SetAlarm(d, 0, &GuardTimeAlarm, MS_TO_TIMEVAL(time), MS_TO_TIMEVAL(time)); 328 | MSG_WAR(0x0, "GuardTime: ", time); 329 | 330 | for (i = 0; i < NMT_MAX_NODE_ID; i++) { 331 | /** Set initial value for the nodes */ 332 | if (d->NMTable[i] != Unknown_state && i != *d->bDeviceNodeId) { 333 | d->nodeGuardStatus[i] = *d->LifeTimeFactor; 334 | } 335 | } 336 | 337 | MSG_WAR(0x0, "Timer for node-guarding startet", 0); 338 | } 339 | 340 | } 341 | 342 | void heartbeatStop(CO_Data* d) 343 | { 344 | UNS8 index; 345 | for( index = (UNS8)0x00; index < *d->ConsumerHeartbeatCount; index++ ) 346 | { 347 | d->ConsumerHeartBeatTimers[index] = DelAlarm(d->ConsumerHeartBeatTimers[index]); 348 | } 349 | 350 | d->ProducerHeartBeatTimer = DelAlarm(d->ProducerHeartBeatTimer); 351 | } 352 | 353 | void nodeguardStop(CO_Data* d) 354 | { 355 | d->GuardTimeTimer = DelAlarm(d->GuardTimeTimer); 356 | } 357 | 358 | 359 | void lifeGuardInit(CO_Data* d) 360 | { 361 | heartbeatInit(d); 362 | nodeguardInit(d); 363 | } 364 | 365 | 366 | void lifeGuardStop(CO_Data* d) 367 | { 368 | heartbeatStop(d); 369 | nodeguardStop(d); 370 | } 371 | 372 | 373 | void _heartbeatError(CO_Data* d, UNS8 heartbeatID){} 374 | void _post_SlaveBootup(CO_Data* d, UNS8 SlaveID){} 375 | void _post_SlaveStateChange(CO_Data* d, UNS8 nodeId, e_nodeState newNodeState){} 376 | void _nodeguardError(CO_Data* d, UNS8 id){} 377 | 378 | -------------------------------------------------------------------------------- /inc/objacces.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @file 24 | * @brief Responsible for accessing the object dictionary. 25 | * 26 | * This file contains functions for accessing the object dictionary and 27 | * variables that are contained by the object dictionary. 28 | * Accessing the object dictionary contains setting local variables 29 | * as PDOs and accessing (read/write) all entries of the object dictionary 30 | * @warning Only the basic entries of an object dictionary are included 31 | * at the moment. 32 | */ 33 | 34 | /** @defgroup od Object Dictionary Management 35 | * @brief The Object Dictionary is the heart of each CANopen device containing all communication and application objects. 36 | * @ingroup userapi 37 | */ 38 | 39 | #ifndef __objacces_h__ 40 | #define __objacces_h__ 41 | 42 | #include 43 | 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | 49 | 50 | typedef UNS32 (*valueRangeTest_t)(UNS8 typeValue, void *Value); 51 | typedef UNS32 (* storeODSubIndex_t)(CO_Data* d, UNS16 wIndex, UNS8 bSubindex); 52 | UNS32 _storeODSubIndex (CO_Data* d, UNS16 wIndex, UNS8 bSubindex); 53 | 54 | /** 55 | * @brief Print MSG_WAR (s) if error to the access to the object dictionary occurs. 56 | * 57 | * You must uncomment the lines in the file objaccess.c :\n 58 | * //\#define DEBUG_CAN\n 59 | * //\#define DEBUG_WAR_CONSOLE_ON\n 60 | * //\#define DEBUG_ERR_CONSOLE_ON\n\n 61 | * Beware that sometimes, we force the sizeDataDict or sizeDataGiven to 0, when we wants to use 62 | * this function but we do not have the access to the right value. One example is 63 | * getSDOerror(). So do not take attention to these variables if they are null. 64 | * @param index 65 | * @param subIndex 66 | * @param sizeDataDict Size of the data defined in the dictionary 67 | * @param sizeDataGiven Size data given by the user. 68 | * @param code error code to print. (SDO abort code. See file def.h) 69 | * @return 70 | */ 71 | UNS8 accessDictionaryError(UNS16 index, UNS8 subIndex, 72 | UNS32 sizeDataDict, UNS32 sizeDataGiven, UNS32 code); 73 | 74 | 75 | /* _getODentry() Reads an entry from the object dictionary.\n 76 | * 77 | * use getODentry() macro to read from object and endianize 78 | * use readLocalDict() macro to read from object and not endianize 79 | * 80 | * @code 81 | * // Example usage: 82 | * UNS8 *pbData; 83 | * UNS8 length; 84 | * UNS32 returnValue; 85 | * 86 | * returnValue = getODentry( (UNS16)0x100B, (UNS8)1, 87 | * (void * *)&pbData, (UNS8 *)&length ); 88 | * if( returnValue != SUCCESSFUL ) 89 | * { 90 | * // error handling 91 | * } 92 | * @endcode 93 | * @param *d Pointer to a CAN object data structure 94 | * @param wIndex The index in the object dictionary where you want to read 95 | * an entry 96 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 97 | * used to tell you how many valid entries you can find 98 | * in this index. Look at the canopen standard for further 99 | * information 100 | * @param *pDestData Pointer to the pointer which points to the variable where 101 | * the value of this object dictionary entry should be copied 102 | * @param pExpectedSize This function writes the size of the copied value (in Byte) 103 | * into this variable. 104 | * @param *pDataType Pointer to the type of the data. See objdictdef.h 105 | * @param CheckAccess if other than 0, do not read if the data is Write Only 106 | * [Not used today. Put always 0]. 107 | * @param Endianize When not 0, data is endianized into network byte order 108 | * when 0, data is not endianized and copied in machine native 109 | * endianness 110 | * @return 111 | * - OD_SUCCESSFUL is returned upon success. 112 | * - SDO abort code is returned if error occurs . (See file def.h) 113 | */ 114 | UNS32 _getODentry( CO_Data* d, 115 | UNS16 wIndex, 116 | UNS8 bSubindex, 117 | void * pDestData, 118 | UNS32 * pExpectedSize, 119 | UNS8 * pDataType, 120 | UNS8 checkAccess, 121 | UNS8 endianize); 122 | 123 | /** 124 | * @ingroup od 125 | * @brief getODentry() to read from object and endianize 126 | * @param OD Pointer to a CAN object data structure 127 | * @param wIndex The index in the object dictionary where you want to read 128 | * an entry 129 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 130 | * used to tell you how many valid entries you can find 131 | * in this index. Look at the canopen standard for further 132 | * information 133 | * @param *pDestData Pointer to the pointer which points to the variable where 134 | * the value of this object dictionary entry should be copied 135 | * @param pExpectedSize This function writes the size of the copied value (in Byte) 136 | * into this variable. 137 | * @param *pDataType Pointer to the type of the data. See objdictdef.h 138 | * @param checkAccess Flag that indicate if a check rights must be perfomed (0 : no , other than 0 : yes) 139 | * @return 140 | * - OD_SUCCESSFUL is returned upon success. 141 | * - SDO abort code is returned if error occurs . (See file def.h) 142 | */ 143 | #define getODentry( OD, wIndex, bSubindex, pDestData, pExpectedSize, \ 144 | pDataType, checkAccess) \ 145 | _getODentry( OD, wIndex, bSubindex, pDestData, pExpectedSize, \ 146 | pDataType, checkAccess, 1) 147 | 148 | /** 149 | * @ingroup od 150 | * @brief readLocalDict() reads an entry from the object dictionary, but in 151 | * contrast to getODentry(), readLocalDict() doesn't endianize entry and reads 152 | * entry in machine native endianness. 153 | * @param OD Pointer to a CAN object data structure 154 | * @param wIndex The index in the object dictionary where you want to read 155 | * an entry 156 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 157 | * used to tell you how many valid entries you can find 158 | * in this index. Look at the canopen standard for further 159 | * information 160 | * @param *pDestData Pointer to the pointer which points to the variable where 161 | * the value of this object dictionary entry should be copied 162 | * @param pExpectedSize This function writes the size of the copied value (in Byte) 163 | * into this variable. 164 | * @param *pDataType Pointer to the type of the data. See objdictdef.h 165 | * @param checkAccess if other than 0, do not read if the data is Write Only 166 | * [Not used today. Put always 0]. 167 | * @return 168 | * - OD_SUCCESSFUL is returned upon success. 169 | * - SDO abort code is returned if error occurs . (See file def.h) 170 | */ 171 | #define readLocalDict( OD, wIndex, bSubindex, pDestData, pExpectedSize, \ 172 | pDataType, checkAccess) \ 173 | _getODentry( OD, wIndex, bSubindex, pDestData, pExpectedSize, \ 174 | pDataType, checkAccess, 0) 175 | 176 | /* By this function you can write an entry into the object dictionary 177 | * @param *d Pointer to a CAN object data structure 178 | * @param wIndex The index in the object dictionary where you want to write 179 | * an entry 180 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 181 | * used to tell you how many valid entries you can find 182 | * in this index. Look at the canopen standard for further 183 | * information 184 | * @param *pSourceData Pointer to the variable that holds the value that should 185 | * be copied into the object dictionary 186 | * @param *pExpectedSize The size of the value (in Byte). 187 | * @param checkAccess Flag that indicate if a check rights must be perfomed (0 : no , other than 0 : yes) 188 | * @return 189 | * - OD_SUCCESSFUL is returned upon success. 190 | * - SDO abort code is returned if error occurs . (See file def.h) 191 | */ 192 | UNS32 _setODentry( CO_Data* d, 193 | UNS16 wIndex, 194 | UNS8 bSubindex, 195 | void * pSourceData, 196 | UNS32 * pExpectedSize, 197 | UNS8 checkAccess, 198 | UNS8 endianize); 199 | 200 | /** 201 | * @ingroup od 202 | * @brief setODentry converts SourceData from network byte order to machine native 203 | * format, and writes that to OD. 204 | * @code 205 | * // Example usage: 206 | * UNS8 B; 207 | * B = 0xFF; // set transmission type 208 | * 209 | * retcode = setODentry( (UNS16)0x1800, (UNS8)2, &B, sizeof(UNS8), 1 ); 210 | * @endcode 211 | * @param d Pointer to a CAN object data structure 212 | * @param wIndex The index in the object dictionary where you want to write 213 | * an entry 214 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 215 | * used to tell you how many valid entries you can find 216 | * in this index. Look at the canopen standard for further 217 | * information 218 | * @param *pSourceData Pointer to the variable that holds the value that should 219 | * be copied into the object dictionary 220 | * @param *pExpectedSize The size of the value (in Byte). 221 | * @param checkAccess Flag that indicate if a check rights must be perfomed (0 : no , other than 0 : yes) 222 | * @return 223 | * - OD_SUCCESSFUL is returned upon success. 224 | * - SDO abort code is returned if error occurs . (See file def.h) 225 | */ 226 | #define setODentry( d, wIndex, bSubindex, pSourceData, pExpectedSize, \ 227 | checkAccess) \ 228 | _setODentry( d, wIndex, bSubindex, pSourceData, pExpectedSize, \ 229 | checkAccess, 1) 230 | 231 | /** @fn UNS32 writeLocalDict(d, wIndex, bSubindex, pSourceData, pExpectedSize, checkAccess) 232 | * @ingroup od 233 | * @hideinitializer 234 | * @brief Writes machine native SourceData to OD. 235 | * @param d Pointer to a CAN object data structure 236 | * @param wIndex The index in the object dictionary where you want to write 237 | * an entry 238 | * @param bSubindex The subindex of the Index. e.g. mostly subindex 0 is 239 | * used to tell you how many valid entries you can find 240 | * in this index. Look at the canopen standard for further 241 | * information 242 | * @param *pSourceData Pointer to the variable that holds the value that should 243 | * be copied into the object dictionary 244 | * @param *pExpectedSize The size of the value (in Byte). 245 | * @param checkAccess Flag that indicate if a check rights must be perfomed (0 : no , other than 0 : yes) 246 | * @return 247 | * - OD_SUCCESSFUL is returned upon success. 248 | * - SDO abort code is returned if error occurs . (See file def.h) 249 | * \n\n 250 | * @code 251 | * // Example usage: 252 | * UNS8 B; 253 | * B = 0xFF; // set transmission type 254 | * 255 | * retcode = writeLocalDict( (UNS16)0x1800, (UNS8)2, &B, sizeof(UNS8), 1 ); 256 | * @endcode 257 | */ 258 | #define writeLocalDict( d, wIndex, bSubindex, pSourceData, pExpectedSize, checkAccess) \ 259 | _setODentry( d, wIndex, bSubindex, pSourceData, pExpectedSize, checkAccess, 0) 260 | 261 | UNS32 RegisterSetODentryCallBack(CO_Data* d, UNS16 wIndex, UNS8 bSubindex, ODCallback_t Callback); 262 | 263 | #ifdef __cplusplus 264 | } 265 | #endif 266 | 267 | #endif /* __objacces_h__ */ 268 | -------------------------------------------------------------------------------- /src/states.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | /*! 23 | ** @file states.c 24 | ** @author Edouard TISSERANT and Francis DUPIN 25 | ** @date Tue Jun 5 09:32:32 2007 26 | ** 27 | ** @brief 28 | ** 29 | ** 30 | */ 31 | 32 | #include "data.h" 33 | #include "sysdep.h" 34 | 35 | /** Prototypes for internals functions */ 36 | /*! 37 | ** 38 | ** 39 | ** @param d 40 | ** @param newCommunicationState 41 | **/ 42 | void switchCommunicationState(CO_Data* d, 43 | s_state_communication *newCommunicationState); 44 | 45 | /*! 46 | ** 47 | ** 48 | ** @param d 49 | ** 50 | ** @return 51 | **/ 52 | e_nodeState getState(CO_Data* d) 53 | { 54 | return d->nodeState; 55 | } 56 | 57 | /*! 58 | ** 59 | ** 60 | ** @param d 61 | ** @param m 62 | **/ 63 | void canDispatch(CO_Data* d, Message *m) 64 | { 65 | UNS16 cob_id = UNS16_LE(m->cob_id); 66 | switch(cob_id >> 7) 67 | { 68 | case SYNC: /* can be a SYNC or a EMCY message */ 69 | if(cob_id == 0x080) /* SYNC */ 70 | { 71 | if(d->CurrentCommunicationState.csSYNC) 72 | proceedSYNC(d); 73 | } else /* EMCY */ 74 | if(d->CurrentCommunicationState.csEmergency) 75 | proceedEMCY(d,m); 76 | break; 77 | /* case TIME_STAMP: */ 78 | case PDO1tx: 79 | case PDO1rx: 80 | case PDO2tx: 81 | case PDO2rx: 82 | case PDO3tx: 83 | case PDO3rx: 84 | case PDO4tx: 85 | case PDO4rx: 86 | if (d->CurrentCommunicationState.csPDO) 87 | proceedPDO(d,m); 88 | break; 89 | case SDOtx: 90 | case SDOrx: 91 | if (d->CurrentCommunicationState.csSDO) 92 | proceedSDO(d,m); 93 | break; 94 | case NODE_GUARD: 95 | if (d->CurrentCommunicationState.csLifeGuard) 96 | proceedNODE_GUARD(d,m); 97 | break; 98 | case NMT: 99 | if (*(d->iam_a_slave)) 100 | { 101 | proceedNMTstateChange(d,m); 102 | } 103 | break; 104 | #ifdef CO_ENABLE_LSS 105 | case LSS: 106 | if (!d->CurrentCommunicationState.csLSS)break; 107 | if ((*(d->iam_a_slave)) && cob_id==MLSS_ADRESS) 108 | { 109 | proceedLSS_Slave(d,m); 110 | } 111 | else if(!(*(d->iam_a_slave)) && cob_id==SLSS_ADRESS) 112 | { 113 | proceedLSS_Master(d,m); 114 | } 115 | break; 116 | #endif 117 | } 118 | } 119 | 120 | #define StartOrStop(CommType, FuncStart, FuncStop) \ 121 | if(newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 0){\ 122 | MSG_WAR(0x9999,#FuncStart, 9999);\ 123 | d->CurrentCommunicationState.CommType = 1;\ 124 | FuncStart;\ 125 | }else if(!newCommunicationState->CommType && d->CurrentCommunicationState.CommType == 1){\ 126 | MSG_WAR(0x9999,#FuncStop, 9999);\ 127 | d->CurrentCommunicationState.CommType = 0;\ 128 | FuncStop;\ 129 | } 130 | #define None 131 | 132 | /*! 133 | ** 134 | ** 135 | ** @param d 136 | ** @param newCommunicationState 137 | **/ 138 | void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState) 139 | { 140 | #ifdef CO_ENABLE_LSS 141 | StartOrStop(csLSS, startLSS(d), stopLSS(d)) 142 | #endif 143 | StartOrStop(csSDO, None, resetSDO(d)) 144 | StartOrStop(csSYNC, startSYNC(d), stopSYNC(d)) 145 | StartOrStop(csLifeGuard, lifeGuardInit(d), lifeGuardStop(d)) 146 | StartOrStop(csEmergency, emergencyInit(d), emergencyStop(d)) 147 | StartOrStop(csPDO, PDOInit(d), PDOStop(d)) 148 | StartOrStop(csBoot_Up, None, slaveSendBootUp(d)) 149 | } 150 | 151 | /*! 152 | ** 153 | ** 154 | ** @param d 155 | ** @param newState 156 | ** 157 | ** @return 158 | **/ 159 | UNS8 setState(CO_Data* d, e_nodeState newState) 160 | { 161 | if(newState != d->nodeState){ 162 | switch( newState ){ 163 | case Initialisation: 164 | { 165 | s_state_communication newCommunicationState = {1, 0, 0, 0, 0, 0, 0}; 166 | d->nodeState = Initialisation; 167 | switchCommunicationState(d, &newCommunicationState); 168 | /* call user app init callback now. */ 169 | /* d->initialisation MUST NOT CALL SetState */ 170 | (*d->initialisation)(d); 171 | } 172 | 173 | /* Automatic transition - No break statement ! */ 174 | /* Transition from Initialisation to Pre_operational */ 175 | /* is automatic as defined in DS301. */ 176 | /* App don't have to call SetState(d, Pre_operational) */ 177 | 178 | case Pre_operational: 179 | { 180 | 181 | s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 0, 1}; 182 | d->nodeState = Pre_operational; 183 | switchCommunicationState(d, &newCommunicationState); 184 | (*d->preOperational)(d); 185 | } 186 | break; 187 | 188 | case Operational: 189 | if(d->nodeState == Initialisation) return 0xFF; 190 | { 191 | s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 1, 0}; 192 | d->nodeState = Operational; 193 | newState = Operational; 194 | switchCommunicationState(d, &newCommunicationState); 195 | (*d->operational)(d); 196 | } 197 | break; 198 | 199 | case Stopped: 200 | if(d->nodeState == Initialisation) return 0xFF; 201 | { 202 | s_state_communication newCommunicationState = {0, 0, 0, 0, 1, 0, 1}; 203 | d->nodeState = Stopped; 204 | newState = Stopped; 205 | switchCommunicationState(d, &newCommunicationState); 206 | (*d->stopped)(d); 207 | } 208 | break; 209 | default: 210 | return 0xFF; 211 | 212 | }/* end switch case */ 213 | 214 | } 215 | /* d->nodeState contains the final state */ 216 | /* may not be the requested state */ 217 | return d->nodeState; 218 | } 219 | 220 | /*! 221 | ** 222 | ** 223 | ** @param d 224 | ** 225 | ** @return 226 | **/ 227 | UNS8 getNodeId(CO_Data* d) 228 | { 229 | return *d->bDeviceNodeId; 230 | } 231 | 232 | /*! 233 | ** 234 | ** 235 | ** @param d 236 | ** @param nodeId 237 | **/ 238 | void setNodeId(CO_Data* d, UNS8 nodeId) 239 | { 240 | UNS16 offset = d->firstIndex->SDO_SVR; 241 | 242 | #ifdef CO_ENABLE_LSS 243 | d->lss_transfer.nodeID=nodeId; 244 | if(nodeId==0xFF){ 245 | *d->bDeviceNodeId = nodeId; 246 | return; 247 | } 248 | else 249 | #endif 250 | if(!(nodeId>0 && nodeId<=127)){ 251 | MSG_WAR(0x2D01, "Invalid NodeID",nodeId); 252 | return; 253 | } 254 | 255 | if(offset){ 256 | /* Adjust COB-ID Client->Server (rx) only id already set to default value or id not valid (id==0xFF)*/ 257 | if((*(UNS32*)d->objdict[offset].pSubindex[1].pObject == ((UNS32)0x600) + *d->bDeviceNodeId)||(*d->bDeviceNodeId==0xFF)){ 258 | /* cob_id_client = 0x600 + nodeId; */ 259 | *(UNS32*)d->objdict[offset].pSubindex[1].pObject = 0x600 + nodeId; 260 | } 261 | /* Adjust COB-ID Server -> Client (tx) only id already set to default value or id not valid (id==0xFF)*/ 262 | if((*(UNS32*)d->objdict[offset].pSubindex[2].pObject == ((UNS32)0x580) + *d->bDeviceNodeId)||(*d->bDeviceNodeId==0xFF)){ 263 | /* cob_id_server = 0x580 + nodeId; */ 264 | *(UNS32*)d->objdict[offset].pSubindex[2].pObject = 0x580 + nodeId; 265 | } 266 | } 267 | 268 | /* 269 | Initialize the server(s) SDO parameters 270 | Remember that only one SDO server is allowed, defined at index 0x1200 271 | 272 | Initialize the client(s) SDO parameters 273 | Nothing to initialize (no default values required by the DS 401) 274 | Initialize the receive PDO communication parameters. Only for 0x1400 to 0x1403 275 | */ 276 | { 277 | UNS8 i = 0; 278 | UNS16 offset = d->firstIndex->PDO_RCV; 279 | UNS16 lastIndex = d->lastIndex->PDO_RCV; 280 | UNS32 cobID[] = {0x200, 0x300, 0x400, 0x500}; 281 | UNS32 canID; 282 | UNS32 otherBits; 283 | if( offset ) while( (offset <= lastIndex) && (i < 4)) { 284 | canID = (*(UNS32*)d->objdict[offset].pSubindex[1].pObject) & 0x1fffffff; 285 | otherBits = (*(UNS32*)d->objdict[offset].pSubindex[1].pObject) & ~0x1fffffff; 286 | if((canID == cobID[i] + *d->bDeviceNodeId)||(*d->bDeviceNodeId==0xFF)) 287 | *(UNS32*)d->objdict[offset].pSubindex[1].pObject = (cobID[i] + nodeId) | otherBits; 288 | i ++; 289 | offset ++; 290 | } 291 | } 292 | /* ** Initialize the transmit PDO communication parameters. Only for 0x1800 to 0x1803 */ 293 | { 294 | UNS8 i = 0; 295 | UNS16 offset = d->firstIndex->PDO_TRS; 296 | UNS16 lastIndex = d->lastIndex->PDO_TRS; 297 | UNS32 cobID[] = {0x180, 0x280, 0x380, 0x480}; 298 | UNS32 canID; 299 | UNS32 otherBits; 300 | i = 0; 301 | if( offset ) while ((offset <= lastIndex) && (i < 4)) { 302 | canID = (*(UNS32*)d->objdict[offset].pSubindex[1].pObject) & 0x1fffffff; 303 | otherBits = (*(UNS32*)d->objdict[offset].pSubindex[1].pObject) & ~0x1fffffff; 304 | if((canID == cobID[i] + *d->bDeviceNodeId)||(*d->bDeviceNodeId==0xFF)) 305 | *(UNS32*)d->objdict[offset].pSubindex[1].pObject = (cobID[i] + nodeId) | otherBits; 306 | i ++; 307 | offset ++; 308 | } 309 | } 310 | 311 | /* Update EMCY COB-ID if already set to default*/ 312 | if((*d->error_cobid == *d->bDeviceNodeId + (UNS32)0x80)||(*d->bDeviceNodeId==0xFF)) 313 | *d->error_cobid = nodeId + 0x80; 314 | 315 | /* bDeviceNodeId is defined in the object dictionary. */ 316 | *d->bDeviceNodeId = nodeId; 317 | } 318 | 319 | void _initialisation(CO_Data* d){} 320 | void _preOperational(CO_Data* d){ 321 | if (!(*(d->iam_a_slave))) 322 | { 323 | masterSendNMTstateChange (d, 0, NMT_Reset_Node); 324 | } 325 | } 326 | void _operational(CO_Data* d){} 327 | void _stopped(CO_Data* d){} 328 | -------------------------------------------------------------------------------- /inc/sdo.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of CanFestival, a library implementing CanOpen Stack. 3 | 4 | Copyright (C): Edouard TISSERANT and Francis DUPIN 5 | 6 | See COPYING file for copyrights details. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | */ 22 | 23 | /** @defgroup comobj Communication Objects 24 | * @ingroup userapi 25 | */ 26 | 27 | /** @defgroup sdo Service Data Object (SDO) 28 | * SDOs provide the access to entries in the CANopen Object Dictionary. 29 | * An SDO is made up of at least two CAN messages with different identifiers. 30 | * SDO s are always confirmed point-to-point communications services. 31 | * @ingroup comobj 32 | */ 33 | 34 | #ifndef __sdo_h__ 35 | #define __sdo_h__ 36 | 37 | struct struct_s_transfer; 38 | 39 | #include "timer.h" 40 | 41 | /* Block mode : Data consumer receive step 42 | * - set to RXSTEP_STARTED when client receive initiate upload response 43 | * - set to RXSTEP_END when last segment of a block received 44 | */ 45 | typedef enum {RXSTEP_INIT, RXSTEP_STARTED, RXSTEP_END } rxStep_t; 46 | 47 | typedef void (*SDOCallback_t)(CO_Data* d, UNS8 nodeId); 48 | 49 | /* The Transfer structure 50 | Used to store the different segments of 51 | - a SDO received before writing in the dictionary 52 | - the reading of the dictionary to put on a SDO to transmit 53 | WARNING : after a change in this structure check the macro s_transfer_Initializer in data.h 54 | */ 55 | 56 | struct struct_s_transfer { 57 | UNS8 CliServNbr; /**< The index of the SDO client / server in our OD minus 0x1280 / 0x1200 */ 58 | 59 | UNS8 whoami; /**< Takes the values SDO_CLIENT or SDO_SERVER */ 60 | UNS8 state; /**< state of the transmission : Takes the values SDO_... */ 61 | UNS8 toggle; 62 | UNS32 abortCode; /**< Sent or received */ 63 | /**< index and subindex of the dictionary where to store */ 64 | /**< (for a received SDO) or to read (for a transmit SDO) */ 65 | UNS16 index; 66 | UNS8 subIndex; 67 | UNS32 count; /**< Number of data received or to be sent. */ 68 | UNS32 offset; /**< stack pointer of data[] 69 | * Used only to tranfer part of a line to or from a SDO. 70 | * offset is always pointing on the next free cell of data[]. 71 | * WARNING s_transfer.data is subject to ENDIANISATION 72 | * (with respect to CANOPEN_BIG_ENDIAN) 73 | */ 74 | UNS8 data [SDO_MAX_LENGTH_TRANSFER]; 75 | #ifdef SDO_DYNAMIC_BUFFER_ALLOCATION 76 | UNS8 *dynamicData; 77 | UNS32 dynamicDataSize; 78 | #endif //SDO_DYNAMIC_BUFFER_ALLOCATION 79 | 80 | UNS8 peerCRCsupport; /**< True if peer supports CRC */ 81 | UNS8 blksize; /**< Number of segments per block with 0 < blksize < 128 */ 82 | UNS8 ackseq; /**< sequence number of last segment that was received successfully */ 83 | UNS32 objsize; /**< Size in bytes of the object provided by data producer */ 84 | UNS32 lastblockoffset; /**< Value of offset before last block */ 85 | UNS8 seqno; /**< Last sequence number received OK or transmitted */ 86 | UNS8 endfield; /**< nbr of bytes in last segment of last block that do not contain data */ 87 | rxStep_t rxstep; /**< data consumer receive step - set to true when last segment of a block received */ 88 | UNS8 tmpData[8]; /**< temporary segment storage */ 89 | 90 | UNS8 dataType; /**< Defined in objdictdef.h Value is visible_string 91 | * if it is a string, any other value if it is not a string, 92 | * like 0. In fact, it is used only if client. 93 | */ 94 | TIMER_HANDLE timer; /**< Time counter to implement a timeout in milliseconds. 95 | * It is automatically incremented whenever 96 | * the line state is in SDO_DOWNLOAD_IN_PROGRESS or 97 | * SDO_UPLOAD_IN_PROGRESS, and reseted to 0 98 | * when the response SDO have been received. 99 | */ 100 | SDOCallback_t Callback; /**< The user callback func to be called at SDO transaction end */ 101 | }; 102 | typedef struct struct_s_transfer s_transfer; 103 | 104 | 105 | #include "data.h" 106 | 107 | /** 108 | * @brief Reset of a SDO exchange on timeout. 109 | * Send a SDO abort. 110 | * @param *d Pointer on a CAN object data structure 111 | * @param id 112 | */ 113 | void SDOTimeoutAlarm(CO_Data* d, UNS32 id); 114 | 115 | /** 116 | * @brief Reset all SDO buffers. 117 | * @param *d Pointer on a CAN object data structure 118 | */ 119 | void resetSDO (CO_Data* d); 120 | 121 | 122 | /** 123 | * @brief Copy the data received from the SDO line transfer to the object dictionary. 124 | * @param *d Pointer on a CAN object data structure 125 | * @param line SDO line 126 | * @return SDO error code if error. Else, returns 0. 127 | */ 128 | UNS32 SDOlineToObjdict (CO_Data* d, UNS8 line); 129 | 130 | /** 131 | * @brief Copy the data from the object dictionary to the SDO line for a network transfer. 132 | * @param *d Pointer on a CAN object data structure 133 | * @param line SDO line 134 | * @return SDO error code if error. Else, returns 0. 135 | */ 136 | UNS32 objdictToSDOline (CO_Data* d, UNS8 line); 137 | 138 | /** 139 | * @brief Copy data from an existant line in the argument "* data" 140 | * @param d Pointer on a CAN object data structure 141 | * @param line SDO line 142 | * @param nbBytes 143 | * @param *data Pointer on the data 144 | * @return 0xFF if error. Else, returns 0. 145 | */ 146 | UNS8 lineToSDO (CO_Data* d, UNS8 line, UNS32 nbBytes, UNS8 * data); 147 | 148 | /** 149 | * @brief Add data to an existant line 150 | * @param d Pointer on a CAN object data structure 151 | * @param line SDO line 152 | * @param nbBytes 153 | * @param *data Pointer on the data 154 | * @return 0xFF if error. Else, returns 0. 155 | */ 156 | UNS8 SDOtoLine (CO_Data* d, UNS8 line, UNS32 nbBytes, UNS8 * data); 157 | 158 | /** 159 | * @brief Called when an internal SDO abort occurs. 160 | * Release the line * Only if server * 161 | * If client, the line must be released manually in the core application. 162 | * The reason of that is to permit the program to read the transfers structure before its reset, 163 | * because many informations are stored on it : index, subindex, data received or trasmited, ... 164 | * In all cases, sends a SDO abort. 165 | * @param *d Pointer on a CAN object data structure 166 | * @param CliServNbr 167 | * @param whoami 168 | * @param index 169 | * @param subIndex 170 | * @param abortCode 171 | * @return 0 172 | */ 173 | UNS8 failedSDO (CO_Data* d, UNS8 CliServNbr, UNS8 whoami, UNS16 index, UNS8 subIndex, UNS32 abortCode); 174 | 175 | /** 176 | * @brief Reset an unused line. 177 | * @param *d Pointer on a CAN object data structure 178 | * @param line SDO line 179 | */ 180 | void resetSDOline (CO_Data* d, UNS8 line); 181 | 182 | /** 183 | * @brief Initialize some fields of the structure. 184 | * @param *d Pointer on a CAN object data structure 185 | * @param line 186 | * @param CliServNbr 187 | * @param index 188 | * @param subIndex 189 | * @param state 190 | * @return 0 191 | */ 192 | UNS8 initSDOline (CO_Data* d, UNS8 line, UNS8 CliServNbr, UNS16 index, UNS8 subIndex, UNS8 state); 193 | 194 | /** 195 | * @brief Search for an unused line in the transfers array 196 | * to store a new SDO. 197 | * ie a line which value of the field "state" is "SDO_RESET" 198 | * An unused line have the field "state" at the value SDO_RESET 199 | * @param *d Pointer on a CAN object data structure 200 | * @param whoami Create the line for a SDO_SERVER or SDO_CLIENT. 201 | * @param *line Pointer on a SDO line 202 | * @return 0xFF if all the lines are on use. Else, return 0. 203 | */ 204 | UNS8 getSDOfreeLine (CO_Data* d, UNS8 whoami, UNS8 *line); 205 | 206 | /** 207 | * @brief Search for the line, in the transfers array, which contains the 208 | * beginning of the reception of a fragmented SDO 209 | * @param *d Pointer on a CAN object data structure 210 | * @param CliServNbr Client or Server object involved 211 | * @param whoami takes 2 values : look for a line opened as SDO_CLIENT or SDO_SERVER 212 | * @param *line Pointer on a SDO line 213 | * @return 0xFF if error. Else, return 0 214 | */ 215 | UNS8 getSDOlineOnUse (CO_Data* d, UNS8 CliServNbr, UNS8 whoami, UNS8 *line); 216 | 217 | /** 218 | * @brief Search for the line, in the transfers array, which contains the 219 | * beginning of the reception of a fragmented SDO 220 | * 221 | * Because getSDOlineOnUse() does not return any line in state \c SDO_ABORTED_INTERNAL, 222 | * this funtion is used to return them, too. 223 | * 224 | * @param *d Pointer on a CAN object data structure 225 | * @param CliServNbr Client or Server object involved 226 | * @param whoami takes 2 values : look for a line opened as SDO_CLIENT or SDO_SERVER 227 | * @param *line Pointer on a SDO line 228 | * @return 0xFF if error. Else, return 0 229 | */ 230 | UNS8 getSDOlineToClose (CO_Data* d, UNS8 CliServNbr, UNS8 whoami, UNS8 *line); 231 | 232 | /** 233 | * @brief Close a transmission. 234 | * @param *d Pointer on a CAN object data structure 235 | * @param CliServNbr Client or Server object involved 236 | * @param whoami Line opened as SDO_CLIENT or SDO_SERVER 237 | */ 238 | UNS8 closeSDOtransfer (CO_Data* d, UNS8 CliServNbr, UNS8 whoami); 239 | 240 | /** 241 | * @brief Bytes in the line structure which must be transmited (or received) 242 | * @param *d Pointer on a CAN object data structure 243 | * @param line SDO line 244 | * @param *nbBytes Pointer on nbBytes 245 | * @return 0. 246 | */ 247 | UNS8 getSDOlineRestBytes (CO_Data* d, UNS8 line, UNS32 * nbBytes); 248 | 249 | /** 250 | * @brief Store in the line structure the nb of bytes which must be transmited (or received) 251 | * @param *d Pointer on a CAN object data structure 252 | * @param line SDO line 253 | * @param nbBytes 254 | * @return 0 if success, 0xFF if error. 255 | */ 256 | UNS8 setSDOlineRestBytes (CO_Data* d, UNS8 line, UNS32 nbBytes); 257 | 258 | /** 259 | * @brief Transmit a SDO frame on the bus bus_id 260 | * @param *d Pointer on a CAN object data structure 261 | * @param whoami Takes 2 values : SDO_CLIENT or SDO_SERVER 262 | * @param CliServNbr Client or Server object involved 263 | * @param data Array of the 8 bytes to transmit 264 | * @return canSend(bus_id,&m) or 0xFF if error. 265 | */ 266 | UNS8 sendSDO (CO_Data* d, UNS8 whoami, UNS8 CliServNbr, UNS8 *pData); 267 | 268 | /** 269 | * @brief Transmit a SDO error to the client. The reasons may be : 270 | * Read/Write to a undefined object 271 | * Read/Write to a undefined subindex 272 | * Read/write a not valid length object 273 | * Write a read only object 274 | * @param *d Pointer on a CAN object data structure 275 | * @param whoami takes 2 values : SDO_CLIENT or SDO_SERVER 276 | * @param CliServNbr 277 | * @param index 278 | * @param subIndex 279 | * @param abortCode 280 | * @return 0 281 | */ 282 | UNS8 sendSDOabort (CO_Data* d, UNS8 whoami, UNS8 CliServNbr, UNS16 index, UNS8 subIndex, UNS32 abortCode); 283 | 284 | /** 285 | * @brief Treat a SDO frame reception 286 | * call the function sendSDO 287 | * @param *d Pointer on a CAN object data structure 288 | * @param *m Pointer on a CAN message structure 289 | * @return code : 290 | * - 0xFF if error 291 | * - 0x80 if transfer aborted by the server 292 | * - 0x0 ok 293 | */ 294 | UNS8 proceedSDO (CO_Data* d, Message *m); 295 | 296 | /** 297 | * @ingroup sdo 298 | * @brief Reset the SDO client line used to communicate with the server nodeId 299 | * Mustbe called after getRead/WriteResultNetworkDict in case of error 300 | * @param d 301 | * @param nodeId 302 | */ 303 | void resetClientSDOLineFromNodeId(CO_Data* d, UNS8 nodeId); 304 | 305 | /** 306 | * @ingroup sdo 307 | * @brief Used to send a SDO request frame to write the data at the index and subIndex indicated 308 | * @param *d Pointer to a CAN object data structure 309 | * @param nodeId Node Id of the slave 310 | * @param index At index indicated 311 | * @param subIndex At subIndex indicated 312 | * @param count number of bytes to write in the dictionnary. 313 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 314 | * @param *data Pointer to data 315 | * @return 316 | * - 0 is returned upon success. 317 | * - 0xFE is returned when no sdo client to communicate with node. 318 | * - 0xFF is returned when error occurs. 319 | */ 320 | UNS8 writeNetworkDict (CO_Data* d, UNS8 nodeId, UNS16 index, 321 | UNS8 subIndex, UNS32 count, UNS8 dataType, void *data, UNS8 useBlockMode); 322 | 323 | /** 324 | * @ingroup sdo 325 | * @brief Used to send a SDO request frame to write in a distant node dictionnary. 326 | * @details The function Callback which must be defined in the user code is called at the 327 | * end of the exchange. (on succes or abort). 328 | * @param *d Pointer to a CAN object data structure 329 | * @param nodeId Node Id of the slave 330 | * @param index At index indicated 331 | * @param subIndex At subIndex indicated 332 | * @param count number of bytes to write in the dictionnary. 333 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 334 | * @param *data Pointer to data 335 | * @param Callback Callback function 336 | * @return 337 | * - 0 is returned upon success. 338 | * - 0xFE is returned when no sdo client to communicate with node. 339 | * - 0xFF is returned when error occurs. 340 | */ 341 | UNS8 writeNetworkDictCallBack (CO_Data* d, UNS8 nodeId, UNS16 index, 342 | UNS8 subIndex, UNS32 count, UNS8 dataType, void *data, SDOCallback_t Callback, UNS8 useBlockMode); 343 | 344 | /** 345 | * @ingroup sdo 346 | * @brief Used to send a SDO request frame to write in a distant node dictionnary. 347 | * @details The function Callback which must be defined in the user code is called at the 348 | * end of the exchange. (on succes or abort). First free SDO client parameter is 349 | * automatically initialized for specific node if not already defined. 350 | * @param *d Pointer to a CAN object data structure 351 | * @param nodeId Node Id of the slave 352 | * @param index At index indicated 353 | * @param subIndex At subIndex indicated 354 | * @param count number of bytes to write in the dictionnary. 355 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 356 | * @param *data Pointer to data 357 | * @param Callback Callback function 358 | * @param endianize When not 0, data is endianized into network byte order 359 | * when 0, data is not endianized and copied in machine native 360 | * endianness 361 | * @param useBlockMode true if block mode transfer is used 362 | * @return 363 | * - 0 is returned upon success. 364 | * - 0xFF is returned when error occurs. 365 | */ 366 | UNS8 writeNetworkDictCallBackAI (CO_Data* d, UNS8 nodeId, UNS16 index, 367 | UNS8 subIndex, UNS32 count, UNS8 dataType, void *data, SDOCallback_t Callback, UNS8 endianize, UNS8 useBlockMode); 368 | 369 | /** 370 | * @ingroup sdo 371 | * @brief Used to send a SDO request frame to read. 372 | * @param *d Pointer to a CAN object data structure 373 | * @param nodeId Node Id of the slave 374 | * @param index At index indicated 375 | * @param subIndex At subIndex indicated 376 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 377 | * @return 378 | * - 0 is returned upon success. 379 | * - 0xFE is returned when no sdo client to communicate with node. 380 | * - 0xFF is returned when error occurs. 381 | */ 382 | UNS8 readNetworkDict (CO_Data* d, UNS8 nodeId, UNS16 index, UNS8 subIndex, UNS8 dataType, UNS8 useBlockMode); 383 | 384 | /** 385 | * @ingroup sdo 386 | * @brief Used to send a SDO request frame to read in a distant node dictionnary. 387 | * @details The function Callback which must be defined in the user code is called at the 388 | * end of the exchange. (on succes or abort). 389 | * @param *d Pointer on a CAN object data structure 390 | * @param nodeId Node Id of the slave 391 | * @param index At index indicated 392 | * @param subIndex At subIndex indicated 393 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 394 | * @param Callback Callback function 395 | * @return 396 | * - 0 is returned upon success. 397 | * - 0xFE is returned when no sdo client to communicate with node. 398 | * - 0xFF is returned when error occurs. 399 | */ 400 | UNS8 readNetworkDictCallback (CO_Data* d, UNS8 nodeId, UNS16 index, UNS8 subIndex, UNS8 dataType, SDOCallback_t Callback, UNS8 useBlockMode); 401 | 402 | /** 403 | * @ingroup sdo 404 | * @brief Used to send a SDO request frame to read in a distant node dictionnary. 405 | * @details The function Callback which must be defined in the user code is called at the 406 | * end of the exchange. (on succes or abort). First free SDO client parameter is 407 | * automatically initialized for specific node if not already defined. 408 | * @param *d Pointer on a CAN object data structure 409 | * @param nodeId Node Id of the slave 410 | * @param index At index indicated 411 | * @param subIndex At subIndex indicated 412 | * @param dataType (defined in objdictdef.h) : put "visible_string" for strings, 0 for integers or reals or other value. 413 | * @param Callback Callback function 414 | * @param useBlockMode use block mode transfer 415 | * @return 416 | * - 0 is returned upon success. 417 | * - 0xFF is returned when error occurs. 418 | */ 419 | UNS8 readNetworkDictCallbackAI (CO_Data* d, UNS8 nodeId, UNS16 index, UNS8 subIndex, UNS8 dataType, SDOCallback_t Callback, UNS8 useBlockMode); 420 | 421 | /** 422 | * @ingroup sdo 423 | * @brief Use this function after calling readNetworkDict to get the result. 424 | * 425 | * @param *d Pointer to a CAN object data structure 426 | * @param nodeId Node Id of the slave 427 | * @param *data Pointer to the buffer to get the data 428 | * @param *size Pointer to the size : MUST contain the size of the buffer before calling 429 | * The function set it to the actual number of written bytes 430 | * @param *abortCode Pointer to the abortcode. (0 = not available. Else : SDO abort code. (received if return SDO_ABORTED_RCV) 431 | * 432 | * 433 | * @return 434 | * - SDO_FINISHED // datas are available 435 | * - SDO_ABORTED_RCV // Transfer failed (abort SDO received) 436 | * - SDO_ABORTED_INTERNAL // Transfer failed (internal abort) 437 | * - SDO_UPLOAD_IN_PROGRESS // Datas are not yet available 438 | * - SDO_DOWNLOAD_IN_PROGRESS // Download is in progress 439 | * - SDO_PROVIDED_BUFFER_TOO_SMALL //The value *size is not enough to store the received data 440 | * \n\n 441 | * example : 442 | * @code 443 | * UNS32 data; 444 | * UNS8 size; 445 | * readNetworkDict(0, 0x05, 0x1016, 1, 0) // get the data index 1016 subindex 1 of node 5 446 | * while (getReadResultNetworkDict (0, 0x05, &data, &size) == SDO_UPLOAD_IN_PROGRESS); 447 | * @endcode 448 | */ 449 | UNS8 getReadResultNetworkDict (CO_Data* d, UNS8 nodeId, void* data, UNS32 *size, UNS32 * abortCode); 450 | 451 | /** 452 | * @ingroup sdo 453 | * @brief Use this function after calling writeNetworkDict function to get the result of the write. 454 | * @details It is mandatory to call this function because it is releasing the line used for the transfer. 455 | * @param *d Pointer to a CAN object data structure 456 | * @param nodeId Node Id of the slave 457 | * @param *abortCode Pointer to the abortcode 458 | * - 0 = not available. 459 | * - SDO abort code (received if return SDO_ABORTED_RCV) 460 | * 461 | * @return : 462 | * - SDO_FINISHED // datas are available 463 | * - SDO_ABORTED_RCV // Transfer failed (abort SDO received) 464 | * - SDO_ABORTED_INTERNAL // Transfer failed (Internal abort) 465 | * - SDO_DOWNLOAD_IN_PROGRESS // Datas are not yet available 466 | * - SDO_UPLOAD_IN_PROGRESS // Upload in progress 467 | * \n\n 468 | * example : 469 | * @code 470 | * UNS32 data = 0x50; 471 | * UNS8 size; 472 | * UNS32 abortCode; 473 | * writeNetworkDict(0, 0x05, 0x1016, 1, size, &data) // write the data index 1016 subindex 1 of node 5 474 | * while (getWriteResultNetworkDict (0, 0x05, &abortCode) == SDO_DOWNLOAD_IN_PROGRESS); 475 | * @endcode 476 | */ 477 | UNS8 getWriteResultNetworkDict (CO_Data* d, UNS8 nodeId, UNS32 * abortCode); 478 | 479 | #endif 480 | --------------------------------------------------------------------------------