DShot out and the reply back.
More...
#include "out_dshot.h"
#include <string.h>
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "hardware/structs/io_bank0.h"
#include "dshot.pio.h"
|
| #define | CAP_WORDS 8u |
| | Eight words is 256 samples at five to a reply bit, which is 51 bits against a reply of 21.
|
| #define | CAP_MIN_WORDS 4u |
| | The fewest words that can hold a whole reply: one idle sample plus 21 bits at five samples each is 106, which is four words.
|
|
| static dshot_out_t * | find (uint8_t pin) |
| | Find the slot bound to a pin.
|
| static const pio_program_t * | tx_program (bool bidir) |
| | Pick the transmitter program for a mode.
|
| static bool | claim_receiver (dshot_out_t *s) |
| | The receiver has to sit on the state machine one above its transmitter, in the same block: the transmitter raises a flag relative to its own number and the receiver waits on the flag one below its own, so the pairing is what keeps two ESCs on one block from starting each other's receivers.
|
| static void | release_pad (uint8_t pin) |
| | Give the pad back exactly as it was at boot: no inversion, no pull, and a plain input rather than a pin still muxed to a PIO block.
|
| static void | teardown (dshot_out_t *s) |
| | Remove the programs, release the state machines and the pad.
|
| bool | out_dshot_bind (uint8_t pin, uint16_t rate_kbit, bool bidirectional) |
| | Take pin at rate_kbit kbit/s.
|
| void | out_dshot_release (uint8_t pin) |
| | Give back the pin and the state machines.
|
| void | out_dshot_stop (uint8_t pin) |
| | Stop sending.
|
| static void | restart (PIO pio, uint sm, uint offset) |
| | Put a state machine back at the top of its program with nothing queued.
|
| void | out_dshot_send (uint8_t pin, uint16_t value, bool telemetry) |
| | Send one frame.
|
| bool | out_dshot_poll (uint8_t pin, bool edt, dshot_telem_t *out) |
| | Take the last reply, if one arrived and decoded.
|
DShot out and the reply back.
See out_dshot.h.
SPDX-License-Identifier: MIT
◆ CAP_MIN_WORDS
The fewest words that can hold a whole reply: one idle sample plus 21 bits at five samples each is 106, which is four words.
A poll that took less than this would take a reply that has not finished arriving. The loop turns over thousands of times in the 70us a capture takes, so the first poll after a frame always finds the queue short – draining it there would throw away every reply this bench will ever get.
◆ CAP_WORDS
Eight words is 256 samples at five to a reply bit, which is 51 bits against a reply of 21.
The state machine stalls when the queue is full, so the extra is idle line after the burst rather than anything that has to arrive in time.
◆ claim_receiver()
The receiver has to sit on the state machine one above its transmitter, in the same block: the transmitter raises a flag relative to its own number and the receiver waits on the flag one below its own, so the pairing is what keeps two ESCs on one block from starting each other's receivers.
- Parameters
-
| s | Binding with its transmitter already loaded. |
- Returns
- true when the receiver is claimed and loaded; false when that state machine is taken or the program does not fit.
◆ find()
Find the slot bound to a pin.
- Parameters
-
- Returns
- The slot, or NULL when
pin is not bound.
◆ out_dshot_bind()
| bool out_dshot_bind |
( |
uint8_t | pin, |
|
|
uint16_t | rate_kbit, |
|
|
bool | bidirectional ) |
Take pin at rate_kbit kbit/s.
bidirectional inverts the line and claims a second state machine, which has to be the one above the transmitter's in the same block: the two rendezvous on a flag relative to their own numbers, so an ESC on one block cannot start another's receiver. A bind that cannot get that pair is refused rather than falling back to a one-way output that would look like an ESC with no telemetry.
A pin that is already bound with the same rate and mode stays as it is and the call returns true. With a different rate or mode it returns false and the old binding keeps running; release the pin first to change it.
- Parameters
-
| pin | GPIO number of the output. |
| rate_kbit | Bit rate in kbit/s; 0 is refused. |
| bidirectional | true inverts the line and claims a receiver. |
- Returns
- true when bound, or already bound the same way; false otherwise.
◆ out_dshot_poll()
| bool out_dshot_poll |
( |
uint8_t | pin, |
|
|
bool | edt, |
|
|
dshot_telem_t * | out ) |
Take the last reply, if one arrived and decoded.
Returns false on a bidirectional pin that has not answered, on a reply that failed its checksum, and always on a plain DShot pin. A false is not an error to report: an ESC that does not do bidirectional DShot simply never answers, and that is what it looks like.
edt says whether this ESC was asked for extended telemetry and is expected to have accepted it. It cannot be read off the wire, and getting it wrong only matters for an ESC that does not normalise its exponent: one that does either sets the mantissa's top bit, which reads as a period, or leaves the type nibble at zero, which reads as a period too.
- Parameters
-
| pin | GPIO number of a bound output. |
| edt | true when extended telemetry was asked for and accepted. |
| out | Receives the decoded reply when true is returned. |
- Returns
- true for a decoded reply; false for none, a bad checksum, or a plain DShot pin.
◆ out_dshot_release()
| void out_dshot_release |
( |
uint8_t | pin | ) |
|
Give back the pin and the state machines.
- Parameters
-
| pin | GPIO number of a bound output; other values do nothing. |
◆ out_dshot_send()
| void out_dshot_send |
( |
uint8_t | pin, |
|
|
uint16_t | value, |
|
|
bool | telemetry ) |
Send one frame.
value is 0..2047 and is used as given, so a caller can send a command as readily as a throttle; dshot_throttle() is what maps travel onto it. The frame is dropped rather than queued if the previous one has not gone yet, because a queue of stale throttle values is worse than a missed update.
On a bidirectional pin this also arms the receiver for the reply.
- Parameters
-
| pin | GPIO number of a bound output; other values do nothing. |
| value | Frame value, 0..2047, used as given. |
| telemetry | true asks the ESC to answer on its telemetry wire. |
◆ out_dshot_stop()
| void out_dshot_stop |
( |
uint8_t | pin | ) |
|
Stop sending.
No frames means no edges, which is what the output bank means by not driving. An ESC stops on silence after its own timeout; sending explicit zeros instead would keep it armed and waiting, which is not what a disarmed bench should look like from the ESC's side.
- Parameters
-
| pin | GPIO number of a bound output; other values do nothing. |
◆ release_pad()
| void release_pad |
( |
uint8_t | pin | ) |
|
|
static |
Give the pad back exactly as it was at boot: no inversion, no pull, and a plain input rather than a pin still muxed to a PIO block.
A pin left muxed is one the next configuration cannot have and one a scope reads as driven by something that no longer exists.
- Parameters
-
◆ restart()
| void restart |
( |
PIO | pio, |
|
|
uint | sm, |
|
|
uint | offset ) |
|
static |
Put a state machine back at the top of its program with nothing queued.
- Parameters
-
| pio | PIO block. |
| sm | State machine number in the block. |
| offset | Program offset in the block. |
◆ teardown()
Remove the programs, release the state machines and the pad.
- Parameters
-
| s | Binding to tear down; cleared to zero. |
◆ tx_program()
| const pio_program_t * tx_program |
( |
bool | bidir | ) |
|
|
static |
Pick the transmitter program for a mode.
- Parameters
-
| bidir | true for the bidirectional program. |
- Returns
- The program to load.