rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
hitec_port.h File Reference

Raw transactions of the Hitec servo wire on a programming port, and their link page (0x34). More...

#include <stdbool.h>
#include <stdint.h>
#include "hitec_wire.h"
#include "link_pages.h"
#include "outputs.h"
#include "supply_page.h"

Go to the source code of this file.

Data Structures

struct  hitec_port_run_t
 What the driver says about the line and the running transaction. More...
struct  hitec_port_hw_t
 What the port needs from the hardware. More...
struct  hitec_port_t
 The state of one Hitec port: the hardware, the staged registers, the running or last transaction and its capture. More...

Macros

#define HITEC_PORT_STOP_MS   50u
 The port renders no pulse this long before the generator gives up the pin: 2 frames at 40Hz, the lowest PWM rate.
#define HITEC_PORT_BUSY_MS   10000u
 A transaction still running this long is aborted: longer than the longest one the driver takes, a 4s lead and a 4s window with 16 bytes.
#define HITEC_PORT_SUPPLY_MS   LINK_HITEC_SUPPLY_MS
 The PD mini has this long to read off after a POWER asked it off.

Functions

bool hitec_port_init (hitec_port_t *p, const hitec_port_hw_t *hw)
 Prepare a free port.
uint8_t hitec_port_write (hitec_port_t *p, uint8_t off, uint8_t n, const uint16_t *in, uint32_t now_ms)
 A write of the page: 0, or the LINK_NACK_* reason with nothing changed.
void hitec_port_read (const hitec_port_t *p, uint8_t off, uint8_t n, uint16_t *out)
 A read of the page, LINK_HT_*.
void hitec_port_step (hitec_port_t *p, bool safe, bool stop, uint32_t now_ms)
 One pass.
link_hitec_state_t hitec_port_state (const hitec_port_t *p)
 Where the port is.
uint8_t hitec_port_quiet_mask (const hitec_port_t *p)
 Bit n: programming port n renders no PWM pulse.
uint8_t hitec_port_pin_mask (const hitec_port_t *p)
 Bit n: programming port n's pin is the wire driver's.
void hitec_port_bench_armed (hitec_port_t *p, bool armed)
 Whether the bank is armed: OPEN is refused while it is.
void hitec_port_other_mask (hitec_port_t *p, uint8_t mask)
 Bit n: the KST page holds programming port n (kst_port_quiet_mask()). OPEN on it is refused with LINK_HITEC_REF_HELD.
bool hitec_port_supply_hold (const hitec_port_t *p)
 The port holds the SUPPLY page (supply_page_hold()): every state but LINK_HITEC_FREE, from OPEN to CLOSE.
bool hitec_port_frame_out (const hitec_port_t *p, uint32_t now_ms)
 A byte may be on the wire: a SEND from its start until the lead and the bytes have had their time, counted in whole ms; a LISTEN or a POWER that answers, while it runs.

Detailed Description

Raw transactions of the Hitec servo wire on a programming port, and their link page (0x34).

Implements the signal-wire programming protocols of Hitec servos. 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 port holds no session. It takes the pin of one programming port (prog_port.h) from the PWM (pulse-width modulation) generator, opens the wire driver of protocols/hitec on it, and runs one transaction at a time as the page's writer stages it: SEND, LISTEN, LEVEL, or POWER, a supply cycle with a listen from the ON. The capture and its decoding are on the page until the next transaction. Which bytes a servo takes, and in which order, is the writer's business.

A port is free, or held by this page, or held by the KST page, never two of them. OPEN is refused with LINK_HITEC_REF_HELD while the KST page holds the port (hitec_port_other_mask()), and the KST page refuses ENTER and a REBOOT from PWM with LINK_KST_REF_BUSY while this one holds it.

The way in, started by OPEN:

STOPPING HITEC_PORT_STOP_MS: the port renders no pulse, so a pulse that runs ends and no other starts. OPEN the generator gives up the pin and the driver opens the line on it, released: classic with its pull-down, the D series with its pull-up. A driver that gets no state machine, program space or DMA (direct memory access) channel leaves the port free with LINK_HITEC_RESULT_NO_LINE.

