From a9496ad9f76880b447941768a9d0f86070ed8c41 Mon Sep 17 00:00:00 2001 From: Hugues Kamba Date: Tue, 8 Oct 2019 12:36:47 +0100 Subject: [PATCH] PmwOut: Add methods to suspend and resume PWM It is now possible to temporarily suspend PWM and safely preserve the duty cycle set. This functionality is needed to allow a device to enter deep sleep as a PWM instance prevents deep sleep in order for the timer it relies on to run so its output can be modified. The duty cycle configuration can be restored upon resuming from deep sleep. --- drivers/PwmOut.h | 27 +++++++++++++++++ drivers/source/PwmOut.cpp | 64 +++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/drivers/PwmOut.h b/drivers/PwmOut.h index 68102caaa57..aa067b37ba9 100644 --- a/drivers/PwmOut.h +++ b/drivers/PwmOut.h @@ -118,6 +118,24 @@ class PwmOut { */ void pulsewidth_us(int us); + /** Suspend PWM operation + * + * Control the PWM state. This is primarily intended + * for temporary power-saving; This call can + * allow pwm to be temporarily disabled to permit power saving without + * losing device state. The subsequent function call must be PwmOut::resume + * for PWM to resume; any other calls prior to resuming are undefined behavior. + */ + void suspend(); + + /** Resume PWM operation + * + * Control the PWM state. This is primarily intended + * to resume PWM operations after a previous PwmOut::suspend call; + * This call restores the device state prior to suspension. + */ + void resume(); + /** A operator shorthand for write() * \sa PwmOut::write() */ @@ -155,8 +173,17 @@ class PwmOut { /** Unlock deep sleep in case it is locked */ void unlock_deep_sleep(); + /** Initialize this instance */ + void init(); + + /** Power down this instance */ + void deinit(); + pwmout_t _pwm; + PinName _pin; bool _deep_sleep_locked; + bool _initialized; + float _duty_cycle; #endif }; diff --git a/drivers/source/PwmOut.cpp b/drivers/source/PwmOut.cpp index 67e5f36b341..2951bbe4144 100644 --- a/drivers/source/PwmOut.cpp +++ b/drivers/source/PwmOut.cpp @@ -22,28 +22,28 @@ #include "platform/mbed_critical.h" #include "platform/mbed_power_mgmt.h" +#include "platform/mbed_assert.h" namespace mbed { -PwmOut::PwmOut(PinName pin) : _deep_sleep_locked(false) +PwmOut::PwmOut(PinName pin) : + _pin(pin), + _deep_sleep_locked(false), + _initialized(false), + _duty_cycle(0) { - core_util_critical_section_enter(); - pwmout_init(&_pwm, pin); - core_util_critical_section_exit(); + PwmOut::init(); } PwmOut::~PwmOut() { - core_util_critical_section_enter(); - pwmout_free(&_pwm); - unlock_deep_sleep(); - core_util_critical_section_exit(); + MBED_ASSERT(!_initialized); + PwmOut::deinit(); } void PwmOut::write(float value) { core_util_critical_section_enter(); - lock_deep_sleep(); pwmout_write(&_pwm, value); core_util_critical_section_exit(); } @@ -98,6 +98,26 @@ void PwmOut::pulsewidth_us(int us) core_util_critical_section_exit(); } +void PwmOut::suspend() +{ + core_util_critical_section_enter(); + if (_initialized) { + _duty_cycle = PwmOut::read(); + PwmOut::deinit(); + } + core_util_critical_section_exit(); +} + +void PwmOut::resume() +{ + core_util_critical_section_enter(); + if (!_initialized) { + PwmOut::init(); + PwmOut::write(_duty_cycle); + } + core_util_critical_section_exit(); +} + void PwmOut::lock_deep_sleep() { if (_deep_sleep_locked == false) { @@ -114,6 +134,32 @@ void PwmOut::unlock_deep_sleep() } } +void PwmOut::init() +{ + core_util_critical_section_enter(); + + if (!_initialized) { + pwmout_init(&_pwm, _pin); + lock_deep_sleep(); + _initialized = true; + } + + core_util_critical_section_exit(); +} + +void PwmOut::deinit() +{ + core_util_critical_section_enter(); + + if (_initialized) { + pwmout_free(&_pwm); + unlock_deep_sleep(); + _initialized = false; + } + + core_util_critical_section_exit(); +} + } // namespace mbed #endif // #if DEVICE_PWMOUT