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] = {
|
||||
{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
|
||||
#define PWM1_ENABLED 0
|
||||
#define PWM1_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> PWM2_ENABLED - Enable PWM2 instance
|
||||
|
||||
|
||||
#ifndef PWM2_ENABLED
|
||||
#define PWM2_ENABLED 0
|
||||
#define PWM2_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> PWM3_ENABLED - Enable PWM3 instance
|
||||
|
|
|
@ -2266,21 +2266,21 @@
|
|||
|
||||
|
||||
#ifndef PWM1_ENABLED
|
||||
#define PWM1_ENABLED 0
|
||||
#define PWM1_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> PWM2_ENABLED - Enable PWM2 instance
|
||||
|
||||
|
||||
#ifndef PWM2_ENABLED
|
||||
#define PWM2_ENABLED 0
|
||||
#define PWM2_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> PWM3_ENABLED - Enable PWM3 instance
|
||||
|
||||
|
||||
#ifndef PWM3_ENABLED
|
||||
#define PWM3_ENABLED 0
|
||||
#define PWM3_ENABLED 1
|
||||
#endif
|
||||
|
||||
// </e>
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#include "PeripheralNames.h"
|
||||
#include "PinNames.h"
|
||||
|
||||
#include "nrf_pwm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -62,10 +64,12 @@ struct port_s {
|
|||
};
|
||||
|
||||
struct pwmout_s {
|
||||
PWMName pwm_name;
|
||||
int instance;
|
||||
PinName pin;
|
||||
uint8_t pwm_channel;
|
||||
void * pwm_struct;
|
||||
nrf_pwm_values_common_t pulse;
|
||||
uint16_t period;
|
||||
float percent;
|
||||
nrf_pwm_sequence_t sequence;
|
||||
};
|
||||
|
||||
struct i2c_s {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
/* Define number of instances */
|
||||
#define NORDIC_TWI_COUNT TWI_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.
|
||||
* The Mbed HAL API doesn't provide a way for signaling initialization errors
|
||||
|
@ -38,7 +39,20 @@
|
|||
* by the driver implementation.
|
||||
*/
|
||||
#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
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* _______ ____ __
|
||||
* | __ \ \ / / \/ |
|
||||
* | |__) \ \ /\ / /| \ / |
|
||||
* | ___/ \ \/ \/ / | |\/| |
|
||||
* | | \ /\ / | | | |
|
||||
* |_| \/ \/ |_| |_|
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* 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[];
|
||||
|
||||
/* 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.
|
||||
*
|
||||
|
@ -83,6 +91,25 @@ int pin_instance_i2c(PinName sda, PinName scl);
|
|||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -36,36 +36,29 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
#include "app_util_platform.h"
|
||||
#include "hal/pwmout_api.h"
|
||||
|
||||
#include "pinmap_ex.h"
|
||||
#include "nrf_drv_pwm.h"
|
||||
|
||||
#include "pwmout_api.h"
|
||||
#include "irq_handlers_hw.h"
|
||||
#if 0
|
||||
#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.
|
||||
#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP * 8) // PWM hw is driven by 16 MHz clock, hence the tick is 1_us/16,
|
||||
// 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
|
||||
/* 0x7FFF is the max of COUNTERTOP pulse for the PWM peripherial of the nRF52. */
|
||||
#define MAX_PWM_COUNTERTOP (0x7FFF)
|
||||
|
||||
/* The PWM is driven by a 1 MHz clock to fit the 1 us resolution expected by the API. */
|
||||
#define MAX_PWM_PERIOD_US (MAX_PWM_COUNTERTOP)
|
||||
#define MAX_PWM_PERIOD_MS (MAX_PWM_PERIOD_US / 1000)
|
||||
#define MAX_PWM_PERIOD_S ((float) MAX_PWM_PERIOD_US / 1000000.0f)
|
||||
|
||||
#define PWM_INSTANCE_COUNT (PWM_COUNT) // import from the nrf_drv_config.h file
|
||||
|
||||
///> instances of nRF52 PWM driver
|
||||
static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
|
||||
{
|
||||
/* Allocate PWM instances. */
|
||||
static nrf_drv_pwm_t nordic_nrf5_pwm_instance[] = {
|
||||
#if PWM0_ENABLED
|
||||
NRF_DRV_PWM_INSTANCE(0),
|
||||
#endif
|
||||
|
@ -73,339 +66,273 @@ static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
|
|||
NRF_DRV_PWM_INSTANCE(1),
|
||||
#endif
|
||||
#if PWM2_ENABLED
|
||||
NRF_DRV_PWM_INSTANCE(2)
|
||||
NRF_DRV_PWM_INSTANCE(2),
|
||||
#endif
|
||||
#if PWM3_ENABLED
|
||||
NRF_DRV_PWM_INSTANCE(3),
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct
|
||||
/* Helper function for (re)initializing the PWM instance.
|
||||
*/
|
||||
static void nordic_pwm_init(pwmout_t *obj)
|
||||
{
|
||||
uint32_t period_us;
|
||||
uint32_t duty_us;
|
||||
float duty;
|
||||
} pwm_signal_t; /// PWM signal description type
|
||||
|
||||
typedef struct
|
||||
{
|
||||
nrf_drv_pwm_t * p_pwm_driver;
|
||||
pwm_signal_t signal;
|
||||
volatile nrf_pwm_values_common_t seq_values[1];
|
||||
} pwm_t; /// internal PWM instance support type
|
||||
|
||||
static pwm_t m_pwm[PWM_INSTANCE_COUNT] =
|
||||
{
|
||||
#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
|
||||
{
|
||||
uint16_t period_hwu; // unit related to pwm_clk
|
||||
uint16_t duty_hwu; // unit related to pwm_clk
|
||||
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] =
|
||||
{
|
||||
{
|
||||
PWM0_IRQn,
|
||||
(uint32_t)PWM0_IRQHandler
|
||||
},
|
||||
#warning
|
||||
#if 0
|
||||
{
|
||||
PWM1_IRQn,
|
||||
(uint32_t)PWM1_IRQHandler
|
||||
},
|
||||
{
|
||||
PWM2_IRQn,
|
||||
(uint32_t)PWM2_IRQHandler
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
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?
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void pwmout_write(pwmout_t *obj, float percent)
|
||||
{
|
||||
|
||||
if (percent < 0)
|
||||
{
|
||||
percent = 0;
|
||||
}
|
||||
else if (percent > 1)
|
||||
{
|
||||
percent = 1;
|
||||
}
|
||||
|
||||
pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
|
||||
|
||||
p_pwm_signal->duty = percent;
|
||||
|
||||
int us = (((int)p_pwm_signal->period_us) * percent);
|
||||
|
||||
pwmout_pulsewidth_us(obj, us);
|
||||
}
|
||||
|
||||
float pwmout_read(pwmout_t *obj)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void pwmout_period(pwmout_t *obj, float seconds)
|
||||
{
|
||||
// raught saturation < 0, quasi-max>
|
||||
if (seconds > MAX_PWM_PERIOD_S)
|
||||
{
|
||||
seconds = MAX_PWM_PERIOD_S;
|
||||
}
|
||||
else if (seconds < 0)
|
||||
{
|
||||
seconds = 0; // f. pwmout_period_us will set period to min. value
|
||||
}
|
||||
|
||||
int us = seconds * 1000000;
|
||||
|
||||
pwmout_period_us(obj, us);
|
||||
}
|
||||
|
||||
void pwmout_period_ms(pwmout_t *obj, int ms)
|
||||
{
|
||||
// reught saturation < 0, quasi-max>
|
||||
if (ms > MAX_PWM_PERIOD_MS)
|
||||
{
|
||||
ms = MAX_PWM_PERIOD_MS;
|
||||
}
|
||||
else if (ms < 0)
|
||||
{
|
||||
ms = 0; // f. pwmout_period_us will set period to min. value
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// saturation <1, real-max>
|
||||
if (us > MAX_PWM_PERIOD_US)
|
||||
{
|
||||
us = MAX_PWM_PERIOD_US;
|
||||
}
|
||||
else if (us < 1)
|
||||
{
|
||||
us = 1;
|
||||
}
|
||||
|
||||
p_pwm_signal->duty_us = (int)((float)us * p_pwm_signal->duty);
|
||||
|
||||
p_pwm_signal->period_us = us;
|
||||
|
||||
internal_pwmout_exe(obj, true, false);
|
||||
}
|
||||
|
||||
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
|
||||
{
|
||||
// raught saturation < 0, quasi-max>
|
||||
if (seconds > MAX_PWM_PERIOD_S)
|
||||
{
|
||||
seconds = MAX_PWM_PERIOD_S;
|
||||
}
|
||||
else if (seconds < 0)
|
||||
{
|
||||
seconds = 0;
|
||||
}
|
||||
|
||||
int us = seconds * 1000000;
|
||||
|
||||
pwmout_pulsewidth_us(obj,us);
|
||||
}
|
||||
|
||||
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
|
||||
{
|
||||
// raught saturation < 0, quasi-max>
|
||||
if (ms > MAX_PWM_PERIOD_MS)
|
||||
{
|
||||
ms = MAX_PWM_PERIOD_MS;
|
||||
}
|
||||
else if (ms < 0)
|
||||
{
|
||||
ms = 0;
|
||||
}
|
||||
|
||||
int us = ms * 1000;
|
||||
|
||||
pwmout_pulsewidth_us(obj, us);
|
||||
}
|
||||
|
||||
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
|
||||
{
|
||||
// saturation <0, real-max>
|
||||
if (us > MAX_PWM_PERIOD_US)
|
||||
{
|
||||
us = MAX_PWM_PERIOD_US;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
MBED_ASSERT(obj);
|
||||
|
||||
/* Default configuration:
|
||||
* 1 pin per instance, otherwise they would share base count.
|
||||
* 1 MHz clock source to match the 1 us resolution.
|
||||
*/
|
||||
nrf_drv_pwm_config_t config = {
|
||||
.output_pins = {
|
||||
obj->pin,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
},
|
||||
.irq_priority = PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
|
||||
.base_clock = pulsewidth_set.pwm_clk,
|
||||
.base_clock = NRF_PWM_CLK_1MHz,
|
||||
.count_mode = NRF_PWM_MODE_UP,
|
||||
.top_value = pulsewidth_set.period_hwu,
|
||||
.top_value = obj->period,
|
||||
.load_mode = NRF_PWM_LOAD_COMMON,
|
||||
.step_mode = NRF_PWM_STEP_AUTO
|
||||
.step_mode = NRF_PWM_STEP_AUTO,
|
||||
};
|
||||
|
||||
if (!initialization)
|
||||
/* Make sure PWM instance is not running before making changes. */
|
||||
nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]);
|
||||
|
||||
/* Initialize instance with new configuration. */
|
||||
ret_code_t result = nrf_drv_pwm_init(&nordic_nrf5_pwm_instance[obj->instance],
|
||||
&config,
|
||||
NULL);
|
||||
|
||||
MBED_ASSERT(result == NRF_SUCCESS);
|
||||
}
|
||||
|
||||
/* Helper function for reinitializing the PWM instance and setting the duty-cycle. */
|
||||
static void nordic_pwm_restart(pwmout_t *obj)
|
||||
{
|
||||
nrf_drv_pwm_uninit(p_pwm_driver);
|
||||
MBED_ASSERT(obj);
|
||||
|
||||
/* (Re)initialize PWM instance. */
|
||||
nordic_pwm_init(obj);
|
||||
|
||||
/* Set duty-cycle from object. */
|
||||
ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance],
|
||||
&obj->sequence,
|
||||
1,
|
||||
NRF_DRV_PWM_FLAG_LOOP);
|
||||
|
||||
MBED_ASSERT(result == NRF_SUCCESS);
|
||||
}
|
||||
|
||||
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
|
||||
/** 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)
|
||||
{
|
||||
MBED_ASSERT(0); // force assertion
|
||||
DEBUG_PRINTF("pwmout_init: %d\r\n", pin);
|
||||
|
||||
MBED_ASSERT(obj);
|
||||
|
||||
/* Get hardware instance from pinmap. */
|
||||
int instance = pin_instance_pwm(pin);
|
||||
|
||||
MBED_ASSERT(instance < (int)(sizeof(nordic_nrf5_pwm_instance) / sizeof(nrf_drv_pwm_t)));
|
||||
|
||||
/* Populate PWM object with default values. */
|
||||
obj->instance = instance;
|
||||
obj->pin = pin;
|
||||
obj->pulse = 0;
|
||||
obj->period = MAX_PWM_COUNTERTOP;
|
||||
obj->percent = 0;
|
||||
obj->sequence.values.p_common = &obj->pulse;
|
||||
obj->sequence.length = NRF_PWM_VALUES_LENGTH(obj->pulse);
|
||||
obj->sequence.repeats = 0;
|
||||
obj->sequence.end_delay = 0;
|
||||
|
||||
/* Initialize PWM instance. */
|
||||
nordic_pwm_init(obj);
|
||||
}
|
||||
|
||||
/** Deinitialize the pwmout object
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
*/
|
||||
void pwmout_free(pwmout_t *obj)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_free\r\n");
|
||||
|
||||
MBED_ASSERT(obj);
|
||||
|
||||
/* Uninitialize PWM instance. */
|
||||
nrf_drv_pwm_uninit(&nordic_nrf5_pwm_instance[obj->instance]);
|
||||
}
|
||||
|
||||
/** Set the output duty-cycle in range <0.0f, 1.0f>
|
||||
*
|
||||
* pulse 0.0f represents 0 percentage, 1.0f represents 100 percent.
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter percent The floating-point percentage number
|
||||
*/
|
||||
void pwmout_write(pwmout_t *obj, float percent)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_write: %f\r\n", percent);
|
||||
|
||||
/* Find counts based on period. */
|
||||
uint16_t pulse = obj->period * percent;
|
||||
|
||||
/* Ensure we don't overcount. */
|
||||
obj->pulse = (pulse > MAX_PWM_COUNTERTOP) ? MAX_PWM_COUNTERTOP : pulse;
|
||||
|
||||
/* Store actual percentage passed as parameter to avoid floating point rounding errors. */
|
||||
obj->percent = percent;
|
||||
|
||||
/* Set new duty-cycle. */
|
||||
ret_code_t result = nrf_drv_pwm_simple_playback(&nordic_nrf5_pwm_instance[obj->instance],
|
||||
&obj->sequence,
|
||||
1,
|
||||
NRF_DRV_PWM_FLAG_LOOP);
|
||||
|
||||
MBED_ASSERT(result == NRF_SUCCESS);
|
||||
}
|
||||
|
||||
/** Read the current float-point output duty-cycle
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Return A floating-point output duty-cycle
|
||||
*/
|
||||
float pwmout_read(pwmout_t *obj)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_read: %f\r\n", obj->percent);
|
||||
|
||||
/* Return percentage stored in object instead of calculating the value.
|
||||
* This prevents floating point rounding errors.
|
||||
*/
|
||||
return obj->percent;
|
||||
}
|
||||
|
||||
/** Set the PWM period specified in seconds, keeping the duty cycle the same
|
||||
*
|
||||
* Periods smaller than microseconds (the lowest resolution) are set to zero.
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter seconds The floating-point seconds period
|
||||
*/
|
||||
void pwmout_period(pwmout_t *obj, float period)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_period: %f\r\n", period);
|
||||
|
||||
/* Cap period if too large. */
|
||||
if (period > MAX_PWM_PERIOD_S) {
|
||||
period = MAX_PWM_PERIOD_S;
|
||||
}
|
||||
|
||||
/* Set new period. */
|
||||
pwmout_period_us(obj, period * 1000000);
|
||||
}
|
||||
|
||||
/** Set the PWM period specified in miliseconds, keeping the duty cycle the same
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter ms The milisecond period
|
||||
*/
|
||||
void pwmout_period_ms(pwmout_t *obj, int period)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_period_ms: %d\r\n", period);
|
||||
|
||||
/* Cap period if too large. */
|
||||
if (period > MAX_PWM_PERIOD_MS) {
|
||||
period = MAX_PWM_PERIOD_MS;
|
||||
}
|
||||
|
||||
/* Set new period. */
|
||||
pwmout_period_us(obj, period * 1000);
|
||||
}
|
||||
|
||||
/** Set the PWM period specified in microseconds, keeping the duty cycle the same
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter us The microsecond period
|
||||
*/
|
||||
void pwmout_period_us(pwmout_t *obj, int period)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_period_us: %d\r\n", period);
|
||||
|
||||
/* Cap period if too large. */
|
||||
if (period > MAX_PWM_PERIOD_US) {
|
||||
period = MAX_PWM_PERIOD_US;
|
||||
}
|
||||
|
||||
/* Scale new count based on stored duty-cycle and new period. */
|
||||
uint32_t pulse = (period * obj->pulse) / obj->period;
|
||||
|
||||
/* Store new values in object. */
|
||||
obj->pulse = pulse;
|
||||
obj->period = period;
|
||||
obj->percent = (float) pulse / (float) period;
|
||||
|
||||
/* Restart instance with new values. */
|
||||
nordic_pwm_restart(obj);
|
||||
}
|
||||
|
||||
/** Set the PWM pulsewidth specified in seconds, keeping the period the same.
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter seconds The floating-point pulsewidth in seconds
|
||||
*/
|
||||
void pwmout_pulsewidth(pwmout_t *obj, float pulse)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_pulsewidt: %f\r\n", pulse);
|
||||
|
||||
/* Cap pulsewidth to period before setting it. */
|
||||
if ((pulse * 1000000) > (float) obj->pulse) {
|
||||
obj->pulse = obj->period;
|
||||
pwmout_pulsewidth_us(obj, obj->pulse);
|
||||
} else {
|
||||
pwmout_pulsewidth_us(obj, pulse * 1000000);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the PWM pulsewidth specified in miliseconds, keeping the period the same.
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter ms The floating-point pulsewidth in miliseconds
|
||||
*/
|
||||
void pwmout_pulsewidth_ms(pwmout_t *obj, int pulse)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_pulsewidth_ms: %d\r\n", ms);
|
||||
|
||||
/* Cap pulsewidth to period before setting it. */
|
||||
if ((pulse * 1000) > (int) obj->period) {
|
||||
obj->pulse = obj->period;
|
||||
pwmout_pulsewidth_us(obj, obj->pulse);
|
||||
} else {
|
||||
pwmout_pulsewidth_us(obj, pulse * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the PWM pulsewidth specified in microseconds, keeping the period the same.
|
||||
*
|
||||
* Parameter obj The pwmout object
|
||||
* Parameter us The floating-point pulsewidth in microseconds
|
||||
*/
|
||||
void pwmout_pulsewidth_us(pwmout_t *obj, int pulse)
|
||||
{
|
||||
DEBUG_PRINTF("pwmout_pulsewidth_us: %d\r\n", pulse);
|
||||
|
||||
/* Cap pulsewidth to period. */
|
||||
if (pulse > obj->period) {
|
||||
pulse = obj->period;
|
||||
}
|
||||
|
||||
/* Store new values in object. */
|
||||
obj->pulse = pulse;
|
||||
obj->percent = (float) pulse / (float) obj->period;
|
||||
|
||||
/* Restart instance with new values. */
|
||||
nordic_pwm_restart(obj);
|
||||
}
|
||||
|
||||
#endif // DEVICE_PWMOUT
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue