mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/pwmout_api.c
- Committer:
- AnnaBridge
- Date:
- 2019-02-20
- Revision:
- 189:f392fc9709a3
- Parent:
- 186:707f6e361f3e
File content as of revision 189:f392fc9709a3:
/* * 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) /* Sequence bit that denotes the polarity of the pwm waveform. */ #define SEQ_POLARITY_BIT (0x8000) /* 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, }; /* 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); /* Uninitialize PWM instace */ nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]); /* (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; /* Set active low logic. */ obj->pulse |= SEQ_POLARITY_BIT; /* 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; /* Clear sequence, but keep the polarity bit */ obj->pulse &= SEQ_POLARITY_BIT; /* 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 & ~SEQ_POLARITY_BIT)) / obj->period; /* Store new values in object. */ obj->pulse &= SEQ_POLARITY_BIT; 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 & ~SEQ_POLARITY_BIT)) { obj->pulse &= SEQ_POLARITY_BIT; 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 &= SEQ_POLARITY_BIT; 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 &= SEQ_POLARITY_BIT; obj->pulse |= pulse; obj->percent = (float) pulse / (float) obj->period; /* Restart instance with new values. */ nordic_pwm_restart(obj); } #endif // DEVICE_PWMOUT