|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
Hitec servo programming protocols: the session with one servo, as a state machine that hands out steps and takes their results. More...
Go to the source code of this file.
Data Structures | |
| struct | hitec_ses_step_t |
| One step. More... | |
| struct | hitec_ses_result_t |
| The result of a step. More... | |
| struct | hitec_session_t |
| The session with one servo. More... | |
Macros | |
| #define | HITEC_SES_TRIES 3u |
| Tries of one transaction that has an answer: the first and 2 more. | |
| #define | HITEC_SES_C_GAP_MS 2u |
| Gap after a classic transaction, ms. | |
| #define | HITEC_SES_D_GAP_MS HITEC_D_WRITE_GAP_MS |
| Gap after a D series transaction, ms: the HFP-30's write spacing. | |
| #define | HITEC_SES_D_ENTRY_WAIT_MS HITEC_D_REBOOT_WAIT_MS |
| Wait after the supply comes on before the first D series frame, ms: the wait after a reboot. | |
| #define | HITEC_SES_C_ENTRY_WINDOW_US 2000000u |
| Classic entry: the window for the servo's first + from the ON, us. | |
| #define | HITEC_SES_C_NEXT_WINDOW_US 1000000u |
| Classic entry: the window for the servo's next +, us. | |
| #define | HITEC_SES_POWER_WINDOW_US 1000u |
| The window of the LISTEN of a POWER that waits for no byte, us: the D series entry and the classic leave. | |
| #define | HITEC_SES_OFF_MS 500u |
| Supply off time of the classic entry and leave, ms. | |
| #define | HITEC_SES_OFF_MS_MAX 10000u |
| Longest supply off time, ms. | |
| #define | HITEC_SES_SET_BYTES 32u |
| Bytes of an address set: one bit for each of 256 addresses. | |
Enumerations | |
| enum | hitec_ses_clock_t { HITEC_SES_CLOCK_US = 0 , HITEC_SES_CLOCK_MS = 1 } |
| The clock the caller passes. More... | |
| enum | hitec_ses_kind_t { HITEC_SES_STEP_XFER = 0 , HITEC_SES_STEP_POWER , HITEC_SES_STEP_WAIT , HITEC_SES_STEP_DONE } |
| What a step asks of the caller. More... | |
| enum | hitec_dec_t { HITEC_DEC_NONE = 0 , HITEC_DEC_C = 1 , HITEC_DEC_D = 2 , HITEC_DEC_LISTEN = 3 } |
| The decoder of a step's capture. More... | |
| enum | hitec_ses_run_t { HITEC_SES_RAN = 0 , HITEC_SES_CUT , HITEC_SES_FAILED } |
| How a step ended. More... | |
| enum | hitec_ses_t { HITEC_SES_OK = 0 , HITEC_SES_BUSY , HITEC_SES_ERR_BUSY , HITEC_SES_ERR_ARG , HITEC_SES_ERR_NOT_IN_MODE , HITEC_SES_ERR_NO_SERVO , HITEC_SES_ERR_ENTRY , HITEC_SES_ERR_READ , HITEC_SES_ERR_WRITE , HITEC_SES_ERR_VERIFY , HITEC_SES_ERR_PLAN , HITEC_SES_ERR_STEP , HITEC_SES_ERR_CUT , HITEC_SES_ERR_LINK , HITEC_SES_ERR_ABORTED } |
| Result of an operation. More... | |
Functions | |
| bool | hitec_session_init (hitec_session_t *s, uint8_t series, uint8_t clock) |
| Prepare a session for one series. | |
| hitec_ses_t | hitec_session_enter (hitec_session_t *s, bool power, uint16_t off_ms) |
| Enter programming mode. | |
| hitec_ses_t | hitec_session_read_all (hitec_session_t *s) |
| Read the series' full set: hitec_d_read_list() or hitec_c_read_ranges(). | |
| hitec_ses_t | hitec_session_read_set (hitec_session_t *s, const uint8_t *addrs, uint16_t n) |
Read n addresses into the image, which keeps the rest. | |
| hitec_ses_t | hitec_session_write (hitec_session_t *s, const hitec_image_t *target) |
Write the servo from the image to target, then read it back. | |
| hitec_ses_t | hitec_session_run_plan (hitec_session_t *s, const hitec_plan_t *plan, const hitec_image_t *expect) |
| Run a plan the caller built, such as hitec_d_factory_plan(), then read back as hitec_session_write() does. | |
| hitec_ses_t | hitec_session_leave (hitec_session_t *s) |
| Leave programming mode. | |
| bool | hitec_session_abort (hitec_session_t *s) |
| End the running operation with HITEC_SES_ERR_ABORTED. | |
| hitec_ses_kind_t | hitec_session_next (hitec_session_t *s, uint32_t now, hitec_ses_step_t *out) |
The step to run at now. | |
| hitec_ses_t | hitec_session_feed (hitec_session_t *s, uint32_t now, const hitec_ses_result_t *r) |
| Take the result of the step that is out. | |
| hitec_ses_t | hitec_session_result (const hitec_session_t *s) |
| The running operation's state. | |
| bool | hitec_ses_in_set (const uint8_t *set, uint8_t addr) |
True when address addr is in the set set. | |
Hitec servo programming protocols: the session with one servo, as a state machine that hands out steps and takes their results.
Implements the session of the Hitec servo programming protocols. Hitec is a name or trademark of its owner; it is used here only to say which servos this module works with (README.md, "Legal notice and disclaimer").
The session runs one operation at a time: entry, a read, a write plan with its read-back, leave. It neither touches the wire nor reads a clock. The caller asks for the next step with hitec_session_next(), runs it and hands its result to hitec_session_feed():
The supply's voltage and current limit are the caller's. So are the port, the refusals before an entry (a classic servo without a supply the bench switches) and the journal; step_index and the plan are there for it.
Time: the caller passes the reading of one free-running 32-bit clock, in microseconds or in milliseconds (hitec_ses_clock_t), to every call that takes now. The session measures the gap before a transaction from the result of the one before, by unsigned difference, so the wrap of the clock at 2^32 does not matter. A gap has passed once the clock has moved by more than the gap in its ticks: one tick more than the gap, since a reading lies up to one tick behind the time.
The session object holds all state. Two sessions on two ports run side by side. No heap, no global or static mutable state.
SPDX-License-Identifier: MIT
| #define HITEC_SES_C_NEXT_WINDOW_US 1000000u |
Classic entry: the window for the servo's next +, us.
Measured 250 to 267ms after its first one.
| enum hitec_dec_t |
The decoder of a step's capture.
The values are the HITEC link page's (link_hitec_dec_t).
| Enumerator | |
|---|---|
| HITEC_DEC_NONE | no decode: the result's rx is not read |
| HITEC_DEC_C | hitec_c_decode(), argument the command |
| HITEC_DEC_D | hitec_d_decode(), argument the register |
| HITEC_DEC_LISTEN | hitec_listen_decode(), argument the byte |
| enum hitec_ses_clock_t |
| enum hitec_ses_kind_t |
| enum hitec_ses_run_t |
| enum hitec_ses_t |
Result of an operation.
| bool hitec_ses_in_set | ( | const uint8_t * | set, |
| uint8_t | addr ) |
True when address addr is in the set set.
| set | HITEC_SES_SET_BYTES bytes, bit a % 8 of byte a / 8. |
| addr | Address. |
| bool hitec_session_abort | ( | hitec_session_t * | s | ) |
End the running operation with HITEC_SES_ERR_ABORTED.
With no step out it ends at once. With a step out it ends when that step's result comes: the caller cuts the step and feeds its result, which the session stores as far as it went. Until then hitec_session_next() gives the same step.
| s | Session. |
| hitec_ses_t hitec_session_enter | ( | hitec_session_t * | s, |
| bool | power, | ||
| uint16_t | off_ms ) |
Enter programming mode.
Classic: POWER (off off_ms, on, a LISTEN of HITEC_SES_C_ENTRY_WINDOW_US that answers the servo's + with +), a LISTEN of HITEC_SES_C_NEXT_WINDOW_US for the servo's next +, then the first command 67 00 00 99, whose reply's value is the version. A listen that gets no + ends the entry; the first command has HITEC_SES_TRIES tries. Classic entry always cycles the supply.
D series: with power, POWER (off off_ms, on, a LISTEN of HITEC_SES_POWER_WINDOW_US that waits for nothing) and HITEC_SES_D_ENTRY_WAIT_MS; without it, on the supply as it is. Then a read of register 0x00, the model, with HITEC_SES_TRIES tries.
The image starts empty and holds the version or the model. The off time is kept for the leave.
| s | Session. |
| power | D series: cycle the supply first; classic: ignored. |
| off_ms | Supply off time, 0 to HITEC_SES_OFF_MS_MAX. |
| hitec_ses_t hitec_session_feed | ( | hitec_session_t * | s, |
| uint32_t | now, | ||
| const hitec_ses_result_t * | r ) |
Take the result of the step that is out.
CUT ends the operation with HITEC_SES_ERR_CUT, FAILED with HITEC_SES_ERR_LINK. A step with a decoder: HITEC_RX_OK goes on; HITEC_RX_ERR_ARG ends it with HITEC_SES_ERR_STEP; HITEC_RX_ERR_ABORTED with HITEC_SES_ERR_CUT; every other code is a failed try. A failed try of a classic entry LISTEN ends the entry. A transaction with tries left goes again after its gap; one with none ends the operation with HITEC_SES_ERR_NO_SERVO when the last try was HITEC_RX_ERR_SILENT, else with the operation's error: ENTRY, READ or WRITE.
Every result starts the gap of the step that ended before anything else, a cut or lost step's included, since its frame may have reached the servo: the series gap, a plan step's longer wait, the D series entry's 1000ms, the 1000ms after a D series leave. An abort, an error or the end of the operation keeps it; the next operation waits it out.
| s | Session. |
| now | The clock when the step ended. |
| r | The result. |
| bool hitec_session_init | ( | hitec_session_t * | s, |
| uint8_t | series, | ||
| uint8_t | clock ) |
Prepare a session for one series.
No operation runs, no step is due, not in mode; off time HITEC_SES_OFF_MS.
| s | Session. |
| series | hitec_family_t, chosen by the operator. |
| clock | hitec_ses_clock_t of the now the caller passes. |
| hitec_ses_t hitec_session_leave | ( | hitec_session_t * | s | ) |
Leave programming mode.
Classic: POWER (off off_ms, on, a LISTEN of HITEC_SES_POWER_WINDOW_US with no answer). D series: the reboot, 0x46 = 1, then HITEC_D_REBOOT_WAIT_MS before the next step of any operation. in_mode is 0 from the start of the leave. Taken in or out of mode.
| s | Session. |
| hitec_ses_kind_t hitec_session_next | ( | hitec_session_t * | s, |
| uint32_t | now, | ||
| hitec_ses_step_t * | out ) |
The step to run at now.
A step stays out, and this call gives it again, until its result is fed. WAIT while the gap before the next transaction runs; DONE when no operation runs.
| s | Session. |
| now | The clock. |
| out | Receives the step; NULL takes the kind only. |
| hitec_ses_t hitec_session_read_all | ( | hitec_session_t * | s | ) |
Read the series' full set: hitec_d_read_list() or hitec_c_read_ranges().
The image starts empty, the classic version kept. Addresses go in ascending order, each with HITEC_SES_TRIES tries; one that fails on every try ends the read, and the image keeps what was read before it.
| s | Session. |
| hitec_ses_t hitec_session_read_set | ( | hitec_session_t * | s, |
| const uint8_t * | addrs, | ||
| uint16_t | n ) |
Read n addresses into the image, which keeps the rest.
As hitec_session_read_all(), in ascending order, each address once.
| s | Session. |
| addrs | Cells, or even D series registers. |
| n | Count, 1 to 256. |
| hitec_ses_t hitec_session_result | ( | const hitec_session_t * | s | ) |
The running operation's state.
| s | Session. |
| hitec_ses_t hitec_session_run_plan | ( | hitec_session_t * | s, |
| const hitec_plan_t * | plan, | ||
| const hitec_image_t * | expect ) |
Run a plan the caller built, such as hitec_d_factory_plan(), then read back as hitec_session_write() does.
| s | Session. |
| plan | The plan, copied; steps of the session's series only. |
| expect | The image the read-back must hold, copied; NULL reads back and compares nothing. |
| hitec_ses_t hitec_session_write | ( | hitec_session_t * | s, |
| const hitec_image_t * | target ) |
Write the servo from the image to target, then read it back.
The plan is hitec_plan(image, target), kept in plan. An empty plan ends with HITEC_SES_OK and sends nothing.
Classic: each cell and each balance (0x1C) as 62 cell value sum, with HITEC_SES_TRIES tries for its + +, HITEC_SES_C_GAP_MS apart. Then a read of cells 0x00 to 0x23.
D series: each write, the save and the reboot as a write frame with no answer, each followed by its plan wait (20ms, 100ms, 1000ms), at least HITEC_SES_D_GAP_MS. Then a read of the full set.
The read-back must hold every writable address (hitec_writable()) and the classic balance as target holds it, where target knows it; diff names the ones that differ. step_index counts the plan's writes that went out.
The image holds only what the servo confirmed. A classic write answered + + goes into it. A D series write has no answer and does not change it; the read-back puts the register there.
A write that ends with anything but HITEC_SES_OK once a plan step was handed out and its result fed (RAN, CUT or FAILED: a cut or lost step may have reached the servo) marks as not known (hitec_image_forget()) every address the servo may have changed and this write's read-back has not read: D series, every register of the full read and every register of the plan, since a save, a reboot or a factory reset may change registers the plan does not name; classic, the cell of a write whose last try got no + + (a cut or lost try included) and the balance 0x1C. hitec_plan() then refuses with HITEC_PLAN_ERR_UNKNOWN (HITEC_SES_ERR_PLAN): the caller reads the servo before it writes again. A write refused or aborted before a step's result was fed, and one whose read-back ended with HITEC_SES_ERR_VERIFY, forget nothing.
| s | Session. |
| target | The draft, copied. |