DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
pca9685.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file pca9685.h
6 * @brief NXP PCA9685 16-channel 12-bit PWM / servo driver codec (DETWS_ENABLE_PCA9685).
7 *
8 * The PCA9685 generates sixteen independent 12-bit PWM outputs from a 25 MHz oscillator. The
9 * output frequency is set by a PRESCALE register value; each channel is four registers (a 12-bit
10 * ON count and a 12-bit OFF count) at `0x06 + 4 * channel`. Driving a hobby servo is a matter of
11 * turning a pulse width (in microseconds) into an OFF count at the configured frequency.
12 *
13 * This codec is pure and host-tested: ::pca9685_prescale computes the prescale for a frequency,
14 * ::pca9685_us_to_count converts a servo pulse width to a 12-bit count, ::pca9685_channel_reg
15 * gives a channel's register base, and ::pca9685_set_pwm_bytes emits the 5-byte channel write.
16 * On an ESP32 the binding replays those writes over I2C (Wire); only that touches hardware.
17 *
18 * A cheap solder-and-bench-test breakout for driving up to 16 servos or LEDs: wire it up, sweep
19 * a servo.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef DETERMINISTICESPASYNCWEBSERVER_PCA9685_H
26#define DETERMINISTICESPASYNCWEBSERVER_PCA9685_H
27
28#include <stddef.h>
29#include <stdint.h>
30
31#define PCA9685_CHANNELS 16 ///< PWM output channels
32#define PCA9685_COUNT_MAX 4095 ///< a PWM count is 12-bit (0..4095)
33#define PCA9685_FULL_ON 0x1000 ///< pass as `on` for a channel fully on (bit 12 flag)
34#define PCA9685_FULL_OFF 0x1000 ///< pass as `off` for a channel fully off (bit 12 flag)
35
36// Registers.
37#define PCA9685_REG_MODE1 0x00
38#define PCA9685_REG_MODE2 0x01
39#define PCA9685_REG_LED0_ON_L 0x06 ///< channel 0 base; channel n is this + 4*n
40#define PCA9685_REG_PRESCALE 0xFE
41
42/**
43 * @brief Compute the PRESCALE register value for a PWM output frequency (25 MHz oscillator):
44 * `round(25e6 / (4096 * freq)) - 1`, clamped to the chip's valid 3..255 range.
45 */
46uint8_t pca9685_prescale(uint32_t freq_hz);
47
48/** @brief The register base (LED_ON_L) for @p channel (0..15); 0 for an out-of-range channel. */
49uint8_t pca9685_channel_reg(uint8_t channel);
50
51/**
52 * @brief Convert a servo pulse width (microseconds) at @p freq_hz to a 12-bit OFF count
53 * (rounded), clamped to 0..4095. ON is taken as 0, so the pulse starts at the period's edge.
54 */
55uint16_t pca9685_us_to_count(uint32_t microseconds, uint32_t freq_hz);
56
57/**
58 * @brief Emit the 5-byte channel PWM write: `[LED_ON_L(channel), ON_L, ON_H, OFF_L, OFF_H]`
59 * (each count 12-bit little-endian; bit 12 / ::PCA9685_FULL_ON is the full-on/off flag).
60 * @return 5, or 0 if @p cap < 5 or @p channel is out of range.
61 */
62size_t pca9685_set_pwm_bytes(uint8_t *buf, size_t cap, uint8_t channel, uint16_t on, uint16_t off);
63
64// --- ESP32 binding (I2C via Wire; no-ops on a host build) ------------------------------------
65
66/** @brief Reset the PCA9685 at @p addr and set the PWM frequency @p freq_hz. @return true on ack. */
67bool pca9685_begin(uint8_t addr, uint32_t freq_hz);
68
69/** @brief Set @p channel's raw 12-bit ON / OFF counts. @return false on I2C error / bad channel. */
70bool pca9685_set_pwm(uint8_t channel, uint16_t on, uint16_t off);
71
72/** @brief Drive a servo on @p channel to a @p microseconds pulse (uses the configured frequency). */
73bool pca9685_set_servo_us(uint8_t channel, uint32_t microseconds);
74
75#endif // DETERMINISTICESPASYNCWEBSERVER_PCA9685_H
bool pca9685_begin(uint8_t addr, uint32_t freq_hz)
Reset the PCA9685 at addr and set the PWM frequency freq_hz.
uint16_t pca9685_us_to_count(uint32_t microseconds, uint32_t freq_hz)
Convert a servo pulse width (microseconds) at freq_hz to a 12-bit OFF count (rounded),...
uint8_t pca9685_prescale(uint32_t freq_hz)
Compute the PRESCALE register value for a PWM output frequency (25 MHz oscillator): round(25e6 / (409...
bool pca9685_set_servo_us(uint8_t channel, uint32_t microseconds)
Drive a servo on channel to a microseconds pulse (uses the configured frequency).
bool pca9685_set_pwm(uint8_t channel, uint16_t on, uint16_t off)
Set channel's raw 12-bit ON / OFF counts.
uint8_t pca9685_channel_reg(uint8_t channel)
The register base (LED_ON_L) for channel (0..15); 0 for an out-of-range channel.
size_t pca9685_set_pwm_bytes(uint8_t *buf, size_t cap, uint8_t channel, uint16_t on, uint16_t off)
Emit the 5-byte channel PWM write: [LED_ON_L(channel), ON_L, ON_H, OFF_L, OFF_H] (each count 12-bit l...