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"
|
| 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...
|
|
| #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.
|
|
| 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.
|
Hardware PWM (pulse-width modulation) servo outputs.
See out_pwm.h.
SPDX-License-Identifier: MIT
◆ 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.
◆ find()
Finds the binding of a pin.
- Parameters
-
| pin | GPIO 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
-
| pin | GPIO number of the pin to bind. |
| rate_hz | Frame 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
-
| pin | GPIO 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
-
| pin | GPIO number of a bound pin. A pin that is not bound is ignored. |
| pulse_us | Pulse 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
-
| pin | GPIO number of a bound pin. A pin that is not bound is refused. |
| pulse_us | Pulse width in microseconds. A value above the frame's last count is clamped to it. |
| frame_us | Receives 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
-
- Returns
- The last count of the frame, in microseconds.