Skip to content

Commit a1caece

Browse files
ankunsnordicjm
authored andcommitted
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 8436408 commit a1caece

File tree

6 files changed

+405
-28
lines changed

6 files changed

+405
-28
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: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,43 @@
99

1010
#include <zephyr/devicetree.h>
1111
#include <mpsl_fem_nrf22xx_twi_config_common.h>
12+
#include <nrfx_twim.h>
1213

13-
/**
14-
* @brief Initializer of structure @c mpsl_fem_twi_if_t allowing to use TWI interface.
15-
*
16-
* @param node_id Devicetree node identifier for the TWI part for the FEM device.
17-
*/
18-
#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, \
14+
/** @brief Structure representing an I2C bus driver for a Front-End Module. */
15+
typedef struct {
16+
const struct device *dev;
17+
nrfx_twim_evt_handler_t nrfx_twim_callback_saved;
18+
void *nrfx_twim_callback_ctx_saved;
19+
mpsl_fem_twi_async_xfer_write_cb_t fem_twi_async_xfwr_write_cb;
20+
void *fem_twi_async_xfwr_write_cb_ctx;
21+
} mpsl_fem_twi_drv_t;
22+
23+
#define MPSL_FEM_TWI_DRV_INITIALIZER(node_id) \
24+
(mpsl_fem_twi_drv_t){ \
25+
.dev = DEVICE_DT_GET(DT_BUS(node_id)), \
2526
}
2627

27-
int32_t mpsl_fem_twi_drv_impl_xfer_read(void *p_instance, uint8_t slave_address,
28-
uint8_t internal_address, uint8_t *p_data, uint8_t data_length);
28+
/** @brief Prepare the FEM TWI interface structure.
29+
*
30+
* @note This function prepares just synchronous part of the interface.
31+
* If Front-End Module driver requires an asynchronous part of the interface call
32+
* the @ref mpsl_fem_twi_drv_fem_twi_if_prepare_add_async function afterwards.
33+
*
34+
* @param drv Pointer to the FEM TWI driver instance object initialized with
35+
* MPSL_FEM_TWI_DRV_INITIALIZER.
36+
* @param twi_if Pointer to the FEM TWI interface structure to fill.
37+
* @param address The address of the FEM device on the TWI bus.
38+
*/
39+
void mpsl_fem_twi_drv_fem_twi_if_prepare(mpsl_fem_twi_drv_t *drv, mpsl_fem_twi_if_t *twi_if,
40+
uint8_t address);
2941

30-
int32_t mpsl_fem_twi_drv_impl_xfer_write(void *p_instance, uint8_t slave_address,
31-
uint8_t internal_address, const uint8_t *p_data, uint8_t data_length);
42+
/** @brief Prepare the asynchronous part of the FEM TWI interface structure.
43+
*
44+
* @note Assumes that the @ref mpsl_fem_twi_drv_fem_twi_if_prepare function
45+
* has already been called.
46+
*
47+
* @param twi_if Pointer to the interface structure to fill.
48+
*/
49+
void mpsl_fem_twi_drv_fem_twi_if_prepare_add_async(mpsl_fem_twi_if_t *twi_if);
3250

3351
#endif /* MPSL_FEM_TWI_DRV__ */

subsys/mpsl/fem/common/mpsl_fem_twi_drv.c

Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,168 @@
66

77
#include <mpsl_fem_twi_drv.h>
88
#include <zephyr/drivers/i2c.h>
9+
#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>
10+
#include <../drivers/i2c/i2c_nrfx_twim_common.h>
911

