26#include <idfxx/intr_alloc>
30#include <frequency/frequency>
53#if SOC_LEDC_SUPPORT_HS_MODE
72#if SOC_LEDC_CHANNEL_NUM > 6
75#if SOC_LEDC_CHANNEL_NUM > 7
89#if SOC_LEDC_SUPPORT_APB_CLOCK
92#if SOC_LEDC_SUPPORT_REF_TICK
95#if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
98#if SOC_LEDC_SUPPORT_XTAL_CLOCK
140template<
int N, speed_mode M>
141struct timer_constant;
170 template<
int N, speed_mode M>
193 [[nodiscard]]
constexpr unsigned int num() const noexcept {
return _num; }
198#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
303 [[nodiscard]] std::chrono::nanoseconds
period() const;
315#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
341#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
358 template<
typename Rep,
typename Period>
377 template<
typename Rep,
typename Period>
379 auto ns = std::chrono::ceil<std::chrono::nanoseconds>(
period).count();
386#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
403#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
420#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
442 , _speed_mode(mode) {}
449template<
int N, speed_mode M>
450struct timer_constant {
451 static_assert(N >= 0 && N < SOC_LEDC_TIMER_NUM,
"Invalid PWM timer number");
452 static constexpr timer value{N, M};
462inline constexpr timer timer_0 = timer_constant<0, speed_mode::low_speed>::value;
463inline constexpr timer timer_1 = timer_constant<1, speed_mode::low_speed>::value;
464inline constexpr timer timer_2 = timer_constant<2, speed_mode::low_speed>::value;
465inline constexpr timer timer_3 = timer_constant<3, speed_mode::low_speed>::value;
467#if SOC_LEDC_SUPPORT_HS_MODE
468inline constexpr timer hs_timer_0 = timer_constant<0, speed_mode::high_speed>::value;
469inline constexpr timer hs_timer_1 = timer_constant<1, speed_mode::high_speed>::value;
470inline constexpr timer hs_timer_2 = timer_constant<2, speed_mode::high_speed>::value;
471inline constexpr timer hs_timer_3 = timer_constant<3, speed_mode::high_speed>::value;
490#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
729 [[nodiscard]]
class timer timer() const noexcept {
return _timer; }
749 [[nodiscard]]
float duty() const;
760#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
786#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
839#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
854 template<
typename Rep,
typename Period>
870 template<
typename Rep,
typename Period>
872 return _try_set_pulse_width(std::chrono::ceil<std::chrono::nanoseconds>(width));
875#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
889 template<
typename Rep,
typename Period>
892 const std::chrono::duration<Rep, Period>& duration,
914 template<
typename Rep,
typename Period>
917 const std::chrono::duration<Rep, Period>& duration,
920 return _try_fade_to_duty(
921 static_cast<uint32_t
>(std::clamp(target, 0.0f, 1.0f) *
static_cast<float>(
ticks_max())),
922 std::chrono::ceil<std::chrono::milliseconds>(duration),
927#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
941 template<
typename Rep,
typename Period>
943 uint32_t target_duty,
944 const std::chrono::duration<Rep, Period>& duration,
966 template<
typename Rep,
typename Period>
968 uint32_t target_duty,
969 const std::chrono::duration<Rep, Period>& duration,
972 return _try_fade_to_duty(target_duty, std::chrono::ceil<std::chrono::milliseconds>(duration), mode);
975#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
991 template<
typename Rep1,
typename Period1,
typename Rep2,
typename Period2>
993 const std::chrono::duration<Rep1, Period1>& target_width,
994 const std::chrono::duration<Rep2, Period2>& duration,
1018 template<
typename Rep1,
typename Period1,
typename Rep2,
typename Period2>
1020 const std::chrono::duration<Rep1, Period1>& target_width,
1021 const std::chrono::duration<Rep2, Period2>& duration,
1024 return _try_fade_to_pulse_width(
1025 std::chrono::ceil<std::chrono::nanoseconds>(target_width),
1026 std::chrono::ceil<std::chrono::milliseconds>(duration),
1031#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1065 uint32_t target_duty,
1071#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
1072#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1087 unwrap(try_multi_fade(start_duty, steps, mode));
1109#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1120 [[nodiscard]]
fade_param read_fade_param(uint32_t range)
const {
return unwrap(try_read_fade_param(range)); }
1133#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1152 template<
typename Rep,
typename Period>
1153 [[nodiscard]] std::vector<fade_param> fill_multi_fade_params(
1154 uint32_t start_duty,
1156 uint32_t linear_phases,
1157 const std::chrono::duration<Rep, Period>& max_fade_time,
1158 uint32_t (*gamma_correction)(uint32_t)
1160 return unwrap(try_fill_multi_fade_params(start_duty, end_duty, linear_phases, max_fade_time, gamma_correction));
1179 template<
typename Rep,
typename Period>
1180 [[nodiscard]] result<std::vector<fade_param>> try_fill_multi_fade_params(
1181 uint32_t start_duty,
1183 uint32_t linear_phases,
1184 const std::chrono::duration<Rep, Period>& max_fade_time,
1185 uint32_t (*gamma_correction)(uint32_t)
1187 return _try_fill_multi_fade_params(
1191 std::chrono::ceil<std::chrono::milliseconds>(max_fade_time),
1197#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1229#if SOC_LEDC_SUPPORT_FADE_STOP
1230#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1239 void fade_stop() {
unwrap(try_fade_stop()); }
1252#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1292#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1332 [[nodiscard]]
result<
void> _try_set_pulse_width(std::chrono::nanoseconds width);
1334 [[nodiscard]]
result<
void> _try_fade_to_pulse_width(
1335 std::chrono::nanoseconds target_width,
1336 std::chrono::milliseconds duration,
1340 [[nodiscard]]
result<
void>
1341 _try_fade_to_duty(uint32_t target_duty, std::chrono::milliseconds duration, enum
fade_mode mode);
1343#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
1345 uint32_t start_duty,
1347 uint32_t linear_phases,
1348 std::chrono::milliseconds max_fade_time,
1349 uint32_t (*gamma_correction)(uint32_t)
1353 void _delete() noexcept;
1357 std::optional<uint32_t> _gen;
1358 uint32_t _duty_ticks = 0;
1359 std::unique_ptr<std::move_only_function<
bool() const>> _fade_cb;
1362#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
1393[[nodiscard]]
inline std::string to_string(
const pwm::timer& t) {
1395#if SOC_LEDC_SUPPORT_HS_MODE
1396 if (t.speed_mode() == pwm::speed_mode::high_speed) {
1397 s =
"PWM_HS_TIMER_";
1404 s += std::to_string(t.num());
1415[[nodiscard]]
inline std::string
to_string(pwm::channel ch) {
1416 return "PWM_CH_" + std::to_string(std::to_underlying(ch));
1426[[nodiscard]]
inline std::string
to_string(pwm::speed_mode m) {
1428 case pwm::speed_mode::low_speed:
1430#if SOC_LEDC_SUPPORT_HS_MODE
1431 case pwm::speed_mode::high_speed:
1432 return "high_speed";
1435 return "unknown(" + std::to_string(std::to_underlying(m)) +
")";
1441#include "sdkconfig.h"
1442#ifdef CONFIG_IDFXX_STD_FORMAT
1447struct formatter<
idfxx::pwm::timer> {
1448 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1450 template<
typename FormatContext>
1453 return std::copy(s.begin(), s.end(), ctx.out());
1457struct formatter<
idfxx::pwm::channel> {
1458 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1460 template<
typename FormatContext>
1463 return std::copy(s.begin(), s.end(), ctx.out());
1467struct formatter<
idfxx::pwm::speed_mode> {
1468 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1470 template<
typename FormatContext>
1473 return std::copy(s.begin(), s.end(), ctx.out());
level
GPIO output/input level.
An active PWM output binding a timer, channel, and GPIO pin.
result< void > try_fade_to(float target, const std::chrono::duration< Rep, Period > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target duty ratio over the specified duration.
bool is_active() const noexcept
Returns true if the output is still actively driving the channel.
enum channel release() noexcept
Releases ownership of the channel without stopping the PWM output.
static result< void > try_install_fade_service(idfxx::intr_levels levels=intr_level_lowmed, idfxx::flags< intr_flag > flags={})
Installs the PWM fade service.
result< void > try_fade_to_duty_ticks(uint32_t target_duty, const std::chrono::duration< Rep, Period > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target duty in ticks over the specified duration.
void fade_to_duty_ticks(uint32_t target_duty, const std::chrono::duration< Rep, Period > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target duty in ticks over the specified duration.
result< void > try_set_pulse_width(const std::chrono::duration< Rep, Period > &width)
Sets the PWM duty cycle as a pulse width duration.
result< void > try_set_duty_ticks(uint32_t duty)
Sets the PWM duty cycle in ticks.
void set_duty_ticks(uint32_t duty)
Sets the PWM duty cycle in ticks.
void fade_with_step(uint32_t target_duty, uint32_t scale, uint32_t cycle_num, enum fade_mode mode=fade_mode::no_wait)
Starts a fade to a target duty in ticks with step control.
uint32_t ticks_max() const noexcept
Returns the maximum duty ticks for the configured resolution.
result< void > try_stop(idfxx::gpio::level idle_level=idfxx::gpio::level::low)
Stops PWM output and sets the GPIO to an idle level.
result< void > try_set_duty(float duty)
Sets the PWM duty cycle as a ratio.
output(output &&other) noexcept
float duty() const
Returns the current duty cycle as a ratio.
void register_fade_end_callback(std::move_only_function< bool() const > callback)
Registers a callback for fade-end events.
output & operator=(output &&other) noexcept
~output()
Destroys the output, stopping PWM on the bound GPIO.
output(const output &)=delete
result< void > try_fade_with_step(uint32_t target_duty, uint32_t scale, uint32_t cycle_num, enum fade_mode mode=fade_mode::no_wait)
Starts a fade to the target duty with step control.
void stop(idfxx::gpio::level idle_level=idfxx::gpio::level::low)
Stops PWM output and sets the GPIO to an idle level.
result< void > try_fade_to_pulse_width(const std::chrono::duration< Rep1, Period1 > &target_width, const std::chrono::duration< Rep2, Period2 > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target pulse width over the specified duration.
static void install_fade_service(idfxx::intr_levels levels=intr_level_lowmed, idfxx::flags< intr_flag > flags={})
Installs the PWM fade service.
void fade_to_pulse_width(const std::chrono::duration< Rep1, Period1 > &target_width, const std::chrono::duration< Rep2, Period2 > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target pulse width over the specified duration.
result< void > try_register_fade_end_callback(std::move_only_function< bool() const > callback)
Registers a callback for fade-end events.
std::chrono::nanoseconds pulse_width() const
Returns the current pulse width as a duration.
result< void > try_set_duty_ticks(uint32_t duty, uint32_t hpoint)
Sets the PWM duty cycle in ticks with high point.
enum channel channel() const noexcept
Returns the channel slot that was bound to this output.
static void uninstall_fade_service()
Uninstalls the PWM fade service.
output & operator=(const output &)=delete
void set_pulse_width(const std::chrono::duration< Rep, Period > &width)
Sets the PWM duty cycle as a pulse width duration.
friend result< output > try_start(idfxx::gpio gpio, const class timer &tmr, enum channel ch, const output_config &cfg)
void set_duty(float duty)
Sets the PWM duty cycle as a ratio.
void set_duty_ticks(uint32_t duty, uint32_t hpoint)
Sets the PWM duty cycle in ticks with high point.
uint32_t duty_ticks() const
Returns the current duty cycle in ticks.
void fade_to(float target, const std::chrono::duration< Rep, Period > &duration, enum fade_mode mode=fade_mode::no_wait)
Fades to a target duty ratio over the specified duration.
A lightweight identifier for a hardware PWM timer.
void configure(const struct config &cfg)
Configures the timer with the given parameters.
result< void > try_configure(const struct config &cfg)
Configures the timer with the given parameters.
uint32_t ticks_max() const noexcept
Returns the maximum duty ticks for the configured resolution.
uint8_t resolution_bits() const noexcept
Returns the configured resolution in bits.
timer & operator=(timer &&)=default
friend result< output > try_start(idfxx::gpio, const config &, const output_config &)
Starts PWM output with automatic allocation and custom output configuration.
constexpr unsigned int num() const noexcept
Returns the timer number (0-3).
freq::hertz frequency() const
Returns the current timer frequency.
result< void > try_set_frequency(freq::hertz frequency)
Changes the timer frequency.
friend struct timer_constant
result< void > try_configure(freq::hertz frequency, uint8_t resolution_bits=13)
Configures the timer with frequency and resolution.
constexpr enum speed_mode speed_mode() const noexcept
Returns the speed mode.
void resume()
Resumes the timer counter.
timer(const timer &)=default
std::chrono::nanoseconds tick_period() const
Returns the duration of a single timer tick.
timer & operator=(const timer &)=default
constexpr bool operator==(const timer &other) const =default
std::chrono::nanoseconds period() const
Returns the PWM period (1 / frequency) as a duration.
void set_period(const std::chrono::duration< Rep, Period > &period)
Changes the timer period.
result< void > try_pause()
Pauses the timer counter.
result< void > try_resume()
Resumes the timer counter.
void pause()
Pauses the timer counter.
void reset()
Resets the timer counter.
void configure(freq::hertz frequency, uint8_t resolution_bits=13)
Configures the timer with frequency and resolution.
result< void > try_reset()
Resets the timer counter.
void set_frequency(freq::hertz frequency)
Changes the timer frequency.
result< void > try_set_period(const std::chrono::duration< Rep, Period > &period)
Changes the timer period.
bool is_configured() const noexcept
Returns true if the timer has been configured.
friend std::optional< timer > get_timer(enum channel, enum speed_mode)
Returns the timer associated with an active channel, if any.
High-resolution timer with microsecond precision.
std::string to_string(core_id c)
Returns a string representation of a CPU core identifier.
result< output > try_start(idfxx::gpio gpio, const timer &tmr, enum channel ch)
Starts PWM output on a GPIO pin.
sleep_mode
Channel behavior during light sleep.
bool is_active(enum channel ch, enum speed_mode mode=speed_mode::low_speed)
Returns true if the specified channel currently has an active output.
speed_mode
PWM speed mode selection.
void stop(enum channel ch, enum speed_mode mode=speed_mode::low_speed, idfxx::gpio::level idle_level=idfxx::gpio::level::low)
Stops PWM output on a channel and sets it to an idle level.
channel
PWM channel slot identifiers.
output start(idfxx::gpio gpio, const timer &tmr, enum channel ch)
Starts PWM output on a GPIO pin.
fade_mode
Fade operation blocking mode.
result< void > try_stop(enum channel ch, enum speed_mode mode=speed_mode::low_speed, idfxx::gpio::level idle_level=idfxx::gpio::level::low)
Stops PWM output on a channel and sets it to an idle level.
std::optional< timer > get_timer(enum channel ch, enum speed_mode mode=speed_mode::low_speed)
Returns the timer associated with an active channel, if any.
clk_source
PWM timer clock source.
@ no_alive_no_pd
No output, keep power domain on (default)
@ keep_alive
Maintain PWM output during light sleep.
@ no_alive_allow_pd
No output, allow power domain off (saves power)
@ low_speed
Low-speed mode (available on all targets)
@ no_wait
Return immediately, fade runs in background.
@ wait_done
Block until the fade completes.
@ auto_select
Automatic clock source selection.
constexpr std::unexpected< std::error_code > error(E e) noexcept
Creates an unexpected error from an error code enum.
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
@ invalid_arg
Invalid argument.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
constexpr intr_levels intr_level_lowmed
Low and medium priority levels (1-3). These can be handled in C / C++.
Parameters for a single step in a multi-step hardware fade.
bool increasing
Duty change direction: true = increase, false = decrease.
uint32_t scale
Duty change per step.
uint32_t cycle_num
Number of PWM cycles per step.
uint32_t step_num
Total number of steps.
Output configuration parameters.
bool output_invert
Invert the output signal.
float duty
Initial duty cycle ratio (0.0 to 1.0).
int hpoint
High point in the PWM cycle.
Timer configuration parameters.
uint8_t resolution_bits
Duty resolution in bits (1 to SOC_LEDC_TIMER_BIT_WIDTH).
freq::hertz frequency
PWM frequency.