In OPEN a command starts one transaction (BUSY) and the port is in OPEN again when the driver hands back its capture. The driver ends every transaction itself (protocols/hitec/rp2350/hitec_line.h); one that runs HITEC_PORT_BUSY_MS is aborted here as well.

POWER needs a PD mini that answers. It asks the supply off, waits until it reads off (HITEC_PORT_SUPPLY_MS at most, else LINK_HITEC_RESULT_SUPPLY), keeps it off for the staged off time, asks it on at the staged LINK_HITEC_SUPPLY_MIN_MV to LINK_HITEC_SUPPLY_MAX_MV (4800 to 6000mV), and starts the staged listen in the same pass. A supply that refuses the ON gives LINK_HITEC_RESULT_SUPPLY; a listen the driver refuses gives LINK_HITEC_RESULT_START with the supply on.

The SUPPLY page is held (hitec_port_supply_hold(), protocol 4.29) in every state but free: from the OPEN that takes the port to the CLOSE that gives it back, as the KST page holds it out of PWM. While it is held the panel can switch the supply off and cannot switch it on or move a set point; POWER switches it as staged. A stop keeps the port and the hold. The hold ends with the port: CLOSE, an ABORT or a stop in STOPPING, an OPEN that ends LINK_HITEC_RESULT_NO_LINE, and a restart of the IOmcu.

The way out is CLOSE, from any state but free: a running transaction is cut, the line closed and the pin the generator's. The supply is left as it is. ABORT cuts what runs and keeps the port.

A stop is the heartbeat lost, the link silent or the bank disarmed. On a stop the running transaction is aborted (the pad released at once); a POWER in its off time ends there with the supply off, and one whose listen runs is cut as a transaction, the supply as the SUPPLY page has it. The port stays held with the pin an input; a port still in STOPPING is free again. The flag LINK_HT_F_STOPPED is set, and SEND, LISTEN, LEVEL and POWER are refused with LINK_HITEC_REF_STOPPED until CLEAR, which is taken while the heartbeat is trusted and the link is not silent, or until CLOSE and OPEN. While the heartbeat is not trusted or the link is silent every command but ABORT and CLOSE is refused with LINK_HITEC_REF_UNSAFE, and every pass counts as a stop.

Every SEND, and a LISTEN or a POWER that answers the first byte, needs LINK_HITEC_WRITE_KEY in the command. OPEN is refused while the bank is armed; the caller arms no output while hitec_port_quiet_mask() is not 0.

Commands: registers 0 to 3 of a write, one frame. SEQ is LINK_HT_SEQ plus 1; the frame just taken, sent again, is acknowledged and does nothing; any other SEQ is refused with LINK_NACK_BAD_VALUE. A command that is taken is acknowledged, and what became of it is on the page: LINK_HT_RESULT's high byte says why it started nothing, or 0.

Receive mode (4.24). Write register 23 (LINK_HT_W_RX_MODE) says how a D series line receives from the next OPEN on: PULL-UP, released for the answer with a pull-up on the board, or PRECHARGE, the IOmcu's sampler (protocols/hitec/include/hitec_pre.h). The OPEN takes the register's value with the family; a classic OPEN ignores it. On a PRECHARGE line a LISTEN or POWER that answers and a receive window above HITEC_PRE_WINDOW_MAX_US are refused with LINK_HITEC_REF_RANGE. A PRECHARGE transaction started with the supply asked on is cut when the supply is no longer asked on, the PD mini stops answering, or the output reads off after it read on. A SEND, LISTEN or LEVEL counts as read on from its start. POWER's listen starts in the pass of the ON and counts from the first pass the output does not read off; one whose output never leaves off runs to the end of its window (HITEC_PRE_WINDOW_MAX_US at most) and ends LINK_HITEC_RESULT_DONE with what it heard.

Nothing here is kept over a restart of the IOmcu: after one the port is free and the register reads PULL-UP.

Pure logic: the pin, the driver and the supply are behind hitec_port_hw_t.

SPDX-License-Identifier: MIT

Function Documentation

◆ hitec_port_bench_armed()

void hitec_port_bench_armed ( hitec_port_t * p,
bool armed )

Whether the bank is armed: OPEN is refused while it is.

Parameters
pThe port, or NULL; a NULL p is ignored.
armedtrue when the bank is armed.

