fixed drive strength
Fork of mbed-dev by
Diff: targets/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c
- Revision:
- 149:156823d33999
- Parent:
- 144:ef7eb2e8f9f7
- Child:
- 150:02e0a0aed4ec
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c Fri Oct 28 11:17:30 2016 +0100 @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2013 Nordic Semiconductor ASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA + * integrated circuit in a product or a software update for such product, must reproduce + * the above copyright notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary or object form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "mbed_assert.h" +#include "mbed_error.h" +#include "pwmout_api.h" +#include "cmsis.h" +#include "pinmap.h" + +#if DEVICE_PWMOUT + +#include "app_util_platform.h" +#include "nrf_drv_pwm.h" + +#define MAX_PWM_COUNTERTOP (0x7FFF) // 0x7FFF is the max of COUNTERTOP value for the PWM peripherial of the nRF52. +#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP * 8) // PWM hw is driven by 16 MHz clock, hence the tick is 1_us/16, + // and 128 is the max prescaler value. +#define MAX_PWM_PERIOD_MS ((MAX_PWM_PERIOD_US / 1000) + 1) // approximations advance +#define MAX_PWM_PERIOD_S ((MAX_PWM_PERIOD_US / 1000000) + 1) // approximations advance + + +#define PWM_INSTANCE_COUNT (PWM_COUNT) // import from the nrf_drv_config.h file + +///> instances of nRF52 PWM driver +static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] = +{ +#if PWM0_ENABLED + NRF_DRV_PWM_INSTANCE(0), +#endif +#if PWM1_ENABLED + NRF_DRV_PWM_INSTANCE(1), +#endif +#if PWM2_ENABLED + NRF_DRV_PWM_INSTANCE(2) +#endif +}; + +typedef struct +{ + uint32_t period_us; + uint32_t duty_us; + float duty; +} pwm_signal_t; /// PWM signal description type + +typedef struct +{ + nrf_drv_pwm_t * p_pwm_driver; + pwm_signal_t signal; + volatile nrf_pwm_values_common_t seq_values[1]; +} pwm_t; /// internal PWM instance support type + +static pwm_t m_pwm[PWM_INSTANCE_COUNT] = +{ +#if PWM0_ENABLED + {.p_pwm_driver = NULL}, +#endif +#if PWM1_ENABLED + {.p_pwm_driver = NULL}, +#endif +#if PWM2_ENABLED + {.p_pwm_driver = NULL} +#endif +}; /// Array of internal PWM instances. + +typedef struct +{ + uint16_t period_hwu; // unit related to pwm_clk + uint16_t duty_hwu; // unit related to pwm_clk + nrf_pwm_clk_t pwm_clk; +} pulsewidth_set_t; /// helper type for timing calculations + + +static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization); + +void pwmout_init(pwmout_t *obj, PinName pin) +{ + uint32_t i; + + for (i = 0; PWM_INSTANCE_COUNT; i++) + { + if (m_pwm[i].p_pwm_driver == NULL) // a driver instance not assigned to the obj? + { + obj->pin = pin; + + obj->pwm_channel = i; + + m_pwm[i].p_pwm_driver = (nrf_drv_pwm_t *) &m_pwm_driver[i]; + m_pwm[i].signal.period_us = 200000; // 0.02 s + m_pwm[i].signal.duty_us = 100000; + m_pwm[i].signal.duty = 0.5f; + + obj->pwm_struct = &m_pwm[i]; + + internal_pwmout_exe(obj, true, true); + + break; + } + } + + MBED_ASSERT(i != PWM_INSTANCE_COUNT); // assert if free instance was not found. +} + +void pwmout_free(pwmout_t *obj) +{ + nrf_drv_pwm_uninit( (nrf_drv_pwm_t*) obj->pwm_struct ); + + m_pwm[obj->pwm_channel].p_pwm_driver = NULL; +} + +void pwmout_write(pwmout_t *obj, float percent) +{ + + if (percent < 0) + { + percent = 0; + } + else if (percent > 1) + { + percent = 1; + } + + pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal); + + p_pwm_signal->duty = percent; + + int us = (((int)p_pwm_signal->period_us) * percent); + + pwmout_pulsewidth_us(obj, us); +} + +float pwmout_read(pwmout_t *obj) +{ + pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal); + + return (float)p_pwm_signal->duty_us / (float)p_pwm_signal->period_us; +} + +void pwmout_period(pwmout_t *obj, float seconds) +{ + // raught saturation < 0, quasi-max> + if (seconds > MAX_PWM_PERIOD_S) + { + seconds = MAX_PWM_PERIOD_S; + } + else if (seconds < 0) + { + seconds = 0; // f. pwmout_period_us will set period to min. value + } + + int us = seconds * 1000000; + + pwmout_period_us(obj, us); +} + +void pwmout_period_ms(pwmout_t *obj, int ms) +{ + // reught saturation < 0, quasi-max> + if (ms > MAX_PWM_PERIOD_MS) + { + ms = MAX_PWM_PERIOD_MS; + } + else if (ms < 0) + { + ms = 0; // f. pwmout_period_us will set period to min. value + } + + int us = ms * 1000; + + pwmout_period_us(obj, us); +} + + +void pwmout_period_us(pwmout_t *obj, int us) +{ + pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal); + + // saturation <1, real-max> + if (us > MAX_PWM_PERIOD_US) + { + us = MAX_PWM_PERIOD_US; + } + else if (us < 1) + { + us = 1; + } + + p_pwm_signal->duty_us = (int)((float)us * p_pwm_signal->duty); + + p_pwm_signal->period_us = us; + + internal_pwmout_exe(obj, true, false); +} + +void pwmout_pulsewidth(pwmout_t *obj, float seconds) +{ + // raught saturation < 0, quasi-max> + if (seconds > MAX_PWM_PERIOD_S) + { + seconds = MAX_PWM_PERIOD_S; + } + else if (seconds < 0) + { + seconds = 0; + } + + int us = seconds * 1000000; + + pwmout_pulsewidth_us(obj,us); +} + +void pwmout_pulsewidth_ms(pwmout_t *obj, int ms) +{ + // raught saturation < 0, quasi-max> + if (ms > MAX_PWM_PERIOD_MS) + { + ms = MAX_PWM_PERIOD_MS; + } + else if (ms < 0) + { + ms = 0; + } + + int us = ms * 1000; + + pwmout_pulsewidth_us(obj, us); +} + +void pwmout_pulsewidth_us(pwmout_t *obj, int us) +{ + // saturation <0, real-max> + if (us > MAX_PWM_PERIOD_US) + { + us = MAX_PWM_PERIOD_US; + } + else if (us < 0) + { + us = 0; + } + + pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal); + + p_pwm_signal->duty_us = us; + p_pwm_signal->duty = us / p_pwm_signal->period_us; + + internal_pwmout_exe(obj, false, false); +} + + + + + + +static ret_code_t pulsewidth_us_set_get(int period_hwu, int duty_hwu, pulsewidth_set_t * p_settings) +{ + uint16_t div; + nrf_pwm_clk_t pwm_clk = NRF_PWM_CLK_16MHz; + + for(div = 1; div <= 128 ; div <<= 1) // 128 is the maximum of clock prescaler for PWM peripherial + { + if (MAX_PWM_COUNTERTOP >= period_hwu) + { + p_settings->period_hwu = period_hwu; // unit [us/16 * div] + p_settings->duty_hwu = duty_hwu; // unit [us/16 * div] + p_settings->pwm_clk = pwm_clk; + + return NRF_SUCCESS; + } + + period_hwu >>= 1; + duty_hwu >>= 1; + pwm_clk++; + } + + return NRF_ERROR_INVALID_PARAM; +} + + +static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization) +{ + pulsewidth_set_t pulsewidth_set; + pwm_signal_t * p_pwm_signal; + nrf_drv_pwm_t * p_pwm_driver; + ret_code_t ret_code; + + p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal); + + if (NRF_SUCCESS == pulsewidth_us_set_get(p_pwm_signal->period_us * 16, // base clk for PWM is 16 MHz + p_pwm_signal->duty_us * 16, // base clk for PWM is 16 MHz + &pulsewidth_set)) + { + p_pwm_driver = (((pwm_t*)obj->pwm_struct)->p_pwm_driver); + + const nrf_pwm_sequence_t seq = + { + .values.p_common = (nrf_pwm_values_common_t*) (((pwm_t*)obj->pwm_struct)->seq_values), + .length = 1, + .repeats = 0, + .end_delay = 0 + }; + + (((pwm_t*)obj->pwm_struct)->seq_values)[0] = pulsewidth_set.duty_hwu | 0x8000; + + if (new_period) + { + nrf_drv_pwm_config_t config0 = + { + .output_pins = + { + obj->pin | NRF_DRV_PWM_PIN_INVERTED, // channel 0 + NRF_DRV_PWM_PIN_NOT_USED, // channel 1 + NRF_DRV_PWM_PIN_NOT_USED, // channel 2 + NRF_DRV_PWM_PIN_NOT_USED, // channel 3 + }, + .irq_priority = APP_IRQ_PRIORITY_LOW, + .base_clock = pulsewidth_set.pwm_clk, + .count_mode = NRF_PWM_MODE_UP, + .top_value = pulsewidth_set.period_hwu, + .load_mode = NRF_PWM_LOAD_COMMON, + .step_mode = NRF_PWM_STEP_AUTO + }; + + if (!initialization) + { + nrf_drv_pwm_uninit(p_pwm_driver); + } + + ret_code = nrf_drv_pwm_init( p_pwm_driver, &config0, NULL); + + MBED_ASSERT(ret_code == NRF_SUCCESS); // assert if free instance was not found. + } + + nrf_drv_pwm_simple_playback(p_pwm_driver, &seq, 0, NRF_DRV_PWM_FLAG_LOOP); + } + else + { + MBED_ASSERT(0); // force assertion + } + +} + +#endif // DEVICE_PWMOUT