From 8af6843566215a48ef1ff952f1e012b3e0b2ea75 Mon Sep 17 00:00:00 2001 From: Alex Tereschenko Date: Tue, 24 May 2016 23:12:24 +0200 Subject: [PATCH] PWM: added workaround for Edison's problem with 0% duty As described in issue #91, on Edison setting 0% duty doesn't disable the PWM on a pin completely. Therefore we add a couple of Edison-specific _pre functions and an internal PWM state variable, which we use to toggle PWM enabled/disabled based on what duty is set for the pin. Closes #91. Signed-off-by: Alex Tereschenko Signed-off-by: Brendan Le Foll --- include/mraa_adv_func.h | 2 ++ src/pwm/pwm.c | 14 ++++++++++++++ src/x86/intel_edison_fab_c.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/include/mraa_adv_func.h b/include/mraa_adv_func.h index e237ef9..ea7fe98 100644 --- a/include/mraa_adv_func.h +++ b/include/mraa_adv_func.h @@ -87,7 +87,9 @@ typedef struct { mraa_result_t (*pwm_period_replace) (mraa_pwm_context dev, int period); float (*pwm_read_replace) (mraa_pwm_context dev); mraa_result_t (*pwm_write_replace) (mraa_pwm_context dev, float duty); + mraa_result_t (*pwm_write_pre) (mraa_pwm_context dev, float percentage); mraa_result_t (*pwm_enable_replace) (mraa_pwm_context dev, int enable); + mraa_result_t (*pwm_enable_pre) (mraa_pwm_context dev, int enable); mraa_result_t (*spi_init_pre) (int bus); mraa_result_t (*spi_init_post) (mraa_spi_context spi); diff --git a/src/pwm/pwm.c b/src/pwm/pwm.c index a5370bc..43a5ba4 100644 --- a/src/pwm/pwm.c +++ b/src/pwm/pwm.c @@ -340,6 +340,13 @@ mraa_pwm_write(mraa_pwm_context dev, float percentage) return MRAA_ERROR_INVALID_HANDLE; } + if (IS_FUNC_DEFINED(dev, pwm_write_pre)) { + if (dev->advance_func->pwm_write_pre(dev, percentage) != MRAA_SUCCESS) { + syslog(LOG_ERR, "mraa_pwm_write (pwm%i): pwm_write_pre failed, see syslog", dev->pin); + return MRAA_ERROR_UNSPECIFIED; + } + } + if (dev->period == -1) { if (mraa_pwm_read_period(dev) <= 0) return MRAA_ERROR_NO_DATA_AVAILABLE; @@ -433,6 +440,13 @@ mraa_pwm_enable(mraa_pwm_context dev, int enable) return dev->advance_func->pwm_enable_replace(dev, enable); } + if (IS_FUNC_DEFINED(dev, pwm_enable_pre)) { + if (dev->advance_func->pwm_enable_pre(dev, enable) != MRAA_SUCCESS) { + syslog(LOG_ERR, "mraa_pwm_enable (pwm%i): pwm_enable_pre failed, see syslog", dev->pin); + return MRAA_ERROR_UNSPECIFIED; + } + } + char bu[MAX_SIZE]; snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/enable", dev->chipid, dev->pin); diff --git a/src/x86/intel_edison_fab_c.c b/src/x86/intel_edison_fab_c.c index dbb18c1..0b5d795 100644 --- a/src/x86/intel_edison_fab_c.c +++ b/src/x86/intel_edison_fab_c.c @@ -76,6 +76,9 @@ static int mmap_fd = 0; static int mmap_size; static unsigned int mmap_count = 0; +// PWM 0% duty workaround state array +static int pwm_disabled[4] = { 0 }; + mraa_result_t mraa_intel_edison_spi_lsbmode_replace(mraa_spi_context dev, mraa_boolean_t lsb) { @@ -367,6 +370,30 @@ mraa_intel_edison_aio_init_post(mraa_aio_context dev) return mraa_gpio_write(tristate, 1); } +mraa_result_t +mraa_intel_edison_pwm_enable_pre(mraa_pwm_context dev, int enable) { + // PWM 0% duty workaround: update state array + // if someone first ran write(0) and then enable(1). + if ((pwm_disabled[dev->pin] == 1) && (enable == 1)) { pwm_disabled[dev->pin] = 0; } + return MRAA_SUCCESS; +} + +mraa_result_t +mraa_intel_edison_pwm_write_pre(mraa_pwm_context dev, float percentage) { + // PWM 0% duty workaround: set the state array and enable/disable pin accordingly + if (percentage == 0.0f) { + syslog(LOG_INFO, "edison_pwm_write_pre (pwm%i): requested zero duty cycle, disabling PWM on the pin", dev->pin); + pwm_disabled[dev->pin] = 1; + return mraa_pwm_enable(dev, 0); + } else if (pwm_disabled[dev->pin] == 1) { + syslog(LOG_INFO, "edison_pwm_write_pre (pwm%i): Re-enabling the pin after setting non-zero duty", dev->pin); + pwm_disabled[dev->pin] = 0; + return mraa_pwm_enable(dev, 1); + } + + return MRAA_SUCCESS; +} + mraa_result_t mraa_intel_edison_pwm_init_pre(int pin) { @@ -414,6 +441,7 @@ mraa_intel_edison_pwm_init_pre(int pin) mraa_result_t mraa_intel_edison_pwm_init_post(mraa_pwm_context pwm) { + pwm_disabled[pwm->pin] = 0; return mraa_gpio_write(tristate, 1); } @@ -802,6 +830,8 @@ mraa_intel_edison_miniboard(mraa_board_t* b) } b->adv_func->gpio_init_post = &mraa_intel_edison_gpio_init_post; b->adv_func->pwm_init_pre = &mraa_intel_edison_pwm_init_pre; + b->adv_func->pwm_enable_pre = &mraa_intel_edison_pwm_enable_pre; + b->adv_func->pwm_write_pre = &mraa_intel_edison_pwm_write_pre; b->adv_func->i2c_init_pre = &mraa_intel_edison_i2c_init_pre; b->adv_func->i2c_set_frequency_replace = &mraa_intel_edison_i2c_freq; b->adv_func->spi_init_pre = &mraa_intel_edison_spi_init_pre; @@ -1259,6 +1289,8 @@ mraa_intel_edison_fab_c() b->adv_func->aio_init_post = &mraa_intel_edison_aio_init_post; b->adv_func->pwm_init_pre = &mraa_intel_edison_pwm_init_pre; b->adv_func->pwm_init_post = &mraa_intel_edison_pwm_init_post; + b->adv_func->pwm_enable_pre = &mraa_intel_edison_pwm_enable_pre; + b->adv_func->pwm_write_pre = &mraa_intel_edison_pwm_write_pre; b->adv_func->spi_init_pre = &mraa_intel_edison_spi_init_pre; b->adv_func->spi_init_post = &mraa_intel_edison_spi_init_post; b->adv_func->gpio_mode_replace = &mraa_intel_edison_gpio_mode_replace;