Add LSS master into gateway-ascii (CiA309).
- Rename local variable 'token' into 'tok' in CO_GTWA_process(). - CO_LSSmaster: replace CO_LSSmaster_InquireNodeId() with more generic CO_LSSmaster_Inquire(). CiA309-3 command 'lss_inquire_addr' now works also according to standard. - CANopen.h: fix doxygen. - gettingStarted.md: Change example "master" device from node-Id=3 to 1...
This commit is contained in:
parent
ae220f9115
commit
92a803f169
6 changed files with 806 additions and 130 deletions
|
|
@ -54,7 +54,7 @@ typedef enum {
|
|||
CO_LSSmaster_COMMAND_INQUIRE_PRODUCT,
|
||||
CO_LSSmaster_COMMAND_INQUIRE_REV,
|
||||
CO_LSSmaster_COMMAND_INQUIRE_SERIAL,
|
||||
CO_LSSmaster_COMMAND_INQUIRE_NODE_ID,
|
||||
CO_LSSmaster_COMMAND_INQUIRE,
|
||||
CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN,
|
||||
} CO_LSSmaster_command_t;
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ static void CO_LSSmaster_receive(void *object, void *msg)
|
|||
* or after the timeout expired. Only if no message has been received we have
|
||||
* to check for timeouts
|
||||
*/
|
||||
static CO_LSSmaster_return_t CO_LSSmaster_check_timeout(
|
||||
static inline CO_LSSmaster_return_t CO_LSSmaster_check_timeout(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
uint32_t timeDifference_us)
|
||||
{
|
||||
|
|
@ -709,14 +709,15 @@ CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(
|
|||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId(
|
||||
CO_LSSmaster_return_t CO_LSSmaster_Inquire(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
uint32_t timeDifference_us,
|
||||
uint8_t *nodeId)
|
||||
CO_LSS_cs_t lssInquireCs,
|
||||
uint32_t *value)
|
||||
{
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if (LSSmaster==NULL || nodeId==NULL){
|
||||
if (LSSmaster==NULL || value==NULL){
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
|
|
@ -725,19 +726,15 @@ CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId(
|
|||
LSSmaster->state==CO_LSSmaster_STATE_CFG_GLOBAL) &&
|
||||
LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_NODE_ID;
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_NODE_ID);
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, lssInquireCs);
|
||||
}
|
||||
/* Check for reply */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_NODE_ID) {
|
||||
uint32_t tmp = 0;
|
||||
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE) {
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us,
|
||||
CO_LSS_INQUIRE_NODE_ID, &tmp);
|
||||
|
||||
*nodeId = tmp & 0xff;
|
||||
lssInquireCs, value);
|
||||
}
|
||||
|
||||
if (ret != CO_LSSmaster_WAIT_SLAVE) {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,9 @@ typedef struct{
|
|||
* Default timeout for LSS slave in ms. This is the same as for SDO. For more
|
||||
* info about LSS timeout see #CO_LSSmaster_changeTimeout()
|
||||
*/
|
||||
#ifndef CO_LSSmaster_DEFAULT_TIMEOUT
|
||||
#define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -364,9 +366,10 @@ CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(
|
|||
|
||||
|
||||
/**
|
||||
* Request LSS inquire node ID
|
||||
* Request LSS inquire node ID or part of LSS address
|
||||
*
|
||||
* The node ID value is read from the node.
|
||||
* The node-ID, identity vendor-ID, product-code, revision-number or
|
||||
* serial-number value is read from the node.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
|
|
@ -376,14 +379,16 @@ CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(
|
|||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* [microseconds]. Zero when request is started.
|
||||
* @param [out] nodeId read result when function returns successfully
|
||||
* @param lssInquireCs One of CO_LSS_INQUIRE_xx commands from #CO_LSS_cs_t.
|
||||
* @param [out] value read result when function returns successfully
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
|
||||
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId(
|
||||
CO_LSSmaster_return_t CO_LSSmaster_Inquire(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
uint32_t timeDifference_us,
|
||||
uint8_t *nodeId);
|
||||
CO_LSS_cs_t lssInquireCs,
|
||||
uint32_t *value);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,10 @@
|
|||
/**
|
||||
* CANopen access from other networks - ASCII mapping (CiA 309-3 DS v2.1.0)
|
||||
* CANopen access from other networks - ASCII mapping (CiA 309-3 DS v3.0.0)
|
||||
*
|
||||
* @file CO_gateway_ascii.h
|
||||
* @ingroup CO_CANopen_309_3
|
||||
* @author Janez Paternoster
|
||||
* @author Martin Wagner
|
||||
* @copyright 2020 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
|
|
@ -212,6 +213,24 @@ typedef enum {
|
|||
CO_GTWA_ST_READ = 0x10U,
|
||||
/** SDO 'write' (download) */
|
||||
CO_GTWA_ST_WRITE = 0x11U,
|
||||
/** LSS 'lss_switch_glob' */
|
||||
CO_GTWA_ST_LSS_SWITCH_GLOB = 0x20U,
|
||||
/** LSS 'lss_switch_sel' */
|
||||
CO_GTWA_ST_LSS_SWITCH_SEL = 0x21U,
|
||||
/** LSS 'lss_set_node' */
|
||||
CO_GTWA_ST_LSS_SET_NODE = 0x22U,
|
||||
/** LSS 'lss_conf_bitrate' */
|
||||
CO_GTWA_ST_LSS_CONF_BITRATE = 0x23U,
|
||||
/** LSS 'lss_store' */
|
||||
CO_GTWA_ST_LSS_STORE = 0x24U,
|
||||
/** LSS 'lss_inquire_addr' or 'lss_get_node' */
|
||||
CO_GTWA_ST_LSS_INQUIRE = 0x25U,
|
||||
/** LSS 'lss_inquire_addr', all parameters */
|
||||
CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL = 0x26U,
|
||||
/** LSS '_lss_fastscan' */
|
||||
CO_GTWA_ST__LSS_FASTSCAN = 0x30U,
|
||||
/** LSS 'lss_allnodes' */
|
||||
CO_GTWA_ST_LSS_ALLNODES = 0x31U,
|
||||
/** print message log */
|
||||
CO_GTWA_ST_LOG = 0x80U,
|
||||
/** print 'help' text */
|
||||
|
|
@ -312,8 +331,26 @@ typedef struct {
|
|||
CO_NMT_t *NMT;
|
||||
#endif
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS) || defined CO_DOXYGEN
|
||||
/** LSS object from CO_GTWA_init() */
|
||||
CO_LSSmaster_t *LSS;
|
||||
/** LSSmaster object from CO_GTWA_init() */
|
||||
CO_LSSmaster_t *LSSmaster;
|
||||
/** 128 bit number, uniquely identifying each node */
|
||||
CO_LSS_address_t lssAddress;
|
||||
/** LSS Node-ID parameter */
|
||||
uint8_t lssNID;
|
||||
/** LSS bitrate parameter */
|
||||
uint16_t lssBitrate;
|
||||
/** LSS inquire parameter */
|
||||
CO_LSS_cs_t lssInquireCs;
|
||||
/** LSS fastscan parameter */
|
||||
CO_LSSmaster_fastscan_t lssFastscan;
|
||||
/** LSS allnodes sub state parameter */
|
||||
uint8_t lssSubState;
|
||||
/** LSS allnodes node count parameter */
|
||||
uint8_t lssNodeCount;
|
||||
/** LSS allnodes store parameter */
|
||||
bool_t lssStore;
|
||||
/** LSS allnodes timeout parameter */
|
||||
uint16_t lssTimeout_ms;
|
||||
#endif
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG) || defined CO_DOXYGEN
|
||||
/** Message log buffer of usable size @ref CO_CONFIG_GTWA_LOG_BUF_SIZE */
|
||||
|
|
@ -336,7 +373,7 @@ typedef struct {
|
|||
* @param SDOtimeoutTimeDefault in milliseconds, 500 typically
|
||||
* @param SDOblockTransferEnableDefault true or false
|
||||
* @param NMT NMT object
|
||||
* @param LSS LSS master object
|
||||
* @param LSSmaster LSS master object
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
|
|
@ -345,7 +382,7 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa,
|
|||
uint16_t SDOtimeoutTimeDefault,
|
||||
bool_t SDOblockTransferEnableDefault,
|
||||
void *NMT,
|
||||
void *LSS);
|
||||
void *LSSmaster);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
46
CANopen.h
46
CANopen.h
|
|
@ -144,36 +144,36 @@ extern "C" {
|
|||
#define CO_NO_NMT (1)
|
||||
/** Number of NMT master objects, 0 or 1 master(CANtx). It depends on
|
||||
* @ref CO_CONFIG_NMT setting. */
|
||||
#define CO_NO_NMT_MST (0...1)
|
||||
#define CO_NO_NMT_MST (0 - 1)
|
||||
/** Number of SYNC objects, 0 or 1 (consumer(CANrx) + producer(CANtx)) */
|
||||
#define CO_NO_SYNC (0...1)
|
||||
#define CO_NO_SYNC (0 - 1)
|
||||
/** Number of Emergency producer objects, fixed to 1 producer(CANtx) */
|
||||
#define CO_NO_EMERGENCY (1)
|
||||
/** Number of Emergency consumer objects, 0 or 1 consumer(CANrx). It depends on
|
||||
* @ref CO_CONFIG_EM setting. */
|
||||
#define CO_NO_EM_CONS (0...1)
|
||||
#define CO_NO_EM_CONS (0 - 1)
|
||||
/** Number of Time-stamp objects, 0 or 1 (consumer(CANrx) + producer(CANtx)) */
|
||||
#define CO_NO_TIME (0...1)
|
||||
#define CO_NO_TIME (0 - 1)
|
||||
/** Number of RPDO objects, 1 to 512 consumers (CANrx) */
|
||||
#define CO_NO_RPDO (1...512)
|
||||
#define CO_NO_RPDO (1 - 512)
|
||||
/** Number of TPDO objects, 1 to 512 producers (CANtx) */
|
||||
#define CO_NO_TPDO (1...512)
|
||||
#define CO_NO_TPDO (1 - 512)
|
||||
/** Number of SDO server objects, from 1 to 128 (CANrx + CANtx) */
|
||||
#define CO_NO_SDO_SERVER (1...128)
|
||||
#define CO_NO_SDO_SERVER (1 - 128)
|
||||
/** Number of SDO client objects, from 0 to 128 (CANrx + CANtx) */
|
||||
#define CO_NO_SDO_CLIENT (0...128)
|
||||
#define CO_NO_SDO_CLIENT (0 - 128)
|
||||
/** Number of HB producer objects, fixed to 1 producer(CANtx) */
|
||||
#define CO_NO_HB_PROD (1)
|
||||
/** Number of HB consumer objects, from 0 to 127 consumers(CANrx) */
|
||||
#define CO_NO_HB_CONS (0...127)
|
||||
#define CO_NO_HB_CONS (0 - 127)
|
||||
/** Number of LSS slave objects, 0 or 1 (CANrx + CANtx). It depends on
|
||||
* @ref CO_CONFIG_LSS setting. */
|
||||
#define CO_NO_LSS_SLAVE (0...1)
|
||||
#define CO_NO_LSS_SLAVE (0 - 1)
|
||||
/** Number of LSS master objects, 0 or 1 (CANrx + CANtx). It depends on
|
||||
* @ref CO_CONFIG_LSS setting. */
|
||||
#define CO_NO_LSS_MASTER (0...1)
|
||||
#define CO_NO_LSS_MASTER (0 - 1)
|
||||
/** Number of Trace objects, 0 to many */
|
||||
#define CO_NO_TRACE (0...)
|
||||
#define CO_NO_TRACE (0 - )
|
||||
/** @} */
|
||||
|
||||
#else /* CO_DOXYGEN */
|
||||
|
|
@ -222,19 +222,19 @@ extern "C" {
|
|||
#endif /* CO_DOXYGEN */
|
||||
|
||||
|
||||
#if CO_NO_SDO_CLIENT != 0
|
||||
#if CO_NO_SDO_CLIENT != 0 || defined CO_DOXYGEN
|
||||
#include "301/CO_SDOclient.h"
|
||||
#endif
|
||||
#if CO_NO_LSS_SLAVE != 0
|
||||
#if CO_NO_LSS_SLAVE != 0 || defined CO_DOXYGEN
|
||||
#include "305/CO_LSSslave.h"
|
||||
#endif
|
||||
#if CO_NO_LSS_MASTER != 0
|
||||
#if CO_NO_LSS_MASTER != 0 || defined CO_DOXYGEN
|
||||
#include "305/CO_LSSmaster.h"
|
||||
#endif
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN
|
||||
#include "309/CO_gateway_ascii.h"
|
||||
#endif
|
||||
#if CO_NO_TRACE != 0
|
||||
#if CO_NO_TRACE != 0 || defined CO_DOXYGEN
|
||||
#include "extra/CO_trace.h"
|
||||
#endif
|
||||
|
||||
|
|
@ -253,19 +253,19 @@ typedef struct {
|
|||
CO_RPDO_t *RPDO[CO_NO_RPDO]; /**< RPDO objects */
|
||||
CO_TPDO_t *TPDO[CO_NO_TPDO]; /**< TPDO objects */
|
||||
CO_HBconsumer_t *HBcons; /**< Heartbeat consumer object*/
|
||||
#if CO_NO_SDO_CLIENT != 0
|
||||
#if CO_NO_SDO_CLIENT != 0 || defined CO_DOXYGEN
|
||||
CO_SDOclient_t *SDOclient[CO_NO_SDO_CLIENT]; /**< SDO client object */
|
||||
#endif
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
#if CO_NO_LSS_SLAVE == 1 || defined CO_DOXYGEN
|
||||
CO_LSSslave_t *LSSslave; /**< LSS slave object */
|
||||
#endif
|
||||
#if CO_NO_LSS_MASTER == 1
|
||||
#if CO_NO_LSS_MASTER == 1 || defined CO_DOXYGEN
|
||||
CO_LSSmaster_t *LSSmaster; /**< LSS master object */
|
||||
#endif
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN
|
||||
CO_GTWA_t *gtwa; /**< Gateway-ascii object (CiA309-3) */
|
||||
#endif
|
||||
#if CO_NO_TRACE > 0
|
||||
#if CO_NO_TRACE > 0 || defined CO_DOXYGEN
|
||||
CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for recording variables */
|
||||
#endif
|
||||
} CO_t;
|
||||
|
|
@ -317,7 +317,7 @@ CO_ReturnError_t CO_CANinit(void *CANptr,
|
|||
uint16_t bitRate);
|
||||
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
#if CO_NO_LSS_SLAVE == 1 || defined CO_DOXYGEN
|
||||
/**
|
||||
* Initialize CANopen LSS slave
|
||||
*
|
||||
|
|
@ -370,7 +370,7 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
uint32_t *timerNext_us);
|
||||
|
||||
|
||||
#if CO_NO_SYNC == 1
|
||||
#if CO_NO_SYNC == 1 || defined CO_DOXYGEN
|
||||
/**
|
||||
* Process CANopen SYNC objects.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -78,13 +78,13 @@ Second terminal now shows operational state (0x05) and one pre-defined PDO messa
|
|||
|
||||
|
||||
### Second CANopen device
|
||||
Open the third terminal and cd to the same directory as is in the first terminal. First generate default storage files. Then start second instance of _canopend_ with NodeID = 3. Use default od_storage files and enable command interface on standard IO (terminal).
|
||||
Open the third terminal and cd to the same directory as is in the first terminal. First generate default storage files. Then start second instance of _canopend_ with NodeID = 1. Use default od_storage files and enable command interface on standard IO (terminal).
|
||||
|
||||
echo "-" > od_storage
|
||||
echo "-" > od_storage_auto
|
||||
./canopend vcan0 -i3 -c "stdio"
|
||||
./canopend vcan0 -i1 -c "stdio"
|
||||
|
||||
Now you should see in second terminal (_candump_) two CANopen devices sending heartbeats in one second interval each. One with node-ID = 4 and one with node-ID = 3. Both should be operational.
|
||||
Now you should see in second terminal (_candump_) two CANopen devices sending heartbeats in one second interval each. One with node-ID = 4 and one with node-ID = 1. Both should be operational.
|
||||
|
||||
|
||||
### CANopen command interface
|
||||
|
|
@ -106,9 +106,9 @@ In CAN dump you can see some SDO communication. SDO communication can be quite c
|
|||
[2] 4 write 0x1017 0 u16 5000
|
||||
[2] OK #response
|
||||
|
||||
In _candump_ you will notice, that heartbeats from node 4 are coming in 5 second interval now. You can do the same also for node 3, but you won't see anything on _candump_, because data are written into itself directly. In "stdio" you can omit sequence number, to make typing easier.
|
||||
In _candump_ you will notice, that heartbeats from node 4 are coming in 5 second interval now. You can do the same also for node 1, but you won't see anything on _candump_, because data are written into itself directly. In "stdio" you can omit sequence number, to make typing easier.
|
||||
|
||||
3 w 0x1017 0 u16 2500
|
||||
1 w 0x1017 0 u16 2500
|
||||
[0] OK
|
||||
|
||||
Now store Object dictionary on node-ID 4, so it will preserve variables on next start of the program.
|
||||
|
|
@ -143,10 +143,10 @@ If node is operational (started), it can exchange all objects, including PDO, SD
|
|||
reset node
|
||||
[0] OK
|
||||
|
||||
3 reset communication
|
||||
1 reset communication
|
||||
[0] OK
|
||||
|
||||
3 reset node
|
||||
1 reset node
|
||||
[0] OK
|
||||
|
||||
|
||||
|
|
@ -159,15 +159,19 @@ Please be careful when exposing your CANopen network to the outside world, it is
|
|||
### Next steps
|
||||
Now you can enter the big world of [CANopen devices](http://can-newsletter.org/hardware).
|
||||
|
||||
Accessing real CANopen devices is the same as described above for virtual CAN interface. Some tested USB to CAN interfaces, which are native integrated into Linux kernel are:
|
||||
You can also build your own CANopen device with your favourite microcontroller, see *deviceSupport.md*. There is also a bare-metal demo for [PIC microcontrollers](https://github.com/CANopenNode/CANopenPIC), most complete example is for PIC32.
|
||||
|
||||
Assigning Node-ID or CAN bitrate to unconfigured nodes, which support LSS configuration, is described in *LSSusage.md*.
|
||||
|
||||
Some further CANopenNode related Linux tools are available in [CANopenSocket](https://github.com/CANopenNode/CANopenSocket).
|
||||
|
||||
Accessing real CANopen devices is the same as described above for virtual CAN interface. Some tested USB to CAN interfaces, which are native in Linux kernel are:
|
||||
- Simple serial [USBtin](http://www.fischl.de/usbtin/) - Start with: `sudo slcand -f -o -c -s8 /dev/ttyACM0 can0; sudo ip link set up can0`
|
||||
- [EMS CPC-USB](https://www.ems-wuensche.com/?post_type=product&p=746) or [PCAN-USB FD](http://www.peak-system.com/PCAN-USB-FD.365.0.html?&L=1) - Start with: `sudo ip link set up can0 type can bitrate 250000`
|
||||
- You can get the idea of other supported CAN interfaces in [Linux kernel source](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/net/can) (Kconfig files).
|
||||
- Beaglebone or Paspberry PI or similar has CAN capes available. On RPI worked also the above USB interfaces, but it was necessary to compile the kernel.
|
||||
|
||||
- Raspberry PI or similar has CAN capes available.
|
||||
|
||||
With [CANopenNode](https://github.com/CANopenNode/CANopenNode) you can also design your own device. There are many very useful and high quality specifications for different [device profiles](http://www.can-cia.org/standardization/specifications/), some of them are public and free to download.
|
||||
|
||||
|
||||
Here we played with virtual CAN interface and result shows as pixels on screen. If you connect real CAN interface to your computer, things may
|
||||
become dangerous. Keep control and safety on your machines!
|
||||
|
|
|
|||
Loading…
Reference in a new issue