parent
2d188b935a
commit
d36df8372a
@ -1,80 +0,0 @@ |
|||||||
# GEX core protocol |
|
||||||
|
|
||||||
## Framing layer |
|
||||||
|
|
||||||
GEX uses [TinyFrame](https://github.com/MightyPork/TinyFrame) to form and parse data frames. |
|
||||||
|
|
||||||
- Frames are sent and received using two USB bulk endpoints (VCOM or raw access) or through UART. |
|
||||||
- Transmitted frame length is virtually unlimited |
|
||||||
- Received frame length is limited by the Rx buffer size. |
|
||||||
|
|
||||||
### Frame structure |
|
||||||
|
|
||||||
```none |
|
||||||
,------+----------+---------+------+------------+- - - - -+------------, |
|
||||||
| SOF | frame_id | pld_len | type | head_cksum | payload | pld_cksum | |
|
||||||
| 1 | 2 | 2 | 1 | 1 | ... | 1 | <- size (bytes) |
|
||||||
'------+----------+---------+------+------------+- - - - -+------------' |
|
||||||
``` |
|
||||||
|
|
||||||
- SOF byte is `0x01` |
|
||||||
- Checksum = inverted XOR of all bytes |
|
||||||
|
|
||||||
The checksum is used to catch implementation mistakes (such as mistaken CR-LF transformations). |
|
||||||
USB bulk transfers have CRC error checking built-in. |
|
||||||
|
|
||||||
*Frame ID* is incremented with each transaction (message, request-response or longer). |
|
||||||
The highest ID bit identifies the peer who started the transaction. |
|
||||||
|
|
||||||
*Type* defines the meaning of the frame. A list is attached below. |
|
||||||
|
|
||||||
## Message types |
|
||||||
|
|
||||||
### General messages |
|
||||||
- `0x00` ... `SUCCESS` - Generic success response; used by default in all responses; payload is transaction-specific |
|
||||||
- `0x01` ... `PING` - Ping request, used to test connection |
|
||||||
- Response: ASCII string with the version and platform name |
|
||||||
- `0x02` ... `ERROR` - Generic failure response (when a request fails to execute) |
|
||||||
- Response: ASCII string with the error message |
|
||||||
|
|
||||||
### Bulk transfer (large multi-frame transfer) |
|
||||||
Bulk transfer is used for reading / writing large files that exceed the TinyFrame buffer sizes. |
|
||||||
|
|
||||||
- `0x03` ... `BULK_READ_OFFER` - Offer of data to read. |
|
||||||
- Payload: u32 total len |
|
||||||
- `0x04` ... `BULK_READ_POLL` - Request to read a previously announced chunk (`BULK_READ_OFFER`) |
|
||||||
- Payload: u32 max chunk size to read |
|
||||||
- Response: a `BULK_DATA` frame |
|
||||||
- `0x05` ... `BULK_WRITE_OFFER` - Offer to receive data in a write transaction. |
|
||||||
- This is an affirmation to some other frame that introduced a request to write bulk data. |
|
||||||
- Payload: u32 max total size, u32 max chunk size |
|
||||||
- `0x06` ... `BULK_DATA` - Writing a chunk, or sending a chunk to master. |
|
||||||
- `0x07` ... `BULK_END` - Bulk transfer is done, no more data to read or write. Recipient shall check total len and discard the received data on mismatch. |
|
||||||
- `0x08` ... `BULK_ABORT` - Discard the ongoing transfer (sent by either peer) |
|
||||||
|
|
||||||
### Unit messages |
|
||||||
Units are instances of a particular unit driver. A unit driver is e.g. Digital Output or SPI. |
|
||||||
Each unit has a TYPE (which driver to use), NAME (for user convenience) and CALLSIGN (for messages) |
|
||||||
|
|
||||||
In the INI file, all three are shown in the unit section: `[type:name@callsign]` |
|
||||||
|
|
||||||
Units can accept commands and generate events. Both are identified by 1-byte codes. |
|
||||||
|
|
||||||
- `0x10` ... `UNIT_REQUEST` - Command addressed to a particular unit |
|
||||||
- Payload: u8 callsign, u8 command, rest - data for a unit driver |
|
||||||
- `0x11` ... `UNIT_REPORT` - Spontaneous report from a unit |
|
||||||
- Payload: u8 callsign, u8 type, u64 usec timestamp, rest: data from unit driver |
|
||||||
|
|
||||||
### System messages |
|
||||||
Settings management etc. |
|
||||||
|
|
||||||
- `0x20` ... `LIST_UNITS` - Get all active unit call-signs, types and names |
|
||||||
- Response: u8 count, { u8 callsign, cstring type, cstring name } |
|
||||||
- `0x21` ... `INI_READ` - Read the ini file via bulk |
|
||||||
- starts a bulk read transfer |
|
||||||
- Response: a `BULK_READ_OFFER` frame |
|
||||||
- `0x22` ... `INI_WRITE` - Write the ini file via bulk |
|
||||||
- starts a bulk write transfer |
|
||||||
- Response: a `BULK_WRITE_OFFER` frame |
|
||||||
- `0x23` ... `PERSIST_CFG` - Write current settings to Flash (the equivalent of replacing the lock jumper / pushing the button if VFS is active) |
|
||||||
|
|
@ -0,0 +1,111 @@ |
|||||||
|
# GEX's low-level communication protocol |
||||||
|
|
||||||
|
## Framing layer |
||||||
|
|
||||||
|
GEX uses [TinyFrame](https://github.com/MightyPork/TinyFrame) to form and parse data frames. |
||||||
|
|
||||||
|
- Frames are normally sent and received using two USB bulk endpoints (VCOM or raw access) or through UART. |
||||||
|
- Transmitted frame length is virtually unlimited |
||||||
|
- Received frame length is limited by the Rx buffer size. This is overcome using a Bulk Write function. |
||||||
|
|
||||||
|
### Frame structure |
||||||
|
|
||||||
|
See the TinyFrame documentation for detailed description of the frame structure and protocol functions. |
||||||
|
|
||||||
|
Here's the configuration used by GEX: |
||||||
|
|
||||||
|
```none |
||||||
|
,------+----------+---------+------+------------+- - - - -+------------, |
||||||
|
| SOF | frame_id | pld_len | type | head_cksum | payload | pld_cksum | |
||||||
|
| 1 | 2 | 2 | 1 | 1 | ... | 1 | <- size (bytes) |
||||||
|
'------+----------+---------+------+------------+- - - - -+------------' |
||||||
|
``` |
||||||
|
|
||||||
|
- SOF byte is `0x01` |
||||||
|
- Checksum = inverted XOR of all bytes |
||||||
|
|
||||||
|
USB bulk transfers have CRC error checking built-in. The checksum field is mainly used to catch |
||||||
|
implementation mistakes, such as a CR-LF transformation. |
||||||
|
|
||||||
|
*Frame ID* is incremented with each transaction (message, request/response, or longer). |
||||||
|
The highest ID bit identifies the peer who started the transaction ("master", "slave"). |
||||||
|
|
||||||
|
*Type* defines the meaning of the frame within a higher level protocol. A list of all types is attached below. |
||||||
|
|
||||||
|
## Message types |
||||||
|
|
||||||
|
### General messages |
||||||
|
- `0x00` ... `SUCCESS` - Generic success response; used by default in all responses; |
||||||
|
- *Payload:* |
||||||
|
- optional, differs based on the request |
||||||
|
- `0x01` ... `PING` - Ping request, used to test connection |
||||||
|
- *Response:* |
||||||
|
- `SUCCESS` frame with an ASCII string containing the GEX version and the hardware platform name |
||||||
|
- `0x02` ... `ERROR` - Generic failure response, used when a request fails to execute |
||||||
|
- *Payload:* |
||||||
|
- ASCII string with the error message |
||||||
|
|
||||||
|
### Bulk transfer (large multi-frame transfer) |
||||||
|
Bulk transfer is used for reading / writing large files that exceed the TinyFrame buffer sizes. |
||||||
|
|
||||||
|
- `0x03` ... `BULK_READ_OFFER` - Offer of data to read. |
||||||
|
- *Payload:* |
||||||
|
- u32 - total len |
||||||
|
- `0x04` ... `BULK_READ_POLL` - Request to read a previously announced chunk (`BULK_READ_OFFER`) |
||||||
|
- *Payload:* |
||||||
|
- u32 - max chunk size to read |
||||||
|
- Response: a `BULK_DATA` frame (see below) |
||||||
|
- `0x05` ... `BULK_WRITE_OFFER` - Offer to receive data in a write transaction. |
||||||
|
- This is a report of readiness after some other frame introduced a request to write bulk data |
||||||
|
(e.g. writing a config file) |
||||||
|
- *Payload:* |
||||||
|
- u32 - max total size |
||||||
|
- u32 - max chunk size |
||||||
|
- `0x06` ... `BULK_DATA` - Writing a chunk, or sending a chunk to master. |
||||||
|
- *Payload:* |
||||||
|
- a block of data |
||||||
|
- `0x07` ... `BULK_END` - Bulk transfer is done, no more data to read or write. Recipient shall check total length and discard the received data on mismatch. |
||||||
|
- `0x08` ... `BULK_ABORT` - Abort and discard the ongoing transfer (sent by either peer) |
||||||
|
|
||||||
|
### Unit messages |
||||||
|
Units are instances of a particular unit driver. A unit driver is e.g. Digital Output or SPI. |
||||||
|
Each unit has a TYPE (which driver to use), NAME (for user convenience) and CALLSIGN (for messages) |
||||||
|
|
||||||
|
In the INI file, all three are shown in the unit section: `[type:name@callsign]` |
||||||
|
|
||||||
|
Units can accept commands and generate events. Both are identified by 1-byte codes. |
||||||
|
|
||||||
|
- `0x10` ... `UNIT_REQUEST` - Command addressed to a particular unit |
||||||
|
- *Payload:* |
||||||
|
- u8 callsign |
||||||
|
- u8 command |
||||||
|
- rest - data for a unit driver |
||||||
|
- `0x11` ... `UNIT_REPORT` - Spontaneous report from a unit |
||||||
|
- *Payload:* |
||||||
|
- u8 callsign |
||||||
|
- u8 report type |
||||||
|
- u64 usec timestamp |
||||||
|
- rest - data from unit driver |
||||||
|
|
||||||
|
### System messages |
||||||
|
Settings management etc. |
||||||
|
|
||||||
|
- `0x20` ... `LIST_UNITS` - Get all active unit call-signs, types and names |
||||||
|
- *Response:* |
||||||
|
- u8 number of units |
||||||
|
- repeat for each unit: |
||||||
|
- u8 callsign |
||||||
|
- cstring type |
||||||
|
- cstring name |
||||||
|
- `0x21` ... `INI_READ` - Read the ini file via bulk |
||||||
|
- starts a bulk read transfer |
||||||
|
- *Response:* |
||||||
|
- a `BULK_READ_OFFER` frame (see above) |
||||||
|
- `0x22` ... `INI_WRITE` - Write the ini file via bulk |
||||||
|
- starts a bulk write transfer |
||||||
|
- *Response:* |
||||||
|
- a `BULK_WRITE_OFFER` frame (see above) |
||||||
|
- `0x23` ... `PERSIST_CFG` - Write current settings to Flash |
||||||
|
- This is the equivalent of replacing the lock jumper / pushing the button when the virtual filesystem is enabled. |
||||||
|
- If the virtual filesystem is enabled, this also closes it. |
||||||
|
|
@ -0,0 +1,97 @@ |
|||||||
|
# GEX's config files |
||||||
|
|
||||||
|
GEX's configuration is stored in the internal flash memory in a packed binary form. |
||||||
|
To make its editing easier for the user, the configuration is accessible as INI files |
||||||
|
annotated by numerous comments explaining the individual config options. |
||||||
|
|
||||||
|
## Filesystem access |
||||||
|
|
||||||
|
There are two config files: `UNITS.INI` and `SYSTEM.INI`. |
||||||
|
|
||||||
|
To access those files, the user presses the LOCK button (or removes the LOCK jumper), |
||||||
|
which enables an emulated FAT16 storage that will appear on the host computer as a USB disk. |
||||||
|
Some hardware platforms require that the jumper be removed before plugging in the USB cable, |
||||||
|
as it can't trigger re-enumeration without external circuitry, which is missing on those |
||||||
|
evaluation boards. |
||||||
|
|
||||||
|
It's recommended to copy the INI files to a real disk before editing, as some editors create |
||||||
|
temporary back-up files that can confuse the filesystem emulator. However, in-place editing |
||||||
|
is also possible with some editors. |
||||||
|
|
||||||
|
*This method is the only way of accessing the `SYSTEM.INI` file, which can reconfigure |
||||||
|
the main communication port.* |
||||||
|
|
||||||
|
After writing the changed files back to the virtual disk, it will briefly disappear |
||||||
|
and an updated version of the files will become available for review. |
||||||
|
This is when you can check for error messages. |
||||||
|
|
||||||
|
To confirm the config modifications (they take effect imemdiately, but are not written to flash yet), |
||||||
|
push the LOCK button again (or replace the LOCK jumper). |
||||||
|
|
||||||
|
## API access |
||||||
|
|
||||||
|
The `UNITS.INI` file may be accessed through the communication port. |
||||||
|
|
||||||
|
The file is read using Bulk Read (see [FRAME_FORMAT.md](FRAME_FORMAT.md)) after sending a `INI_READ` request. |
||||||
|
|
||||||
|
The modified file is written back using Bulk Write after sending a `INI_WRITE` request. |
||||||
|
|
||||||
|
After writing the changed configuration, it may be persisted to flash using a `PERSIST_CFG` command. |
||||||
|
This is not required if the new configuration will be used only temporarily; the original settings |
||||||
|
will be restored after a restart. |
||||||
|
|
||||||
|
## UNITS.INI file structure |
||||||
|
|
||||||
|
The `UNITS.INI` file is interactive. The general layout is as follows: |
||||||
|
|
||||||
|
```ini |
||||||
|
## UNITS.INI |
||||||
|
## GEX v0.0.1 on STM32F072-HUB |
||||||
|
## built Mar 17 2018 at 17:53:15 |
||||||
|
|
||||||
|
# Overwrite this file to change settings. |
||||||
|
# Press the LOCK button to save them to Flash. |
||||||
|
|
||||||
|
[UNITS] |
||||||
|
# Create units by adding their names next to a type (e.g. DO=A,B), |
||||||
|
# remove the same way. Reload to update the unit sections below. |
||||||
|
|
||||||
|
# Digital output |
||||||
|
DO= |
||||||
|
# Digital input with triggers |
||||||
|
DI=btn,btn2 |
||||||
|
# (... more unit types here) |
||||||
|
|
||||||
|
[DO:btn@1] |
||||||
|
# Port name |
||||||
|
port=B |
||||||
|
# Pins (comma separated, supports ranges) |
||||||
|
pins=2 |
||||||
|
# Initially high pins |
||||||
|
initial=2 |
||||||
|
# Open-drain pins |
||||||
|
open-drain= |
||||||
|
``` |
||||||
|
|
||||||
|
- The keys in the `[UNITS]` section are lists of instances for each unit type. |
||||||
|
After adding a name to the list and saving the file, a new unit section will appear below, offering to configure |
||||||
|
the new unit as needed. |
||||||
|
- Each unit section starts with `[type:name@callsign]`.The name must match the name used in the unit lists above. |
||||||
|
The callsign can be freely changed, as long as there is no conflict. Callsign 0 is forbidden and will be changed |
||||||
|
to a new unique callsign on save. |
||||||
|
- If an error occurs during unit initialization, an error message will appear under the unit's section header. |
||||||
|
|
||||||
|
## GEX reconfiguration workflow |
||||||
|
|
||||||
|
1. Enable the mass storage using the LOCK button or jumper (or use the API for reading and writing the file) |
||||||
|
2. Open the `UNITS.INI` file in a text editor |
||||||
|
3. If adding or removing unit instances, add their names to the right type (eg. `DO=LED`), save and reload the file. |
||||||
|
On some systems, it's necessary to close the editor right after saving and then reopen it again after the disk |
||||||
|
refreshes. |
||||||
|
4. Configure the units as needed; adjust callsigns if desired. Save and reload the file. |
||||||
|
5. Check for error messages under unit headers. Correct any problems. Save and reload to apply any changes. |
||||||
|
6. Push the LOCK button or replace the jumper to persist all changes to flash. If using the API, issue |
||||||
|
the `PERSIST_CFG` command for the same effect. |
||||||
|
|
||||||
|
This is much easier when done through the API, perhaps using the PyQt GEX configuration utility included as an |
||||||
|
example in the Python client repository. |
@ -0,0 +1,44 @@ |
|||||||
|
# GEX's internal structure |
||||||
|
|
||||||
|
GEX is modular, composed of functional blocks called *units*. A unit can be e.g. SPI or a digital output. |
||||||
|
|
||||||
|
There can be more than one instance of a given unit, based on the microcontroller's hardware resources. |
||||||
|
Some units use hardware peripherals, others are implemented in software. Those "soft" units (typically |
||||||
|
bit-banged protocols) are limited only by the available GPIO pins. |
||||||
|
|
||||||
|
## Resources |
||||||
|
|
||||||
|
A system of resource claiming is implemented to avoid units conflicting with each other and interfering with |
||||||
|
peripherals used internally by the system (such as the LED pin). Each unit claims the resources it uses during |
||||||
|
initialization. If the resource needed is not available, it may try to claim an alternate resource or fail to |
||||||
|
initialize. |
||||||
|
|
||||||
|
A init failure is reported in the config file as an error message under the unit's header. |
||||||
|
|
||||||
|
An example of defined resources: |
||||||
|
|
||||||
|
- all GPIO pins |
||||||
|
- timers/counters |
||||||
|
- SPI, I2C, USART peripherals |
||||||
|
- DMA channels |
||||||
|
|
||||||
|
## Unit identification |
||||||
|
|
||||||
|
Each unit has a *name* and a *callsign* (1-255) used for message addressing. Both are defined in the config file. |
||||||
|
|
||||||
|
There can be up to 255 units, in practice fewer due to storage limitations. |
||||||
|
|
||||||
|
## Unit commands and events |
||||||
|
|
||||||
|
Units can receive command messages (`UNIT_REQUEST`) and report asynchronous events using `UNIT_REPORT` frames. |
||||||
|
|
||||||
|
A command is identified by a number 0-127. The highest bit (0x80) of the command number is reserved to request |
||||||
|
confirmation (a `SUCCESS` frame) if the command doesn't normally generate a response. This is used by the client |
||||||
|
library to check that an operation was completed correctly (as opposed to e.g. the firmware crashing and rebooting |
||||||
|
through the watchdog). |
||||||
|
|
||||||
|
Unit events include the unit's callsign, a number 0-255 identifying the event type, and a 64-bit microsecond |
||||||
|
timestamp. |
||||||
|
|
||||||
|
See [FRAME_FORMAT.md](FRAME_FORMAT.md) for the payloads structure. |
||||||
|
|
@ -1,3 +1,10 @@ |
|||||||
# GEX protocol documentation |
# GEX protocol documentation |
||||||
|
|
||||||
This repository specifies the control protocol implemented by [gex-core](https://github.com/gexpander/gex-core). |
This repository specifies the control protocol implemented by [gex-core](https://github.com/gexpander/gex-core). |
||||||
|
|
||||||
|
- [FRAME_FORMAT.md](FRAME_FORMAT.md) - Low level frame format, message types |
||||||
|
- [INTERNAL_STRUCTURE.md](INTERNAL_STRUCTURE.md) - GEX's internal structure, units, requests and events |
||||||
|
- [INI_FILES.md](INI_FILES.md) - INI config files, filesystem and API access |
||||||
|
|
||||||
|
- [UNIT_DO.md](UNIT_DO.md) - Digital Output unit |
||||||
|
|
||||||
|
@ -0,0 +1,52 @@ |
|||||||
|
# Digital Output |
||||||
|
|
||||||
|
- Direct output access to one or more pins of a port. |
||||||
|
- All selected pins are written simultaneously. |
||||||
|
- Supports generating output pulses of precise length. |
||||||
|
|
||||||
|
Pins are ordered in a descending order (DOWNTO) and accessed in a packed format, |
||||||
|
e.g. if pins 1 and 4 are selected (0b10010 on the port), the control word has |
||||||
|
two bits (4)(1) and e.g. 0b10 means pin 4, 0b11 both. This makes accessing the |
||||||
|
block of pins easier, e.g. when using them to drive a parallel bus. |
||||||
|
|
||||||
|
## Commands |
||||||
|
|
||||||
|
### WRITE |
||||||
|
Write a value to all defined pins. |
||||||
|
|
||||||
|
*Payload:* |
||||||
|
- u16 - new value (packed) |
||||||
|
|
||||||
|
### SET |
||||||
|
Set pins high |
||||||
|
|
||||||
|
*Payload:* |
||||||
|
- u16 - pins to set high (packed) |
||||||
|
|
||||||
|
### CLEAR |
||||||
|
Set pins low |
||||||
|
|
||||||
|
*Payload:* |
||||||
|
- u16 - pins to set low (packed) |
||||||
|
|
||||||
|
### TOGGLE |
||||||
|
Toggle selected pins (high - low) |
||||||
|
|
||||||
|
*Payload:* |
||||||
|
- u16 - pins to toggle (packed) |
||||||
|
|
||||||
|
### PULSE |
||||||
|
Send a pulse. The start will be aligned to 1 us or 1 ms (based on pulse length) of the internal timebase to ensure the highest length precision. (This alignment reduces time jitter.) |
||||||
|
|
||||||
|
*Payload:* |
||||||
|
- u16 - pins to generate the pulse on (packed) |
||||||
|
- u8 - pulse active level (0, 1) |
||||||
|
- u8 - range (0 - milliseconds, 1 - microseconds) |
||||||
|
- u16 - duration in the selected range |
||||||
|
|
||||||
|
*NOTE:* The microsecond range supports durations only up to 999 (1 ms), higher |
||||||
|
numbers will be divided by 1000 and use the millisecond range instead. |
||||||
|
|
||||||
|
## Events |
||||||
|
|
||||||
|
*No events defined for this unit type.* |
Loading…
Reference in new issue