nrf52 PwmOut in progress.

pull/2234/head
Andrzej Puzdrowski 2016-07-01 13:44:08 +02:00
parent 0d9dc1e079
commit 07ce12fcc5
6 changed files with 1647 additions and 6 deletions

View File

@ -1773,6 +1773,6 @@
"NRF52_PAN_62",
"NRF52_PAN_63"
],
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"]
"device_has": ["ANALOGIN", "ERROR_PATTERN", "I2C", "I2C_ASYNCH", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE"]
}
}

View File

@ -44,49 +44,249 @@
#if DEVICE_PWMOUT
// TODO - provide an implementation.
#include "app_util_platform.h"
#include "nrf_drv_pwm.h"
//#include "nrf_pwm.h"
#define PWM_INSTANCE_COUNT 3
static nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
{
NRF_DRV_PWM_INSTANCE(0),
NRF_DRV_PWM_INSTANCE(1),
NRF_DRV_PWM_INSTANCE(2)
};
typedef struct
{
uint32_t period_us;
uint32_t duty_us;
} pwm_signal_t;
typedef struct
{
nrf_drv_pwm_t * p_pwm_driver;
pwm_signal_t signal;
} pwm_t;
static pwm_t m_pwm[PWM_INSTANCE_COUNT] =
{
{.p_pwm_driver = NULL},
{.p_pwm_driver = NULL},
{.p_pwm_driver = NULL}
};
typedef struct
{
uint16_t period;
uint16_t duty;
nrf_pwm_clk_t pwm_clk;
} pulsewidth_set_t;
static void internal_pwmout_exe(pwmout_t *obj);
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;
/// @todo obj->pwm_name =
obj->pwm_channel = i;
m_pwm[i].p_pwm_driver = &m_pwm_driver[i];
m_pwm[i].signal.period_us = 200000; // 0.02 s
m_pwm[i].signal.duty_us = 100000;
obj->pwm_struct = &m_pwm[i];
internal_pwmout_exe(obj);
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;
/// @todo release gpio
}
void pwmout_write(pwmout_t *obj, float percent)
{
if (percent < 0)
{
percent = 0;
}
else if (percent > 100)
{
percent = 100;
}
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
int us = (((int)p_pwm_signal->period_us) * percent) / 100;
pwmout_pulsewidth_us(obj, us);
}
float pwmout_read(pwmout_t *obj)
{
return 0.0f;
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 * 100;
}
void pwmout_period(pwmout_t *obj, float seconds)
{
// @todo saturation
int us = seconds * 1000000;
pwmout_period_us(obj, us);
}
void pwmout_period_ms(pwmout_t *obj, int ms)
{
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);
p_pwm_signal->period_us = us;
internal_pwmout_exe(obj);
}
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
{
// @todo saturation
int us = seconds * 1000000;
pwmout_pulsewidth_us(obj,us);
}
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
{
// @todo saturation
int us = ms * 1000;
pwmout_pulsewidth_us(obj,us);
}
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
{
// @todo saturation
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
p_pwm_signal->duty_us = us;
internal_pwmout_exe(obj);
}
static ret_code_t pulsewidth_us_set_get(int period_us, int duty_us, pulsewidth_set_t * const p_settings)
{
uint16_t div;
nrf_pwm_clk_t pwm_clk = NRF_PWM_CLK_16MHz;
for(div = 1; div <= 128 ; div <<= 1)
{
if (0xFFFF >= period_us)
{
p_settings->period = period_us; // unit [us * div]
p_settings->duty = duty_us; // unit [us * div]
p_settings->pwm_clk = pwm_clk;
return NRF_SUCCESS;
}
period_us >>= 1;
duty_us >>= 1;
pwm_clk++;
}
return NRF_ERROR_INVALID_PARAM;
}
static nrf_pwm_values_common_t seq_values[1];
static nrf_pwm_sequence_t const seq =
{
.values.p_common = seq_values,
.length = NRF_PWM_VALUES_LENGTH(seq_values),
.repeats = 0,
.end_delay = 0
};
static void internal_pwmout_exe(pwmout_t *obj)
{
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,
p_pwm_signal->duty_us,
&pulsewidth_set))
{
//@todo apply pulsewidth_set
p_pwm_driver = (((pwm_t*)obj->pwm_struct)->p_pwm_driver);
nrf_drv_pwm_config_t config0 =
{
.output_pins =
{
obj->pin, // 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,
.load_mode = NRF_PWM_LOAD_COMMON,
.step_mode = NRF_PWM_STEP_AUTO
};
//printf("clock = %d, top = %d\r\n", pulsewidth_set.pwm_clk, pulsewidth_set.period);
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.
seq_values[0] = pulsewidth_set.duty;
nrf_drv_pwm_simple_playback(p_pwm_driver, &seq, 3, NRF_DRV_PWM_FLAG_LOOP);
}
else
{
MBED_ASSERT(0); // force assertion
}
}
#endif // DEVICE_PWMOUT

View File

