cancel
Showing results for 
Search instead for 
Did you mean: 

STM32WLE SubGHz without Middleware: Using LL or HAL Functions like SUBGHZSPI_Transmit

benultima
Associate

Hi everyone,

I am working with the STM32WLE and would like to use the internal SubGHz module directly without relying on any middleware, such as LoRaWAN. My goal is to control the SubGHz module in a simple way, similar to an external RF module like the SX1276 or RFM69.

Goals:

  1. Initialize the SubGHz module manually.
  2. Transmit and receive data using low-level functions such as SUBGHZSPI_Transmit or HAL_SUBGHZ_ExecSetCmd.
  3. Use the SubGHz module with LL (Low Layer) or HAL functions only, without middleware or additional libraries.

Questions:

  1. How can I correctly initialize the SubGHz module to use it for basic RF communication, such as FSK?
  2. Is there a way to create a lightweight API that uses only HAL or LL for SubGHz communication?
  3. Are there examples or documentation available for configuring and using the SubGHz module as a simple RF transceiver, including setting frequency, configuring registers, and handling RX/TX?

What I’m Looking For: I want to operate the SubGHz module as if it were a standalone RF chip, like the SX126x or RFM69, with basic control over SPI or equivalent HAL/LL functions.

If anyone has experience with this or knows how to implement such functionality, I would appreciate your guidance.

Thanks in advance for your help! EDIT: I don’t care about the RF switches for now; I’ll handle them later once the basics are working. I’m using a custom board and want to avoid using the BSP. Ideally, I’d like everything in main.c with short, straightforward code, without unnecessary abstraction or additional layers—if that’s possible.

Best regards,

3 REPLIES 3
STTwo-32
ST Employee

Hello @benultima and welcome to the ST Community.

For such an implementation, I suggest you refer to the chapter 4 of the RM0461 specially the paragraph 4.9. There is a full description for both transmission and reception of data throw the STM32WLE. You can also inspire from the SUBGHZ_Tx_Mode

Best Regards.

STTwo-32

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

benultima
Associate

Thank you for your reply, but I'm not able to get it to work. I'm trying the example, added all the necessary files from the Nucleo BSP library, and included some UART debug information. I can set it into standby mode, but I cannot manage to transmit using the example. I'm trying to program the LoRa-E5-HF module; maybe that's the problem, but I cannot identify the issue.

this is the debug output
=== Start Main ===
[Radio] Setting Sleep Mode.
[Radio] Setting Standby Mode.
[Radio] Retrieving Status.
[Radio] Radio is in Standby RC Mode.
[Radio] Setting TX Mode.
[Radio] Retrieving TX Status.
[Error] Radio is not in TX Mode.

#include "main.h"
#include <stdio.h>
#include <stdarg.h>

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define RADIO_MODE_STANDBY_RC        0x02
#define RADIO_MODE_TX                0x06
#define RADIO_COMMAND_TX_DONE        0x06