◆ hitec_port_frame_out()

bool hitec_port_frame_out ( const hitec_port_t * p,
uint32_t now_ms )

A byte may be on the wire: a SEND from its start until the lead and the bytes have had their time, counted in whole ms; a LISTEN or a POWER that answers, while it runs.

Parameters
pThe port, or NULL.
now_msCurrent time in milliseconds.
Returns
true while a byte may be on the wire; false for a NULL p or a port that is not BUSY.

◆ hitec_port_init()

bool hitec_port_init ( hitec_port_t * p,
const hitec_port_hw_t * hw )

Prepare a free port.

False when hw names no port or lacks a function other than run and the supply's two, or has one of those two.

Parameters
pThe port to prepare; zeroed first.
hwThe hardware hooks, copied into the port.
Returns
true when the port is prepared; false for a NULL p, or for a hw that fails the checks above.

◆ hitec_port_other_mask()

void hitec_port_other_mask ( hitec_port_t * p,
uint8_t mask )

Bit n: the KST page holds programming port n (kst_port_quiet_mask()). OPEN on it is refused with LINK_HITEC_REF_HELD.

Parameters
pThe port, or NULL; a NULL p is ignored.
maskBit n: the KST page holds programming port n.

◆ hitec_port_pin_mask()

uint8_t hitec_port_pin_mask ( const hitec_port_t * p)

Bit n: programming port n's pin is the wire driver's.

From the take until CLOSE.

Parameters
pThe port, or NULL.
Returns
Bit n set for port n while the wire driver holds its pin; 0 for a NULL p.

◆ hitec_port_quiet_mask()

uint8_t hitec_port_quiet_mask ( const hitec_port_t * p)

Bit n: programming port n renders no PWM pulse.

The held port in every state but LINK_HITEC_FREE.

Parameters
pThe port, or NULL.
Returns
Bit n set for port n while the port is held and not free; 0 for a NULL p.

◆ hitec_port_read()

void hitec_port_read ( const hitec_port_t * p,
uint8_t off,
uint8_t n,
uint16_t * out )

A read of the page, LINK_HT_*.

Parameters
pThe port.
offFirst register of the window.
nNumber of registers to read.
outReceives n register values; left unchanged for a NULL p or out, or a window past LINK_HT_COUNT.

◆ hitec_port_state()

link_hitec_state_t hitec_port_state ( const hitec_port_t * p)

Where the port is.

Parameters
pThe port, or NULL.
Returns
The state of the port; LINK_HITEC_FREE for a NULL p.

◆ hitec_port_step()

void hitec_port_step ( hitec_port_t * p,
bool safe,
bool stop,
uint32_t now_ms )

One pass.

safe: the heartbeat is trusted and the link is not silent. stop: the bank was switched off or disarmed since the last pass.

Parameters
pThe port.
safetrue when the heartbeat is trusted and the link is not silent.
stoptrue when the bank was switched off or disarmed since the last pass.
now_msCurrent time in milliseconds.

◆ hitec_port_supply_hold()

bool hitec_port_supply_hold ( const hitec_port_t * p)

The port holds the SUPPLY page (supply_page_hold()): every state but LINK_HITEC_FREE, from OPEN to CLOSE.

Parameters
pThe port, or NULL.
Returns
true while the port is held, a stopped one included; false for a free port and for a NULL p.

◆ hitec_port_write()

uint8_t hitec_port_write ( hitec_port_t * p,
uint8_t off,
uint8_t n,
const uint16_t * in,
uint32_t now_ms )

A write of the page: 0, or the LINK_NACK_* reason with nothing changed.

A window that starts in registers 0 to 3 is a command and has to be exactly those 4. Registers 4 to 23 are taken in any windows; 24 and up are LINK_NACK_READ_ONLY. A VIEW or an RX_MODE above the last is LINK_NACK_BAD_VALUE.

Parameters
pThe port.
offFirst register of the window.
nNumber of registers in the window; 0 is refused.
inThe n register values to write.
now_msCurrent time in milliseconds.
Returns
0 when the write is applied or the command is acknowledged; otherwise LINK_NACK_BAD_RANGE, LINK_NACK_READ_ONLY or LINK_NACK_BAD_VALUE.