Skip to content

Commit 939bf1a

Browse files
committed
mpsl: fem: nrf2220: add temperature compensation
For the nRF2220 the temperature compensation feature is added. It allows to achieve better output power accuracy among the whole supported temperature range. The feature is enabled by the MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION Kconfig option. It relies on functions provided by MPSL in the sdk-nrfxlib. The temperature source can be taken from the SoC or can be set by custom implementation. Signed-off-by: Andrzej Kuros <[email protected]>
1 parent 8967257 commit 939bf1a

File tree

5 files changed

+291
-9
lines changed

5 files changed

+291
-9
lines changed

doc/nrf/app_dev/device_guides/fem/fem_nrf2220.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ To use nRF2220, complete the following steps:
2222
};
2323
};
2424
25+
Additionally, you can consider setting the :kconfig:option:`CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION` Kconfig option.
26+
2527
#. Optionally replace the device name ``name_of_fem_node``.
2628
#. Replace the pin numbers provided for each of the required properties:
2729

subsys/mpsl/fem/Kconfig

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,56 @@ config MPSL_FEM_USE_TWI_DRV
288288
select I2C
289289
default y
290290

291+
config MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
292+
bool "Temperature compensation of the nRF2220 Front-End Module [EXPERIMENTAL]"
293+
depends on MPSL_FEM_NRF2220 && MPSL_FEM_USE_TWI_DRV
294+
select EXPERIMENTAL
295+
help
296+
This allows to achieve better accuracy in output power among all
297+
temperature ranges for the nRF2220 Front-End modules.
298+
299+
config MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER
300+
bool
301+
depends on MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
302+
default y if MPSL && !MPSL_FEM_ONLY
303+
help
304+
Enable this option when using protocols relying on the MPSL scheduler.
305+
After it turns out that for the current temperature some registers
306+
of the nRF2220 must be written, the writes are performed within a timeslot
307+
negotiated with the MPSL scheduler. For operation with Bluetooth, the timeslot
308+
for the nRF2220 register update is between other Bluetooth timeslots.
309+
For operation with the nRF 802.15.4 Radio Driver, the grant for the timeslot
310+
for the nRF2220 register update causes short inability to transmit or receive.
311+
312+
choice MPSL_FEM_NRF2220_TEMPERATURE_SOURCE
313+
prompt "Source of temperature measurement of the nRF2220"
314+
depends on MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION
315+
default MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC
316+
317+
config MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC
318+
bool "Temperature of the SoC as the temperature of the nRF2220"
319+
select SENSOR
320+
help
321+
The temperature of the SoC controlling the nRF2220 Front-End Module
322+
is taken as an input for the temperature compensation.
323+
Use this option if there is good thermal coupling between the
324+
temperature of the SoC and the nRF2220 Front-End Module.
325+
326+
config MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM
327+
bool "Custom nRF2220 temperature measurement provider"
328+
help
329+
Use this option if you have a custom temperature sensor measuring the
330+
temperature of the nRF2220 device. Your custom code must call
331+
the fem_temperature_change() function to notify the module handling the FEM
332+
that the new temperature value is to be processed.
333+
334+
endchoice # MPSL_FEM_NRF2220_TEMPERATURE_SOURCE
335+
336+
config MPSL_FEM_NRF2220_TEMPERATURE_POLL_PERIOD
337+
int "Temperature measurement poll period (in ms) for temperature compensation of the nRF2220"
338+
depends on MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC
339+
default 5000
340+
291341
config MPSL_FEM_INIT_PRIORITY
292342
int "Init priority of the Front-End Module support code"
293343
depends on MPSL_FEM

subsys/mpsl/fem/common/include/mpsl_fem_twi_drv.h

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,63 @@
1010
#include <zephyr/devicetree.h>
1111
#include <mpsl_fem_nrf22xx_twi_config_common.h>
1212

