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

Servo pulses from the RP2350's hardware PWM (pulse-width modulation). More...

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

Go to the source code of this file.

Macros

#define OUT_PWM_STAMP_WAIT_US   5u
 How long the stamped write may wait for the counter to leave the end of its frame, us.

Functions

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.

Detailed Description

Servo pulses from the RP2350's hardware PWM (pulse-width modulation).

A servo pulse is a level held for a number of microseconds, repeated at a frame rate, and that is what a PWM slice does with no processor involved. There is no reason to spend a PIO (programmable input/output) state machine on it: the part has twelve slices and twenty-four channels, more outputs than this bench has connectors.

What a caller has to know is how GPIO (general-purpose input/output) numbers fold onto them. A slice is two channels sharing one counter, so two pins on the same slice run at the same frame rate whatever the second one asked for, and a second binding with a different rate is refused rather than quietly retimed. Below that, a channel is one compare register: pins 16 apart under GP32 – GP0 and GP16 – and pins 8 apart above it – GP32 and GP40 – are the same channel of the same slice and cannot hold two pulse widths, so the second of such a pair is refused outright. out_pwm_map.h has the fold and the host suite tests it.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ OUT_PWM_STAMP_WAIT_US

#define OUT_PWM_STAMP_WAIT_US   5u

How long the stamped write may wait for the counter to leave the end of its frame, us.

Two counts at most when the slice runs.

Function Documentation

◆ 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.