@ -0,0 +1,350 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include <string.h>
#include "nrf_drv_pwm.h"
#include "nrf_drv_common.h"
#include "nrf_gpio.h"
#include "app_util_platform.h"
#if (PWM_COUNT == 0)
#error "No PWM instances enabled in the driver configuration file."
#endif
// Control block - driver instance local data.
typedef struct
{
nrf_drv_pwm_handler_t handler;
nrf_drv_state_t volatile state;
} pwm_control_block_t;
static pwm_control_block_t m_cb[PWM_COUNT];
static nrf_drv_pwm_config_t const m_default_config[PWM_COUNT] = {
#if PWM0_ENABLED
NRF_DRV_PWM_DEFAULT_CONFIG(0),
#endif
#if PWM1_ENABLED
NRF_DRV_PWM_DEFAULT_CONFIG(1),
#endif
#if PWM2_ENABLED
NRF_DRV_PWM_DEFAULT_CONFIG(2),
#endif
};
static void configure_pins(nrf_drv_pwm_t const * const p_instance,
nrf_drv_pwm_config_t const * p_config)
{
uint32_t out_pins[NRF_PWM_CHANNEL_COUNT];
uint8_t i;
for (i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
{
uint8_t output_pin = p_config->output_pins[i];
if (output_pin != NRF_DRV_PWM_PIN_NOT_USED)
{
bool inverted = output_pin & NRF_DRV_PWM_PIN_INVERTED;
out_pins[i] = output_pin & ~NRF_DRV_PWM_PIN_INVERTED;
if (inverted)
{
nrf_gpio_pin_set(out_pins[i]);
}
else
{
nrf_gpio_pin_clear(out_pins[i]);
}
nrf_gpio_cfg_output(out_pins[i]);
}
else
{
out_pins[i] = NRF_PWM_PIN_NOT_CONNECTED;
}
}
nrf_pwm_pins_set(p_instance->p_registers, out_pins);
}
ret_code_t nrf_drv_pwm_init(nrf_drv_pwm_t const * const p_instance,
nrf_drv_pwm_config_t const * p_config,
nrf_drv_pwm_handler_t handler)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
{
return NRF_ERROR_INVALID_STATE;
}
if (p_config == NULL)
{
p_config = &m_default_config[p_instance->drv_inst_idx];
}
p_cb->handler = handler;
configure_pins(p_instance, p_config);
nrf_pwm_enable(p_instance->p_registers);
nrf_pwm_configure(p_instance->p_registers,
p_config->base_clock, p_config->count_mode, p_config->top_value);
nrf_pwm_decoder_set(p_instance->p_registers,
p_config->load_mode, p_config->step_mode);
nrf_pwm_shorts_set(p_instance->p_registers, 0);
nrf_pwm_int_set(p_instance->p_registers, 0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
if (p_cb->handler)
{
nrf_drv_common_irq_enable(nrf_drv_get_IRQn(p_instance->p_registers),
p_config->irq_priority);
}
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return NRF_SUCCESS;
}
void nrf_drv_pwm_uninit(nrf_drv_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
nrf_drv_common_irq_disable(nrf_drv_get_IRQn(p_instance->p_registers));
nrf_pwm_disable(p_instance->p_registers);
p_cb->state = NRF_DRV_STATE_UNINITIALIZED;
}
static void start_playback(nrf_drv_pwm_t const * const p_instance,
pwm_control_block_t * p_cb,
uint8_t flags,
nrf_pwm_task_t starting_task)
{
p_cb->state = NRF_DRV_STATE_POWERED_ON;
if (p_cb->handler)
{
// The notification about finished playback is by default enabled, but
// this can be suppressed. The notification that the peripheral has been
// stopped is always enable.
uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
NRF_PWM_INT_STOPPED_MASK;
if (flags & NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0)
{
int_mask |= NRF_PWM_INT_SEQEND0_MASK;
}
if (flags & NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1)
{
int_mask |= NRF_PWM_INT_SEQEND1_MASK;
}
if (flags & NRF_DRV_PWM_FLAG_NO_EVT_FINISHED)
{
int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
}
nrf_pwm_int_set(p_instance->p_registers, int_mask);
}
nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);
nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
}
void nrf_drv_pwm_simple_playback(nrf_drv_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(playback_count > 0);
ASSERT(nrf_drv_is_in_RAM(p_sequence->values.p_raw));
// To take advantage of the looping mechanism, we need to use both sequences
// (single sequence can be played back only once).
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
bool odd = (playback_count & 1);
nrf_pwm_loop_set(p_instance->p_registers, playback_count/2 + (odd ? 1 : 0));
uint32_t shorts_mask;
if (flags & NRF_DRV_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRF_DRV_PWM_FLAG_LOOP)
{
shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
: NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
start_playback(p_instance, p_cb, flags, odd ? NRF_PWM_TASK_SEQSTART1
: NRF_PWM_TASK_SEQSTART0);
}
void nrf_drv_pwm_complex_playback(nrf_drv_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence_0,
nrf_pwm_sequence_t const * p_sequence_1,
uint16_t playback_count,
uint32_t flags)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
ASSERT(playback_count > 0);
ASSERT(nrf_drv_is_in_RAM(p_sequence_0->values.p_raw));
ASSERT(nrf_drv_is_in_RAM(p_sequence_1->values.p_raw));
nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence_0);
nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence_1);
nrf_pwm_loop_set(p_instance->p_registers, playback_count);
uint32_t shorts_mask;
if (flags & NRF_DRV_PWM_FLAG_STOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
}
else if (flags & NRF_DRV_PWM_FLAG_LOOP)
{
shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
}
else
{
shorts_mask = 0;
}
nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);
start_playback(p_instance, p_cb, flags, NRF_PWM_TASK_SEQSTART0);
}
bool nrf_drv_pwm_stop(nrf_drv_pwm_t const * const p_instance,
bool wait_until_stopped)
{
ASSERT(m_cb[p_instance->drv_inst_idx].state != NRF_DRV_STATE_UNINITIALIZED);
if (nrf_drv_pwm_is_stopped(p_instance))
{
return true;
}
nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_STOP);
do {
if (nrf_drv_pwm_is_stopped(p_instance))
{
return true;
}
} while (wait_until_stopped);
return false;
}
bool nrf_drv_pwm_is_stopped(nrf_drv_pwm_t const * const p_instance)
{
pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED);
// If the event handler is used (interrupts are enabled), the state will
// be changed in interrupt handler when the STOPPED event occurs.
if (p_cb->state != NRF_DRV_STATE_POWERED_ON)
{
return true;
}
// If interrupts are disabled, we must check the STOPPED event here.
if (nrf_pwm_event_check(p_instance->p_registers, NRF_PWM_EVENT_STOPPED))
{
p_cb->state = NRF_DRV_STATE_INITIALIZED;
return true;
}
return false;
}
static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
{
ASSERT(p_cb->handler);
// The SEQEND0 and SEQEND1 events are only handled when the user asked for
// it (by setting proper flags when starting the playback).
if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_SEQEND0_MASK) &&
nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
p_cb->handler(NRF_DRV_PWM_EVT_END_SEQ0);
}
if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_SEQEND1_MASK) &&
nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
p_cb->handler(NRF_DRV_PWM_EVT_END_SEQ1);
}
// The LOOPSDONE event is handled by default, but this can be disabled.
if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_LOOPSDONE_MASK) &&
nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
p_cb->handler(NRF_DRV_PWM_EVT_FINISHED);
}
if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
{
nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);
p_cb->state = NRF_DRV_STATE_INITIALIZED;
p_cb->handler(NRF_DRV_PWM_EVT_STOPPED);
}
}
#if PWM0_ENABLED
void PWM0_IRQHandler(void)
{
irq_handler(NRF_PWM0, &m_cb[PWM0_INSTANCE_INDEX]);
}
#endif
#if PWM1_ENABLED
void PWM1_IRQHandler(void)
{
irq_handler(NRF_PWM1, &m_cb[PWM1_INSTANCE_INDEX]);
}
#endif
#if PWM2_ENABLED
void PWM2_IRQHandler(void)
{
irq_handler(NRF_PWM2, &m_cb[PWM2_INSTANCE_INDEX]);
}
#endif

View File

