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