Skip to main content
Planet Zephyr

Using STM32 HAL with Zephyr: Setting the Brown Out Reset Threshold

By October 27, 2023No Comments

The Zephyr RTOS has good support for the STM32 family, but every now and again you need to access something that is not supported. In my most recent experience, I needed to set the Brown-Out Reset (BOR) thresholds on a STM32WLE5CC.

Adding vendor or device specific functions can break Zephyr’s code portability between devices and hence should only be done as a last resort. Where possible, use Zephyr’s supported API.

Documentation

The STM32 HAL (Hardware Abstraction Layer) and STM32 LL (Low-Level Drivers) comes bundled as a module with Zephyr. The source tree can be found on Github at https://github.com/zephyrproject-rtos/hal_stm32 with some basic instructions as the readme.

Documentation on the API can be found from ST Micro. For example, my target device was the STM32WL series MCU, hence details can be found in UM2642 Description of STM32WL HAL and low-layer driver.

Enabling the STM32 HAL and LL Drivers

Before you can use the STM32 HAL (Hardware Abstraction Layer) or STM32 LL (Low-Level Drivers) in your Zephyr application, you must enable support. Unfortunately, you can’t just add CONFIG_ parameters to your prj.conf file.

Rather, you will need to add a Kconfig file to your project that contains the following contents

mainmenu "My Application"

config MYAPP_STM32
  default y
  bool
  select USE_STM32_HAL_FLASH
  select USE_STM32_HAL_FLASH_EX

source "Kconfig.zephyr"

The select statements will be specific to your application. In my case I needed to flash the option bytes and these API were included in the flash and flash_ex (extended) files.

A list of USE_STM32_* options can be found at https://github.com/zephyrproject-rtos/zephyr/blob/main/modules/Kconfig.stm32

Alternatively, you may also search for them in menuconfig of guiconfig.

And finally, in your code you will need to add the following header:

#include 

STM32 HAL Source Code

Our task is to determine what the brown out threshold is currently set to and if not set to the desired state, reflash or set the threshold.

Section 3.4.1 FLASH option bytes of RM0461 STM32WLEx Reference Manual shows the organization of the option bytes below. The BOR Level is bits 9 to 11.

Setting and retrieving the option bytes can be performed using FLASHEx_OBGetConfig() and FLASHEx_OBProgram() functions. Data is passed using the FLASH_OBProgramInitTypeDef structure.

FLASH_OBProgramInitTypeDef OptionBytes;
HAL_FLASHEx_OBGetConfig(&OptionBytes);

LOG_INF("Option Bytes = 0x%04X", OptionBytes.UserConfig);
LOG_INF("BOR_LEV = 0x%X", (unsigned int)(OptionBytes.UserConfig & OB_USER_BOR_LEV));

if ((OptionBytes.UserConfig & OB_USER_BOR_LEV) != OB_BOR_LEVEL_3)
{
	LOG_INF("Brown out reset not set to Level 3 (approximately 2.5V)");

The code above reads a copy of the entire 32 bits of the option bytes into OptionBytes.UserConfig. To determine if it is set correctly, we perform a logical AND with OB_USER_BOR_LEV to mask out bits other than the BOR level that we are interested in. Then we can compare it with out intended level, OB_BOR_LEVEL_3.

If the value isn’t set to our desired value, we attempt to set it:

if ((OptionBytes.UserConfig & OB_USER_BOR_LEV) != OB_BOR_LEVEL_3)
{
	LOG_INF("Brown out reset not set to Level 3 (approximately 2.5V)");

	HAL_FLASH_Unlock();
	HAL_FLASH_OB_Unlock();

	OptionBytes.OptionType = OPTIONBYTE_USER;
	OptionBytes.UserType = OB_USER_BOR_LEV;
	OptionBytes.UserConfig = OB_BOR_LEVEL_3;

	if (HAL_FLASHEx_OBProgram(&OptionBytes) != HAL_OK)
	{
		HAL_FLASH_OB_Lock();
		HAL_FLASH_Lock();
		return HAL_ERROR;
	}

	HAL_FLASH_OB_Launch();

	/* We should not make it past the Launch, so lock
	 * flash memory and return an error from function
	 */
	HAL_FLASH_OB_Lock();
	HAL_FLASH_Lock();
}

Before we can program the option bytes with FLASHEx_OBProgram(), we must first unlock the flash, and then the Option Bytes.

We then fill out the structure with what we want to change and pass the structure to FLASHEx_OBProgram().

Trouble Shooting

If the above code compiles correctly, but fails to link – e..g. undefined reference errors

undefined reference to `HAL_FLASHEx_OBGetConfig'
undefined reference to `HAL_FLASH_Unlock'
undefined reference to `HAL_FLASH_OB_Unlock'
undefined reference to `HAL_FLASHEx_OBProgram'
undefined reference to `HAL_FLASH_OB_Lock'
undefined reference to `HAL_FLASH_Lock'
undefined reference to `HAL_FLASH_OB_Launch'
undefined reference to `HAL_FLASH_OB_Lock'
undefined reference to `HAL_FLASH_Lock'

then it is likely you don’t have the kconfig file set up correctly with the correct USE_STM32_ options.

Complete Example

The complete project and source code for this example can be found on Github:

Benjamin Cabé