12+
static int32_t mpsl_fem_twi_drv_impl_xfer_read(void *p_instance, uint8_t slave_address,
13+
uint8_t internal_address, uint8_t *p_data,
14+
uint8_t data_length)
15+
{
16+
mpsl_fem_twi_drv_t *drv = (mpsl_fem_twi_drv_t *)p_instance;
17+
18+
return i2c_burst_read(drv->dev, slave_address, internal_address, p_data, data_length);
19+
}
20+
21+
static int32_t mpsl_fem_twi_drv_impl_xfer_write(void *p_instance, uint8_t slave_address,
22+
uint8_t internal_address, const uint8_t *p_data,
23+
uint8_t data_length)
24+
{
25+
mpsl_fem_twi_drv_t *drv = (mpsl_fem_twi_drv_t *)p_instance;
26+
27+
return i2c_burst_write(drv->dev, slave_address, internal_address, p_data, data_length);
28+
}
29+
30+
static inline void mpsl_fem_twi_drv_nrfx_twim_callback_replace(mpsl_fem_twi_drv_t *drv,
31+
nrfx_twim_evt_handler_t callback)
32+
{
33+
const struct i2c_nrfx_twim_common_config *config = drv->dev->config;
34+
nrfx_err_t err;
35+
36+
nrfx_twim_callback_get(&config->twim, &drv->nrfx_twim_callback_saved,
37+
&drv->nrfx_twim_callback_ctx_saved);
38+
39+
err = nrfx_twim_callback_set(&config->twim, callback, drv);
40+
41+
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
42+
(void)err;
43+
}
44+
45+
static inline void mpsl_fem_twi_drv_nrfx_twim_callback_restore(mpsl_fem_twi_drv_t *drv)
46+
{
47+
const struct i2c_nrfx_twim_common_config *config = drv->dev->config;
48+
nrfx_err_t err;
49+
50+
err = nrfx_twim_callback_set(&config->twim, drv->nrfx_twim_callback_saved,
51+
drv->nrfx_twim_callback_ctx_saved);
52+
53+
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
54+
(void)err;
55+
}
56+
57+
static void mpsl_fem_twi_drv_nrfx_twim_evt_handler(nrfx_twim_evt_t const *p_event, void *p_context)
58+
{
59+
mpsl_fem_twi_drv_t *drv = (mpsl_fem_twi_drv_t *)p_context;
60+
int32_t res = 0;
61+
62+
mpsl_fem_twi_drv_nrfx_twim_callback_restore(drv);
1063

11-
int32_t mpsl_fem_twi_drv_impl_xfer_read(void *p_instance, uint8_t slave_address,
12-
uint8_t internal_address, uint8_t *p_data, uint8_t data_length)
64+
if (p_event->type != NRFX_TWIM_EVT_DONE) {
65+
res = -EIO;
66+
}
67+
68+
/* Call the callback which was passed to the mpsl_fem_twi_drv_impl_xfer_write_async call,
69+
* that started the transfer.
70+
*/
71+
drv->fem_twi_async_xfwr_write_cb(drv, res, drv->fem_twi_async_xfwr_write_cb_ctx);
72+
}
73+
74+
static int32_t mpsl_fem_twi_drv_impl_xfer_write_async(void *p_instance, uint8_t slave_address,
75+
const uint8_t *p_data, uint8_t data_length,
76+
mpsl_fem_twi_async_xfer_write_cb_t p_callback,
77+
void *p_context)
1378
{
14-
const struct device *dev = (const struct device *)p_instance;
79+
mpsl_fem_twi_drv_t *drv = (mpsl_fem_twi_drv_t *)p_instance;
80+
const struct i2c_nrfx_twim_common_config *config = drv->dev->config;
81+
82+
/* At this moment the exclusive access to the drv->dev should have been already acquired.
83+
* No ongoing twi transfers are expected. Because of that it is safe to replace
84+
* original event handler of the TWIM with custom one, perform twim transfer and then
85+
* restore the original event handler.
86+
*/
87+
88+
nrfx_twim_xfer_desc_t cur_xfer = {
89+
.address = slave_address,
90+
.type = NRFX_TWIM_XFER_TX,
91+
.p_primary_buf = (uint8_t *)p_data,
92+
.primary_length = data_length,
93+
};
94+
nrfx_err_t err;
95+
int32_t ret = 0;
96+
97+
drv->fem_twi_async_xfwr_write_cb = p_callback;
98+
drv->fem_twi_async_xfwr_write_cb_ctx = p_context;
1599

16-
return i2c_burst_read(dev, slave_address, internal_address, p_data, data_length);
100+
mpsl_fem_twi_drv_nrfx_twim_callback_replace(drv, mpsl_fem_twi_drv_nrfx_twim_evt_handler);
101+
102+
err = nrfx_twim_xfer(&config->twim, &cur_xfer, 0);
103+
104+
if (err != NRFX_SUCCESS) {
105+
mpsl_fem_twi_drv_nrfx_twim_callback_restore(drv);
106+
if (err == NRFX_ERROR_BUSY) {
107+
ret = -EBUSY;
108+
} else {
109+
ret = -EIO;
110+
}
111+
}
112+
113+
return ret;
17114
}
18115

