mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
Diff: targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/pwmout_api.c
- Revision:
- 186:707f6e361f3e
- Child:
- 189:f392fc9709a3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/pwmout_api.c Fri Jun 22 16:45:37 2018 +0100 @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2018 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. + * + */ + +#if DEVICE_PWMOUT + +#include "hal/pwmout_api.h" + +#include "pinmap_ex.h" +#include "nrf_drv_pwm.h" + +#if 0 +#define DEBUG_PRINTF(...) do { printf(__VA_ARGS__); } while(0) +#else +#define DEBUG_PRINTF(...) {} +#endif + +/* 0x7FFF is the max of COUNTERTOP pulse for the PWM peripherial of the nRF52. */ +#define MAX_PWM_COUNTERTOP (0x7FFF) + +/* The PWM is driven by a 1 MHz clock to fit the 1 us resolution expected by the API. */ +#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP) +#define MAX_PWM_PERIOD_MS (MAX_PWM_PERIOD_US / 1000) +#define MAX_PWM_PERIOD_S ((float) MAX_PWM_PERIOD_US / 1000000.0f) + +/* Allocate PWM instances. */ +static nrf_drv_pwm_t nordic_nrf5_pwm_instance[] = { +#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 +#if PWM3_ENABLED + NRF_DRV_PWM_INSTANCE(3), +#endif +}; + +/* Helper function for (re)initializing the PWM instance. + */ +static void nordic_pwm_init(pwmout_t *obj) +{ + MBED_ASSERT(obj); + + /* Default configuration: + * 1 pin per instance, otherwise they would share base count. + * 1 MHz clock source to match the 1 us resolution. + */ + nrf_drv_pwm_config_t config = { + .output_pins = { + obj->pin, + NRF_DRV_PWM_PIN_NOT_USED, + NRF_DRV_PWM_PIN_NOT_USED, + NRF_DRV_PWM_PIN_NOT_USED, + }, + .irq_priority = PWM_DEFAULT_CONFIG_IRQ_PRIORITY, + .base_clock = NRF_PWM_CLK_1MHz, + .count_mode = NRF_PWM_MODE_UP, + .top_value = obj->period, + .load_mode = NRF_PWM_LOAD_COMMON, + .step_mode = NRF_PWM_STEP_AUTO, + }; + + /* Make sure PWM instance is not running before making changes. */ + nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]); + + /* Initialize instance with new configuration. */ + ret_code_t result = nrf_drv_pwm_init(&nordic_nrf5_pwm_instance[obj->instance], + &config, + NULL); + + MBED_ASSERT(result == NRF_SUCCESS); +} + +/* Helper function for reinitializing the PWM instance and setting the duty-cycle. */ +static void nordic_pwm_restart(pwmout_t *obj) +{ + MBED_ASSERT(obj); + + /* (Re)initialize PWM instance. */ + nordic_pwm_init(obj); + + /* Set duty-cycle from object. */ + ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance], + &obj->sequence, + 1, + NRF_DRV_PWM_FLAG_LOOP); + + MBED_ASSERT(result == NRF_SUCCESS); +} + +/** Initialize the pwm out peripheral and configure the pin + * + * Parameter obj The pwmout object to initialize + * Parameter pin The pwmout pin to initialize + */ +void pwmout_init(pwmout_t *obj, PinName pin) +{ + DEBUG_PRINTF("pwmout_init: %d\r\n", pin); + + MBED_ASSERT(obj); + + /* Get hardware instance from pinmap. */ + int instance = pin_instance_pwm(pin); + + MBED_ASSERT(instance < (int)(sizeof(nordic_nrf5_pwm_instance) / sizeof(nrf_drv_pwm_t))); + + /* Populate PWM object with default values. */ + obj->instance = instance; + obj->pin = pin; + obj->pulse = 0; + obj->period = MAX_PWM_COUNTERTOP; + obj->percent = 0; + obj->sequence.values.p_common = &obj->pulse; + obj->sequence.length = NRF_PWM_VALUES_LENGTH(obj->pulse); + obj->sequence.repeats = 0; + obj->sequence.end_delay = 0; + + /* Initialize PWM instance. */ + nordic_pwm_init(obj); +} + +/** Deinitialize the pwmout object + * + * Parameter obj The pwmout object + */ +void pwmout_free(pwmout_t *obj) +{ + DEBUG_PRINTF("pwmout_free\r\n"); + + MBED_ASSERT(obj); + + /* Uninitialize PWM instance. */ + nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]); +} + +/** Set the output duty-cycle in range <0.0f, 1.0f> + * + * pulse 0.0f represents 0 percentage, 1.0f represents 100 percent. + * Parameter obj The pwmout object + * Parameter percent The floating-point percentage number + */ +void pwmout_write(pwmout_t *obj, float percent) +{ + DEBUG_PRINTF("pwmout_write: %f\r\n", percent); + + /* Find counts based on period. */ + uint16_t pulse = obj->period * percent; + + /* Ensure we don't overcount. */ + obj->pulse = (pulse > MAX_PWM_COUNTERTOP) ? MAX_PWM_COUNTERTOP : pulse; + + /* Store actual percentage passed as parameter to avoid floating point rounding errors. */ + obj->percent = percent; + + /* Set new duty-cycle. */ + ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance], + &obj->sequence, + 1, + NRF_DRV_PWM_FLAG_LOOP); + + MBED_ASSERT(result == NRF_SUCCESS); +} + +/** Read the current float-point output duty-cycle + * + * Parameter obj The pwmout object + * Return A floating-point output duty-cycle + */ +float pwmout_read(pwmout_t *obj) +{ + DEBUG_PRINTF("pwmout_read: %f\r\n", obj->percent); + + /* Return percentage stored in object instead of calculating the value. + * This prevents floating point rounding errors. + */ + return obj->percent; +} + +/** Set the PWM period specified in seconds, keeping the duty cycle the same + * + * Periods smaller than microseconds (the lowest resolution) are set to zero. + * Parameter obj The pwmout object + * Parameter seconds The floating-point seconds period + */ +void pwmout_period(pwmout_t *obj, float period) +{ + DEBUG_PRINTF("pwmout_period: %f\r\n", period); + + /* Cap period if too large. */ + if (period > MAX_PWM_PERIOD_S) { + period = MAX_PWM_PERIOD_S; + } + + /* Set new period. */ + pwmout_period_us(obj, period * 1000000); +} + +/** Set the PWM period specified in miliseconds, keeping the duty cycle the same + * + * Parameter obj The pwmout object + * Parameter ms The milisecond period + */ +void pwmout_period_ms(pwmout_t *obj, int period) +{ + DEBUG_PRINTF("pwmout_period_ms: %d\r\n", period); + + /* Cap period if too large. */ + if (period > MAX_PWM_PERIOD_MS) { + period = MAX_PWM_PERIOD_MS; + } + + /* Set new period. */ + pwmout_period_us(obj, period * 1000); +} + +/** Set the PWM period specified in microseconds, keeping the duty cycle the same + * + * Parameter obj The pwmout object + * Parameter us The microsecond period + */ +void pwmout_period_us(pwmout_t *obj, int period) +{ + DEBUG_PRINTF("pwmout_period_us: %d\r\n", period); + + /* Cap period if too large. */ + if (period > MAX_PWM_PERIOD_US) { + period = MAX_PWM_PERIOD_US; + } + + /* Scale new count based on stored duty-cycle and new period. */ + uint32_t pulse = (period * obj->pulse) / obj->period; + + /* Store new values in object. */ + obj->pulse = pulse; + obj->period = period; + obj->percent = (float) pulse / (float) period; + + /* Restart instance with new values. */ + nordic_pwm_restart(obj); +} + +/** Set the PWM pulsewidth specified in seconds, keeping the period the same. + * + * Parameter obj The pwmout object + * Parameter seconds The floating-point pulsewidth in seconds + */ +void pwmout_pulsewidth(pwmout_t *obj, float pulse) +{ + DEBUG_PRINTF("pwmout_pulsewidt: %f\r\n", pulse); + + /* Cap pulsewidth to period before setting it. */ + if ((pulse * 1000000) > (float) obj->pulse) { + obj->pulse = obj->period; + pwmout_pulsewidth_us(obj, obj->pulse); + } else { + pwmout_pulsewidth_us(obj, pulse * 1000000); + } +} + +/** Set the PWM pulsewidth specified in miliseconds, keeping the period the same. + * + * Parameter obj The pwmout object + * Parameter ms The floating-point pulsewidth in miliseconds + */ +void pwmout_pulsewidth_ms(pwmout_t *obj, int pulse) +{ + DEBUG_PRINTF("pwmout_pulsewidth_ms: %d\r\n", ms); + + /* Cap pulsewidth to period before setting it. */ + if ((pulse * 1000) > (int) obj->period) { + obj->pulse = obj->period; + pwmout_pulsewidth_us(obj, obj->pulse); + } else { + pwmout_pulsewidth_us(obj, pulse * 1000); + } +} + +/** Set the PWM pulsewidth specified in microseconds, keeping the period the same. + * + * Parameter obj The pwmout object + * Parameter us The floating-point pulsewidth in microseconds + */ +void pwmout_pulsewidth_us(pwmout_t *obj, int pulse) +{ + DEBUG_PRINTF("pwmout_pulsewidth_us: %d\r\n", pulse); + + /* Cap pulsewidth to period. */ + if (pulse > obj->period) { + pulse = obj->period; + } + + /* Store new values in object. */ + obj->pulse = pulse; + obj->percent = (float) pulse / (float) obj->period; + + /* Restart instance with new values. */ + nordic_pwm_restart(obj); +} + +#endif // DEVICE_PWMOUT