13+
#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
14+
(mpsl_fem_twi_if_t){ \
15+
.enabled = true, \
16+
.slave_address = ((uint8_t)DT_REG_ADDR(node_id)), \
17+
.p_instance = (void *)DEVICE_DT_GET(DT_BUS(node_id)),
18+
19+
#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
20+
.p_xfer_read = mpsl_fem_twi_drv_impl_xfer_read, \
21+
.p_xfer_write = mpsl_fem_twi_drv_impl_xfer_write,
22+
23+
#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS_ASYNC \
24+
.p_xfer_write_async = mpsl_fem_twi_drv_impl_xfer_write_async, \
25+
.p_xfer_write_async_time_get = mpsl_fem_twi_drv_impl_xfer_write_async_time_get,
26+
27+
#define MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END \
28+
}
29+
1330
/**
1431
* @brief Initializer of structure @c mpsl_fem_twi_if_t allowing to use TWI interface.
1532
*
33+
* @note Initializes only the part related to the synchonous interface for boot-time
34+
* initialization of the FEM.
35+
*
36+
* @sa @ref @ref MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC
37+
*
1638
* @param node_id Devicetree node identifier for the TWI part for the FEM device.
1739
*/
1840
#define MPSL_FEM_TWI_DRV_IF_INITIALIZER(node_id) \
19-
(mpsl_fem_twi_if_t){ \
20-
.enabled = true, \
21-
.slave_address = ((uint8_t)DT_REG_ADDR(node_id)), \
22-
.p_instance = (void *)DEVICE_DT_GET(DT_BUS(node_id)), \
23-
.p_xfer_read = mpsl_fem_twi_drv_impl_xfer_read, \
24-
.p_xfer_write = mpsl_fem_twi_drv_impl_xfer_write, \
25-
}
41+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
42+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
43+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END
44+
45+
/**
46+
* @brief Initializer of structure @c mpsl_fem_twi_if_t allowing to use TWI interface
47+
* with asynchronous extension.
48+
*
49+
* @sa @ref @ref MPSL_FEM_TWI_DRV_IF_INITIALIZER
50+
*
51+
* @param node_id Devicetree node identifier for the TWI part for the FEM device.
52+
*/
53+
#define MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC(node_id) \
54+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_BEGIN(node_id) \
55+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS \
56+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_XFERS_ASYNC \
57+
MPSL_FEM_TWI_DRV_IF_PARTIAL_INITIALIZER_END
58+
2659

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

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

66+
int32_t mpsl_fem_twi_drv_impl_xfer_write_async(void *p_instance, uint8_t slave_address,
67+
const uint8_t *p_data, uint8_t data_length, mpsl_fem_twi_async_xfer_write_cb_t p_callback,
68+
void *p_context);
69+
70+
uint32_t mpsl_fem_twi_drv_impl_xfer_write_async_time_get(void *p_instance, uint8_t data_length);
71+
3372
#endif /* MPSL_FEM_TWI_DRV__ */

subsys/mpsl/fem/common/mpsl_fem_twi_drv.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#include <mpsl_fem_twi_drv.h>
88
#include <zephyr/drivers/i2c.h>
9-
9+
#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>
1010

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

2424
return i2c_burst_write(dev, slave_address, internal_address, p_data, data_length);
2525
}
26+
27+
int32_t mpsl_fem_twi_drv_impl_xfer_write_async(void *p_instance, uint8_t slave_address,
28+
const uint8_t *p_data, uint8_t data_length, mpsl_fem_twi_async_xfer_write_cb_t p_callback,
29+
void *p_context)
30+
{
31+
const struct device *dev = (const struct device *)p_instance;
32+
33+
struct i2c_msg msg = {
34+
.buf = (uint8_t *)p_data,
35+
.len = data_length,
36+
.flags = I2C_MSG_WRITE | I2C_MSG_STOP
37+
};
38+
39+
return i2c_nrfx_twim_async_transfer_begin(dev, &msg, slave_address,
40+
(i2c_nrfx_twim_async_transfer_handler_t)p_callback, p_context);
41+
}
42+
43+
uint32_t mpsl_fem_twi_drv_impl_xfer_write_async_time_get(void *p_instance, uint8_t data_length)
44+
{
45+
(void)p_instance;
46+
47+
static const uint32_t sw_overhead_safety_margin_time_us = 10U;
48+
/* Note: on nRF devices the first bit of each data octet is delayed by one period,
49+
* thus +1 below.
50+
*/
51+
static const uint32_t i2c_data_byte_periods_with_ack = 1U + (8U + 1U);
52+
static const uint32_t i2c_start_bit_periods = 2U;
53+
static const uint32_t i2c_stop_bit_periods = 2U;
54+
static const uint32_t i2c_speed_hz = 100000;
55+
56+
/* Total number of sck periods needed to perform a write transfer. */
57+
uint32_t total_periods = i2c_start_bit_periods
58+
+ (data_length + 1U) * i2c_data_byte_periods_with_ack
59+
+ i2c_stop_bit_periods;
60+
61+
return (total_periods * 1000000 / i2c_speed_hz) + sw_overhead_safety_margin_time_us;
62+
}