19-
int32_t mpsl_fem_twi_drv_impl_xfer_write(void *p_instance, uint8_t slave_address,
20-
uint8_t internal_address, const uint8_t *p_data, uint8_t data_length)
116+
static uint32_t mpsl_fem_twi_drv_frequency_hz_get(mpsl_fem_twi_drv_t *drv)
21117
{
22-
const struct device *dev = (const struct device *)p_instance;
118+
const struct i2c_nrfx_twim_common_config *config = drv->dev->config;
119+
120+
switch (config->twim_config.frequency) {
121+
case NRF_TWIM_FREQ_100K:
122+
return 100000;
123+
case NRF_TWIM_FREQ_250K:
124+
return 250000;
125+
case NRF_TWIM_FREQ_400K:
126+
return 400000;
127+
#if NRF_TWIM_HAS_1000_KHZ_FREQ
128+
case NRF_TWIM_FREQ_1000K:
129+
return 1000000;
130+
#endif
131+
default:
132+
__ASSERT_NO_MSG(false);
133+
return 100000;
134+
}
135+
}
23136

24-
return i2c_burst_write(dev, slave_address, internal_address, p_data, data_length);
137+
static uint32_t mpsl_fem_twi_drv_impl_xfer_write_async_time_get(void *p_instance,
138+
uint8_t data_length)
139+
{
140+
mpsl_fem_twi_drv_t *drv = (mpsl_fem_twi_drv_t *)p_instance;
141+
142+
static const uint32_t sw_overhead_safety_margin_time_us = 10U;
143+
/* Note: on nRF5 devices the first bit of each data octet is delayed by one period,
144+
* thus +1 below.
145+
*/
146+
static const uint32_t i2c_data_byte_sck_periods_with_ack = 1U + 8U + 1U;
147+
static const uint32_t i2c_start_bit_sck_periods = 2U;
148+
static const uint32_t i2c_stop_bit_sck_periods = 2U;
149+
uint32_t i2c_sck_frequency_hz = mpsl_fem_twi_drv_frequency_hz_get(drv);
150+
151+
/* Total number of sck periods needed to perform a write transfer. */
152+
uint32_t total_periods = i2c_start_bit_sck_periods
153+
+ (data_length + 1U) * i2c_data_byte_sck_periods_with_ack
154+
+ i2c_stop_bit_sck_periods;
155+
156+
return (total_periods * 1000000 / i2c_sck_frequency_hz) + sw_overhead_safety_margin_time_us;
157+
}
158+
159+
void mpsl_fem_twi_drv_fem_twi_if_prepare(mpsl_fem_twi_drv_t *drv, mpsl_fem_twi_if_t *twi_if,
160+
uint8_t address)
161+
{
162+
twi_if->enabled = true;
163+
twi_if->slave_address = address;
164+
twi_if->p_instance = (void *)drv;
165+
twi_if->p_xfer_read = mpsl_fem_twi_drv_impl_xfer_read;
166+
twi_if->p_xfer_write = mpsl_fem_twi_drv_impl_xfer_write;
167+
}
168+
169+
void mpsl_fem_twi_drv_fem_twi_if_prepare_add_async(mpsl_fem_twi_if_t *twi_if)
170+
{
171+
twi_if->p_xfer_write_async = mpsl_fem_twi_drv_impl_xfer_write_async;
172+
twi_if->p_xfer_write_async_time_get = mpsl_fem_twi_drv_impl_xfer_write_async_time_get;
25173
}

0 commit comments

Comments
 (0)