#define RADIO_MODE_BITFIELD          0x70
#define RADIO_STATUS_BITFIELD        0x0E
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SUBGHZ_HandleTypeDef hsubghz;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
uint8_t RadioCmd[3] = {0x00, 0x00, 0x00};
uint8_t RadioResult = 0x00;
uint8_t RadioParam  = 0x00;
uint8_t RadioMode   = 0x00;
uint8_t RadioStatus = 0x00;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_SUBGHZ_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Debug_Printf(const char *fmt, ...)
{
    char buf[128];
    va_list args;
    va_start(args, fmt);
    int len = vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    if (len > 0) {
        HAL_UART_Transmit(&huart1, (uint8_t *)buf, len, HAL_MAX_DELAY);
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* Configure LED2 & LED3 */
  BSP_LED_Init(LED2);
  BSP_LED_Init(LED3);

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_USART1_UART_Init();
  MX_SUBGHZ_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE BEGIN 2 */

  // Debug-Ausgabe
  Debug_Printf("\r\n=== Start Main ===\r\n");

  /*## 1 - Wakeup the SUBGHZ Radio ###########################################*/
  /* Set Sleep Mode */
  Debug_Printf("[Radio] Setting Sleep Mode.\r\n");
  if (HAL_SUBGHZ_ExecSetCmd(&hsubghz, RADIO_SET_SLEEP, &RadioParam, 1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Set Standby Mode */
  Debug_Printf("[Radio] Setting Standby Mode.\r\n");
  if (HAL_SUBGHZ_ExecSetCmd(&hsubghz, RADIO_SET_STANDBY, &RadioParam, 1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Retrieve Status from SUBGHZ Radio */
  Debug_Printf("[Radio] Retrieving Status.\r\n");
  if (HAL_SUBGHZ_ExecGetCmd(&hsubghz, RADIO_GET_STATUS, &RadioResult, 1) != HAL_OK)
  {
    Error_Handler();
  }
  else
  {
    /* Format Mode and Status receive from SUBGHZ Radio */
    RadioMode   = ((RadioResult & RADIO_MODE_BITFIELD) >> 4);

    /* Check if SUBGHZ Radio is in RADIO_MODE_STANDBY_RC mode */
    if(RadioMode != RADIO_MODE_STANDBY_RC)
    {
      Error_Handler();
    }
    else
    {
      Debug_Printf("[Radio] Radio is in Standby RC Mode.\r\n");
    }
  }

  /*## 2 - Set a TX on SUBGHZ Radio side #####################################*/
  /* Set Tx Mode. RadioCmd = 0x00 Timeout deactivated */
  Debug_Printf("[Radio] Setting TX Mode.\r\n");
  if (HAL_SUBGHZ_ExecSetCmd(&hsubghz, RADIO_SET_TX, RadioCmd, 3) != HAL_OK)
  {
    Error_Handler();
  }

  /*## 3 - Get TX status from SUBGHZ Radio side ##############################*/
  /* Check that TX is well ongoing (RADIO_MODE_TX), wait end of transfer */

  /* Reset RadioResult */
  RadioResult = 0x00;

   /* Retrieve Status from SUBGHZ Radio */
  Debug_Printf("[Radio] Retrieving TX Status.\r\n");
  if (HAL_SUBGHZ_ExecGetCmd(&hsubghz, RADIO_GET_STATUS, &RadioResult, 1) != HAL_OK)
  {
    Error_Handler();
  }

  /* Format Mode and Status receive from SUBGHZ Radio */

  RadioMode   = ((RadioResult & RADIO_MODE_BITFIELD) >> 4);
  RadioStatus = ((RadioResult & RADIO_STATUS_BITFIELD) >> 1);

  if (RadioMode == RADIO_MODE_TX)
  {
    Debug_Printf("[Radio] TX Mode active. Waiting for transfer to complete.\r\n");
    /* Wait end of transfer. SUBGHZ Radio go in Standby Mode */
    do
    {
      /* Reset RadioResult */
      RadioResult = 0x00;

      /* Retrieve Status from SUBGHZ Radio */
      if (HAL_SUBGHZ_ExecGetCmd(&hsubghz, RADIO_GET_STATUS, &RadioResult, 1) != HAL_OK)
      {
        Error_Handler();
      }

      /* Format Mode and Status receive from SUBGHZ Radio */
      RadioMode   = ((RadioResult & RADIO_MODE_BITFIELD) >> 4);
      RadioStatus = ((RadioResult & RADIO_STATUS_BITFIELD) >> 1);
    }
    while (RadioMode != RADIO_MODE_STANDBY_RC);
  }
  else
  {
    /* Call Error Handler; LED1 blinking */
    Debug_Printf("[Error] Radio is not in TX Mode.\r\n");
    Error_Handler();
  }

  /* Check if TX is well done  (SUBGHZ Radio already in Standby mode) */
  if (RadioStatus == RADIO_COMMAND_TX_DONE)
  {
    /* Turn LED2 on */
    Debug_Printf("[Radio] TX Done. Turning on LED2.\r\n");
    BSP_LED_On(LED2);
  }
  else
  {
    /* Call Error Handler; LED1 blinking */
    Debug_Printf("[Error] TX not completed successfully.\r\n");
    Error_Handler();
  }
  /* USER CODE END 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK3|RCC_CLOCKTYPE_HCLK
                              |RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
                              |RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.AHBCLK3Divider = RCC_SYSCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

 

This is not the original topic. This question is related to another behavior. So, please close this post as answered by selecting as Best answer the comment that answer your original question. After that, create a new post for this new problem to give it more visibility.

Best Regards. 
STTwo-32

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.