Files
ecmc_plugin_socketcan/README.md
2021-03-18 10:15:10 +01:00

255 lines
8.5 KiB
Markdown

e3-ecmc_plugin_socketcan
======
ESS Site-specific EPICS module : ecmcPlugin_socketcan
# ScoketCAN
This module adds some CAN support to ecmc based on SocketCAN:
https://en.wikipedia.org/wiki/SocketCAN
# CANOpen
This module implements a few of the CANOpen functionalities (but it is far from a full CANOpen Implementation).
The current implemented features are:
* Simple master:
* Simple LSS
* Heartbeat
* Sync
* Simple generic (slave) device
* SDO segmented r/w
* PDO r/w
These functionalities are configured through iocsh cmds:
* ecmcCANOpenAddMaster
* ecmcCANOpenAddDevice
* ecmcCANOpenAddSDO
* ecmcCANOpenAddPDO
## ecmcCANOpenAddMaster
Issueing "ecmcCANOpenAddMaster -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddMaster -h
Use ecmcCANOpenAddMaster(<name>, <node id>,....)
<name> : Name of master device.
<node id> : CANOpen node id of master.
<LSS sample time ms> : Sample time for LSS.
<Sync sample time ms> : Sample time for SYNC.
<Heartbeat sample time ms> : Sample time for Heartbeat.
```
Example to add a master with LSS, sync, heartbeat sample time of 1s:
```
ecmcCANOpenAddMaster("ecmcCANOpenMaster",0,1000,1000,1000)
```
Note:: You can only have one master in each ioc (one call to "ecmcCANOpenAddMaster").
## ecmcCANOpenAddDevice
Issueing "ecmcCANOpenAddDevice -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddDevice -h
Use ecmcCANOpenAddDevice(<name>, <node id>)
<name> : Name of device.
<node id> : CANOpen node id of device.
```
Example to add a device with node id 3:
```
ecmcCANOpenAddDevice("testDevice",3)
```
Note:: You can only use the below commands (ecmcCANOpenAddSDO and ecmcCANOpenAddODO) to nodes of an existing device or master.
## ecmcCANOpenAddSDO
Issueing "ecmcCANOpenAddSDO -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddSDO -h
ecmcCANOpenAddSDO( in the iocsh<name>, <node id>,.....)
<name> : Name of master device.
<node id> : CANOpen node id of device/master.
<cob id tx> : CANOpen cob id of Tx of slave SDO.
<cob id rx> : CANOpen cob id of Rx of slave SDO.
<dir> : Direction 1=write and 2=read.
<ODIndex> : OD index of SDO.
<ODSubIndex> : OD sub index of SDO.
<ODSize> : OS Siz
<readSampleTimeMs>: Sample time for read in ms (write is always on demand).
```
A few examples:
```
# Example read SDO from device, will be cyclically read at an intervall of 7000ms:
ecmcCANOpenAddSDO("analogValues",3,0x583,0x603,2,0x2640,0x0,56,7000)
dbLoadRecords(ecmcPluginSocketCAN_SDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=AI03-Array,source=basicConfig,DTYP=asynInt8ArrayIn,FTVL=CHAR,NELM=7")
# Example write SDO to device, will be written on demand (when write data from epics):
ecmcCANOpenAddSDO("basicConfig",3,0x583,0x603,1,0x2690,0x1,7,0)
dbLoadRecords(ecmcPluginSocketCAN_SDO_output.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=BasicConfig,source=basicConfig,DTYP=asynInt8ArrayOut,FTVL=CHAR,NELM=7")
```
## ecmcCANOpenAddPDO
Issueing "ecmcCANOpenAddPDO -h" in the iocsh you will get the following help printout:
```
ecmcCANOpenAddPDO -h
Use "ecmcCANOpenAddPDO(<name>, <node id>
<name> : Name of master device.
<node id> : CANOpen node id of device/master.
<cob id> : CANOpen cob id of PDO.
<dir> : Direction 1=write and 2=read.
<ODSize> : Size of PDO (max 8 bytes).
<readTimeoutMs> : Readtimeout in ms.
<writeCycleMs> : Cycle time for write (if <= 0 then only write on change).
```
An example:
```
# Read PDO, if not recived pdo within 10s the PDO object will go into error state:
ecmcCANOpenAddPDO("status",3,0x183,2,8,10000,0)
dbLoadRecords(ecmcPluginSocketCAN_PDO_input.template, "P=${ECMC_PREFIX},PORT=${ECMC_ASYN_PORT},ADDR=0,TIMEOUT=1,T_SMP_MS=${ECMC_SAMPLE_RATE_MS},TSE=${ECMC_TSE},NELM=${NELM=1},CH_ID=03,DEV_ID=3,suffix=PDO01-Array,source=status,NELM=8")
```
# Plugin
Plugin report printout:
```
Plugin info:
Index = 0
Name = ecmcPlugin_socketcan
Description = SocketCAN plugin for use with ecmc.
Option description =
DBG_PRINT=<1/0> : Enables/disables printouts from plugin, default = disabled (=0).
IF=<if name> : Sets can interface (example: can0, vcan0..).
CONNECT=<1/0> : Auto connect to if at startup, default = autoconnect (=1).
Filename = /home/dev/epics/base-7.0.4/require/3.4.0/siteMods/ecmc_plugin_socketcan/master/lib/linux-x86_64/libecmc_plugin_socketcan.so
Config string = IF=vcan0;DBG_PRINT=1;
Version = 2
Interface version = 65536 (ecmc = 65536)
max plc funcs = 64
max plc func args = 10
max plc consts = 64
Construct func = @0x7fb36b81d310
Enter realtime func = @0x7fb36b81d220
Exit realtime func = @0x7fb36b81d230
Realtime func = @0x7fb36b81d270
Destruct func = @0x7fb36b81d240
dlhandle = @0xa45a50
Plc functions:
funcs[00]:
Name = "can_connect();"
Desc = double can_connect() : Connect to can interface (from config str).
Arg count = 0
func = @0x7fb36b81d280
funcs[01]:
Name = "can_connected();"
Desc = double can_connected() : Connected to can interface.
Arg count = 0
func = @0x7fb36b81d2a0
funcs[02]:
Name = "can_add_write(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);"
Desc = double can_add_write(canId,len,data0..data7) : Add write frame to can interface output buffer.
Arg count = 10
func = @0x7fb36b81d2e0
funcs[03]:
Name = "can_last_writes_error();"
Desc = double can_last_writes_error() : get error from last writes.
Arg count = 0
func = @0x7fb36b81d2c0
Plc constants:
```
# ecmcByteToArray Asub record
This module implements an asub record that can merge bytes into an array (waveform)
Usage:
1. Link bytes to inputs A..S.
2. Link array to VALA.
3. Set size of output in NOVA. This also defines how many inputs will be used.
Note: Max 18 bytes (input A..S) will be merged into the array.
EPICS database example of 7 bytes merged intio an array:
```
record(aSub, "$(P)CAN${CH_ID}-BasicConfigPackArray_") {
field(INAM, "ecmcByteToArrayInit")
field(SNAM, "ecmcByteToArray")
field(FTA, "UCHAR")
field(NOA, "1")
field(INPA, "$(P)CAN${CH_ID}-BasicConfigB0_.VAL") # Byte 0
field(FTB, "UCHAR")
field(NOB, "1")
field(INPB, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB1_.VAL") # Byte 1
field(FTC, "UCHAR")
field(NOC, "1")
field(INPC, "$(P)CAN${CH_ID}-VrefPwrCmdCalcB2_.VAL") # Byte 2
field(FTD, "UCHAR")
field(NOD, "1")
field(INPD, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB3_.VAL") # Byte 3
field(FTE, "UCHAR")
field(NOE, "1")
field(INPE, "$(P)CAN${CH_ID}-VdcCtrlCmdCalcB4_.VAL") # Byte 4
field(FTF, "UCHAR")
field(NOF, "1")
field(INPF, "0") # Byte 5
field(FTG, "UCHAR")
field(NOG, "1")
field(INPG, "0") # Byte 6
field(FTVA, "UCHAR")
field(OUTA, "$(P)CAN${CH_ID}-SDO02-BasicConfig")
field(NOVA, "7") # 7 bytes (0..6 corresponds to input A..G)
field(FLNK, "$(P)CAN${CH_ID}-SDO02-BasicConfig.PROC") # Send the data
}
```
# Testing
You can use a virtual can, vcan network for testing:
## Prerequirements
Install can utils:
```
$ git clone https://github.com/linux-can/can-utils
$ cd can-utils
$ make
$ make install
```
## vccn setup
Start virt can 0 (vcan0) and candump:
```
$ sudo modprobe vcan
$ sudo ip link add dev vcan0 type vcan
$ sudo ip link set up vcan0
$ candump vcan0
```
candump will start to printout the raw can meassages on vcan0 network.
# Other intressting things
In future it could be an option to use one of these repos for the CANOpen support:
1. https://github.com/CANopenNode/CANopenNode
2. https://github.com/CANopenNode/CANopenSocket
3. https://github.com/marel-keytech/openCANopen