Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/nrf/app_dev/device_guides/fem/fem_nrf2220.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ To use nRF2220, complete the following steps:
};
};

Additionally, you can consider setting the :kconfig:option:`CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION` Kconfig option.

#. Optionally replace the device name ``name_of_fem_node``.
#. Replace the pin numbers provided for each of the required properties:

Expand Down
13 changes: 13 additions & 0 deletions lib/fem_al/fem_al.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

#include <mpsl_fem_config_common.h>
#include <mpsl_fem_protocol_api.h>
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION) && \
!defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
#include <protocol/mpsl_fem_nrf2220_protocol_api.h>
#endif

#include "fem_al/fem_al.h"
#include "fem_interface.h"
Expand Down Expand Up @@ -186,6 +190,15 @@ int fem_tx_configure(uint32_t ramp_up_time)

fem_activate_event.event.timer.counter_period.end = ramp_up_time;

#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION) && \
!defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
err = mpsl_fem_nrf2220_temperature_changed_update_now();
if (err) {
printk("mpsl_fem_nrf2220_temperature_changed_update_now failed (err %d)\n", err);
return -EFAULT;
}
#endif

mpsl_fem_enable();
err = mpsl_fem_pa_configuration_set(&fem_activate_event, &fem_deactivate_evt);
if (err) {
Expand Down
50 changes: 50 additions & 0 deletions subsys/mpsl/fem/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,56 @@ config MPSL_FEM_USE_TWI_DRV
select I2C
default y

config MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
bool "Temperature compensation of the nRF2220 Front-End Module [EXPERIMENTAL]"
depends on MPSL_FEM_NRF2220 && MPSL_FEM_USE_TWI_DRV
select EXPERIMENTAL
help
This allows to achieve better accuracy in output power among all
temperature ranges for the nRF2220 Front-End modules.

config MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER
bool
depends on MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
default y if MPSL && !MPSL_FEM_ONLY
help
Enable this option when using protocols relying on the MPSL scheduler.
After it turns out that for the current temperature some registers
of the nRF2220 must be written, the writes are performed within a timeslot
negotiated with the MPSL scheduler. For operation with Bluetooth, the timeslot
for the nRF2220 register update is between other Bluetooth timeslots.
For operation with the nRF 802.15.4 Radio Driver, the grant for the timeslot
for the nRF2220 register update causes short inability to transmit or receive.

choice MPSL_FEM_NRF2220_TEMPERATURE_SOURCE
prompt "Source of temperature measurement of the nRF2220"
depends on MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
default MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC

config MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC
bool "Temperature of the SoC as the temperature of the nRF2220"
select SENSOR
help
The temperature of the SoC controlling the nRF2220 Front-End Module
is taken as an input for the temperature compensation.
Use this option if there is good thermal coupling between the
temperature of the SoC and the nRF2220 Front-End Module.

config MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM
bool "Custom nRF2220 temperature measurement provider"
help
Use this option if you have a custom temperature sensor measuring the
temperature of the nRF2220 device. Your custom code must call
the fem_temperature_change() function to notify the module handling the FEM
that the new temperature value is to be processed.

endchoice # MPSL_FEM_NRF2220_TEMPERATURE_SOURCE

config MPSL_FEM_NRF2220_TEMPERATURE_POLL_PERIOD
int "Temperature measurement poll period (in ms) for temperature compensation of the nRF2220"
depends on MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC
default 5000

config MPSL_FEM_INIT_PRIORITY
int "Init priority of the Front-End Module support code"
depends on MPSL_FEM
Expand Down
53 changes: 46 additions & 7 deletions subsys/mpsl/fem/common/include/mpsl_fem_twi_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,63 @@
#include <zephyr/devicetree.h>
#include <mpsl_fem_nrf22xx_twi_config_common.h>

#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
(mpsl_fem_twi_if_t){ \
.enabled = true, \
.slave_address = ((uint8_t)DT_REG_ADDR(node_id)), \
.p_instance = (void *)DEVICE_DT_GET(DT_BUS(node_id)),

#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
.p_xfer_read = mpsl_fem_twi_drv_impl_xfer_read, \
.p_xfer_write = mpsl_fem_twi_drv_impl_xfer_write,

#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS_ASYNC \
.p_xfer_write_async = mpsl_fem_twi_drv_impl_xfer_write_async, \
.p_xfer_write_async_time_get = mpsl_fem_twi_drv_impl_xfer_write_async_time_get,

#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END \
}

/**
* @brief Initializer of structure @c mpsl_fem_twi_if_t allowing to use TWI interface.
*
* @note Initializes only the part related to the synchonous interface for boot-time
* initialization of the FEM.
*
* @sa @ref @ref MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC
*
* @param node_id Devicetree node identifier for the TWI part for the FEM device.
*/
#define MPSL_FEM_TWI_DRV_IF_INITIALIZER(node_id) \
(mpsl_fem_twi_if_t){ \
.enabled = true, \
.slave_address = ((uint8_t)DT_REG_ADDR(node_id)), \
.p_instance = (void *)DEVICE_DT_GET(DT_BUS(node_id)), \
.p_xfer_read = mpsl_fem_twi_drv_impl_xfer_read, \
.p_xfer_write = mpsl_fem_twi_drv_impl_xfer_write, \
}
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END

/**
* @brief Initializer of structure @c mpsl_fem_twi_if_t allowing to use TWI interface
* with asynchronous extension.
*
* @sa @ref @ref MPSL_FEM_TWI_DRV_IF_INITIALIZER
*
* @param node_id Devicetree node identifier for the TWI part for the FEM device.
*/
#define MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC(node_id) \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS_ASYNC \
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END


int32_t mpsl_fem_twi_drv_impl_xfer_read(void *p_instance, uint8_t slave_address,
uint8_t internal_address, uint8_t *p_data, uint8_t data_length);

int32_t mpsl_fem_twi_drv_impl_xfer_write(void *p_instance, uint8_t slave_address,
uint8_t internal_address, const uint8_t *p_data, uint8_t data_length);

int32_t mpsl_fem_twi_drv_impl_xfer_write_async(void *p_instance, uint8_t slave_address,
const uint8_t *p_data, uint8_t data_length, mpsl_fem_twi_async_xfer_write_cb_t p_callback,
void *p_context);

uint32_t mpsl_fem_twi_drv_impl_xfer_write_async_time_get(void *p_instance, uint8_t data_length);

#endif /* MPSL_FEM_TWI_DRV__ */
39 changes: 38 additions & 1 deletion subsys/mpsl/fem/common/mpsl_fem_twi_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <mpsl_fem_twi_drv.h>
#include <zephyr/drivers/i2c.h>

#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>

int32_t mpsl_fem_twi_drv_impl_xfer_read(void *p_instance, uint8_t slave_address,
uint8_t internal_address, uint8_t *p_data, uint8_t data_length)
Expand All @@ -23,3 +23,40 @@ int32_t mpsl_fem_twi_drv_impl_xfer_write(void *p_instance, uint8_t slave_address

return i2c_burst_write(dev, slave_address, internal_address, p_data, data_length);
}

int32_t mpsl_fem_twi_drv_impl_xfer_write_async(void *p_instance, uint8_t slave_address,
const uint8_t *p_data, uint8_t data_length, mpsl_fem_twi_async_xfer_write_cb_t p_callback,
void *p_context)
{
const struct device *dev = (const struct device *)p_instance;

struct i2c_msg msg = {
.buf = (uint8_t *)p_data,
.len = data_length,
.flags = I2C_MSG_WRITE | I2C_MSG_STOP
};

return i2c_nrfx_twim_async_transfer_begin(dev, &msg, slave_address,
(i2c_nrfx_twim_async_transfer_handler_t)p_callback, p_context);
}

uint32_t mpsl_fem_twi_drv_impl_xfer_write_async_time_get(void *p_instance, uint8_t data_length)
{
(void)p_instance;

static const uint32_t sw_overhead_safety_margin_time_us = 10U;
/* Note: on nRF devices the first bit of each data octet is delayed by one period,
* thus +1 below.
*/
static const uint32_t i2c_data_byte_periods_with_ack = 1U + (8U + 1U);
static const uint32_t i2c_start_bit_periods = 2U;
static const uint32_t i2c_stop_bit_periods = 2U;
static const uint32_t i2c_speed_hz = 100000;

/* Total number of sck periods needed to perform a write transfer. */
uint32_t total_periods = i2c_start_bit_periods
+ (data_length + 1U) * i2c_data_byte_periods_with_ack
+ i2c_stop_bit_periods;

return (total_periods * 1000000 / i2c_speed_hz) + sw_overhead_safety_margin_time_us;
}
156 changes: 155 additions & 1 deletion subsys/mpsl/fem/nrf2220/mpsl_fem_nrf2220.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
#include <pinctrl_soc.h>
#endif

#if IS_ENABLED(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION)
#include <protocol/mpsl_fem_nrf2220_protocol_api.h>
#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>
#endif

#if !defined(CONFIG_PINCTRL)
#error "The nRF2220 driver must be used with CONFIG_PINCTRL! Set CONFIG_PINCTRL=y"
#endif
Expand Down Expand Up @@ -66,7 +71,15 @@ static void fem_nrf2220_twi_init_regs_configure(mpsl_fem_nrf2220_interface_confi

static void fem_nrf2220_twi_configure(mpsl_fem_nrf2220_interface_config_t *cfg)
{
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER(MPSL_FEM_TWI_IF);
if (IS_ENABLED(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)) {
/* The nRF2220 temperature compensation with the MPSL scheduler requires
* asynchronous interface.
* See mpsl_fem_nrf2220_temperature_changed_update_request function.
*/
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC(MPSL_FEM_TWI_IF);
} else {
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER(MPSL_FEM_TWI_IF);
}
}
#endif /* DT_NODE_HAS_PROP(DT_NODELABEL(nrf_radio_fem), twi_if) */

Expand Down Expand Up @@ -193,4 +206,145 @@ BUILD_ASSERT(CONFIG_MPSL_FEM_INIT_PRIORITY > CONFIG_I2C_INIT_PRIORITY,

SYS_INIT(mpsl_fem_init, POST_KERNEL, CONFIG_MPSL_FEM_INIT_PRIORITY);

#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION)

static K_SEM_DEFINE(fem_temperature_new_value_sem, 0, 1);

#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
static int32_t fem_temperature_update_result;
static K_SEM_DEFINE(fem_temperature_updated_cb_sem, 0, 1);

static void fem_temperature_update_cb(int32_t res)
{
fem_temperature_update_result = res;
k_sem_give(&fem_temperature_updated_cb_sem);
}

static int32_t fem_temperature_changed_update_now(void)
{
const struct device *i2c_bus_dev = DEVICE_DT_GET(DT_BUS(MPSL_FEM_TWI_IF));

/* Let's have initially "taken" semaphore that will inform the operation
* on the nrf2220 is finished.
*/
k_sem_take(&fem_temperature_updated_cb_sem, K_NO_WAIT);

(void)i2c_nrfx_twim_exclusive_access_acquire(i2c_bus_dev, K_FOREVER);

/* Temporary raise i2c_bus_dev IRQ priority, as required by the
* mpsl_fem_nrf2220_temperature_changed_update_request function.
* The IRQs from i2c may not be delayed while performing operations
* in a timeslot granted by the MPSL scheduler.
*/
z_arm_irq_priority_set(DT_IRQN(DT_BUS(MPSL_FEM_TWI_IF)), 0, IRQ_ZERO_LATENCY);

mpsl_fem_nrf2220_temperature_changed_update_request(fem_temperature_update_cb);

/* Let's wait until the operation is finished */
k_sem_take(&fem_temperature_updated_cb_sem, K_FOREVER);

/* Restore original i2c_bus_dev IRQ priority. */
z_arm_irq_priority_set(DT_IRQN(DT_BUS(MPSL_FEM_TWI_IF)),
DT_IRQ(DT_BUS(MPSL_FEM_TWI_IF), priority), 0);

i2c_nrfx_twim_exclusive_access_release(i2c_bus_dev);

return fem_temperature_update_result;
}
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_MPSL_SCHEDULER */

#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC)

#include <zephyr/drivers/sensor.h>
#define FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT \
(K_MSEC(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_POLL_PERIOD))

static int8_t fem_temperature_sensor_value_get(void)
{
const struct device *dev = DEVICE_DT_GET_ONE(nordic_nrf_temp);

if (!device_is_ready(dev)) {
__ASSERT(false, "Temperature sensor not ready");
}

struct sensor_value v;
int ret = sensor_sample_fetch(dev);

__ASSERT(ret == 0, "Can't fetch temperature sensor sample");

ret = sensor_channel_get(dev, SENSOR_CHAN_DIE_TEMP, &v);
__ASSERT(ret == 0, "Can't get temperature of the die");

(void)ret;

return v.val1;
}
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC */

#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM)

#define FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT K_FOREVER

#define NRF2220_TEMPERATURE_DEFAULT 25

static int8_t fem_temperature_value = NRF2220_TEMPERATURE_DEFAULT;

void fem_temperature_change(int8_t temperature)
{
fem_temperature_value = temperature;
k_sem_give(&fem_temperature_new_value_sem);
}

static int8_t fem_temperature_sensor_value_get(void)
{
return fem_temperature_value;
}
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM */

static void fem_temperature_compensation_thread(void *dummy1, void *dummy2, void *dummy3)
{
ARG_UNUSED(dummy1);
ARG_UNUSED(dummy2);
ARG_UNUSED(dummy3);

while (true) {
(void)k_sem_take(&fem_temperature_new_value_sem,
FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT);

int8_t new_temperature = fem_temperature_sensor_value_get();

if (mpsl_fem_nrf2220_temperature_changed(new_temperature)) {
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
int32_t res = fem_temperature_changed_update_now();

__ASSERT(res == 0, "FEM update on temperature change failed");
(void)res;
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER */
}
}
}

#define FEM_TEMPERATURE_COMPENSATION_THREAD_STACK_SIZE 512

static K_THREAD_STACK_DEFINE(fem_temperature_compensation_thread_stack,
FEM_TEMPERATURE_COMPENSATION_THREAD_STACK_SIZE);
static struct k_thread fem_temperature_compensation_thread_data;

static int fem_nrf2220_temperature_compensation_init(void)
{
(void)k_thread_create(&fem_temperature_compensation_thread_data,
fem_temperature_compensation_thread_stack,
K_THREAD_STACK_SIZEOF(fem_temperature_compensation_thread_stack),
fem_temperature_compensation_thread,
NULL, NULL, NULL,
K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);

return 0;
}

SYS_INIT(fem_nrf2220_temperature_compensation_init, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION */

#endif /* defined(CONFIG_MPSL_FEM_NRF2220) */
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ manifest:
# https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html
- name: zephyr
repo-path: sdk-zephyr
revision: 4ba36f2e6f4e9ba5384c664b398d5186c9a1252b
revision: pull/2624/head
import:
# In addition to the zephyr repository itself, NCS also
# imports the contents of zephyr/west.yml at the above
Expand Down
Loading