-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
54 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,11 @@ This is a device support module for the EK9000, which couples EtherCAT terminals | |
Because it uses modbus, it's runnable on any IOC without needing a kernel module or other support software. | ||
|
||
## Dependencies | ||
|
||
This module depends on Mark River's modbus device support module/driver. You can grab that from here and make sure to grab it's dependencies too. | ||
|
||
## Building | ||
|
||
See the wiki page for instructions on building and a small example. | ||
|
||
## Contributing | ||
|
@@ -18,16 +20,19 @@ If you'd like to contribute to this, you can open a pull request with changes, a | |
If you have any questions or comments about the module, you can reach me at [email protected] | ||
|
||
# Known Issues | ||
|
||
The EK9000 doesn't really make figuring out a register map easy, so please keep these things in mind: | ||
|
||
* If you install an unsupported terminal, the device support module may fail to correctly determine the register map, resulting in weirdness and stuff. | ||
* If you place an unsupported analog ONLY terminal AFTER all other slaves on the EK9000, the register map will be correct. This is because the device maps all analog terminals in order of their position on the rail. Digital terminals are mapped to coils, and therefore, not affected by weirdness in the holding/input register space. | ||
* If you place an unsupported digital ONLY terminal AFTER all other slaves on the EK9000, the register map will also be correct. | ||
* If you place an unsupported digital ONLY terminal AFTER all other slaves on the EK9000, the register map will also be correct. | ||
* If you change the Pdo mapping for analog input terminals to something other than standard, the analog IO AND register mapping will not work correctly. | ||
To recap: | ||
* Only supported terminals (EL10XX, EL11XX, EL20XX, EL30XX, EL31XX, EL40XX) should be placed on the rail first. Unsupported terminals can be placed after the ones you are trying to access through EPICS. | ||
* Only supported terminals (EL10XX, EL11XX, EL20XX, EL30XX, EL31XX, EL40XX) should be placed on the rail first. Unsupported terminals can be placed after the ones you are trying to access through EPICS. | ||
* Analog input Pdo mapping should ALWAYS be standard, or else the module will not work. | ||
|
||
# Supported Terminals | ||
## Supported Terminals | ||
|
||
* EL10XX (EL1004, etc.) | ||
* EL11XX (EL1124, etc.) | ||
* EL20XX (EL2008, etc.) | ||
|
@@ -36,5 +41,6 @@ To recap: | |
* EL31XX (EL3174, etc.) | ||
* EL40XX (EL4004, etc.) | ||
* EL41XX (EL4104, etc.) | ||
Anything else is generally not supported. | ||
Support for motor terminals (EL70XX) is in progress. | ||
|
||
Any other terminals are not generally supported, but a few exceptions exist. | ||
Support for motor terminals (EL70XX) is currently being developed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,68 @@ | ||
# EPICS Device Support for the EK9000 | ||
|
||
This is the manual for the EK9000 device support module for EPICS 3.14 and later. | ||
|
||
If you don't know already, the EK9000 is a low-cost Modbus to EtherCAT coupler. It allows you to control EtherCAT terminals (such as the EL3064 analog input) using a simple Modbus interface, which can be used on virtually any IOC with internet access. | ||
|
||
## Building and Installing | ||
This device support module was built and tested with EPICS R3.14, but should function fine in EPICS 7 or any earlier versions of EPICS. | ||
|
||
This device support module was built and tested with EPICS R3.14, but should function fine in EPICS 7 or any earlier versions of EPICS. | ||
|
||
This module depends on the EPICS modbus module (R3-0 or later) and subsequently requires the asyn driver. | ||
|
||
Once you clone this module to your workspace, you'll need to change some of the configuration Makefiles to reflect your personal envrionment. Once done, building is pretty easy: | ||
Once you clone this module to your workspace, you'll need to change some of the configuration Makefiles to reflect your personal environment. Once done, building is pretty easy: | ||
|
||
```bash | ||
make | ||
make | ||
``` | ||
|
||
You can run the test IOC with `./ek9000Test st.cmd` or with `./st.cmd` from the `iocBoot` directory. | ||
|
||
## How It Works | ||
|
||
Although the EK9000 is pretty easy to access with Modbus, it has some odd quirks and shortcomings that made making this module somewhat difficult, so I thought I'd include an explanation of how the module functions. | ||
|
||
### Initialization | ||
|
||
There are two steps to initialization: startup script functions and the actual record initialization. | ||
|
||
In the startup script, the function `ek9000Configure(name, ip, port, slavecount)` is used to create an EK9000 device at the specified ip and port with the specified number of slaves. | ||
Internally, it allocates memory for terminal structures and holds some important parameters. | ||
In the startup script, the function `ek9000Configure(name, ip, port, slave_count)` is used to create an EK9000 device at the specified ip and port with the specified number of slaves. | ||
Internally, it allocates memory for terminal structures and holds some important parameters. | ||
|
||
Once the device is created, it will try to connect to the device. If it can't connect, the records and other startup script functions (such as `ek9000ConfigureTerminal`) will fail. | ||
|
||
#### Startup script | ||
|
||
After this, you can use `ek9000ConfigureTerminak(ek9000_name, record_name, type, slave_number)` to tell the driver to expect a terminal attached to the specified ek9000 with the given type, record name and slave position. The parameter `record_name` is the **base** name of the record that this terminal is supposed to be attached to. | ||
Type is an integral value representing the terminal type, for example, 3064, which is found in the string EL3064. | ||
`slave_number` simply represents the position on the rail: keep in mind, this is a 1-based index. | ||
After this, you can use `ek9000ConfigureTerminal(ek9000_name, record_name, type, slave_number)` to tell the driver to expect a terminal attached to the specified ek9000 with the given type, record name and slave position. The parameter `record_name` is the **base** name of the record that this terminal is supposed to be attached to. | ||
Type is an integral value representing the terminal type, for example, 3064, which is found in the string EL3064. | ||
`slave_number` simply represents the position on the rail: keep in mind, this is a 1-based index. | ||
|
||
It's important to make sure that the type of terminal that you specify actually matches what's on the rail. | ||
It's important to make sure that the type of terminal that you specify actually matches what's on the rail. | ||
The module **WILL** verify that they match, and throw an error if they don't. | ||
This also means that you can't initialize terminals without a connection to the device. | ||
|
||
After those functions are called, you can load a database or a substitutions file that contains the records you want to be used by terminals attached to that ek9000. It's important that you ONLY load these records after you've registerd all terminals to the device. | ||
After those functions are called, you can load a database or a substitutions file that contains the records you want to be used by terminals attached to that ek9000. It's important that you ONLY load these records after you've registered all terminals to the device. | ||
|
||
#### Record initialization | ||
|
||
During record initialization, terminals with the `DTYP` field set to `EL10XX`, `EL11XX`, etc. will initiate a search for the terminal they're supposed to be attached to. | ||
During record initialization, terminals with the `DTYP` field set to `EL10XX`, `EL11XX`, etc. will initiate a search for the terminal they're supposed to be attached to. | ||
The name of the record usually will be suffixed with a `:x`, where x is an integer representing the channel that the record represents. | ||
The string preceding the `:x` acts as the base record name, which you specified in your init script. | ||
I decided to do this, because it made sense to me.. Comments on this design decision are welcome. | ||
|
||
For example, if you use the template `EL1002.template`, and substitute all the stuff you want to, the records `MyTerminal1:1` and `MyTerminal1:2` will be created. | ||
For example, if you use the template `EL1002.template`, and substitute all the stuff you want to, the records `MyTerminal1:1` and `MyTerminal1:2` will be created. | ||
Given that you ran `ek9000ConfigureTerminal("EK9K1", "MyTerminal1", 1002, 1)` or something similar in your ioc init script, the records will initialize successfully. | ||
|
||
Once the record initializes, it associates some private data with itself which contains a pointer to the descriptor of the terminal and some other stuff. | ||
|
||
#### Final initialization | ||
|
||
The final thing the module does is compute a register map for the terminals inside of the EK9000's register space. | ||
The EK9000 will map the PDO (program data objects) from each slave into it's Modbus register space. Digital inputs will be mapped to discrete input coils and digital outputs will be mapped to output coils. Analog inputs get mapped to input registers and analog outputs are mapped to output registers. | ||
The EK9000 will map the PDO (program data objects) from each slave into it's Modbus register space. Digital inputs will be mapped to discrete input coils and digital outputs will be mapped to output coils. Analog inputs get mapped to input registers and analog outputs are mapped to output registers. | ||
|
||
Unfortunately, there is no way to ask the EK9000 for a register map, instead we have to use a register space (Starting at 0x6000) to figure out where each terminal is located on the rail. | ||
This doesn't tell us anything about the size of the PDOs for each slave, nor does it tell us about the PDO types. Thus, all PDO sizes are hard-coded into the module (well, they're actually in an autogenerated header). | ||
Devices with multiple PDO types, for example the EL3064 which has a compact and a standard PDO, can't really have their PDO sizes prediced, as they can be set to who knows what. In the next release of the module, a remedy for this is planned (it will actually involve a big restructure of the initialization commands). For all intents and purposes, the module will assume all devices have a PDO set to standard (if they have multiple PDO types). | ||
This doesn't tell us anything about the size of the PDOs for each slave, nor does it tell us about the PDO types. Thus, all PDO sizes are hard-coded into the module (well, they're actually in an auto generated header). | ||
Devices with multiple PDO types, for example the EL3064 which has a compact and a standard PDO, can't really have their PDO sizes predicted, as they can be set to who knows what. In the next release of the module, a remedy for this is planned (it will actually involve a big restructure of the initialization commands). For all intents and purposes, the module will assume all devices have a PDO set to standard (if they have multiple PDO types). | ||
|
||
With this in mind, the module will loop through all registered terminals and determine the register map in the same way the EK9000 does. | ||
It will store this info with a terminal's descriptor, which will then be used by the reading and writing functions. | ||
|
@@ -71,14 +76,15 @@ Reading from a digital input terminal is done in a similar way, except we read f | |
### Reading/Writing to analog terminals | ||
|
||
Once again, using the channel that is determined from the record name, the module computes which registers need to be read with the following equation: | ||
``` | ||
|
||
```c | ||
register = base_register + ((channel-1) * channel_size) | ||
``` | ||
In the above example, `channel_size` refers to the size of each channel in modbus registers. Since modbus registers are 16-bits wide, `channel_size` will equal 2, since each channel is 4 bytes wide by default | ||
|
||
|
||
In the above example, `channel_size` refers to the size of each channel in modbus registers. Since modbus registers are 16-bits wide, `channel_size` will equal 2, since each channel is 4 bytes wide by default | ||
|
||
## Supported Terminals | ||
|
||
This module supports most digital and analog I/O terminals, but a few are not supported. You can use the following terminals normally with your EK9000: | ||
|
||
* EL10XX and EL11XX digital input terminals | ||
|
@@ -99,21 +105,22 @@ If you have an unsupported terminal, but want to use it on the same rail as othe | |
* When placing an unsupported terminal on the same rail as other terminals to be accessed by an EPICS IOC, place it last on the rail. | ||
|
||
Here's a visual example of these rules: | ||
| Slave Number | Terminal Type | Accessable by EPICS IOC | | ||
| Slave Number | Terminal Type | Accessible by EPICS IOC | | ||
|---|---|---| | ||
| 1 | EL3064 | Yes | | ||
| 2 | EL2008 | Yes | | ||
| 3 | EL7047 | No | | ||
| 4 | EL3064 | No | | ||
| 4 | EL3064 | No | | ||
| 5 | EL2008 | No | | ||
|
||
In the above table, all terminals that are placed after the **unsupported** slave (number 3), cannot be _properly_ accessed by the IOC. | ||
In the above table, all terminals that are placed after the **unsupported** slave (number 3), cannot be _properly_ accessed by the IOC. | ||
In the above example, slave 4, which is placed after the EL7047, will accidentally get mapped to the address space of slave 3, the unsupported terminal, since the module doesn't know the PDO sizes of the EL7047. | ||
|
||
## Startup Script Example | ||
|
||
Here's an example of how a rail and IOC might be set up. | ||
|
||
Lets assume the rail looks something like this: | ||
Lets assume the rail looks something like this: | ||
| Slave number | Terminal Type | | ||
|---|---| | ||
| 1 | EL3064 | | ||
|
@@ -123,6 +130,7 @@ Lets assume the rail looks something like this: | |
| 5 | EL1008 | | ||
|
||
The initialization script might look something like this: | ||
|
||
```bash | ||
... | ||
ek9000Configure("EK9K1", "192.168.1.3", 502, 5) | ||
|
@@ -137,24 +145,28 @@ dbLoadTemplate("MySubs.substitutions") | |
``` | ||
|
||
Lets assume that `MySubs.substitutions` contains the following records, which are created from the proper template file: | ||
|
||
* MyTerminal1 (Created from EL3064.template) | ||
* MyTerminal2 (Created from EL2008.template) | ||
* MyTerminal3 (Created from EL3154.template) | ||
* MyTerminal4 (Created from EL1004.template) | ||
* MyTerminal5 (Created from EL1008.template) | ||
|
||
Given this, you can write to the first digital output on slave 2 using: | ||
``` | ||
|
||
```bash | ||
caput MyTerminal1:1 1 | ||
``` | ||
|
||
You can also read from the 3rd analog input on slave 3 using: | ||
``` | ||
|
||
```bash | ||
caget MyTerminal3.RVAL | ||
``` | ||
|
||
## IOCsh Function Documentation | ||
|
||
``` | ||
```c | ||
---------------------------------------------------------- | ||
ek9000Configure(ek9k, ip, port, num_slaves) | ||
|
||
|
@@ -208,15 +220,19 @@ Params: | |
``` | ||
## Contributing | ||
If you'd like to contribute to this project, feed free to open a pull request at any time, and I'll hopefully get around to reviewing and merging it quickly. | ||
If you'd like to contribute to this project, feel free to open a pull request at any time! | ||
## Bugs | ||
If you find any bugs, you can open an issue or send me an email, and I'll hopefully get around to fixing it fairly quickly. | ||
## Suggestions or Requests | ||
If you have any suggestions or feature requests, you can either open an issue or email me at [email protected] | ||
If you have any suggestions or feature requests, you can either open an issue on the github repository or email me at [email protected] | ||
Usually I'll respond within a day or two. | ||
## Developers | ||
* Jeremy Lorelli ([email protected]) |