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

PPM (pulse-position modulation): several channels multiplexed onto one pin. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

Go to the source code of this file.

Data Structures

struct  ppm_cfg_t

Macros

#define PPM_MAX_CHANNELS   8u
 Eight in a 22.5ms frame is the convention; more fits only by shortening the frame, which is what ppm_cfg_t.frame_us is for.
#define PPM_MAX_RUNS   ((PPM_MAX_CHANNELS + 1u) * 2u)
 Runs a frame needs: a mark and a space per channel, plus the terminating mark and the sync gap.
#define PPM_CHANNEL_MIN_US   400u
 The range a channel is carried in, in microseconds, both ends included.
#define PPM_CHANNEL_MAX_US   2500u
#define PPM_DEFAULT_MARK_US   300u
#define PPM_DEFAULT_FRAME_US   22500u
#define PPM_SYNC_MIN_US   3000u
 The shortest gap that is unambiguously a gap.
#define PPM_CFG_DEFAULT()

Functions

size_t ppm_frame (const uint16_t *channel_us, uint8_t channels, const ppm_cfg_t *cfg, uint16_t *runs, size_t max_runs)
 Lay a frame out as alternating run lengths in microseconds.
uint32_t ppm_min_frame_us (uint8_t channels, const ppm_cfg_t *cfg)
 The shortest frame that can carry channels channels whatever they are commanded to, in microseconds: every channel at PPM_CHANNEL_MAX_US, plus the terminating mark, plus the smallest gap that is still a gap.

Detailed Description

PPM (pulse-position modulation): several channels multiplexed onto one pin.

A frame is a run of marks. The time from the start of one mark to the start of the next is one channel's pulse width, so a channel is carried by where its mark falls rather than by how long it is, and the mark itself is a fixed 300us that belongs to no channel. After the last channel comes a terminating mark and then a gap long enough that a receiver cannot mistake it for a channel; that gap is what locates the start of the next frame.

The consequence is that channels and frame rate are not independent. Eight channels at 2000us each already spend 16ms of a 22.5ms frame, and the sync gap is whatever is left. Asking for more channels, longer travel or a faster frame eats the gap, and a frame whose gap has shrunk into the range a receiver reads as a channel does not fail visibly: it decodes as a different number of channels, shifted. So this refuses rather than emits a frame whose sync gap is too short.

The polarity is not decided here. Positive and negative shift are the same frame with the levels exchanged, and on the RP2350 that is a pad inversion rather than a different program.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ PPM_CFG_DEFAULT

#define PPM_CFG_DEFAULT ( )
Value:
(ppm_cfg_t) { \
.mark_us = PPM_DEFAULT_MARK_US, \
.frame_us = PPM_DEFAULT_FRAME_US, \
.sync_min_us = PPM_SYNC_MIN_US, \
}
#define PPM_SYNC_MIN_US
The shortest gap that is unambiguously a gap.
Definition ppm.h:70
Definition ppm.h:72

◆ PPM_CHANNEL_MIN_US

#define PPM_CHANNEL_MIN_US   400u

The range a channel is carried in, in microseconds, both ends included.

A width outside it is refused, not clamped.

◆ PPM_SYNC_MIN_US

#define PPM_SYNC_MIN_US   3000u

The shortest gap that is unambiguously a gap.

A receiver frames on the first interval longer than any channel can be. The ceiling on a channel is PPM_CHANNEL_MAX_US (2500us), so a gap under about 3ms is one that some receiver will read as a very long channel.

Function Documentation

◆ ppm_frame()

size_t ppm_frame ( const uint16_t * channel_us,
uint8_t channels,
const ppm_cfg_t * cfg,
uint16_t * runs,
size_t max_runs )

Lay a frame out as alternating run lengths in microseconds.

runs receives mark, space, mark, space, ... and ends with the terminating mark and the sync gap, so it always holds an even number of entries and always begins with a mark. A driver toggles the pin at each one and does not need to know which is which.

Returns the number of runs written, or 0 when the frame cannot be built: no channels or too many, a channel shorter than the mark it has to contain, a channel outside PPM_CHANNEL_MIN_US..PPM_CHANNEL_MAX_US, or a sync gap below cfg->sync_min_us once the channels have been paid for.

Parameters
channel_usPulse width of each channel, in us.
channelsNumber of channels in channel_us.
cfgFrame timing; NULL takes the defaults.
runsReceives the runs.
max_runsCapacity of runs, in entries.
Returns
Number of runs written, or 0 when the frame cannot be built.

◆ ppm_min_frame_us()

uint32_t ppm_min_frame_us ( uint8_t channels,
const ppm_cfg_t * cfg )

The shortest frame that can carry channels channels whatever they are commanded to, in microseconds: every channel at PPM_CHANNEL_MAX_US, plus the terminating mark, plus the smallest gap that is still a gap.

A configuration screen wants this before it offers a frame rate, so the refusal above becomes a range the operator never leaves rather than an error they hit. Returns 0 for a channel count this module cannot carry.

Parameters
channelsNumber of channels to carry.
cfgFrame timing; NULL takes the defaults.
Returns
Frame length in us, or 0 when channels is 0 or above PPM_MAX_CHANNELS.