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

The numbers and words a PIO pin driver for the Hitec wire works with. More...

#include <stdbool.h>
#include <stdint.h>
#include "hitec_wire.h"

Go to the source code of this file.

Data Structures

struct  hitec_pio_clock_t
 The divider and the counts a system clock gives for one family. More...
struct  hitec_pio_run_t
 One transaction, as hitec_pio_tx() laid it out. More...

Macros

#define HITEC_PIO_CYCLES_PER_BIT   16u
 State machine cycles in one bit.
#define HITEC_PIO_TICK_CYCLES   2u
 Cycles of one tick of the receiver's counter: 1/8 of a bit.
#define HITEC_PIO_MIN_DIV   4u
 Smallest clock divider: the input synchroniser's 2 system clock cycles are then half a state machine cycle or less.
#define HITEC_PIO_MAX_DIV   65535u
 Largest integer part of the clock divider.
#define HITEC_PIO_SYNC_SYS_CYCLES   2u
 Cycles the input synchroniser delays the pin, in system clock cycles.
#define HITEC_PIO_MAX_ERROR_PPM   500u
 Largest bit time error a clock may give, in parts per million: 0.05%.
#define HITEC_PIO_LEAD_CYCLES   8u
 Cycles of a lead beyond 2 per count.
#define HITEC_PIO_BYTE_CYCLES   157u
 Cycles of the receive path of one byte, start bit to start bit, beyond the waiting: detection, 9.5 bits of samples and the push.
#define HITEC_PIO_ANSWER_CYCLES   165u
 Cycles an answer adds to the receive path of the first byte: 5 to its first bit time, its 10 bit times and the release.
#define HITEC_PIO_ANSWER_CYCLES_TO_EDGE   160u
 Cycles from the sample that sees the first byte's start bit to the answer's first change of direction: 10 bit times.
#define HITEC_PIO_ANSWER_BITS   10u
 Bit times the answer sends: start bit, 8 data bits, stop bit.
#define HITEC_PIO_ANSWER_DROP_BITS   6u
 Released bit times at the front of the answer word, which the program drops (out null, 12) before it sends the other 10.
#define HITEC_PIO_DRAIN_CYCLES   2u
 Cycles of the drain on the way from the end of the window to idle.
#define HITEC_PIO_PROGRAM_WORDS   30u
 Words of rp2350/hitec_line.pio.
#define HITEC_PIO_IDLE_PC   1u
 Addresses in the program, from its first word.
#define HITEC_PIO_BIT_PC   8u
#define HITEC_PIO_RELEASE_PC   11u
#define HITEC_PIO_ASK_PC   22u
#define HITEC_PIO_ANSWER_PC   24u
#define HITEC_PIO_DRAIN_PC   28u
 The mov osr, null that starts the drain at the end of the window.
#define HITEC_PIO_STAMP_BITS   23u
 Bits of the tick counter a pushed word carries; the window is shorter.
#define HITEC_PIO_MAX_WINDOW_TICKS   ((1u << HITEC_PIO_STAMP_BITS) - 1u)
 Largest window the tick counter carries, in ticks.
#define HITEC_PIO_MAX_LEAD   ((1u << 30) - 1u)
 Largest lead count.