subsys/mpsl/fem/nrf2220/mpsl_fem_nrf2220.c

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
#include <pinctrl_soc.h>
3131
#endif
3232

33+
#if IS_ENABLED(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION)
34+
#include <protocol/mpsl_fem_nrf2220_protocol_api.h>
35+
#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>
36+
#endif
37+
3338
#if !defined(CONFIG_PINCTRL)
3439
#error "The nRF2220 driver must be used with CONFIG_PINCTRL! Set CONFIG_PINCTRL=y"
3540
#endif
@@ -66,7 +71,15 @@ static void fem_nrf2220_twi_init_regs_configure(mpsl_fem_nrf2220_interface_confi
6671

6772
static void fem_nrf2220_twi_configure(mpsl_fem_nrf2220_interface_config_t *cfg)
6873
{
69-
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER(MPSL_FEM_TWI_IF);
74+
if (IS_ENABLED(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)) {
75+
/* The nRF2220 temperature compensation with the MPSL scheduler requires
76+
* asynchronous interface.
77+
* See mpsl_fem_nrf2220_temperature_changed_update_request function.
78+
*/
79+
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER_WASYNC(MPSL_FEM_TWI_IF);
80+
} else {
81+
cfg->twi_if = MPSL_FEM_TWI_DRV_IF_INITIALIZER(MPSL_FEM_TWI_IF);
82+
}
7083
}
7184
#endif /* DT_NODE_HAS_PROP(DT_NODELABEL(nrf_radio_fem), twi_if) */
7285

@@ -193,4 +206,145 @@ BUILD_ASSERT(CONFIG_MPSL_FEM_INIT_PRIORITY > CONFIG_I2C_INIT_PRIORITY,
193206

194207
SYS_INIT(mpsl_fem_init, POST_KERNEL, CONFIG_MPSL_FEM_INIT_PRIORITY);
195208

209+
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION)
210+
211+
static K_SEM_DEFINE(fem_temperature_new_value_sem, 0, 1);
212+
213+
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
214+
static int32_t fem_temperature_update_result;
215+
static K_SEM_DEFINE(fem_temperature_updated_cb_sem, 0, 1);
216+
217+
static void fem_temperature_update_cb(int32_t res)
218+
{
219+
fem_temperature_update_result = res;
220+
k_sem_give(&fem_temperature_updated_cb_sem);
221+
}
222+
223+
static int32_t fem_temperature_changed_update_now(void)
224+
{
225+
const struct device *i2c_bus_dev = DEVICE_DT_GET(DT_BUS(MPSL_FEM_TWI_IF));
226+
227+
/* Let's have initially "taken" semaphore that will inform the operation
228+
* on the nrf2220 is finished.
229+
*/
230+
k_sem_take(&fem_temperature_updated_cb_sem, K_NO_WAIT);
231+
232+
(void)i2c_nrfx_twim_exclusive_access_acquire(i2c_bus_dev, K_FOREVER);
233+
234+
/* Temporary raise i2c_bus_dev IRQ priority, as required by the
235+
* mpsl_fem_nrf2220_temperature_changed_update_request function.
236+
* The IRQs from i2c may not be delayed while performing operations
237+
* in a timeslot granted by the MPSL scheduler.
238+
*/
239+
z_arm_irq_priority_set(DT_IRQN(DT_BUS(MPSL_FEM_TWI_IF)), 0, IRQ_ZERO_LATENCY);
240+
241+
mpsl_fem_nrf2220_temperature_changed_update_request(fem_temperature_update_cb);
242+
243+
/* Let's wait until the operation is finished */
244+
k_sem_take(&fem_temperature_updated_cb_sem, K_FOREVER);
245+
246+
/* Restore original i2c_bus_dev IRQ priority. */
247+
z_arm_irq_priority_set(DT_IRQN(DT_BUS(MPSL_FEM_TWI_IF)),
248+
DT_IRQ(DT_BUS(MPSL_FEM_TWI_IF), priority), 0);
249+
250+
i2c_nrfx_twim_exclusive_access_release(i2c_bus_dev);
251+
252+
return fem_temperature_update_result;
253+
}
254+
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_MPSL_SCHEDULER */
255+
256+
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC)
257+
258+
#include <zephyr/drivers/sensor.h>
259+
#define FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT \
260+
(K_MSEC(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_POLL_PERIOD))
261+
262+
static int8_t fem_temperature_sensor_value_get(void)
263+
{
264+
const struct device *dev = DEVICE_DT_GET_ONE(nordic_nrf_temp);
265+
266+
if (!device_is_ready(dev)) {
267+
__ASSERT(false, "Temperature sensor not ready");
268+
}
269+
270+
struct sensor_value v;
271+
int ret = sensor_sample_fetch(dev);
272+
273+
__ASSERT(ret == 0, "Can't fetch temperature sensor sample");
274+
275+
ret = sensor_channel_get(dev, SENSOR_CHAN_DIE_TEMP, &v);
276+
__ASSERT(ret == 0, "Can't get temperature of the die");
277+
278+
(void)ret;
279+
280+
return v.val1;
281+
}
282+
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_SOC */
283+
284+
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM)
285+
286+
#define FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT K_FOREVER
287+
288+
#define NRF2220_TEMPERATURE_DEFAULT 25
289+
290+
static int8_t fem_temperature_value = NRF2220_TEMPERATURE_DEFAULT;
291+
292+
void fem_temperature_change(int8_t temperature)
293+
{
294+
fem_temperature_value = temperature;
295+
k_sem_give(&fem_temperature_new_value_sem);
296+
}
297+
298+
static int8_t fem_temperature_sensor_value_get(void)
299+
{
300+
return fem_temperature_value;
301+
}
302+
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_SOURCE_CUSTOM */
303+
304+
static void fem_temperature_compensation_thread(void *dummy1, void *dummy2, void *dummy3)
305+
{
306+
ARG_UNUSED(dummy1);
307+
ARG_UNUSED(dummy2);
308+
ARG_UNUSED(dummy3);
309+
310+
while (true) {
311+
(void)k_sem_take(&fem_temperature_new_value_sem,
312+
FEM_TEMPERATURE_NEW_VALUE_SEM_TIMEOUT);
313+
314+
int8_t new_temperature = fem_temperature_sensor_value_get();
315+
316+
if (mpsl_fem_nrf2220_temperature_changed(new_temperature)) {
317+
#if defined(CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER)
318+
int32_t res = fem_temperature_changed_update_now();
319+
320+
__ASSERT(res == 0, "FEM update on temperature change failed");
321+
(void)res;
322+
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION_WITH_MPSL_SCHEDULER */
323+
}
324+
}
325+
}
326+
327+
#define FEM_TEMPERATURE_COMPENSATION_THREAD_STACK_SIZE 512
328+
329+
static K_THREAD_STACK_DEFINE(fem_temperature_compensation_thread_stack,
330+
FEM_TEMPERATURE_COMPENSATION_THREAD_STACK_SIZE);
331+
static struct k_thread fem_temperature_compensation_thread_data;
332+
333+
static int fem_nrf2220_temperature_compensation_init(void)
334+
{
335+
(void)k_thread_create(&fem_temperature_compensation_thread_data,
336+
fem_temperature_compensation_thread_stack,
337+
K_THREAD_STACK_SIZEOF(fem_temperature_compensation_thread_stack),
338+
fem_temperature_compensation_thread,
339+
NULL, NULL, NULL,
340+
K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
341+
342+
return 0;
343+
}
344+
345+
SYS_INIT(fem_nrf2220_temperature_compensation_init, APPLICATION,
346+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
347+
348+
#endif /* CONFIG_MPSL_FEM_NRF2220_TEMPERATURE_COMPENSATION */
349+
196350
#endif /* defined(CONFIG_MPSL_FEM_NRF2220) */

0 commit comments

Comments
 (0)