@ -0,0 +1,426 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**@file
* @addtogroup nrf_pwm PWM HAL and driver
* @ingroup nrf_drivers
* @brief @tagAPI52 Pulse Width Modulation (PWM) module APIs.
*
* @defgroup nrf_drv_pwm PWM driver
* @{
* @ingroup nrf_pwm
* @brief @tagAPI52 Pulse Width Modulation (PWM) module driver.
*/
#ifndef NRF_DRV_PWM_H__
#define NRF_DRV_PWM_H__
#include "nordic_common.h"
#include "nrf_drv_config.h"
#include "nrf_pwm.h"
#include "sdk_errors.h"
/**
* @brief PWM driver instance data structure.
*/
typedef struct
{
NRF_PWM_Type * p_registers; ///< Pointer to the structure with PWM peripheral instance registers.
uint8_t drv_inst_idx; ///< Driver instance index.
} nrf_drv_pwm_t;
/**
* @brief Macro for creating a PWM driver instance.
*/
#define NRF_DRV_PWM_INSTANCE(id) \
{ \
.p_registers = CONCAT_2(NRF_PWM, id), \
.drv_inst_idx = CONCAT_3(PWM, id, _INSTANCE_INDEX), \
}
/**
* @brief This value can be provided instead of a pin number for any channel
* to specify that its output is not used and therefore does not need
* to be connected to a pin.
*/
#define NRF_DRV_PWM_PIN_NOT_USED 0xFF
/**
* @brief This value can be added to a pin number to inverse its polarity
* (set idle state = 1).
*/
#define NRF_DRV_PWM_PIN_INVERTED 0x80
/**
* @brief PWM driver configuration structure.
*/
typedef struct
{
uint8_t output_pins[NRF_PWM_CHANNEL_COUNT]; ///< Pin numbers for individual output channels (optional).
/**< Use @ref NRF_DRV_PWM_PIN_NOT_USED
* if a given output channel is not needed. */
uint8_t irq_priority; ///< Interrupt priority.
nrf_pwm_clk_t base_clock; ///< Base clock frequency.
nrf_pwm_mode_t count_mode; ///< Operating mode of the pulse generator counter.
uint16_t top_value; ///< Value up to which the pulse generator counter counts.
nrf_pwm_dec_load_t load_mode; ///< Mode of loading sequence data from RAM.
nrf_pwm_dec_step_t step_mode; ///< Mode of advancing the active sequence.
} nrf_drv_pwm_config_t;
/**
* @brief PWM driver default configuration.
*/
#define NRF_DRV_PWM_DEFAULT_CONFIG(id) \
{ \
.output_pins = { CONCAT_3(PWM, id, _CONFIG_OUT0_PIN), \
CONCAT_3(PWM, id, _CONFIG_OUT1_PIN), \
CONCAT_3(PWM, id, _CONFIG_OUT2_PIN), \
CONCAT_3(PWM, id, _CONFIG_OUT3_PIN) }, \
.irq_priority = CONCAT_3(PWM, id, _CONFIG_IRQ_PRIORITY), \
.base_clock = CONCAT_3(PWM, id, _CONFIG_BASE_CLOCK), \
.count_mode = CONCAT_3(PWM, id, _CONFIG_COUNT_MODE), \
.top_value = CONCAT_3(PWM, id, _CONFIG_TOP_VALUE), \
.load_mode = CONCAT_3(PWM, id, _CONFIG_LOAD_MODE), \
.step_mode = CONCAT_3(PWM, id, _CONFIG_STEP_MODE), \
}
/**
* @brief PWM flags providing additional playback options.
*/
typedef enum
{
NRF_DRV_PWM_FLAG_STOP = 0x01, /**< When the requested playback is finished,
the peripheral should be stopped.
@note The STOP task is triggered when
the last value of the final sequence is
loaded from RAM, and the peripheral stops
at the end of the current PWM period.
For sequences with configured repeating
of duty cycle values, this might result in
less than the requested number of repeats
of the last value. */
NRF_DRV_PWM_FLAG_LOOP = 0x02, /**< When the requested playback is finished,
it should be started from the beginning.
This flag is ignored if used together
with @ref NRF_DRV_PWM_FLAG_STOP. */
NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0 = 0x04, /**< The event handler should be
called when the last value
from sequence 0 is loaded. */
NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1 = 0x08, /**< The event handler should be
called when the last value
from sequence 1 is loaded. */
NRF_DRV_PWM_FLAG_NO_EVT_FINISHED = 0x10, /**< The playback finished event
(enabled by default) should be
suppressed. */
} nrf_drv_pwm_flag_t;
/**
* @brief PWM driver event type.
*/
typedef enum
{
NRF_DRV_PWM_EVT_FINISHED, ///< Sequence playback finished.
NRF_DRV_PWM_EVT_END_SEQ0, /**< End of sequence 0 reached. Its data can be
safely modified now. */
NRF_DRV_PWM_EVT_END_SEQ1, /**< End of sequence 1 reached. Its data can be
safely modified now. */
NRF_DRV_PWM_EVT_STOPPED, ///< The PWM peripheral has been stopped.
} nrf_drv_pwm_evt_type_t;
/**
* @brief PWM driver event handler type.
*/
typedef void (* nrf_drv_pwm_handler_t)(nrf_drv_pwm_evt_type_t event_type);
/**
* @brief Function for initializing the PWM driver.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_config Pointer to the structure with initial configuration.
* If NULL, the default configuration is used.
* @param[in] handler Event handler provided by the user. If NULL is passed
* instead, event notifications are not done and PWM
* interrupts are disabled.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver was already initialized.
*/
ret_code_t nrf_drv_pwm_init(nrf_drv_pwm_t const * const p_instance,
nrf_drv_pwm_config_t const * p_config,
nrf_drv_pwm_handler_t handler);
/**
* @brief Function for uninitializing the PWM driver.
*
* If any sequence playback is in progress, it is stopped immediately.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
void nrf_drv_pwm_uninit(nrf_drv_pwm_t const * const p_instance);
/**
* @brief Function for starting a single sequence playback.
*
* To take advantage of the looping mechanism in the PWM peripheral, both
* sequences must be used (single sequence can be played back only once by
* the peripheral). Therefore, the provided sequence is internally set and
* played back as both sequence 0 and sequence 1. Consequently, if end of
* sequence notifications are required, events for both sequences should be
* used (that means that both the @ref NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0 flag
* and the @ref NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1 flag should be specified and
* the @ref NRF_DRV_PWM_EVT_END_SEQ0 event and the @ref NRF_DRV_PWM_EVT_END_SEQ1
* event should be handled in the same way).
*
* @note The array containing the duty cycle values for the specified sequence
* must be in RAM and cannot be allocated on stack.
* For detailed information, see @ref nrf_pwm_sequence_t.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_sequence Sequence to be played back.
* @param[in] playback_count Number of playbacks to be performed (must not be 0).
* @param[in] flags Additional options. Pass any combination of
* @ref nrf_drv_pwm_flag_t "playback flags", or 0
* for default settings.
*/
void nrf_drv_pwm_simple_playback(nrf_drv_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags);
/**
* @brief Function for starting a two-sequence playback.
*
* @note The array containing the duty cycle values for the specified sequence
* must be in RAM and cannot be allocated on stack.
* For detailed information, see @ref nrf_pwm_sequence_t.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] p_sequence_0 First sequence to be played back.
* @param[in] p_sequence_1 Second sequence to be played back.
* @param[in] playback_count Number of playbacks to be performed (must not be 0).
* @param[in] flags Additional options. Pass any combination of
* @ref nrf_drv_pwm_flag_t "playback flags", or 0
* for default settings.
*/
void nrf_drv_pwm_complex_playback(nrf_drv_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence_0,
nrf_pwm_sequence_t const * p_sequence_1,
uint16_t playback_count,
uint32_t flags);
/**
* @brief Function for advancing the active sequence.
*
* This function only applies to @ref NRF_PWM_STEP_TRIGGERED mode.
*
* @param[in] p_instance Pointer to the driver instance structure.
*/
__STATIC_INLINE void nrf_drv_pwm_step(nrf_drv_pwm_t const * const p_instance);
/**
* @brief Function for stopping the sequence playback.
*
* The playback is stopped at the end of the current PWM period.
* This means that if the active sequence is configured to repeat each duty
* cycle value for a certain number of PWM periods, the last played value
* might appear on the output less times than requested.
*
* @note This function can be instructed to wait until the playback is stopped
* (by setting @p wait_until_stopped to true). Note that, depending on
* the length of the PMW period, this might take a significant amount of
* time. Alternatively, the @ref nrf_drv_pwm_is_stopped function can be
* used to poll the status, or the @ref NRF_DRV_PWM_EVT_STOPPED event can
* be used to get the notification when the playback is stopped, provided
* the event handler is defined.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] wait_until_stopped If true, the function will not return until
* the playback is stopped.
*
* @retval true If the PWM peripheral is stopped.
* @retval false If the PWM peripheral is not stopped.
*/
bool nrf_drv_pwm_stop(nrf_drv_pwm_t const * const p_instance,
bool wait_until_stopped);
/**
* @brief Function for checking the status of the PWM peripheral.
*
* @param[in] p_instance Pointer to the driver instance structure.
*
* @retval true If the PWM peripheral is stopped.
* @retval false If the PWM peripheral is not stopped.
*/
bool nrf_drv_pwm_is_stopped(nrf_drv_pwm_t const * const p_instance);
/**
* @brief Function for updating the sequence data during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] p_sequence Pointer to the new sequence definition.
*/
__STATIC_INLINE void nrf_drv_pwm_sequence_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_sequence);
/**
* @brief Function for updating the pointer to the duty cycle values
* in the specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] values New pointer to the duty cycle values.
*/
__STATIC_INLINE void nrf_drv_pwm_sequence_values_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_values_t values);
/**
* @brief Function for updating the number of duty cycle values
* in the specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] length New number of the duty cycle values.
*/
__STATIC_INLINE void nrf_drv_pwm_sequence_length_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint16_t length);
/**
* @brief Function for updating the number of repeats for duty cycle values
* in specified sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] repeats New number of repeats.
*/
__STATIC_INLINE void nrf_drv_pwm_sequence_repeats_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t repeats);
/**
* @brief Function for updating the additional delay after the specified
* sequence during playback.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] end_delay New end delay value (in PWM periods).
*/
__STATIC_INLINE void nrf_drv_pwm_sequence_end_delay_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t end_delay);
/**
* @brief Function for returning the address of a specified PWM task that can
* be used in PPI module.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] task Requested task.
*
* @return Task address.
*/
__STATIC_INLINE uint32_t nrf_drv_pwm_task_address_get(
nrf_drv_pwm_t const * const p_instance,
nrf_pwm_task_t task);
/**@brief Function for returning the address of a specified PWM event that can
* be used in PPI module.
*
* @param[in] p_instance Pointer to the driver instance structure.
* @param[in] event Requested event.
*
* @return Event address.
*/
__STATIC_INLINE uint32_t nrf_drv_pwm_event_address_get(
nrf_drv_pwm_t const * const p_instance,
nrf_pwm_event_t event);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_drv_pwm_step(nrf_drv_pwm_t const * const p_instance)
{
nrf_pwm_task_trigger(p_instance->p_registers, NRF_PWM_TASK_NEXTSTEP);
}
__STATIC_INLINE void nrf_drv_pwm_sequence_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_sequence)
{
nrf_pwm_sequence_set(p_instance->p_registers, seq_id, p_sequence);
}
__STATIC_INLINE void nrf_drv_pwm_sequence_values_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
nrf_pwm_values_t values)
{
nrf_pwm_seq_ptr_set(p_instance->p_registers, seq_id, values.p_raw);
}
__STATIC_INLINE void nrf_drv_pwm_sequence_length_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint16_t length)
{
nrf_pwm_seq_cnt_set(p_instance->p_registers, seq_id, length);
}
__STATIC_INLINE void nrf_drv_pwm_sequence_repeats_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t repeats)
{
nrf_pwm_seq_refresh_set(p_instance->p_registers, seq_id, repeats);
}
__STATIC_INLINE void nrf_drv_pwm_sequence_end_delay_update(
nrf_drv_pwm_t const * const p_instance,
uint8_t seq_id,
uint32_t end_delay)
{
nrf_pwm_seq_end_delay_set(p_instance->p_registers, seq_id, end_delay);
}
__STATIC_INLINE uint32_t nrf_drv_pwm_task_address_get(
nrf_drv_pwm_t const * const p_instance,
nrf_pwm_task_t task)
{
return nrf_pwm_task_address_get(p_instance->p_registers, task);
}
__STATIC_INLINE uint32_t nrf_drv_pwm_event_address_get(
nrf_drv_pwm_t const * const p_instance,
nrf_pwm_event_t event)
{
return nrf_pwm_event_address_get(p_instance->p_registers, event);
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_DRV_PWM_H__
/** @} */

View File

@ -198,7 +198,7 @@
#define PWM0_INSTANCE_INDEX 0
#endif
#define PWM1_ENABLED 0
#define PWM1_ENABLED 1
#if (PWM1_ENABLED == 1)
#define PWM1_CONFIG_OUT0_PIN 2
@ -215,7 +215,7 @@
#define PWM1_INSTANCE_INDEX (PWM0_ENABLED)
#endif
#define PWM2_ENABLED 0
#define PWM2_ENABLED 1
#if (PWM2_ENABLED == 1)
#define PWM2_CONFIG_OUT0_PIN 2

View File

@ -0,0 +1,665 @@
/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
/**
* @defgroup nrf_pwm_hal PWM HAL
* @{
* @ingroup nrf_pwm
*
* @brief @tagAPI52 Hardware access layer for managing the Pulse Width Modulation (PWM)
* peripheral.
*/
#ifndef NRF_PWM_H__
#define NRF_PWM_H__
#ifdef NRF52
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_assert.h"
/**
* @brief This value can be provided as a parameter for the @ref nrf_pwm_pins_set
* function call to specify that a given output channel shall not be
* connected to a physical pin.
*/
#define NRF_PWM_PIN_NOT_CONNECTED 0xFFFFFFFF
/**
* @brief Number of channels in each PWM instance.
*/
#define NRF_PWM_CHANNEL_COUNT 4
/**
* @brief PWM tasks.
*/
typedef enum
{
/*lint -save -e30*/
NRF_PWM_TASK_STOP = offsetof(NRF_PWM_Type, TASKS_STOP), ///< Stops PWM pulse generation on all channels at the end of the current PWM period, and stops the sequence playback.
NRF_PWM_TASK_SEQSTART0 = offsetof(NRF_PWM_Type, TASKS_SEQSTART[0]), ///< Starts playback of sequence 0.
NRF_PWM_TASK_SEQSTART1 = offsetof(NRF_PWM_Type, TASKS_SEQSTART[1]), ///< Starts playback of sequence 1.
NRF_PWM_TASK_NEXTSTEP = offsetof(NRF_PWM_Type, TASKS_NEXTSTEP) ///< Steps by one value in the current sequence if the decoder is set to @ref NRF_PWM_STEP_TRIGGERED mode.
/*lint -restore*/
} nrf_pwm_task_t;
/**
* @brief PWM events.
*/
typedef enum
{
/*lint -save -e30*/
NRF_PWM_EVENT_STOPPED = offsetof(NRF_PWM_Type, EVENTS_STOPPED), ///< Response to STOP task, emitted when PWM pulses are no longer generated.
NRF_PWM_EVENT_SEQSTARTED0 = offsetof(NRF_PWM_Type, EVENTS_SEQSTARTED[0]), ///< First PWM period started on sequence 0.
NRF_PWM_EVENT_SEQSTARTED1 = offsetof(NRF_PWM_Type, EVENTS_SEQSTARTED[1]), ///< First PWM period started on sequence 1.
NRF_PWM_EVENT_SEQEND0 = offsetof(NRF_PWM_Type, EVENTS_SEQEND[0]), ///< Emitted at the end of every sequence 0 when its last value has been read from RAM.
NRF_PWM_EVENT_SEQEND1 = offsetof(NRF_PWM_Type, EVENTS_SEQEND[1]), ///< Emitted at the end of every sequence 1 when its last value has been read from RAM.
NRF_PWM_EVENT_PWMPERIODEND = offsetof(NRF_PWM_Type, EVENTS_PWMPERIODEND), ///< Emitted at the end of each PWM period.
NRF_PWM_EVENT_LOOPSDONE = offsetof(NRF_PWM_Type, EVENTS_LOOPSDONE) ///< Concatenated sequences have been played the requested number of times.
/*lint -restore*/
} nrf_pwm_event_t;
/**
* @brief PWM interrupts.
*/
typedef enum
{
NRF_PWM_INT_STOPPED_MASK = PWM_INTENSET_STOPPED_Msk, ///< Interrupt on STOPPED event.
NRF_PWM_INT_SEQSTARTED0_MASK = PWM_INTENSET_SEQSTARTED0_Msk, ///< Interrupt on SEQSTARTED[0] event.
NRF_PWM_INT_SEQSTARTED1_MASK = PWM_INTENSET_SEQSTARTED1_Msk, ///< Interrupt on SEQSTARTED[1] event.
NRF_PWM_INT_SEQEND0_MASK = PWM_INTENSET_SEQEND0_Msk, ///< Interrupt on SEQEND[0] event.
NRF_PWM_INT_SEQEND1_MASK = PWM_INTENSET_SEQEND1_Msk, ///< Interrupt on SEQEND[1] event.
NRF_PWM_INT_PWMPERIODEND_MASK = PWM_INTENSET_PWMPERIODEND_Msk, ///< Interrupt on PWMPERIODEND event.
NRF_PWM_INT_LOOPSDONE_MASK = PWM_INTENSET_LOOPSDONE_Msk ///< Interrupt on LOOPSDONE event.
} nrf_pwm_int_mask_t;
/**
* @brief PWM shortcuts.
*/
typedef enum
{
NRF_PWM_SHORT_SEQEND0_STOP_MASK = PWM_SHORTS_SEQEND0_STOP_Msk, ///< Shortcut between SEQEND[0] event and STOP task.
NRF_PWM_SHORT_SEQEND1_STOP_MASK = PWM_SHORTS_SEQEND1_STOP_Msk, ///< Shortcut between SEQEND[1] event and STOP task.
NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK = PWM_SHORTS_LOOPSDONE_SEQSTART0_Msk, ///< Shortcut between LOOPSDONE event and SEQSTART[0] task.
NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK = PWM_SHORTS_LOOPSDONE_SEQSTART1_Msk, ///< Shortcut between LOOPSDONE event and SEQSTART[1] task.
NRF_PWM_SHORT_LOOPSDONE_STOP_MASK = PWM_SHORTS_LOOPSDONE_STOP_Msk ///< Shortcut between LOOPSDONE event and STOP task.
} nrf_pwm_short_mask_t;
/**
* @brief PWM modes of operation.
*/
typedef enum
{
NRF_PWM_MODE_UP = PWM_MODE_UPDOWN_Up, ///< Up counter (edge-aligned PWM duty cycle).
NRF_PWM_MODE_UP_AND_DOWN = PWM_MODE_UPDOWN_UpAndDown, ///< Up and down counter (center-aligned PWM duty cycle).
} nrf_pwm_mode_t;
/**
* @brief PWM base clock frequencies.
*/
typedef enum
{
NRF_PWM_CLK_16MHz = PWM_PRESCALER_PRESCALER_DIV_1, ///< 16 MHz / 1 = 16 MHz.
NRF_PWM_CLK_8MHz = PWM_PRESCALER_PRESCALER_DIV_2, ///< 16 MHz / 2 = 8 MHz.
NRF_PWM_CLK_4MHz = PWM_PRESCALER_PRESCALER_DIV_4, ///< 16 MHz / 4 = 4 MHz.
NRF_PWM_CLK_2MHz = PWM_PRESCALER_PRESCALER_DIV_8, ///< 16 MHz / 8 = 2 MHz.
NRF_PWM_CLK_1MHz = PWM_PRESCALER_PRESCALER_DIV_16, ///< 16 MHz / 16 = 1 MHz.
NRF_PWM_CLK_500kHz = PWM_PRESCALER_PRESCALER_DIV_32, ///< 16 MHz / 32 = 500 kHz.
NRF_PWM_CLK_250kHz = PWM_PRESCALER_PRESCALER_DIV_64, ///< 16 MHz / 64 = 250 kHz.
NRF_PWM_CLK_125kHz = PWM_PRESCALER_PRESCALER_DIV_128 ///< 16 MHz / 128 = 125 kHz.
} nrf_pwm_clk_t;
/**
* @brief PWM decoder load modes.
*
* The selected mode determines how the sequence data is read from RAM and
* spread to the compare registers.
*/
typedef enum
{
NRF_PWM_LOAD_COMMON = PWM_DECODER_LOAD_Common, ///< 1st half word (16-bit) used in all PWM channels (0-3).
NRF_PWM_LOAD_GROUPED = PWM_DECODER_LOAD_Grouped, ///< 1st half word (16-bit) used in channels 0 and 1; 2nd word in channels 2 and 3.
NRF_PWM_LOAD_INDIVIDUAL = PWM_DECODER_LOAD_Individual, ///< 1st half word (16-bit) used in channel 0; 2nd in channel 1; 3rd in channel 2; 4th in channel 3.
NRF_PWM_LOAD_WAVE_FORM = PWM_DECODER_LOAD_WaveForm ///< 1st half word (16-bit) used in channel 0; 2nd in channel 1; ... ; 4th as the top value for the pulse generator counter.
} nrf_pwm_dec_load_t;
/**
* @brief PWM decoder next step modes.
*
* The selected mode determines when the next value from the active sequence
* is loaded.
*/
typedef enum
{
NRF_PWM_STEP_AUTO = PWM_DECODER_MODE_RefreshCount, ///< Automatically after the current value is played and repeated the requested number of times.
NRF_PWM_STEP_TRIGGERED = PWM_DECODER_MODE_NextStep ///< When the @ref NRF_PWM_TASK_NEXTSTEP task is triggered.
} nrf_pwm_dec_step_t;
/**
* @brief Type used for defining duty cycle values for a sequence
* loaded in @ref NRF_PWM_LOAD_COMMON mode.
*/
typedef uint16_t nrf_pwm_values_common_t;
/**
* @brief Structure for defining duty cycle values for a sequence
* loaded in @ref NRF_PWM_LOAD_GROUPED mode.
*/
typedef struct {
uint16_t group_0; ///< Duty cycle value for group 0 (channels 0 and 1).
uint16_t group_1; ///< Duty cycle value for group 1 (channels 2 and 3).
} nrf_pwm_values_grouped_t;
/**
* @brief Structure for defining duty cycle values for a sequence
* loaded in @ref NRF_PWM_LOAD_INDIVIDUAL mode.
*/
typedef struct
{
uint16_t channel_0; ///< Duty cycle value for channel 0.
uint16_t channel_1; ///< Duty cycle value for channel 1.
uint16_t channel_2; ///< Duty cycle value for channel 2.
uint16_t channel_3; ///< Duty cycle value for channel 3.
} nrf_pwm_values_individual_t;
/**
* @brief Structure for defining duty cycle values for a sequence
* loaded in @ref NRF_PWM_LOAD_WAVE_FORM mode.
*/
typedef struct {
uint16_t channel_0; ///< Duty cycle value for channel 0.
uint16_t channel_1; ///< Duty cycle value for channel 1.
uint16_t channel_2; ///< Duty cycle value for channel 2.
uint16_t counter_top; ///< Top value for the pulse generator counter.
} nrf_pwm_values_wave_form_t;
/**
* @brief Union grouping pointers to arrays of duty cycle values applicable to
* various loading modes.
*/
typedef union {
nrf_pwm_values_common_t const * p_common; ///< Pointer to be used in @ref NRF_PWM_LOAD_COMMON mode.
nrf_pwm_values_grouped_t const * p_grouped; ///< Pointer to be used in @ref NRF_PWM_LOAD_GROUPED mode.
nrf_pwm_values_individual_t const * p_individual; ///< Pointer to be used in @ref NRF_PWM_LOAD_INDIVIDUAL mode.
nrf_pwm_values_wave_form_t const * p_wave_form; ///< Pointer to be used in @ref NRF_PWM_LOAD_WAVE_FORM mode.
uint16_t const * p_raw; ///< Pointer providing raw access to the values.
} nrf_pwm_values_t;
/**
* @brief Structure for defining a sequence of PWM duty cycles.
*
* When the sequence is set (by a call to @ref nrf_pwm_sequence_set), the
* provided duty cycle values are not copied. The @p values pointer is stored
* in the peripheral's internal register, and the values are loaded from RAM
* during the sequence playback. Therefore, you must ensure that the values
* do not change before and during the sequence playback (for example,
* the values cannot be placed in a local variable that is allocated on stack).
* If the sequence is played in a loop and the values should be updated
* before the next iteration, it is safe to modify them when the corresponding
* event signaling the end of sequence occurs (@ref NRF_PWM_EVENT_SEQEND0
* or @ref NRF_PWM_EVENT_SEQEND1, respectively).
*
* @note The @p repeats and @p end_delay values (which are written to the
* SEQ[n].REFRESH and SEQ[n].ENDDELAY registers in the peripheral,
* respectively) are ignored at the end of a complex sequence
* playback, indicated by the LOOPSDONE event.
* See the @linkProductSpecification52 for more information.
*/
typedef struct
{
nrf_pwm_values_t values; ///< Pointer to an array with duty cycle values. This array must be in Data RAM.
/**< This field is defined as an union of pointers
* to provide a convenient way to define duty
* cycle values in various loading modes
* (see @ref nrf_pwm_dec_load_t).
* In each value, the most significant bit (15)
* determines the polarity of the output and the
* others (14-0) compose the 15-bit value to be
* compared with the pulse generator counter. */
uint16_t length; ///< Number of 16-bit values in the array pointed by @p values.
uint32_t repeats; ///< Number of times that each duty cycle should be repeated (after being played once). Ignored in @ref NRF_PWM_STEP_TRIGGERED mode.
uint32_t end_delay; ///< Additional time (in PWM periods) that the last duty cycle is to be kept after the sequence is played. Ignored in @ref NRF_PWM_STEP_TRIGGERED mode.
} nrf_pwm_sequence_t;
/**
* @brief Helper macro for calculating the number of 16-bit values in specified
* array of duty cycle values.
*/
#define NRF_PWM_VALUES_LENGTH(array) (sizeof(array)/sizeof(uint16_t))
/**
* @brief Function for activating a specific PWM task.
*
* @param[in] p_pwm PWM instance.
* @param[in] task Task to activate.
*/
__STATIC_INLINE void nrf_pwm_task_trigger(NRF_PWM_Type * p_pwm,
nrf_pwm_task_t task);
/**
* @brief Function for getting the address of a specific PWM task register.
*
* @param[in] p_pwm PWM instance.
* @param[in] task Requested task.
*
* @return Address of the specified task register.
*/
__STATIC_INLINE uint32_t nrf_pwm_task_address_get(NRF_PWM_Type const * p_pwm,
nrf_pwm_task_t task);
/**
* @brief Function for clearing a specific PWM event.
*
* @param[in] p_pwm PWM instance.
* @param[in] event Event to clear.
*/
__STATIC_INLINE void nrf_pwm_event_clear(NRF_PWM_Type * p_pwm,
nrf_pwm_event_t event);
/**
* @brief Function for checking the state of a specific PWM event.
*
* @param[in] p_pwm PWM instance.
* @param[in] event Event to check.
*
* @retval true If the event is set.
* @retval false If the event is not set.
*/
__STATIC_INLINE bool nrf_pwm_event_check(NRF_PWM_Type const * p_pwm,
nrf_pwm_event_t event);
/**
* @brief Function for getting the address of a specific PWM event register.
*
* @param[in] p_pwm PWM instance.
* @param[in] event Requested event.
*
* @return Address of the specified event register.
*/
__STATIC_INLINE uint32_t nrf_pwm_event_address_get(NRF_PWM_Type const * p_pwm,
nrf_pwm_event_t event);
/**
* @brief Function for enabling specified shortcuts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_shorts_mask Shortcuts to enable.
*/
__STATIC_INLINE void nrf_pwm_shorts_enable(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask);
/**
* @brief Function for disabling specified shortcuts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_shorts_mask Shortcuts to disable.
*/
__STATIC_INLINE void nrf_pwm_shorts_disable(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask);
/**
* @brief Function for setting the configuration of PWM shortcuts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_shorts_mask Shortcuts configuration to set.
*/
__STATIC_INLINE void nrf_pwm_shorts_set(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask);
/**
* @brief Function for enabling specified interrupts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_int_mask Interrupts to enable.
*/
__STATIC_INLINE void nrf_pwm_int_enable(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask);
/**
* @brief Function for disabling specified interrupts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_int_mask Interrupts to disable.
*/
__STATIC_INLINE void nrf_pwm_int_disable(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask);
/**
* @brief Function for setting the configuration of PWM interrupts.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_int_mask Interrupts configuration to set.
*/
__STATIC_INLINE void nrf_pwm_int_set(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask);
/**
* @brief Function for retrieving the state of a given interrupt.
*
* @param[in] p_pwm PWM instance.
* @param[in] pwm_int Interrupt to check.
*
* @retval true If the interrupt is enabled.
* @retval false If the interrupt is not enabled.
*/
__STATIC_INLINE bool nrf_pwm_int_enable_check(NRF_PWM_Type const * p_pwm,
nrf_pwm_int_mask_t pwm_int);
/**
* @brief Function for enabling the PWM peripheral.
*
* @param[in] p_pwm PWM instance.
*/
__STATIC_INLINE void nrf_pwm_enable(NRF_PWM_Type * p_pwm);
/**
* @brief Function for disabling the PWM peripheral.
*
* @param[in] p_pwm PWM instance.
*/
__STATIC_INLINE void nrf_pwm_disable(NRF_PWM_Type * p_pwm);
/**
* @brief Function for assigning pins to PWM output channels.
*
* Usage of all PWM output channels is optional. If a given channel is not
* needed, pass the @ref NRF_PWM_PIN_NOT_CONNECTED value instead of its pin
* number.
*
* @param[in] p_pwm PWM instance.
* @param[in] out_pins Array with pin numbers for individual PWM output channels.
*/
__STATIC_INLINE void nrf_pwm_pins_set(NRF_PWM_Type * p_pwm,
uint32_t out_pins[NRF_PWM_CHANNEL_COUNT]);
/**
* @brief Function for configuring the PWM peripheral.
*
* @param[in] p_pwm PWM instance.
* @param[in] base_clock Base clock frequency.
* @param[in] mode Operating mode of the pulse generator counter.
* @param[in] top_value Value up to which the pulse generator counter counts.
*/
__STATIC_INLINE void nrf_pwm_configure(NRF_PWM_Type * p_pwm,
nrf_pwm_clk_t base_clock,
nrf_pwm_mode_t mode,
uint16_t top_value);
/**
* @brief Function for defining a sequence of PWM duty cycles.
*
* @param[in] p_pwm PWM instance.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] p_seq Pointer to the sequence definition.
*/
__STATIC_INLINE void nrf_pwm_sequence_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_seq);
/**
* @brief Function for modifying the pointer to the duty cycle values
* in the specified sequence.
*
* @param[in] p_pwm PWM instance.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] p_values Pointer to an array with duty cycle values.
*/
__STATIC_INLINE void nrf_pwm_seq_ptr_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint16_t const * p_values);
/**
* @brief Function for modifying the total number of duty cycle values
* in the specified sequence.
*
* @param[in] p_pwm PWM instance.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] length Number of duty cycle values.
*/
__STATIC_INLINE void nrf_pwm_seq_cnt_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint16_t length);
/**
* @brief Function for modifying the additional number of PWM periods spent
* on each duty cycle value in the specified sequence.
*
* @param[in] p_pwm PWM instance.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] refresh Number of additional PWM periods for each duty cycle value.
*/
__STATIC_INLINE void nrf_pwm_seq_refresh_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint32_t refresh);
/**
* @brief Function for modifying the additional time added after the sequence
* is played.
*
* @param[in] p_pwm PWM instance.
* @param[in] seq_id Identifier of the sequence (0 or 1).
* @param[in] end_delay Number of PWM periods added at the end of the sequence.
*/
__STATIC_INLINE void nrf_pwm_seq_end_delay_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint32_t end_delay);
/**
* @brief Function for setting the mode of loading sequence data from RAM
* and advancing the sequence.
*
* @param[in] p_pwm PWM instance.
* @param[in] dec_load Mode of loading sequence data from RAM.
* @param[in] dec_step Mode of advancing the active sequence.
*/
__STATIC_INLINE void nrf_pwm_decoder_set(NRF_PWM_Type * p_pwm,
nrf_pwm_dec_load_t dec_load,
nrf_pwm_dec_step_t dec_step);
/**
* @brief Function for setting the number of times the sequence playback
* should be performed.
*
* This function applies to two-sequence playback (concatenated sequence 0 and 1).
* A single sequence can be played back only once.
*
* @param[in] p_pwm PWM instance.
* @param[in] loop_count Number of times to perform the sequence playback.
*/
__STATIC_INLINE void nrf_pwm_loop_set(NRF_PWM_Type * p_pwm,
uint16_t loop_count);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_pwm_task_trigger(NRF_PWM_Type * p_pwm,
nrf_pwm_task_t task)
{
*((volatile uint32_t *)((uint8_t *)p_pwm + (uint32_t)task)) = 0x1UL;
}
__STATIC_INLINE uint32_t nrf_pwm_task_address_get(NRF_PWM_Type const * p_pwm,
nrf_pwm_task_t task)
{
return ((uint32_t)p_pwm + (uint32_t)task);
}
__STATIC_INLINE void nrf_pwm_event_clear(NRF_PWM_Type * p_pwm,
nrf_pwm_event_t event)
{
*((volatile uint32_t *)((uint8_t *)p_pwm + (uint32_t)event)) = 0x0UL;
}
__STATIC_INLINE bool nrf_pwm_event_check(NRF_PWM_Type const * p_pwm,
nrf_pwm_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_pwm + (uint32_t)event);
}
__STATIC_INLINE uint32_t nrf_pwm_event_address_get(NRF_PWM_Type const * p_pwm,
nrf_pwm_event_t event)
{
return ((uint32_t)p_pwm + (uint32_t)event);
}
__STATIC_INLINE void nrf_pwm_shorts_enable(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask)
{
p_pwm->SHORTS |= pwm_shorts_mask;
}
__STATIC_INLINE void nrf_pwm_shorts_disable(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask)
{
p_pwm->SHORTS &= ~(pwm_shorts_mask);
}
__STATIC_INLINE void nrf_pwm_shorts_set(NRF_PWM_Type * p_pwm,
uint32_t pwm_shorts_mask)
{
p_pwm->SHORTS = pwm_shorts_mask;
}
__STATIC_INLINE void nrf_pwm_int_enable(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask)
{
p_pwm->INTENSET = pwm_int_mask;
}
__STATIC_INLINE void nrf_pwm_int_disable(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask)
{
p_pwm->INTENCLR = pwm_int_mask;
}
__STATIC_INLINE void nrf_pwm_int_set(NRF_PWM_Type * p_pwm,
uint32_t pwm_int_mask)
{
p_pwm->INTEN = pwm_int_mask;
}
__STATIC_INLINE bool nrf_pwm_int_enable_check(NRF_PWM_Type const * p_pwm,
nrf_pwm_int_mask_t pwm_int)
{
return (bool)(p_pwm->INTENSET & pwm_int);
}
__STATIC_INLINE void nrf_pwm_enable(NRF_PWM_Type * p_pwm)
{
p_pwm->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_pwm_disable(NRF_PWM_Type * p_pwm)
{
p_pwm->ENABLE = (PWM_ENABLE_ENABLE_Disabled << PWM_ENABLE_ENABLE_Pos);
}
__STATIC_INLINE void nrf_pwm_pins_set(NRF_PWM_Type * p_pwm,
uint32_t out_pins[NRF_PWM_CHANNEL_COUNT])
{
uint8_t i;
for (i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
{
p_pwm->PSEL.OUT[i] = out_pins[i];
}
}
__STATIC_INLINE void nrf_pwm_configure(NRF_PWM_Type * p_pwm,
nrf_pwm_clk_t base_clock,
nrf_pwm_mode_t mode,
uint16_t top_value)
{
ASSERT(top_value <= PWM_COUNTERTOP_COUNTERTOP_Msk);
p_pwm->PRESCALER = base_clock;
p_pwm->MODE = mode;
p_pwm->COUNTERTOP = top_value;
}
__STATIC_INLINE void nrf_pwm_sequence_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
nrf_pwm_sequence_t const * p_seq)
{
ASSERT(p_seq != NULL);
nrf_pwm_seq_ptr_set( p_pwm, seq_id, p_seq->values.p_raw);
nrf_pwm_seq_cnt_set( p_pwm, seq_id, p_seq->length);
nrf_pwm_seq_refresh_set( p_pwm, seq_id, p_seq->repeats);
nrf_pwm_seq_end_delay_set(p_pwm, seq_id, p_seq->end_delay);
}
__STATIC_INLINE void nrf_pwm_seq_ptr_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint16_t const * p_values)
{
ASSERT(seq_id <= 1);
ASSERT(p_values != NULL);
p_pwm->SEQ[seq_id].PTR = (uint32_t)p_values;
}
__STATIC_INLINE void nrf_pwm_seq_cnt_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint16_t length)
{
ASSERT(seq_id <= 1);
ASSERT(length != 0);
ASSERT(length <= PWM_SEQ_CNT_CNT_Msk);
p_pwm->SEQ[seq_id].CNT = length;
}
__STATIC_INLINE void nrf_pwm_seq_refresh_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint32_t refresh)
{
ASSERT(seq_id <= 1);
ASSERT(refresh <= PWM_SEQ_REFRESH_CNT_Msk);
p_pwm->SEQ[seq_id].REFRESH = refresh;
}
__STATIC_INLINE void nrf_pwm_seq_end_delay_set(NRF_PWM_Type * p_pwm,
uint8_t seq_id,
uint32_t end_delay)
{
ASSERT(seq_id <= 1);
ASSERT(end_delay <= PWM_SEQ_ENDDELAY_CNT_Msk);
p_pwm->SEQ[seq_id].ENDDELAY = end_delay;
}
__STATIC_INLINE void nrf_pwm_decoder_set(NRF_PWM_Type * p_pwm,
nrf_pwm_dec_load_t dec_load,
nrf_pwm_dec_step_t dec_step)
{
p_pwm->DECODER = ((uint32_t)dec_load << PWM_DECODER_LOAD_Pos) |
((uint32_t)dec_step << PWM_DECODER_MODE_Pos);
}
__STATIC_INLINE void nrf_pwm_loop_set(NRF_PWM_Type * p_pwm,
uint16_t loop_count)
{
p_pwm->LOOP = loop_count;
}
#endif // SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF52
#endif // NRF_PWM_H__
/** @} */