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

Hardware PWM (pulse-width modulation) servo outputs. More...

#include "out_pwm.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/sync.h"
#include "hardware/timer.h"
#include "link_pages.h"
#include "out_pwm_map.h"
#include "outputs.h"

Data Structures

struct  binding_t
 One pin binding: whether the entry is in use, its pin, its frame rate and the last count of its frame. More...

Macros

#define TICK_HZ   OUT_PWM_TICK_HZ
 One microsecond per count (OUT_PWM_TICK_HZ).
#define BINDINGS   (OUT_MAX_SLOTS + LINK_PG_PORTS_MAX)
 What is bound, so the sharing rules can be checked and a release can put the pin back. One entry per slot the bank can hold and one per programming port; nothing here has more outputs than that.
#define STAMP_GUARD   2u
 Counts at the end of a frame in which a write may meet the wrap.

Functions

static binding_t * find (uint8_t pin)
 Finds the binding of a pin.
static uint16_t wrap_for (uint16_t rate_hz)
 The frame's last count, which is the longest pulse the output pages let through at this rate: the same function answers both.
bool out_pwm_bind (uint8_t pin, uint16_t rate_hz)
 Take pin at rate_hz frames a second.
void out_pwm_release (uint8_t pin)
 Give the pin back: the counter stops and the pin is left low.
void out_pwm_write (uint8_t pin, uint16_t pulse_us)
 Emit pulse_us on every frame.
bool out_pwm_write_stamped (uint8_t pin, uint16_t pulse_us, uint64_t *frame_us)
 out_pwm_write(), and the time the first frame carrying pulse_us starts, on the 64-bit microsecond timer (time_us_64()), into frame_us.

Variables

static binding_t s_bound [BINDINGS]
 The pin bindings, one entry per bound pin.

Detailed Description

Hardware PWM (pulse-width modulation) servo outputs.

See out_pwm.h.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ TICK_HZ

#define TICK_HZ   OUT_PWM_TICK_HZ

One microsecond per count (OUT_PWM_TICK_HZ).

The frame period then fits the sixteen-bit counter down to 16Hz, past the 40Hz floor the driver table sets, and a microsecond is the resolution every pulse in this firmware is expressed in.

Function Documentation

◆ find()

binding_t * find ( uint8_t pin)
static

Finds the binding of a pin.

Parameters
pinGPIO number to look up.
Returns
The entry holding pin, or NULL when the pin is not bound.

◆ out_pwm_bind()

bool out_pwm_bind ( uint8_t pin,
uint16_t rate_hz )

Take pin at rate_hz frames a second.

Refuses a pin whose compare register another bound pin already holds, a pin that is already bound to a different rate's slice, a pin the package does not have, and a rate whose period does not fit the counter. Binding an already-bound pin at the same rate succeeds and changes nothing, so a reconfiguration that did not move a slot does not glitch its output.

A refused bind leaves the slot unbound and the OUTPUTS page reading back what was asked for. Nothing on the wire reports whether a slot is bound, so an unbound slot is not distinguishable from a bound one at the panel.

Parameters
pinGPIO number of the pin to bind.
rate_hzFrame rate in Hz. Zero is refused.
Returns
True when the pin is bound at rate_hz. False when the bind is refused.

◆ out_pwm_release()

void out_pwm_release ( uint8_t pin)

Give the pin back: the counter stops and the pin is left low.

Parameters
pinGPIO number of the pin to release. A pin that is not bound is ignored.

◆ out_pwm_write()

void out_pwm_write ( uint8_t pin,
uint16_t pulse_us )

Emit pulse_us on every frame.

Zero stops the pin edging altogether, which is what the bank means by not driving – a servo held at a pulse it was not commanded is worse than one that is let go.

Parameters
pinGPIO number of a bound pin. A pin that is not bound is ignored.
pulse_usPulse width in microseconds. A value above the frame's last count is clamped to it.

◆ out_pwm_write_stamped()

bool out_pwm_write_stamped ( uint8_t pin,
uint16_t pulse_us,
uint64_t * frame_us )

out_pwm_write(), and the time the first frame carrying pulse_us starts, on the 64-bit microsecond timer (time_us_64()), into frame_us.

The compare register is double-buffered: a level written during a frame takes effect at the wrap that ends it, and the pin rises at counter 0. The counter is read and the level written with interrupts off, never in the frame's last 2 counts – a write there could meet the wrap that latches it, and the frame it lands in would be unknown – so the write lands in the frame the counter was read in, and that frame ends (wrap - counter + 1) counts later. One count is 1us, from clk_sys divided to 1MHz; the timer ticks at 1MHz from the same crystal. The stamp is good to the 1us of each, within +-1us.

False, with the level written and nothing stamped, for a pin not bound or a slice whose counter does not leave the frame's end within OUT_PWM_STAMP_WAIT_US: one that is not counting.

Parameters
pinGPIO number of a bound pin. A pin that is not bound is refused.
pulse_usPulse width in microseconds. A value above the frame's last count is clamped to it.
frame_usReceives the start time of the frame. Written only when the function returns true.
Returns
True when the write is stamped. See the description for the false cases.

◆ wrap_for()

uint16_t wrap_for ( uint16_t rate_hz)
static

The frame's last count, which is the longest pulse the output pages let through at this rate: the same function answers both.

Parameters
rate_hzFrame rate in Hz.
Returns
The last count of the frame, in microseconds.