1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file RADIO_utils.c
5 * @author GPM WBL Application Team
6 * @brief Miscellaneous utilities for interfacing to HW.
7 ******************************************************************************
8 * @attention
9 *
10 * Copyright (c) 2024 STMicroelectronics.
11 * All rights reserved.
12 *
13 * This software is licensed under terms that can be found in the LICENSE file
14 * in the root directory of this software component.
15 * If no LICENSE file comes with this software, it is provided AS-IS.
16 *
17 ******************************************************************************
18 */
19 /* USER CODE END Header */
20
21 /* Includes ------------------------------------------------------------------*/
22 #include "RADIO_utils.h"
23 #include "stm32wb0x_ll_pwr.h"
24 #include "stm32wb0x_ll_gpio.h"
25 #include "ble.h"
26
27 /* Private typedef -----------------------------------------------------------*/
28
29 /* Private define ------------------------------------------------------------*/
30 #define TX_POWER_LEVELS (32U)
31 #if defined(STM32WB09)
32 #define HP_TX_POWER_LEVELS (TX_POWER_LEVELS+1)
33 #else
34 #define HP_TX_POWER_LEVELS (TX_POWER_LEVELS)
35 #endif
36
37 #define LOWEST_TX_POWER_LEVEL_INDEX (1U)
38 #if defined(STM32WB09)
39 #define HIGH_POWER_LEVEL_INDEX (HP_TX_POWER_LEVELS - 1)
40 #endif
41
42 /** Minimum supported TX power in dBm. */
43 #define MIN_TX_POWER_LOW (normal_pa_level_table[LOWEST_TX_POWER_LEVEL_INDEX]) /* high power mode disabled */
44 #define MIN_TX_POWER_HIGH (high_power_pa_level_table[LOWEST_TX_POWER_LEVEL_INDEX]) /* high power mode enabled */
45
46 /** Maximum supported TX power in dBm. */
47 #define MAX_TX_POWER_LOW (normal_pa_level_table[TX_POWER_LEVELS-1]) /* high power mode disabled */
48 #define MAX_TX_POWER_HIGH (high_power_pa_level_table[HP_TX_POWER_LEVELS-1]) /* high power mode enabled */
49
50 /* Parameters of the RSSI Exponential Moving Average algorithm */
51 #define MAX_RSSI_FILTER_COEFF (4U)
52 #define RSSI_EMA_SMOOTH_FACTOR_BITS (3)
53
54 #if defined(STM32WB09)
55 #define LDO_ANA_TST (*(&RRM->RXADC_ANA_USR + 0x02))
56 #define RRM_LDO_ANA_TST_RFD_LDO_TRANSFO_BYPASS_Msk 0x08
57 #define RRM_LDO_ANA_TST_LDO_ANA_TST_SEL_Msk 0x01
58 #endif
59 #define RRM_TEST (*(&RRM->RRM_CTRL + 0x01))
60
61 /* This macro can be set to avoid breaking communication by changing the function
62 of pins with aci_set_antenna_switch_parameters(). */
63 #define RESERVED_GPIOS 0x00
64
65 /* Private macro -------------------------------------------------------------*/
66 /* Private variables ---------------------------------------------------------*/
67
68 /* Expected TX output power (dBm) for each PA level when SMPS voltage is 1.4V */
69 static const int8_t normal_pa_level_table[TX_POWER_LEVELS] = {
70 -54, -21, -20, -19, -17, -16, -15, -14,
71 -13, -12, -11, -10, -9, -8, -7, -6,
72 -6, -4, -3, -3, -2, -2, -1, -1,
73 0, 0, 1, 2, 3, 4, 5, 6
74 };
75
76 #if defined(STM32WB09)
77 static const int8_t high_power_pa_level_table[HP_TX_POWER_LEVELS] = {
78 -54, -21, -20, -19, -17, -16, -15, -14,
79 -13, -12, -11, -10, -9, -8, -7, -6,
80 -6, -4, -3, -3, -2, -2, -1, -1,
81 0, 0, 1, 2, 3, 4, 5, 6,
82 8
83 };
84 #else
85 /* Expected TX output power (dBm) for each PA level when SMPS voltage is 1.9V
86 (high power mode). */
87 static const int8_t high_power_pa_level_table[HP_TX_POWER_LEVELS] = {
88 -54, -19, -18, -17, -16, -15, -14, -13,
89 -12, -11, -10, -9, -8, -7, -6, -5,
90 -4, -3, -3, -2, -1, 0, 1, 2,
91 3, 8, 8, 8, 8, 8, 8, 8
92 };
93 #endif
94
95 struct antenna_conf_s {
96 uint8_t Antenna_IDs;
97 uint8_t Antenna_ID_Shift;
98 uint8_t Default_Antenna_ID;
99 uint8_t RF_Activity_Enable;
100 }antenna_conf = {0, ANTENNA_ID_BIT_SHIFT, 0, 0};
101
102 uint8_t high_power = FALSE;
103 uint8_t tone_started = FALSE;
104 uint8_t pa_level = DEFAULT_TX_PA_LEVEL;
105
RADIO_DBmToPALevel(int8_t TX_dBm)106 uint8_t RADIO_DBmToPALevel(int8_t TX_dBm)
107 {
108 uint8_t i;
109 const int8_t *pa_level_table = high_power ? high_power_pa_level_table : normal_pa_level_table;
110 const uint8_t tx_power_levels = high_power ? HP_TX_POWER_LEVELS : TX_POWER_LEVELS;
111
112 for(i = 0; i < tx_power_levels; i++)
113 {
114 if(pa_level_table[i] >= TX_dBm)
115 break;
116 }
117 if (((pa_level_table[i] > TX_dBm) && (i > LOWEST_TX_POWER_LEVEL_INDEX)) ||
118 (i == tx_power_levels))
119 {
120 i--;
121 }
122
123 return i;
124 }
125
RADIO_DBmToPALevelGe(int8_t TX_dBm)126 uint8_t RADIO_DBmToPALevelGe(int8_t TX_dBm)
127 {
128 const int8_t *pa_level_table = high_power ? high_power_pa_level_table : normal_pa_level_table;
129 const uint8_t tx_power_levels = high_power ? HP_TX_POWER_LEVELS : TX_POWER_LEVELS;
130 uint8_t i;
131
132 for(i = LOWEST_TX_POWER_LEVEL_INDEX; i < tx_power_levels; i++)
133 {
134 if (pa_level_table[i] >= TX_dBm)
135 break;
136 }
137
138 if(i == tx_power_levels)
139 {
140 i--;
141 }
142
143 return i;
144 }
145
RADIO_PALevelToDBm(uint8_t PA_Level)146 int8_t RADIO_PALevelToDBm(uint8_t PA_Level)
147 {
148 const int8_t *pa_level_table = high_power ? high_power_pa_level_table : normal_pa_level_table;
149 const uint8_t tx_power_levels = high_power ? HP_TX_POWER_LEVELS : TX_POWER_LEVELS;
150
151 if(PA_Level < LOWEST_TX_POWER_LEVEL_INDEX || PA_Level >= tx_power_levels)
152 {
153 return 127;
154 }
155
156 return pa_level_table[PA_Level];
157 }
158
RADIO_GetMaxPALevel(void)159 uint8_t RADIO_GetMaxPALevel(void)
160 {
161 return HP_TX_POWER_LEVELS - 1;
162 }
163
RADIO_GetDefaultPALevel(void)164 uint8_t RADIO_GetDefaultPALevel(void)
165 {
166 return DEFAULT_TX_PA_LEVEL;
167 }
168
RADIO_ReadTransmitPower(int8_t * Min_Tx_Power,int8_t * Max_Tx_Power)169 void RADIO_ReadTransmitPower(int8_t *Min_Tx_Power, int8_t *Max_Tx_Power)
170 {
171 if (high_power)
172 {
173 *Min_Tx_Power = MIN_TX_POWER_HIGH;
174 *Max_Tx_Power = MAX_TX_POWER_HIGH;
175 }
176 else
177 {
178 *Min_Tx_Power = MIN_TX_POWER_LOW;
179 *Max_Tx_Power = MAX_TX_POWER_LOW;
180 }
181 }
182
RADIO_SetHighPower(FunctionalState state)183 void RADIO_SetHighPower(FunctionalState state)
184 {
185 if(state != DISABLE)
186 {
187 if(high_power == FALSE)
188 {
189 high_power = TRUE;
190 #if defined(RRM_LDO_ANA_ENG_RFD_LDO_TRANSFO_BYPASS)
191 LL_APB0_GRP1_EnableClock(LL_APB0_GRP1_PERIPH_SYSCFG);
192 LL_SYSCFG_BLERXTX_SetTrigger(LL_SYSCFG_BLERXTX_TRIGGER_BOTH_EDGE, LL_SYSCFG_BLE_TX_EVENT);
193 LL_SYSCFG_BLERXTX_SetType(LL_SYSCFG_BLERXTX_DET_TYPE_EDGE, LL_SYSCFG_BLE_TX_EVENT);
194 LL_SYSCFG_BLERXTX_EnableIT(LL_SYSCFG_BLE_TX_EVENT);
195 LL_SYSCFG_BLERXTX_ClearInterrupt(LL_SYSCFG_BLE_TX_EVENT);
196 NVIC_EnableIRQ(RADIO_TXRX_SEQ_IRQn);
197 #endif
198 }
199 }
200 else
201 {
202 if(high_power == TRUE)
203 {
204 high_power = FALSE;
205 #if defined(RRM_LDO_ANA_ENG_RFD_LDO_TRANSFO_BYPASS)
206 LL_SYSCFG_BLERXTX_DisableIT(LL_SYSCFG_BLE_TX_EVENT);
207 NVIC_DisableIRQ(RADIO_TXRX_SEQ_IRQn);
208 LL_SYSCFG_BLERXTX_ClearInterrupt(LL_SYSCFG_BLE_TX_EVENT);
209 #endif
210 }
211 }
212
213 if (LL_PWR_IsEnabledSMPSPrechargeMode() || (LL_PWR_GetSMPSMode() == LL_PWR_NO_SMPS))
214 return;
215
216 /* Bypass SMPS */
217 LL_PWR_SetSMPSPrechargeMode(LL_PWR_SMPS_PRECHARGE);
218 while(LL_PWR_IsSMPSReady());
219 /* Change level */
220 if(state != DISABLE)
221 {
222 LL_PWR_SMPS_SetOutputVoltageLevel(PWR_SMPS_OUTPUT_VOLTAGE_1V90);
223 }
224 else
225 {
226 LL_PWR_SMPS_SetOutputVoltageLevel(PWR_SMPS_OUTPUT_VOLTAGE_1V40);
227 }
228 /* Disable bypass*/
229 LL_PWR_SetSMPSPrechargeMode(LL_PWR_NO_SMPS_PRECHARGE);
230 while(!LL_PWR_IsSMPSReady());
231 }
232
HAL_RADIO_TxRxSeqCallback(void)233 void HAL_RADIO_TxRxSeqCallback(void)
234 {
235 #if defined(RRM_LDO_ANA_ENG_RFD_LDO_TRANSFO_BYPASS)
236 if(high_power == FALSE)
237 return;
238
239 if(LL_SYSCFG_BLERXTX_IsInterruptPending(LL_SYSCFG_BLE_TX_EVENT)){
240 if(RRM->FSM_STATUS_DIG_OUT & RRM_FSM_STATUS_DIG_OUT_STATUS_4)
241 {
242 // Rising edge
243 MODIFY_REG_FIELD(RRM->LDO_ANA_ENG, RRM_LDO_ANA_ENG_RFD_LDO_TRANSFO_BYPASS, 1);
244 }
245 else
246 {
247 // Falling edge
248 MODIFY_REG_FIELD(RRM->LDO_ANA_ENG, RRM_LDO_ANA_ENG_RFD_LDO_TRANSFO_BYPASS, 0);
249 }
250 LL_SYSCFG_BLERXTX_ClearInterrupt(LL_SYSCFG_BLE_TX_EVENT);
251 }
252 #endif
253 }
254
255 /* ------------ Section for tone generation functions ------------------------*/
256
RADIO_ToneStart(uint8_t RF_Channel,uint8_t Offset,uint8_t PA_Level)257 void RADIO_ToneStart(uint8_t RF_Channel, uint8_t Offset, uint8_t PA_Level)
258 {
259 uint8_t internal_pa_level = PA_Level;
260
261 /* Calculate the synt divide factor for 16 MHz quarts and +250 kHz offset wrt the channel center frequency
262 * Algorithmically MAK = F_rf*(2^20)/35 = F_rf*(2^15)
263 * With F_rf = (2402+2*RF_Channel)+0.25 MHz
264 * K corresponds to b19-b0 of MAK
265 * A corresponds to b22-b20 of MAK
266 * M corresponds to b27-b23 of MAK
267 */
268 uint32_t kHz_250_scaled = 8192; /* = 0.250*2^20/32 */
269 uint32_t MAK = ((2402UL + 2UL * RF_Channel) << 15);
270
271 if (Offset == 1U)
272 {
273 MAK += kHz_250_scaled;
274 }
275 else if (Offset == 2U)
276 {
277 MAK -= kHz_250_scaled;
278 }
279 else
280 {
281 /* MISRAC2012-Rule-15.7: explicit else; no action required */
282 }
283
284 uint8_t M = ((uint8_t)(MAK >> 23) & 0x1FU);
285 uint8_t A = ((uint8_t)(MAK >> 20) & 0x07U);
286 uint32_t K = (MAK & 0x000FFFFFUL) + 1UL;
287
288 #if defined(STM32WB09)
289 if(PA_Level == HIGH_POWER_LEVEL_INDEX)
290 {
291 internal_pa_level = HIGH_POWER_LEVEL_INDEX - 1;
292 LDO_ANA_TST = RRM_LDO_ANA_TST_RFD_LDO_TRANSFO_BYPASS_Msk|RRM_LDO_ANA_TST_LDO_ANA_TST_SEL_Msk;
293 }
294 #endif
295 RRM->RADIO_FSM_USR = ((uint32_t)internal_pa_level << 3) | RRM_RADIO_FSM_USR_EN_CALIB_SYNTH_Msk | RRM_RADIO_FSM_USR_EN_CALIB_CBP_Msk;
296 RRM->MOD3_DIG_TST = ((uint32_t)M << 3) | ((uint32_t)A & 0x7UL);
297 RRM->MOD2_DIG_TST = (K >> 12) & 0xFFU;
298 RRM->MOD1_DIG_TST = (K >> 4) & 0xFFU;
299 RRM->MOD0_DIG_TST = ((K & 0x0FU) << 4) | 0x09U;
300
301 /* Take control of the radio FSM through the test bus */
302 RRM->DTB5_DIG_ENG = 0x09;
303 RRM_TEST = 0x03;
304 RRM->DTB5_DIG_ENG = 0x39;
305 while (RRM->FSM_STATUS_DIG_OUT != 0x04UL);
306 RRM->DTB5_DIG_ENG = 0x3B;
307 }
308
RADIO_ToneStop(void)309 void RADIO_ToneStop(void)
310 {
311 /* Release control of the radio FSM through the test bus */
312 RRM->DTB5_DIG_ENG = 0x00;
313 volatile uint32_t *rrm_udra_test = &RRM->RRM_CTRL + 0x01;
314 *rrm_udra_test = 0x00;
315 RRM->MOD0_DIG_TST = 0;
316 #if defined(STM32WB09)
317 LDO_ANA_TST = 0;
318 #endif
319 }
320
321 /* Review with the use of linear values instead of dBm values to have more precision */
322 static const int8_t rssi_ema_smoothing_factor_table[MAX_RSSI_FILTER_COEFF + 1] = {
323 7, 5, 3, 2, 1
324 };
325
RADIO_UpdateAvgRSSI(int8_t avg_rssi,int8_t rssi,uint8_t rssi_filter_coeff)326 int8_t RADIO_UpdateAvgRSSI(int8_t avg_rssi, int8_t rssi, uint8_t rssi_filter_coeff)
327 {
328 if (avg_rssi == RSSI_INVALID)
329 {
330 return rssi;
331 }
332
333 if ((rssi == RSSI_INVALID) || (rssi_filter_coeff > MAX_RSSI_FILTER_COEFF))
334 {
335 return avg_rssi;
336 }
337
338 return (avg_rssi +
339 (((rssi - avg_rssi) * rssi_ema_smoothing_factor_table[rssi_filter_coeff])
340 >> RSSI_EMA_SMOOTH_FACTOR_BITS));
341 }
342
343 /* ---------- Utility functions for antenna switching ------------------------*/
344
RADIO_AntIdxRemap(uint8_t AntPattLen,uint8_t * pAntRamTable,const uint8_t * pAntPatt)345 void RADIO_AntIdxRemap(uint8_t AntPattLen, uint8_t *pAntRamTable, const uint8_t* pAntPatt)
346 {
347 #if defined(STM32WB07) || defined(STM32WB06) || defined(STM32WB09)
348 for (uint8_t i=0; i<AntPattLen; i++)
349 {
350 pAntRamTable[i] = (pAntPatt[i] << antenna_conf.Antenna_ID_Shift);
351 }
352 #endif
353 }
354
355 #if defined(STM32WB05) || defined(STM32WB09)
aci_hal_set_antenna_switch_parameters(uint8_t Antenna_IDs,uint8_t Antenna_ID_Shift,uint8_t Default_Antenna_ID,uint8_t RF_Activity_Enable)356 tBleStatus aci_hal_set_antenna_switch_parameters(uint8_t Antenna_IDs,
357 uint8_t Antenna_ID_Shift,
358 uint8_t Default_Antenna_ID,
359 uint8_t RF_Activity_Enable)
360 {
361 GPIO_InitTypeDef GPIO_Init = {
362 .Pin = Antenna_IDs, // With this we assume that Antenna_IDs bitmask is equal to the pin bitmask.
363 .Mode = GPIO_MODE_AF_PP,
364 .Pull = GPIO_NOPULL,
365 .Speed = GPIO_SPEED_FREQ_VERY_HIGH,
366 .Alternate = LL_GPIO_AF_6
367 };
368
369 if(Antenna_IDs > 0x7F || Antenna_IDs & RESERVED_GPIOS || Antenna_ID_Shift > 7 ||
370 Default_Antenna_ID > 0x7F || RF_Activity_Enable > 1)
371 {
372 return BLE_ERROR_INVALID_HCI_CMD_PARAMS;
373 }
374
375 antenna_conf.Antenna_IDs = Antenna_IDs;
376 antenna_conf.Antenna_ID_Shift = Antenna_ID_Shift;
377 antenna_conf.Default_Antenna_ID = Default_Antenna_ID;
378 antenna_conf.RF_Activity_Enable = RF_Activity_Enable;
379
380 LL_RADIO_SetDefaultAntennaID(Default_Antenna_ID);
381
382 if(RF_Activity_Enable)
383 {
384 GPIO_Init.Pin |= GPIO_PIN_7;
385 }
386
387 HAL_GPIO_Init(GPIOB, &GPIO_Init);
388 HAL_GPIO_WritePin(GPIOB, Antenna_IDs, GPIO_PIN_RESET);
389
390 return BLE_STATUS_SUCCESS;
391 }
392 #else
aci_hal_set_antenna_switch_parameters(uint8_t Antenna_IDs,uint8_t Antenna_ID_Shift,uint8_t Default_Antenna_ID,uint8_t RF_Activity_Enable)393 tBleStatus aci_hal_set_antenna_switch_parameters(uint8_t Antenna_IDs,
394 uint8_t Antenna_ID_Shift,
395 uint8_t Default_Antenna_ID,
396 uint8_t RF_Activity_Enable)
397 {
398 return BLE_ERROR_UNKNOWN_HCI_COMMAND;
399 }
400 #endif
401
aci_hal_set_tx_power_level_preprocess(uint8_t En_High_Power,uint8_t PA_Level)402 tBleStatus aci_hal_set_tx_power_level_preprocess(uint8_t En_High_Power,
403 uint8_t PA_Level)
404 {
405 if((PA_Level > RADIO_GetMaxPALevel()) || (En_High_Power > 1))
406 {
407 return BLE_ERROR_INVALID_HCI_CMD_PARAMS;
408 }
409
410 pa_level = PA_Level;
411
412 return BLE_STATUS_SUCCESS;
413 }
414
415 /* ---------- Utility functions for star ttone ------------------------*/
416
LL_busy(void)417 static bool LL_busy(void)
418 {
419 uint8_t n_banks = ((CFG_BLE_NUM_RADIO_TASKS-1)/8+1);
420 uint8_t link_status[8];
421 uint16_t link_connection_handles[8];
422
423 for(int i = 0; i < n_banks; i++)
424 {
425 aci_hal_get_link_status(i, link_status, link_connection_handles);
426
427 for(int j = 0; j < 8; j++)
428 {
429 if(link_status[j] != LL_IDLE)
430 {
431 return TRUE;
432 }
433 }
434 }
435
436 return FALSE;
437 }
438
aci_hal_tone_start(uint8_t RF_Channel,uint8_t Offset)439 tBleStatus aci_hal_tone_start(uint8_t RF_Channel, uint8_t Offset)
440 {
441 tBleStatus status;
442
443 if (tone_started) /* already started before */
444 {
445 status = BLE_ERROR_COMMAND_DISALLOWED;
446 }
447 else
448 {
449 if ((RF_Channel >= 40U) || (Offset > 2U))
450 { /* channel ID must be from 0-39 */
451 status = BLE_ERROR_INVALID_HCI_CMD_PARAMS;
452 }
453 else
454 {
455 if (LL_busy() == FALSE)
456 {
457
458 #if defined(STM32WB07) || defined(STM32WB06) || defined(STM32WB09)
459 /* Set GPIOs for antenna switch in output mode. */
460 GPIO_InitTypeDef GPIO_Init = {
461 .Pin = antenna_conf.Antenna_IDs,
462 .Mode = GPIO_MODE_OUTPUT_PP,
463 .Pull = GPIO_NOPULL,
464 .Speed = GPIO_SPEED_FREQ_LOW,
465 };
466
467 if(antenna_conf.RF_Activity_Enable)
468 {
469 GPIO_Init.Pin |= GPIO_PIN_7;
470 }
471
472 HAL_GPIO_Init(GPIOB, &GPIO_Init);
473
474 HAL_GPIO_WritePin(GPIOB, antenna_conf.Antenna_IDs, GPIO_PIN_RESET);
475 HAL_GPIO_WritePin(GPIOB, antenna_conf.Default_Antenna_ID & antenna_conf.Antenna_IDs, GPIO_PIN_SET);
476
477 if(antenna_conf.RF_Activity_Enable)
478 {
479 /* Drive RF activity pin to enable the antenna switch. */
480 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
481 }
482 #endif
483
484 RADIO_ToneStart(RF_Channel, Offset, pa_level);
485 tone_started = TRUE;
486
487 status = BLE_STATUS_SUCCESS;
488 }else
489 {
490 status = BLE_ERROR_COMMAND_DISALLOWED;
491 }
492 }
493 }
494
495 return status;
496 }
497
aci_hal_tone_stop()498 tBleStatus aci_hal_tone_stop()
499 {
500 tBleStatus status;
501
502 if (tone_started)
503 {
504 RADIO_ToneStop();
505 tone_started = FALSE;
506
507 #if defined(STM32WB07) || defined(STM32WB06) || defined(STM32WB09)
508 aci_hal_set_antenna_switch_parameters(antenna_conf.Antenna_IDs,
509 antenna_conf.Antenna_ID_Shift,
510 antenna_conf.Default_Antenna_ID,
511 antenna_conf.RF_Activity_Enable);
512 #endif
513 status = BLE_STATUS_SUCCESS;
514 }
515 else
516 {
517 status = BLE_ERROR_COMMAND_DISALLOWED;
518 }
519
520 return status;
521 }
522