#define HITEC_PIO_TX_WORDS   11u
 Words of the longest stream: 3 counts and 8 words of bit times (8 bytes of 10 bits with 6 idle bits between two, and the D series' settle bit, padded to 128 bit times).

Functions

bool hitec_pio_clock (uint32_t sys_hz, uint8_t family, hitec_pio_clock_t *out)
 The divider for family at a system clock of sys_hz.
uint32_t hitec_pio_bit_ns (const hitec_pio_clock_t *clock)
 The bit time clock gives, in ns, rounded to nearest.
uint32_t hitec_pio_cycles_ns (const hitec_pio_clock_t *clock, uint64_t cycles)
 cycles state machine cycles of clock, in ns, rounded down.
unsigned hitec_pio_tx (const hitec_pio_clock_t *clock, uint8_t drive, const hitec_xfer_t *xfer, uint32_t *words, hitec_pio_run_t *run)
 The word stream for xfer on a line with drive (hitec_drive_t).
bool hitec_pio_answer_due (const hitec_pio_run_t *run, unsigned n, unsigned pc)
 Whether the answer of run is still to go out with n bytes received and the state machine at pc, counted from the program's first word.
void hitec_pio_capture (const hitec_pio_clock_t *clock, const hitec_pio_run_t *run, const uint32_t *stamps, unsigned n, bool full, bool lost, bool aborted, bool cut, hitec_capture_t *out)
 The capture from the n words the program pushed, in their order.

Detailed Description

The numbers and words a PIO pin driver for the Hitec wire works with.

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").

rp2350/hitec_line.pio is one PIO (programmable input/output) program for both families: a half-duplex 8N1 UART (universal asynchronous receiver-transmitter) on one pin. The clock divider makes one bit HITEC_PIO_CYCLES_PER_BIT state machine cycles, so the bit rate is the divider's. The input override inverts the pin for the D series, so the program reads the idle level as 1 in both families. The program sends every bit time as two bits of its stream: the pad's direction and its output level. An open-drain line sends a level of 0 and switches the direction; a push-pull line holds the direction and switches the level. This file lays a transaction out as that stream, computes the divider and the counts from the system clock, and turns the words the program pushes into a hitec_capture_t. It has no hardware access, so the host suite runs it against a model of the state machine.

The word stream of one transaction, in the order the program reads it, every word shifted out least significant bit first:

lead bit 0 the lead's level, bit 1 its direction, bits 2 to 31 the lead count L: the lead lasts 2 L + HITEC_PIO_LEAD_CYCLES window W, ticks the receiver waits for a start bit bits B, bit times sent B / 16 words of 16 bit times, each bit time the direction (bit 2 i) and the level (bit 2 i + 1) answer LISTEN with an answer only: 16 bit times, the first 6 released and dropped by the program, then the 10 of the byte it sends after the first byte received

Bit times at the lead's level fill the front of the first word, so the last bit time ends a word and nothing of the stream is left when the receiver starts. The receiver answers when the answer word is still in the output shift register after a byte, and waits again from the end of the answer's stop bit; at the end of the window the program drains the register, so an answer no byte called for is dropped.

A tick is HITEC_PIO_TICK_CYCLES cycles, 1/8 of a bit. The receiver samples the pin every 1 or 2 cycles while it waits, and every byte it takes is one pushed word: bits 0 to 7 the data, bit 8 the stop bit as sampled (1 is the idle level), bits 9 to 31 the tick counter at the start bit. The counter counts down from W only while the receiver waits, so the time of byte k is

2 + 2 (W - X_k) + HITEC_PIO_BYTE_CYCLES k (+ HITEC_PIO_ANSWER_CYCLES for k > 0 after an answer)

cycles after the release, X_k the counter it pushed. The start bit's edge lies within the 2 cycles before that sample; the capture puts it 1 cycle before, so a time is right to 1 cycle, 1/16 of a bit.

The module includes nothing outside the C standard headers and holds no global state.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ HITEC_PIO_IDLE_PC

#define HITEC_PIO_IDLE_PC   1u

Addresses in the program, from its first word.

IDLE: the out the machine stalls on between transactions. BIT to RELEASE - 1: the bit times. RELEASE: the release before the receiver. ASK: the jmp !osre after a byte. ANSWER to ANSWER + 2: the way into the answer's bit times. DRAIN: the end of the window.

◆ HITEC_PIO_MAX_ERROR_PPM

#define HITEC_PIO_MAX_ERROR_PPM   500u

Largest bit time error a clock may give, in parts per million: 0.05%.

The 8 fractional bits of the divider and a divider of 4 or more keep every clock within 489ppm.

Function Documentation

◆ hitec_pio_answer_due()

bool hitec_pio_answer_due ( const hitec_pio_run_t * run,
unsigned n,
unsigned pc )

Whether the answer of run is still to go out with n bytes received and the state machine at pc, counted from the program's first word.

True only for a run with an answer, n of 1 and pc at ASK, at ANSWER to ANSWER + 2, or in the bit times. A receive that has its count waits for this to turn false before the driver restarts the machine, so a LISTEN for 1 byte sends its whole answer.

Parameters
runLayout of the transaction.
nBytes received so far.
pcState machine address, counted from the first program word.
Returns
true while the answer is still to go out.

◆ hitec_pio_bit_ns()

uint32_t hitec_pio_bit_ns ( const hitec_pio_clock_t * clock)

The bit time clock gives, in ns, rounded to nearest.

Parameters
clockDivider and counts.
Returns
The bit time in ns, rounded to nearest.

◆ hitec_pio_capture()

void hitec_pio_capture ( const hitec_pio_clock_t * clock,
const hitec_pio_run_t * run,
const uint32_t * stamps,
unsigned n,
bool full,
bool lost,
bool aborted,
bool cut,
hitec_capture_t * out )

The capture from the n words the program pushed, in their order.

full says the receive ended at max_rx bytes, lost that the driver ended a program that ran past run->span_us, aborted that the transaction was cut, cut that the driver restarted the machine while hitec_pio_answer_due() was true, so the answer did not go out whole. A counter above the window or above the one before sets overflow and ends the capture there; so does lost. answered is set for a run with an answer, 1 byte or more and no cut.

Parameters
clockDivider and counts.
runLayout of the transaction.
stampsWords the program pushed, in order.
nNumber of words in stamps.
fulltrue when the receive ended at max_rx bytes.
losttrue when the driver ended a program that ran past its span.
abortedtrue when the transaction was cut.
cuttrue when the answer was due at a restart.
outReceives the capture.

◆ hitec_pio_clock()

bool hitec_pio_clock ( uint32_t sys_hz,
uint8_t family,
hitec_pio_clock_t * out )

The divider for family at a system clock of sys_hz.

False when the divider is below HITEC_PIO_MIN_DIV (a clock below 1.2282MHz classic, 7.3692MHz D series: 64 bit rates less half a step of the divider), its integer part above HITEC_PIO_MAX_DIV, or the bit time it gives more than HITEC_PIO_MAX_ERROR_PPM from the family's. out is left as it was on false.

Parameters
sys_hzSystem clock, in Hz.
familyhitec_family_t value.
outReceives the divider; left unchanged when false is returned.
Returns
true when the clock gives a usable divider.

◆ hitec_pio_cycles_ns()

uint32_t hitec_pio_cycles_ns ( const hitec_pio_clock_t * clock,
uint64_t cycles )

cycles state machine cycles of clock, in ns, rounded down.

Parameters
clockDivider and counts.
cyclesState machine cycle count.
Returns
cycles converted to ns, rounded down.

◆ hitec_pio_tx()

unsigned hitec_pio_tx ( const hitec_pio_clock_t * clock,
uint8_t drive,
const hitec_xfer_t * xfer,
uint32_t * words,
hitec_pio_run_t * run )

The word stream for xfer on a line with drive (hitec_drive_t).

Returns the number of words written to words, at most HITEC_PIO_TX_WORDS, or 0 when xfer is not valid, is of another family than clock, asks classic for push-pull, or asks an open-drain line to hold a high level.

Parameters
clockDivider and counts.
drivehitec_drive_t value of the line.
xferTransaction to lay out.
wordsReceives the word stream.
runReceives the layout of the transaction.
Returns
Number of words written, or 0 when the transaction is not valid for this clock.