1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009 |
- /**
- * Copyright (c) 2015 - 2019, 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 form under this license must not be reverse
- * engineered, decompiled, modified and/or disassembled.
- *
- * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 "sdk_common.h"
- #if NRF_MODULE_ENABLED(APP_PWM)
- #include "app_pwm.h"
- #include "nrf_drv_timer.h"
- #include "nrf_drv_ppi.h"
- #include "nrf_drv_gpiote.h"
- #include "nrf_gpiote.h"
- #include "nrf_gpio.h"
- #include "app_util_platform.h"
- #include "nrf_assert.h"
- #define APP_PWM_CHANNEL_INITIALIZED 1
- #define APP_PWM_CHANNEL_UNINITIALIZED 0
- #define APP_PWM_CHANNEL_ENABLED 1
- #define APP_PWM_CHANNEL_DISABLED 0
- #define TIMER_PRESCALER_MAX 9
- #define TIMER_MAX_PULSEWIDTH_US_ON_16M 4095
- #ifndef GPIOTE_SET_CLEAR_TASKS
- #define APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE 2
- #endif
- #define APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL 2
- #define UNALLOCATED 0xFFFFFFFFUL
- #define BUSY_STATE_CHANGING 0xFE
- #define BUSY_STATE_IDLE 0xFF
- #define PWM_MAIN_CC_CHANNEL 2
- #define PWM_SECONDARY_CC_CHANNEL 3
- #ifdef GPIOTE_SET_CLEAR_TASKS
- static bool m_use_ppi_delay_workaround;
- #endif
- /**
- * @brief PWM busy status
- *
- * Stores the number of a channel being currently updated.
- *
- */
- static volatile uint8_t m_pwm_busy[TIMER_COUNT];
- /**
- * @brief New duty cycle value
- *
- * When the channel duty cycle reaches this value, the update process is complete.
- */
- static volatile uint32_t m_pwm_target_value[TIMER_COUNT];
- /**
- * @brief PWM ready counter
- *
- * The value in this counter is decremented in every PWM cycle after initiating the update.
- * If an event handler function was specified by the user, it is being called
- * after two cycle events (at least one full PWM cycle).
- */
- volatile uint8_t m_pwm_ready_counter[TIMER_COUNT][APP_PWM_CHANNELS_PER_INSTANCE];
- /**
- * @brief Pointers to instances
- *
- * This array connects any active timer instance number with the pointer to the PWM instance.
- * It is used by the interrupt runtime.
- */
- static const app_pwm_t * m_instances[TIMER_COUNT];
- // Macros for getting the polarity of given instance/channel.
- #define POLARITY_ACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
- APP_PWM_POLARITY_ACTIVE_LOW)?(0):(1))
- #define POLARITY_INACTIVE(INST,CH) (( ((INST)->p_cb)->channels_cb[(CH)].polarity == \
- APP_PWM_POLARITY_ACTIVE_LOW)?(1):(0))
- //lint -save -e534
- /**
- * @brief Workaround for PAN-73.
- *
- * @param[in] timer Timer.
- * @param[in] enable Enable or disable.
- */
- static void pan73_workaround(NRF_TIMER_Type * p_timer, bool enable)
- {
- #ifndef GPIOTE_SET_CLEAR_TASKS
- if (p_timer == NRF_TIMER0)
- {
- *(uint32_t *)0x40008C0C = (enable ? 1 : 0);
- }
- else if (p_timer == NRF_TIMER1)
- {
- *(uint32_t *)0x40009C0C = (enable ? 1 : 0);
- }
- else if (p_timer == NRF_TIMER2)
- {
- *(uint32_t *)0x4000AC0C = (enable ? 1 : 0);
- }
- #else
- UNUSED_PARAMETER(p_timer);
- UNUSED_PARAMETER(enable);
- #endif
- }
- bool app_pwm_busy_check(app_pwm_t const * const p_instance)
- {
- uint8_t busy_state = (m_pwm_busy[p_instance->p_timer->instance_id]);
- bool busy = true;
- if (busy_state != BUSY_STATE_IDLE)
- {
- if (busy_state != BUSY_STATE_CHANGING)
- {
- if (nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) busy_state)
- == m_pwm_target_value[p_instance->p_timer->instance_id])
- {
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- busy = false;
- }
- }
- }
- else
- {
- busy = false;
- }
- return busy;
- }
- /**
- * @brief Function for enabling the IRQ for a given PWM instance.
- *
- * @param[in] p_instance PWM instance.
- */
- __STATIC_INLINE void pwm_irq_enable(app_pwm_t const * const p_instance)
- {
- nrf_drv_timer_compare_int_enable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
- }
- /**
- * @brief Function for disabling the IRQ for a given PWM instance.
- *
- * @param[in] p_instance PWM instance.
- */
- __STATIC_INLINE void pwm_irq_disable(app_pwm_t const * const p_instance)
- {
- nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
- }
- #ifndef GPIOTE_SET_CLEAR_TASKS
- /**
- * @brief Function for disabling PWM channel PPI.
- *
- * @param[in] p_instance PWM instance.
- */
- __STATIC_INLINE void pwm_channel_ppi_disable(app_pwm_t const * const p_instance, uint8_t channel)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[0]);
- nrf_drv_ppi_channel_disable(p_cb->channels_cb[channel].ppi_channels[1]);
- }
- /**
- * @brief Function for disabling PWM PPI.
- *
- * @param[in] p_instance PWM instance.
- */
- __STATIC_INLINE void pwm_ppi_disable(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- nrf_drv_ppi_channel_disable(p_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_disable(p_cb->ppi_channels[1]);
- }
- #endif
- /**
- * @brief This function is called on interrupt after duty set.
- *
- * @param[in] timer Timer used by PWM.
- * @param[in] timer_instance_id Timer index.
- */
- void pwm_ready_tick(nrf_timer_event_t event_type, void * p_context)
- {
- uint32_t timer_instance_id = (uint32_t)p_context;
- uint8_t disable = 1;
- for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
- {
- if (m_pwm_ready_counter[timer_instance_id][channel])
- {
- --m_pwm_ready_counter[timer_instance_id][channel];
- if (!m_pwm_ready_counter[timer_instance_id][channel])
- {
- app_pwm_cb_t * p_cb = m_instances[timer_instance_id]->p_cb;
- p_cb->p_ready_callback(timer_instance_id);
- }
- else
- {
- disable = 0;
- }
- }
- }
- if (disable)
- {
- pwm_irq_disable(m_instances[timer_instance_id]);
- }
- }
- /**
- * @brief Function for resource de-allocation.
- *
- * @param[in] p_instance PWM instance.
- */
- //lint -e{650}
- static void pwm_dealloc(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- #ifdef GPIOTE_SET_CLEAR_TASKS
- nrf_drv_ppi_channel_free(p_cb->ppi_channel);
- #else
- for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
- {
- if (p_cb->ppi_channels[i] != (nrf_ppi_channel_t)(uint8_t)(UNALLOCATED))
- {
- nrf_drv_ppi_channel_free(p_cb->ppi_channels[i]);
- }
- }
- if (p_cb->ppi_group != (nrf_ppi_channel_group_t)UNALLOCATED)
- {
- nrf_drv_ppi_group_free(p_cb->ppi_group);
- }
- #endif //GPIOTE_SET_CLEAR_TASKS
- for (uint8_t ch = 0; ch < APP_PWM_CHANNELS_PER_INSTANCE; ++ch)
- {
- for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
- {
- if (p_cb->channels_cb[ch].ppi_channels[i] != (nrf_ppi_channel_t)UNALLOCATED)
- {
- nrf_drv_ppi_channel_free(p_cb->channels_cb[ch].ppi_channels[i]);
- p_cb->channels_cb[ch].ppi_channels[i] = (nrf_ppi_channel_t)UNALLOCATED;
- }
- }
- if (p_cb->channels_cb[ch].gpio_pin != UNALLOCATED)
- {
- nrf_drv_gpiote_out_uninit(p_cb->channels_cb[ch].gpio_pin);
- p_cb->channels_cb[ch].gpio_pin = UNALLOCATED;
- }
- p_cb->channels_cb[ch].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
- }
- nrf_drv_timer_uninit(p_instance->p_timer);
- return;
- }
- #ifndef GPIOTE_SET_CLEAR_TASKS
- /**
- * @brief PWM state transition from (0%, 100%) to 0% or 100%.
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel PWM channel number.
- * @param[in] ticks Number of clock ticks.
- */
- static void pwm_transition_n_to_0or100(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
- pwm_ppi_disable(p_instance);
- nrf_drv_ppi_group_clear(p_ppigrp);
- nrf_drv_ppi_channels_include_in_group(
- nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0]) |
- nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
- p_ppigrp);
- if (!ticks)
- {
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
- m_pwm_target_value[p_instance->p_timer->instance_id] =
- nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
- }
- else
- {
- ticks = p_cb->period;
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- nrf_drv_ppi_task_addr_group_disable_get(p_ppigrp));
- // Set secondary CC channel to non-zero value:
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
- m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
- // The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
- }
- nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
- p_ch_cb->pulsewidth = ticks;
- m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
- }
- /**
- * @brief PWM state transition from (0%, 100%) to (0%, 100%).
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel PWM channel number.
- * @param[in] ticks Number of clock ticks.
- */
- static void pwm_transition_n_to_m(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
- pwm_ppi_disable(p_instance);
- nrf_drv_ppi_group_clear(p_ppigrp);
- nrf_drv_ppi_channels_include_in_group(
- nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[0]) |
- nrf_drv_ppi_channel_to_mask(p_cb->ppi_channels[1]),
- p_ppigrp);
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
- nrf_drv_timer_capture_task_address_get(p_instance->p_timer, channel));
- if (ticks + ((nrf_timer_frequency_get(p_instance->p_timer->p_reg) == NRF_TIMER_FREQ_16MHz) ? 1 : 0)
- < p_ch_cb->pulsewidth)
- {
- // For lower value, we need one more transition. Timer task delay is included.
- // If prescaler is disabled, one tick must be added because of 1 PCLK16M clock cycle delay.
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL),
- nrf_drv_gpiote_out_task_addr_get(p_ch_cb->gpio_pin));
- }
- else
- {
- nrf_drv_ppi_channel_remove_from_group(p_cb->ppi_channels[1], p_ppigrp);
- }
- p_ch_cb->pulsewidth = ticks;
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, ticks, false);
- nrf_drv_ppi_group_enable(p_ppigrp);
- m_pwm_target_value[p_instance->p_timer->instance_id] = ticks;
- m_pwm_busy[p_instance->p_timer->instance_id] = channel;
- }
- /**
- * @brief PWM state transition from 0% or 100% to (0%, 100%).
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel PWM channel number.
- * @param[in] ticks Number of clock ticks.
- */
- static void pwm_transition_0or100_to_n(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- nrf_ppi_channel_group_t p_ppigrp = p_cb->ppi_group;
- nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
- pwm_ppi_disable(p_instance);
- pwm_channel_ppi_disable(p_instance, channel);
- nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
- nrf_drv_ppi_group_clear(p_ppigrp);
- nrf_drv_ppi_channels_include_in_group(
- nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[0])|
- nrf_drv_ppi_channel_to_mask(p_ch_cb->ppi_channels[1]),
- p_ppigrp);
- if (!p_ch_cb->pulsewidth)
- {
- // Channel is at 0%.
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 0, false);
- m_pwm_target_value[p_instance->p_timer->instance_id] =
- nrf_drv_timer_capture_get(p_instance->p_timer, (nrf_timer_cc_channel_t) channel);
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
- }
- else
- {
- // Channel is at 100%.
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- nrf_drv_ppi_task_addr_group_enable_get(p_ppigrp));
- // Set secondary CC channel to non-zero value:
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_SECONDARY_CC_CHANNEL, 1, false);
- m_pwm_target_value[p_instance->p_timer->instance_id] = 0;
- // The captured value will be equal to 0, because timer clear on main PWM CC channel compare is enabled.
- nrf_drv_ppi_channel_assign(p_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- nrf_drv_timer_capture_task_address_get(p_instance->p_timer, PWM_SECONDARY_CC_CHANNEL));
- }
- nrf_drv_ppi_channel_enable(p_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_enable(p_cb->ppi_channels[1]);
- p_ch_cb->pulsewidth = ticks;
- m_pwm_busy[p_instance->p_timer->instance_id] = PWM_SECONDARY_CC_CHANNEL;
- }
- /**
- * @brief PWM state transition from 0% or 100% to 0% or 100%.
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel PWM channel number.
- * @param[in] ticks Number of clock ticks.
- */
- static void pwm_transition_0or100_to_0or100(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
- pwm_ppi_disable(p_instance);
- pwm_channel_ppi_disable(p_instance, channel);
- if (!ticks)
- {
- // Set to 0%.
- nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
- }
- else if (ticks >= p_cb->period)
- {
- // Set to 100%.
- ticks = p_cb->period;
- nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_ACTIVE(p_instance, channel));
- }
- nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
- p_ch_cb->pulsewidth = ticks;
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- return;
- }
- static void pwm_transition(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_instance->p_cb->channels_cb[channel];
- // Pulse width change sequence:
- if (!p_ch_cb->pulsewidth || p_ch_cb->pulsewidth >= p_cb->period)
- {
- // Channel is disabled (0%) or at 100%.
- if (!ticks || ticks >= p_cb->period)
- {
- // Set to 0 or 100%.
- pwm_transition_0or100_to_0or100(p_instance, channel, ticks);
- }
- else
- {
- // Other value.
- pwm_transition_0or100_to_n(p_instance, channel, ticks);
- }
- }
- else
- {
- // Channel is at other value.
- if (!ticks || ticks >= p_cb->period)
- {
- // Disable channel (set to 0%) or set to 100%.
- pwm_transition_n_to_0or100(p_instance, channel, ticks);
- }
- else
- {
- // Set to any other value.
- pwm_transition_n_to_m(p_instance, channel, ticks);
- }
- }
- }
- #else //GPIOTE_SET_CLEAR_TASKS
- /**
- * @brief PWM state transition.
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel PWM channel number.
- * @param[in] ticks Number of clock ticks.
- */
- static void pwm_transition(app_pwm_t const * const p_instance,
- uint8_t channel, uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- nrf_timer_cc_channel_t pwm_ch_cc = (nrf_timer_cc_channel_t)(channel);
- nrf_drv_ppi_channel_disable(p_cb->ppi_channel);
- if (!ticks)
- {
- nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[1]);
- nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[0]);
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- }
- else if (ticks >= p_cb->period)
- {
- ticks = p_cb->period;
- nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[1]);
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- }
- else
- {
- // Set to any other value.
- if ((p_ch_cb->pulsewidth != p_cb->period) && (p_ch_cb->pulsewidth != 0) && (ticks < p_ch_cb->pulsewidth))
- {
- nrf_drv_timer_compare(p_instance->p_timer, (nrf_timer_cc_channel_t)PWM_SECONDARY_CC_CHANNEL, p_ch_cb->pulsewidth, false);
- nrf_drv_ppi_channel_assign(p_cb->ppi_channel,
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, (nrf_timer_cc_channel_t)PWM_SECONDARY_CC_CHANNEL),
- p_ch_cb->polarity ? nrf_drv_gpiote_clr_task_addr_get(p_ch_cb->gpio_pin) : nrf_drv_gpiote_set_task_addr_get(p_ch_cb->gpio_pin));
- nrf_drv_ppi_channel_enable(p_cb->ppi_channel);
- m_pwm_busy[p_instance->p_timer->instance_id] = channel;
- m_pwm_target_value[p_instance->p_timer->instance_id] = ticks;
- }
- else
- {
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- }
- nrf_drv_timer_compare(p_instance->p_timer, pwm_ch_cc, ticks, false);
- nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_enable(p_ch_cb->ppi_channels[1]);
- }
- p_ch_cb->pulsewidth = ticks;
- return;
- }
- #endif //GPIOTE_SET_CLEAR_TASKS
- ret_code_t app_pwm_channel_duty_ticks_set(app_pwm_t const * const p_instance,
- uint8_t channel,
- uint16_t ticks)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
- ASSERT(p_ch_cb->initialized == APP_PWM_CHANNEL_INITIALIZED);
- if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
- {
- return NRF_ERROR_INVALID_STATE;
- }
- if (ticks == p_ch_cb->pulsewidth)
- {
- if (p_cb->p_ready_callback)
- {
- p_cb->p_ready_callback(p_instance->p_timer->instance_id);
- }
- return NRF_SUCCESS; // No action required.
- }
- if (app_pwm_busy_check(p_instance))
- {
- return NRF_ERROR_BUSY; // PPI channels for synchronization are still in use.
- }
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_CHANGING;
- // Set new value.
- pwm_transition(p_instance, channel, ticks);
- if (p_instance->p_cb->p_ready_callback)
- {
- //PWM ready interrupt handler will be called after one full period.
- m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 2;
- pwm_irq_enable(p_instance);
- }
- return NRF_SUCCESS;
- }
- uint16_t app_pwm_channel_duty_ticks_get(app_pwm_t const * const p_instance, uint8_t channel)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- return p_ch_cb->pulsewidth;
- }
- uint16_t app_pwm_cycle_ticks_get(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- return (uint16_t)p_cb->period;
- }
- ret_code_t app_pwm_channel_duty_set(app_pwm_t const * const p_instance,
- uint8_t channel, app_pwm_duty_t duty)
- {
- uint32_t ticks = ((uint32_t)app_pwm_cycle_ticks_get(p_instance) * (uint32_t)duty) / 100UL;
- return app_pwm_channel_duty_ticks_set(p_instance, channel, ticks);
- }
- app_pwm_duty_t app_pwm_channel_duty_get(app_pwm_t const * const p_instance, uint8_t channel)
- {
- uint32_t value = ((uint32_t)app_pwm_channel_duty_ticks_get(p_instance, channel) * 100UL) \
- / (uint32_t)app_pwm_cycle_ticks_get(p_instance);
- return (app_pwm_duty_t)value;
- }
- /**
- * @brief Function for initializing the PWM channel.
- *
- * @param[in] p_instance PWM instance.
- * @param[in] channel Channel number.
- * @param[in] pin GPIO pin number.
- *
- * @retval NRF_SUCCESS If initialization was successful.
- * @retval NRF_ERROR_NO_MEM If there were not enough free resources.
- * @retval NRF_ERROR_INVALID_STATE If the timer is already in use or initialization failed.
- */
- static ret_code_t app_pwm_channel_init(app_pwm_t const * const p_instance, uint8_t channel,
- uint32_t pin, app_pwm_polarity_t polarity)
- {
- ASSERT(channel < APP_PWM_CHANNELS_PER_INSTANCE);
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- app_pwm_channel_cb_t * p_channel_cb = &p_cb->channels_cb[channel];
- if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
- {
- return NRF_ERROR_INVALID_STATE;
- }
- p_channel_cb->pulsewidth = 0;
- p_channel_cb->polarity = polarity;
- ret_code_t err_code;
- /* GPIOTE setup: */
- nrf_drv_gpiote_out_config_t out_cfg = GPIOTE_CONFIG_OUT_TASK_TOGGLE( POLARITY_INACTIVE(p_instance, channel) );
- err_code = nrf_drv_gpiote_out_init((nrf_drv_gpiote_pin_t)pin,&out_cfg);
- if (err_code != NRF_SUCCESS)
- {
- return NRF_ERROR_NO_MEM;
- }
- p_cb->channels_cb[channel].gpio_pin = pin;
- // Set output to inactive state.
- if (polarity)
- {
- nrf_gpio_pin_clear(pin);
- }
- else
- {
- nrf_gpio_pin_set(pin);
- }
- /* PPI setup: */
- for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_CHANNEL; ++i)
- {
- if (nrf_drv_ppi_channel_alloc(&p_channel_cb->ppi_channels[i]) != NRF_SUCCESS)
- {
- return NRF_ERROR_NO_MEM; // Resource de-allocation is done by callee.
- }
- }
- nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_disable(p_channel_cb->ppi_channels[1]);
- #ifdef GPIOTE_SET_CLEAR_TASKS
- uint32_t deactivate_task_addr = polarity ? nrf_drv_gpiote_clr_task_addr_get(p_channel_cb->gpio_pin) : nrf_drv_gpiote_set_task_addr_get(p_channel_cb->gpio_pin);
- uint32_t activate_task_addr = polarity ? nrf_drv_gpiote_set_task_addr_get(p_channel_cb->gpio_pin) : nrf_drv_gpiote_clr_task_addr_get(p_channel_cb->gpio_pin);
- nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- deactivate_task_addr);
- nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- activate_task_addr);
- #else //GPIOTE_SET_CLEAR_TASKS
- nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[0],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, channel),
- nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
- nrf_drv_ppi_channel_assign(p_channel_cb->ppi_channels[1],
- nrf_drv_timer_compare_event_address_get(p_instance->p_timer, PWM_MAIN_CC_CHANNEL),
- nrf_drv_gpiote_out_task_addr_get(p_channel_cb->gpio_pin));
- #endif //GPIOTE_SET_CLEAR_TASKS
- p_channel_cb->initialized = APP_PWM_CHANNEL_INITIALIZED;
- m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
- return NRF_SUCCESS;
- }
- /**
- * @brief Function for calculating target timer frequency, which will allow to set given period length.
- *
- * @param[in] period_us Desired period in microseconds.
- *
- * @retval Timer frequency.
- */
- __STATIC_INLINE nrf_timer_frequency_t pwm_calculate_timer_frequency(uint32_t period_us)
- {
- uint32_t f = (uint32_t) NRF_TIMER_FREQ_16MHz;
- uint32_t min = (uint32_t) NRF_TIMER_FREQ_31250Hz;
- while ((period_us > TIMER_MAX_PULSEWIDTH_US_ON_16M) && (f < min))
- {
- period_us >>= 1;
- ++f;
- }
- #ifdef GPIOTE_SET_CLEAR_TASKS
- if ((m_use_ppi_delay_workaround) && (f == (uint32_t) NRF_TIMER_FREQ_16MHz))
- {
- f = (uint32_t) NRF_TIMER_FREQ_8MHz;
- }
- #endif // GPIOTE_SET_CLEAR_TASKS
- return (nrf_timer_frequency_t) f;
- }
- ret_code_t app_pwm_init(app_pwm_t const * const p_instance, app_pwm_config_t const * const p_config,
- app_pwm_callback_t p_ready_callback)
- {
- ASSERT(p_instance);
- if (!p_config)
- {
- return NRF_ERROR_INVALID_DATA;
- }
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
- {
- return NRF_ERROR_INVALID_STATE;
- }
- uint32_t err_code = nrf_drv_ppi_init();
- if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED))
- {
- return NRF_ERROR_NO_MEM;
- }
- if (!nrf_drv_gpiote_is_init())
- {
- err_code = nrf_drv_gpiote_init();
- if (err_code != NRF_SUCCESS)
- {
- return NRF_ERROR_INTERNAL;
- }
- }
- #ifdef GPIOTE_SET_CLEAR_TASKS
- if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30)
- {
- m_use_ppi_delay_workaround = false;
- }
- else
- {
- m_use_ppi_delay_workaround = true;
- }
- #endif
- // Innitialize resource status:
- #ifdef GPIOTE_SET_CLEAR_TASKS
- p_cb->ppi_channel = (nrf_ppi_channel_t)UNALLOCATED;
- #else
- p_cb->ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
- p_cb->ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
- p_cb->ppi_group = (nrf_ppi_channel_group_t)UNALLOCATED;
- #endif //GPIOTE_SET_CLEAR_TASKS
- for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
- {
- p_cb->channels_cb[i].initialized = APP_PWM_CHANNEL_UNINITIALIZED;
- p_cb->channels_cb[i].ppi_channels[0] = (nrf_ppi_channel_t)UNALLOCATED;
- p_cb->channels_cb[i].ppi_channels[1] = (nrf_ppi_channel_t)UNALLOCATED;
- p_cb->channels_cb[i].gpio_pin = UNALLOCATED;
- }
- // Allocate PPI channels and groups:
- #ifdef GPIOTE_SET_CLEAR_TASKS
- if (nrf_drv_ppi_channel_alloc(&p_cb->ppi_channel) != NRF_SUCCESS)
- {
- pwm_dealloc(p_instance);
- return NRF_ERROR_NO_MEM;
- }
- #else //GPIOTE_SET_CLEAR_TASKS
- if (nrf_drv_ppi_group_alloc(&p_cb->ppi_group) != NRF_SUCCESS)
- {
- pwm_dealloc(p_instance);
- return NRF_ERROR_NO_MEM;
- }
- for (uint8_t i = 0; i < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++i)
- {
- if (nrf_drv_ppi_channel_alloc(&p_cb->ppi_channels[i]) != NRF_SUCCESS)
- {
- pwm_dealloc(p_instance);
- return NRF_ERROR_NO_MEM;
- }
- }
- #endif //GPIOTE_SET_CLEAR_TASKS
- // Initialize channels:
- for (uint8_t i = 0; i < APP_PWM_CHANNELS_PER_INSTANCE; ++i)
- {
- if (p_config->pins[i] != APP_PWM_NOPIN)
- {
- err_code = app_pwm_channel_init(p_instance, i, p_config->pins[i], p_config->pin_polarity[i]);
- if (err_code != NRF_SUCCESS)
- {
- pwm_dealloc(p_instance);
- return err_code;
- }
- app_pwm_channel_duty_ticks_set(p_instance, i, 0);
- }
- }
- // Initialize timer:
- nrf_timer_frequency_t timer_freq = pwm_calculate_timer_frequency(p_config->period_us);
- nrf_drv_timer_config_t timer_cfg = {
- .frequency = timer_freq,
- .mode = NRF_TIMER_MODE_TIMER,
- .bit_width = NRF_TIMER_BIT_WIDTH_16,
- .interrupt_priority = APP_IRQ_PRIORITY_LOWEST,
- .p_context = (void *) (uint32_t) p_instance->p_timer->instance_id
- };
- err_code = nrf_drv_timer_init(p_instance->p_timer, &timer_cfg,
- pwm_ready_tick);
- if (err_code != NRF_SUCCESS)
- {
- pwm_dealloc(p_instance);
- return err_code;
- }
- uint32_t ticks = nrf_drv_timer_us_to_ticks(p_instance->p_timer, p_config->period_us);
- p_cb->period = ticks;
- nrf_drv_timer_clear(p_instance->p_timer);
- nrf_drv_timer_extended_compare(p_instance->p_timer, (nrf_timer_cc_channel_t) PWM_MAIN_CC_CHANNEL,
- ticks, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK, true);
- nrf_drv_timer_compare_int_disable(p_instance->p_timer, PWM_MAIN_CC_CHANNEL);
- p_cb->p_ready_callback = p_ready_callback;
- m_instances[p_instance->p_timer->instance_id] = p_instance;
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- p_cb->state = NRFX_DRV_STATE_INITIALIZED;
- return NRF_SUCCESS;
- }
- void app_pwm_enable(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
- for (uint32_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
- {
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- m_pwm_ready_counter[p_instance->p_timer->instance_id][channel] = 0;
- if (p_ch_cb->initialized)
- {
- nrf_drv_gpiote_out_task_force(p_ch_cb->gpio_pin, POLARITY_INACTIVE(p_instance, channel));
- nrf_drv_gpiote_out_task_enable(p_ch_cb->gpio_pin);
- p_ch_cb->pulsewidth = 0;
- }
- }
- m_pwm_busy[p_instance->p_timer->instance_id] = BUSY_STATE_IDLE;
- pan73_workaround(p_instance->p_timer->p_reg, true);
- nrf_drv_timer_clear(p_instance->p_timer);
- nrf_drv_timer_enable(p_instance->p_timer);
- p_cb->state = NRFX_DRV_STATE_POWERED_ON;
- return;
- }
- void app_pwm_disable(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
- nrf_drv_timer_disable(p_instance->p_timer);
- pwm_irq_disable(p_instance);
- #ifdef GPIOTE_SET_CLEAR_TASKS
- nrf_drv_ppi_channel_disable(p_cb->ppi_channel);
- #else
- for (uint8_t ppi_channel = 0; ppi_channel < APP_PWM_REQUIRED_PPI_CHANNELS_PER_INSTANCE; ++ppi_channel)
- {
- nrf_drv_ppi_channel_disable(p_cb->ppi_channels[ppi_channel]);
- }
- #endif //GPIOTE_SET_CLEAR_TASKS
- for (uint8_t channel = 0; channel < APP_PWM_CHANNELS_PER_INSTANCE; ++channel)
- {
- app_pwm_channel_cb_t * p_ch_cb = &p_cb->channels_cb[channel];
- if (p_ch_cb->initialized)
- {
- uint8_t polarity = POLARITY_INACTIVE(p_instance, channel);
- if (polarity)
- {
- nrf_gpio_pin_set(p_ch_cb->gpio_pin);
- }
- else
- {
- nrf_gpio_pin_clear(p_ch_cb->gpio_pin);
- }
- nrf_drv_gpiote_out_task_disable(p_ch_cb->gpio_pin);
- nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[0]);
- nrf_drv_ppi_channel_disable(p_ch_cb->ppi_channels[1]);
- }
- }
- pan73_workaround(p_instance->p_timer->p_reg, false);
- p_cb->state = NRFX_DRV_STATE_INITIALIZED;
- return;
- }
- ret_code_t app_pwm_uninit(app_pwm_t const * const p_instance)
- {
- app_pwm_cb_t * p_cb = p_instance->p_cb;
- if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
- {
- app_pwm_disable(p_instance);
- }
- else if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
- {
- return NRF_ERROR_INVALID_STATE;
- }
- pwm_dealloc(p_instance);
- p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
- return NRF_SUCCESS;
- }
- //lint -restore
- #endif //NRF_MODULE_ENABLED(APP_PWM)
|