mirror of https://github.com/ARMmbed/mbed-os.git
NRF52 series PWM reimplementation for SDK 14.2
Driver uses new API in SDK 14.2pull/6547/head
parent
f9b371fc0f
commit
d11f74cb4c
|
@ -30,3 +30,10 @@ MBED_WEAK const PinMapI2C PinMap_I2C[1] = {
|
||||||
MBED_WEAK const PinMapSPI PinMap_SPI[1] = {
|
MBED_WEAK const PinMapSPI PinMap_SPI[1] = {
|
||||||
{NC, NC, NC, NC}
|
{NC, NC, NC, NC}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Default mapping between PWM pins and PWM instance.
|
||||||
|
* Can be overwritten by user.
|
||||||
|
*/
|
||||||
|
MBED_WEAK const PinMapPWM PinMap_PWM[1] = {
|
||||||
|
{NC, NC}
|
||||||
|
};
|
||||||
|
|
|
@ -2266,14 +2266,14 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef PWM1_ENABLED
|
#ifndef PWM1_ENABLED
|
||||||
#define PWM1_ENABLED 0
|
#define PWM1_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <q> PWM2_ENABLED - Enable PWM2 instance
|
// <q> PWM2_ENABLED - Enable PWM2 instance
|
||||||
|
|
||||||
|
|
||||||
#ifndef PWM2_ENABLED
|
#ifndef PWM2_ENABLED
|
||||||
#define PWM2_ENABLED 0
|
#define PWM2_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <q> PWM3_ENABLED - Enable PWM3 instance
|
// <q> PWM3_ENABLED - Enable PWM3 instance
|
||||||
|
|
|
@ -2266,21 +2266,21 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef PWM1_ENABLED
|
#ifndef PWM1_ENABLED
|
||||||
#define PWM1_ENABLED 0
|
#define PWM1_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <q> PWM2_ENABLED - Enable PWM2 instance
|
// <q> PWM2_ENABLED - Enable PWM2 instance
|
||||||
|
|
||||||
|
|
||||||
#ifndef PWM2_ENABLED
|
#ifndef PWM2_ENABLED
|
||||||
#define PWM2_ENABLED 0
|
#define PWM2_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <q> PWM3_ENABLED - Enable PWM3 instance
|
// <q> PWM3_ENABLED - Enable PWM3 instance
|
||||||
|
|
||||||
|
|
||||||
#ifndef PWM3_ENABLED
|
#ifndef PWM3_ENABLED
|
||||||
#define PWM3_ENABLED 0
|
#define PWM3_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// </e>
|
// </e>
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
#include "PeripheralNames.h"
|
#include "PeripheralNames.h"
|
||||||
#include "PinNames.h"
|
#include "PinNames.h"
|
||||||
|
|
||||||
|
#include "nrf_pwm.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -62,10 +64,12 @@ struct port_s {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pwmout_s {
|
struct pwmout_s {
|
||||||
PWMName pwm_name;
|
int instance;
|
||||||
PinName pin;
|
PinName pin;
|
||||||
uint8_t pwm_channel;
|
nrf_pwm_values_common_t pulse;
|
||||||
void * pwm_struct;
|
uint16_t period;
|
||||||
|
float percent;
|
||||||
|
nrf_pwm_sequence_t sequence;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct i2c_s {
|
struct i2c_s {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
/* Define number of instances */
|
/* Define number of instances */
|
||||||
#define NORDIC_TWI_COUNT TWI_COUNT
|
#define NORDIC_TWI_COUNT TWI_COUNT
|
||||||
#define NORDIC_SPI_COUNT SPI_COUNT
|
#define NORDIC_SPI_COUNT SPI_COUNT
|
||||||
|
#define NORDIC_PWM_COUNT PWM_COUNT
|
||||||
|
|
||||||
/* Define which instance to return when there are no free instances left.
|
/* Define which instance to return when there are no free instances left.
|
||||||
* The Mbed HAL API doesn't provide a way for signaling initialization errors
|
* The Mbed HAL API doesn't provide a way for signaling initialization errors
|
||||||
|
@ -38,7 +39,20 @@
|
||||||
* by the driver implementation.
|
* by the driver implementation.
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_I2C_INSTANCE 0 // SPI counts down, choose instance furthest away
|
#define DEFAULT_I2C_INSTANCE 0 // SPI counts down, choose instance furthest away
|
||||||
#define DEFAULT_SPI_INSTANCE (SPI_COUNT - 1) // I2C counts up, choose instance furthers away
|
#define DEFAULT_SPI_INSTANCE (NORDIC_SPI_COUNT - 1) // I2C counts up, choose instance furthers away
|
||||||
|
#define DEFAULT_PWM_INSTANCE (NORDIC_PWM_COUNT - 1)
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* _____ _____ _____ ___ _____
|
||||||
|
* / ____| __ \_ _|__ \ / ____|
|
||||||
|
* | (___ | |__) || | ) | |
|
||||||
|
* \___ \| ___/ | | / /| |
|
||||||
|
* ____) | | _| |_ / /_| |____
|
||||||
|
* |_____/|_| |_____|____|\_____|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/* Internal data structure shared between SPI and I2C to keep track of allocated
|
/* Internal data structure shared between SPI and I2C to keep track of allocated
|
||||||
* instances. The data structure is shared to reflect the hardware sharing.
|
* instances. The data structure is shared to reflect the hardware sharing.
|
||||||
|
@ -246,3 +260,108 @@ int pin_instance_spi(PinName mosi, PinName miso, PinName clk)
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* _______ ____ __
|
||||||
|
* | __ \ \ / / \/ |
|
||||||
|
* | |__) \ \ /\ / /| \ / |
|
||||||
|
* | ___/ \ \/ \/ / | |\/| |
|
||||||
|
* | | \ /\ / | | | |
|
||||||
|
* |_| \/ \/ |_| |_|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Internal data structure to keep track of allocated instances.
|
||||||
|
*/
|
||||||
|
static PinName nordic_internal_pwm[NORDIC_PWM_COUNT] = {
|
||||||
|
NC,
|
||||||
|
NC,
|
||||||
|
NC,
|
||||||
|
#if (NORDIC_PWM_COUNT == 4)
|
||||||
|
NC,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brief Find hardware instance for the provided PWM pin.
|
||||||
|
*
|
||||||
|
* The function will search the PeripheralPin map for a pre-allocated
|
||||||
|
* assignment. If none is found the allocation map will be searched
|
||||||
|
* to see if the same pins have been assigned an instance before.
|
||||||
|
*
|
||||||
|
* If no assignement is found and there is an empty slot left in the
|
||||||
|
* map, the pins are stored in the map and the hardware instance is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* If no free instances are available, the default instance is returned.
|
||||||
|
*
|
||||||
|
* Parameter pwm pwm pin.
|
||||||
|
*
|
||||||
|
* Return Hardware instance associated with provided pins.
|
||||||
|
*/
|
||||||
|
int pin_instance_pwm(PinName pwm)
|
||||||
|
{
|
||||||
|
int instance = NC;
|
||||||
|
|
||||||
|
/* Search pin map for pre-allocated instance */
|
||||||
|
for (size_t index = 0; (PinMap_PWM[index].pwm != NC); index++) {
|
||||||
|
|
||||||
|
/* Compare pins to entry. */
|
||||||
|
if (PinMap_PWM[index].pwm == pwm) {
|
||||||
|
|
||||||
|
DEBUG_PRINTF("found: %d %d\r\n", pwm, PinMap_PWM[index].instance);
|
||||||
|
|
||||||
|
/* Instance found, save result. */
|
||||||
|
instance = PinMap_PWM[index].instance;
|
||||||
|
|
||||||
|
/* Lock out entry in map. */
|
||||||
|
nordic_internal_pwm[instance] = pwm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No instance was found in static map. */
|
||||||
|
if (instance == NC) {
|
||||||
|
|
||||||
|
/* Search dynamic map for entry. */
|
||||||
|
for (size_t index = 0; index < NORDIC_PWM_COUNT; index++) {
|
||||||
|
|
||||||
|
/* Pins match previous dynamic allocation, return instance. */
|
||||||
|
if (nordic_internal_pwm[index] == pwm) {
|
||||||
|
|
||||||
|
instance = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No instance was found in dynamic map. */
|
||||||
|
if (instance == NC) {
|
||||||
|
|
||||||
|
/* Search dynamic map for empty slot. */
|
||||||
|
for (size_t index = 0; index < NORDIC_PWM_COUNT; index++) {
|
||||||
|
|
||||||
|
/* Empty slot found, reserve slot by storing pins. */
|
||||||
|
if (nordic_internal_pwm[index] == NC) {
|
||||||
|
|
||||||
|
nordic_internal_pwm[index] = pwm;
|
||||||
|
|
||||||
|
instance = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(DEFAULT_PWM_INSTANCE)
|
||||||
|
/* Exhausted all options. Return default value. */
|
||||||
|
if (instance == NC) {
|
||||||
|
instance = DEFAULT_PWM_INSTANCE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEBUG_PRINTF("PWM: %d %d\r\n", pwm, instance);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,14 @@ typedef struct {
|
||||||
|
|
||||||
extern const PinMapSPI PinMap_SPI[];
|
extern const PinMapSPI PinMap_SPI[];
|
||||||
|
|
||||||
|
/* Data structure for pre-allocated PWM instances. */
|
||||||
|
typedef struct {
|
||||||
|
PinName pwm;
|
||||||
|
int instance;
|
||||||
|
} PinMapPWM;
|
||||||
|
|
||||||
|
extern const PinMapPWM PinMap_PWM[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find hardware instance for the provided I2C pins.
|
* @brief Find hardware instance for the provided I2C pins.
|
||||||
*
|
*
|
||||||
|
@ -83,6 +91,25 @@ int pin_instance_i2c(PinName sda, PinName scl);
|
||||||
*/
|
*/
|
||||||
int pin_instance_spi(PinName mosi, PinName miso, PinName clk);
|
int pin_instance_spi(PinName mosi, PinName miso, PinName clk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find hardware instance for the provided PWM pins.
|
||||||
|
*
|
||||||
|
* The function will search the PeripheralPin map for a pre-allocated
|
||||||
|
* assignment. If none is found the allocation map will be searched
|
||||||
|
* to see if the same pins have been assigned an instance before.
|
||||||
|
*
|
||||||
|
* If no assignement is found and there is an empty slot left in the
|
||||||
|
* map, the pins are stored in the map and the hardware instance is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* If no free instances are available, the default instance is returned.
|
||||||
|
*
|
||||||
|
* @param[in] pwm pwm pin.
|
||||||
|
*
|
||||||
|
* @return Hardware instance associated with provided pins.
|
||||||
|
*/
|
||||||
|
int pin_instance_pwm(PinName pwm);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Nordic Semiconductor ASA
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without modification,
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
* are permitted provided that the following conditions are met:
|
* are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this list
|
* 1. Redistributions of source code must retain the above copyright notice, this list
|
||||||
* of conditions and the following disclaimer.
|
* of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA
|
* 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
|
* 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 above copyright notice, this list of conditions and the following disclaimer in
|
||||||
* the documentation and/or other materials provided with the distribution.
|
* 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
|
* 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
|
* used to endorse or promote products derived from this software without specific prior
|
||||||
* written permission.
|
* written permission.
|
||||||
*
|
*
|
||||||
* 4. This software, with or without modification, must only be used with a
|
* 4. This software, with or without modification, must only be used with a
|
||||||
* Nordic Semiconductor ASA integrated circuit.
|
* Nordic Semiconductor ASA integrated circuit.
|
||||||
*
|
*
|
||||||
* 5. Any software provided in binary or object form under this license must not be reverse
|
* 5. Any software provided in binary or object form under this license must not be reverse
|
||||||
* engineered, decompiled, modified and/or disassembled.
|
* engineered, decompiled, modified and/or disassembled.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
* 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
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
@ -33,39 +33,32 @@
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
* 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
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#warning arm porting pending
|
|
||||||
|
|
||||||
#ifndef TARGET_MCU_NRF51822
|
|
||||||
|
|
||||||
#include "mbed_assert.h"
|
|
||||||
#include "mbed_error.h"
|
|
||||||
#include "cmsis.h"
|
|
||||||
#include "pinmap.h"
|
|
||||||
#include "sdk_config.h"
|
|
||||||
|
|
||||||
#if DEVICE_PWMOUT
|
#if DEVICE_PWMOUT
|
||||||
|
|
||||||
#include "app_util_platform.h"
|
#include "hal/pwmout_api.h"
|
||||||
|
|
||||||
|
#include "pinmap_ex.h"
|
||||||
#include "nrf_drv_pwm.h"
|
#include "nrf_drv_pwm.h"
|
||||||
|
|
||||||
#include "pwmout_api.h"
|
#if 0
|
||||||
#include "irq_handlers_hw.h"
|
#define DEBUG_PRINTF(...) do { printf(__VA_ARGS__); } while(0)
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINTF(...) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX_PWM_COUNTERTOP (0x7FFF) // 0x7FFF is the max of COUNTERTOP value for the PWM peripherial of the nRF52.
|
/* 0x7FFF is the max of COUNTERTOP pulse for the PWM peripherial of the nRF52. */
|
||||||
#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP * 8) // PWM hw is driven by 16 MHz clock, hence the tick is 1_us/16,
|
#define MAX_PWM_COUNTERTOP (0x7FFF)
|
||||||
// and 128 is the max prescaler value.
|
|
||||||
#define MAX_PWM_PERIOD_MS ((MAX_PWM_PERIOD_US / 1000) + 1) // approximations advance
|
|
||||||
#define MAX_PWM_PERIOD_S ((MAX_PWM_PERIOD_US / 1000000) + 1) // approximations advance
|
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
|
||||||
#define PWM_INSTANCE_COUNT (PWM_COUNT) // import from the nrf_drv_config.h file
|
/* Allocate PWM instances. */
|
||||||
|
static nrf_drv_pwm_t nordic_nrf5_pwm_instance[] = {
|
||||||
///> instances of nRF52 PWM driver
|
|
||||||
static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
|
|
||||||
{
|
|
||||||
#if PWM0_ENABLED
|
#if PWM0_ENABLED
|
||||||
NRF_DRV_PWM_INSTANCE(0),
|
NRF_DRV_PWM_INSTANCE(0),
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,339 +66,273 @@ static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
|
||||||
NRF_DRV_PWM_INSTANCE(1),
|
NRF_DRV_PWM_INSTANCE(1),
|
||||||
#endif
|
#endif
|
||||||
#if PWM2_ENABLED
|
#if PWM2_ENABLED
|
||||||
NRF_DRV_PWM_INSTANCE(2)
|
NRF_DRV_PWM_INSTANCE(2),
|
||||||
|
#endif
|
||||||
|
#if PWM3_ENABLED
|
||||||
|
NRF_DRV_PWM_INSTANCE(3),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
/* Helper function for (re)initializing the PWM instance.
|
||||||
|
*/
|
||||||
|
static void nordic_pwm_init(pwmout_t *obj)
|
||||||
{
|
{
|
||||||
uint32_t period_us;
|
MBED_ASSERT(obj);
|
||||||
uint32_t duty_us;
|
|
||||||
float duty;
|
|
||||||
} pwm_signal_t; /// PWM signal description type
|
|
||||||
|
|
||||||
typedef struct
|
/* Default configuration:
|
||||||
{
|
* 1 pin per instance, otherwise they would share base count.
|
||||||
nrf_drv_pwm_t * p_pwm_driver;
|
* 1 MHz clock source to match the 1 us resolution.
|
||||||
pwm_signal_t signal;
|
*/
|
||||||
volatile nrf_pwm_values_common_t seq_values[1];
|
nrf_drv_pwm_config_t config = {
|
||||||
} pwm_t; /// internal PWM instance support type
|
.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,
|
||||||
|
};
|
||||||
|
|
||||||
static pwm_t m_pwm[PWM_INSTANCE_COUNT] =
|
/* Make sure PWM instance is not running before making changes. */
|
||||||
{
|
nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]);
|
||||||
#if PWM0_ENABLED
|
|
||||||
{.p_pwm_driver = NULL},
|
|
||||||
#endif
|
|
||||||
#if PWM1_ENABLED
|
|
||||||
{.p_pwm_driver = NULL},
|
|
||||||
#endif
|
|
||||||
#if PWM2_ENABLED
|
|
||||||
{.p_pwm_driver = NULL}
|
|
||||||
#endif
|
|
||||||
}; /// Array of internal PWM instances.
|
|
||||||
|
|
||||||
typedef struct
|
/* Initialize instance with new configuration. */
|
||||||
{
|
ret_code_t result = nrf_drv_pwm_init(&nordic_nrf5_pwm_instance[obj->instance],
|
||||||
uint16_t period_hwu; // unit related to pwm_clk
|
&config,
|
||||||
uint16_t duty_hwu; // unit related to pwm_clk
|
NULL);
|
||||||
nrf_pwm_clk_t pwm_clk;
|
|
||||||
} pulsewidth_set_t; /// helper type for timing calculations
|
|
||||||
|
|
||||||
|
|
||||||
static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization);
|
|
||||||
|
|
||||||
// extern PWM nIRQ handler implementations
|
|
||||||
void PWM0_IRQHandler(void);
|
|
||||||
void PWM1_IRQHandler(void);
|
|
||||||
void PWM2_IRQHandler(void);
|
|
||||||
|
|
||||||
static const peripheral_handler_desc_t pwm_handlers[PWM_INSTANCE_COUNT] =
|
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);
|
||||||
PWM0_IRQn,
|
|
||||||
(uint32_t)PWM0_IRQHandler
|
/* (Re)initialize PWM instance. */
|
||||||
},
|
nordic_pwm_init(obj);
|
||||||
#warning
|
|
||||||
#if 0
|
/* Set duty-cycle from object. */
|
||||||
{
|
ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance],
|
||||||
PWM1_IRQn,
|
&obj->sequence,
|
||||||
(uint32_t)PWM1_IRQHandler
|
1,
|
||||||
},
|
NRF_DRV_PWM_FLAG_LOOP);
|
||||||
{
|
|
||||||
PWM2_IRQn,
|
MBED_ASSERT(result == NRF_SUCCESS);
|
||||||
(uint32_t)PWM2_IRQHandler
|
}
|
||||||
}
|
|
||||||
#endif
|
/** 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)
|
void pwmout_init(pwmout_t *obj, PinName pin)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
DEBUG_PRINTF("pwmout_init: %d\r\n", pin);
|
||||||
|
|
||||||
for (i = 0; PWM_INSTANCE_COUNT; i++)
|
|
||||||
{
|
|
||||||
if (m_pwm[i].p_pwm_driver == NULL) // a driver instance not assigned to the obj?
|
|
||||||
{
|
|
||||||
NVIC_SetVector(pwm_handlers[i].IRQn, pwm_handlers[i].vector);
|
|
||||||
|
|
||||||
obj->pin = pin;
|
|
||||||
|
|
||||||
obj->pwm_channel = i;
|
|
||||||
|
|
||||||
m_pwm[i].p_pwm_driver = (nrf_drv_pwm_t *) &m_pwm_driver[i];
|
|
||||||
m_pwm[i].signal.period_us = 200000; // 0.02 s
|
|
||||||
m_pwm[i].signal.duty_us = 100000;
|
|
||||||
m_pwm[i].signal.duty = 0.5f;
|
|
||||||
|
|
||||||
obj->pwm_struct = &m_pwm[i];
|
|
||||||
|
|
||||||
internal_pwmout_exe(obj, true, true);
|
MBED_ASSERT(obj);
|
||||||
|
|
||||||
break;
|
/* 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)));
|
||||||
MBED_ASSERT(i != PWM_INSTANCE_COUNT); // assert if free instance was not found.
|
|
||||||
|
/* Populate PWM object with default values. */
|
||||||
|
obj->instance = instance;
|
||||||
|
obj->pin = pin;
|
||||||
|
obj->pulse = 0;
|
||||||
|
obj->period = MAX_PWM_COUNTERTOP;
|
||||||
|
obj->percent = 0;
|
||||||
|
obj->sequence.values.p_common = &obj->pulse;
|
||||||
|
obj->sequence.length = NRF_PWM_VALUES_LENGTH(obj->pulse);
|
||||||
|
obj->sequence.repeats = 0;
|
||||||
|
obj->sequence.end_delay = 0;
|
||||||
|
|
||||||
|
/* Initialize PWM instance. */
|
||||||
|
nordic_pwm_init(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Deinitialize the pwmout object
|
||||||
|
*
|
||||||
|
* Parameter obj The pwmout object
|
||||||
|
*/
|
||||||
void pwmout_free(pwmout_t *obj)
|
void pwmout_free(pwmout_t *obj)
|
||||||
{
|
{
|
||||||
nrf_drv_pwm_uninit( (nrf_drv_pwm_t*) obj->pwm_struct );
|
DEBUG_PRINTF("pwmout_free\r\n");
|
||||||
|
|
||||||
m_pwm[obj->pwm_channel].p_pwm_driver = NULL;
|
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)
|
void pwmout_write(pwmout_t *obj, float percent)
|
||||||
{
|
{
|
||||||
|
DEBUG_PRINTF("pwmout_write: %f\r\n", percent);
|
||||||
if (percent < 0)
|
|
||||||
{
|
/* Find counts based on period. */
|
||||||
percent = 0;
|
uint16_t pulse = obj->period * percent;
|
||||||
}
|
|
||||||
else if (percent > 1)
|
/* Ensure we don't overcount. */
|
||||||
{
|
obj->pulse = (pulse > MAX_PWM_COUNTERTOP) ? MAX_PWM_COUNTERTOP : pulse;
|
||||||
percent = 1;
|
|
||||||
}
|
/* Store actual percentage passed as parameter to avoid floating point rounding errors. */
|
||||||
|
obj->percent = percent;
|
||||||
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
|
||||||
|
/* Set new duty-cycle. */
|
||||||
p_pwm_signal->duty = percent;
|
ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance],
|
||||||
|
&obj->sequence,
|
||||||
int us = (((int)p_pwm_signal->period_us) * percent);
|
1,
|
||||||
|
NRF_DRV_PWM_FLAG_LOOP);
|
||||||
pwmout_pulsewidth_us(obj, us);
|
|
||||||
|
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)
|
float pwmout_read(pwmout_t *obj)
|
||||||
{
|
{
|
||||||
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
DEBUG_PRINTF("pwmout_read: %f\r\n", obj->percent);
|
||||||
|
|
||||||
return (float)p_pwm_signal->duty_us / (float)p_pwm_signal->period_us;
|
/* Return percentage stored in object instead of calculating the value.
|
||||||
|
* This prevents floating point rounding errors.
|
||||||
|
*/
|
||||||
|
return obj->percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmout_period(pwmout_t *obj, float seconds)
|
/** 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)
|
||||||
{
|
{
|
||||||
// raught saturation < 0, quasi-max>
|
DEBUG_PRINTF("pwmout_period: %f\r\n", period);
|
||||||
if (seconds > MAX_PWM_PERIOD_S)
|
|
||||||
{
|
/* Cap period if too large. */
|
||||||
seconds = MAX_PWM_PERIOD_S;
|
if (period > MAX_PWM_PERIOD_S) {
|
||||||
|
period = MAX_PWM_PERIOD_S;
|
||||||
}
|
}
|
||||||
else if (seconds < 0)
|
|
||||||
{
|
/* Set new period. */
|
||||||
seconds = 0; // f. pwmout_period_us will set period to min. value
|
pwmout_period_us(obj, period * 1000000);
|
||||||
}
|
|
||||||
|
|
||||||
int us = seconds * 1000000;
|
|
||||||
|
|
||||||
pwmout_period_us(obj, us);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmout_period_ms(pwmout_t *obj, int ms)
|
/** 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)
|
||||||
{
|
{
|
||||||
// reught saturation < 0, quasi-max>
|
DEBUG_PRINTF("pwmout_period_ms: %d\r\n", period);
|
||||||
if (ms > MAX_PWM_PERIOD_MS)
|
|
||||||
{
|
/* Cap period if too large. */
|
||||||
ms = MAX_PWM_PERIOD_MS;
|
if (period > MAX_PWM_PERIOD_MS) {
|
||||||
|
period = MAX_PWM_PERIOD_MS;
|
||||||
}
|
}
|
||||||
else if (ms < 0)
|
|
||||||
{
|
/* Set new period. */
|
||||||
ms = 0; // f. pwmout_period_us will set period to min. value
|
pwmout_period_us(obj, period * 1000);
|
||||||
}
|
|
||||||
|
|
||||||
int us = ms * 1000;
|
|
||||||
|
|
||||||
pwmout_period_us(obj, us);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the PWM period specified in microseconds, keeping the duty cycle the same
|
||||||
void pwmout_period_us(pwmout_t *obj, int us)
|
*
|
||||||
|
* Parameter obj The pwmout object
|
||||||
|
* Parameter us The microsecond period
|
||||||
|
*/
|
||||||
|
void pwmout_period_us(pwmout_t *obj, int period)
|
||||||
{
|
{
|
||||||
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
DEBUG_PRINTF("pwmout_period_us: %d\r\n", period);
|
||||||
|
|
||||||
// saturation <1, real-max>
|
/* Cap period if too large. */
|
||||||
if (us > MAX_PWM_PERIOD_US)
|
if (period > MAX_PWM_PERIOD_US) {
|
||||||
{
|
period = MAX_PWM_PERIOD_US;
|
||||||
us = MAX_PWM_PERIOD_US;
|
|
||||||
}
|
}
|
||||||
else if (us < 1)
|
|
||||||
{
|
/* Scale new count based on stored duty-cycle and new period. */
|
||||||
us = 1;
|
uint32_t pulse = (period * obj->pulse) / obj->period;
|
||||||
}
|
|
||||||
|
/* Store new values in object. */
|
||||||
p_pwm_signal->duty_us = (int)((float)us * p_pwm_signal->duty);
|
obj->pulse = pulse;
|
||||||
|
obj->period = period;
|
||||||
p_pwm_signal->period_us = us;
|
obj->percent = (float) pulse / (float) period;
|
||||||
|
|
||||||
internal_pwmout_exe(obj, true, false);
|
/* Restart instance with new values. */
|
||||||
|
nordic_pwm_restart(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
|
/** 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)
|
||||||
{
|
{
|
||||||
// raught saturation < 0, quasi-max>
|
DEBUG_PRINTF("pwmout_pulsewidt: %f\r\n", pulse);
|
||||||
if (seconds > MAX_PWM_PERIOD_S)
|
|
||||||
{
|
/* Cap pulsewidth to period before setting it. */
|
||||||
seconds = MAX_PWM_PERIOD_S;
|
if ((pulse * 1000000) > (float) obj->pulse) {
|
||||||
|
obj->pulse = obj->period;
|
||||||
|
pwmout_pulsewidth_us(obj, obj->pulse);
|
||||||
|
} else {
|
||||||
|
pwmout_pulsewidth_us(obj, pulse * 1000000);
|
||||||
}
|
}
|
||||||
else if (seconds < 0)
|
|
||||||
{
|
|
||||||
seconds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int us = seconds * 1000000;
|
|
||||||
|
|
||||||
pwmout_pulsewidth_us(obj,us);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
|
/** 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)
|
||||||
{
|
{
|
||||||
// raught saturation < 0, quasi-max>
|
DEBUG_PRINTF("pwmout_pulsewidth_ms: %d\r\n", ms);
|
||||||
if (ms > MAX_PWM_PERIOD_MS)
|
|
||||||
{
|
/* Cap pulsewidth to period before setting it. */
|
||||||
ms = MAX_PWM_PERIOD_MS;
|
if ((pulse * 1000) > (int) obj->period) {
|
||||||
|
obj->pulse = obj->period;
|
||||||
|
pwmout_pulsewidth_us(obj, obj->pulse);
|
||||||
|
} else {
|
||||||
|
pwmout_pulsewidth_us(obj, pulse * 1000);
|
||||||
}
|
}
|
||||||
else if (ms < 0)
|
|
||||||
{
|
|
||||||
ms = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int us = ms * 1000;
|
|
||||||
|
|
||||||
pwmout_pulsewidth_us(obj, us);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
|
/** 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)
|
||||||
{
|
{
|
||||||
// saturation <0, real-max>
|
DEBUG_PRINTF("pwmout_pulsewidth_us: %d\r\n", pulse);
|
||||||
if (us > MAX_PWM_PERIOD_US)
|
|
||||||
{
|
/* Cap pulsewidth to period. */
|
||||||
us = MAX_PWM_PERIOD_US;
|
if (pulse > obj->period) {
|
||||||
|
pulse = obj->period;
|
||||||
}
|
}
|
||||||
else if (us < 0)
|
|
||||||
{
|
|
||||||
us = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
|
||||||
|
|
||||||
p_pwm_signal->duty_us = us;
|
|
||||||
p_pwm_signal->duty = us / p_pwm_signal->period_us;
|
|
||||||
|
|
||||||
internal_pwmout_exe(obj, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Store new values in object. */
|
||||||
|
obj->pulse = pulse;
|
||||||
|
obj->percent = (float) pulse / (float) obj->period;
|
||||||
|
|
||||||
|
/* Restart instance with new values. */
|
||||||
|
nordic_pwm_restart(obj);
|
||||||
|
|
||||||
|
|
||||||
static ret_code_t pulsewidth_us_set_get(int period_hwu, int duty_hwu, pulsewidth_set_t * p_settings)
|
|
||||||
{
|
|
||||||
uint16_t div;
|
|
||||||
nrf_pwm_clk_t pwm_clk = NRF_PWM_CLK_16MHz;
|
|
||||||
|
|
||||||
for(div = 1; div <= 128 ; div <<= 1) // 128 is the maximum of clock prescaler for PWM peripherial
|
|
||||||
{
|
|
||||||
if (MAX_PWM_COUNTERTOP >= period_hwu)
|
|
||||||
{
|
|
||||||
p_settings->period_hwu = period_hwu; // unit [us/16 * div]
|
|
||||||
p_settings->duty_hwu = duty_hwu; // unit [us/16 * div]
|
|
||||||
p_settings->pwm_clk = pwm_clk;
|
|
||||||
|
|
||||||
return NRF_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
period_hwu >>= 1;
|
|
||||||
duty_hwu >>= 1;
|
|
||||||
pwm_clk++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NRF_ERROR_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization)
|
|
||||||
{
|
|
||||||
pulsewidth_set_t pulsewidth_set;
|
|
||||||
pwm_signal_t * p_pwm_signal;
|
|
||||||
nrf_drv_pwm_t * p_pwm_driver;
|
|
||||||
ret_code_t ret_code;
|
|
||||||
|
|
||||||
p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
|
||||||
|
|
||||||
if (NRF_SUCCESS == pulsewidth_us_set_get(p_pwm_signal->period_us * 16, // base clk for PWM is 16 MHz
|
|
||||||
p_pwm_signal->duty_us * 16, // base clk for PWM is 16 MHz
|
|
||||||
&pulsewidth_set))
|
|
||||||
{
|
|
||||||
p_pwm_driver = (((pwm_t*)obj->pwm_struct)->p_pwm_driver);
|
|
||||||
|
|
||||||
const nrf_pwm_sequence_t seq =
|
|
||||||
{
|
|
||||||
.values.p_common = (nrf_pwm_values_common_t*) (((pwm_t*)obj->pwm_struct)->seq_values),
|
|
||||||
.length = 1,
|
|
||||||
.repeats = 0,
|
|
||||||
.end_delay = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
(((pwm_t*)obj->pwm_struct)->seq_values)[0] = pulsewidth_set.duty_hwu | 0x8000;
|
|
||||||
|
|
||||||
if (new_period)
|
|
||||||
{
|
|
||||||
nrf_drv_pwm_config_t config0 =
|
|
||||||
{
|
|
||||||
.output_pins =
|
|
||||||
{
|
|
||||||
obj->pin | NRF_DRV_PWM_PIN_INVERTED, // channel 0
|
|
||||||
NRF_DRV_PWM_PIN_NOT_USED, // channel 1
|
|
||||||
NRF_DRV_PWM_PIN_NOT_USED, // channel 2
|
|
||||||
NRF_DRV_PWM_PIN_NOT_USED, // channel 3
|
|
||||||
},
|
|
||||||
.irq_priority = PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
|
|
||||||
.base_clock = pulsewidth_set.pwm_clk,
|
|
||||||
.count_mode = NRF_PWM_MODE_UP,
|
|
||||||
.top_value = pulsewidth_set.period_hwu,
|
|
||||||
.load_mode = NRF_PWM_LOAD_COMMON,
|
|
||||||
.step_mode = NRF_PWM_STEP_AUTO
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!initialization)
|
|
||||||
{
|
|
||||||
nrf_drv_pwm_uninit(p_pwm_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret_code = nrf_drv_pwm_init( p_pwm_driver, &config0, NULL);
|
|
||||||
|
|
||||||
MBED_ASSERT(ret_code == NRF_SUCCESS); // assert if free instance was not found.
|
|
||||||
}
|
|
||||||
|
|
||||||
nrf_drv_pwm_simple_playback(p_pwm_driver, &seq, 0, NRF_DRV_PWM_FLAG_LOOP);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MBED_ASSERT(0); // force assertion
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DEVICE_PWMOUT
|
#endif // DEVICE_PWMOUT
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in New Issue