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

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 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 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 SoC, 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 and what subset of HAL/LL API you need to link with. 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 for the function prototypes and structures:

#include <soc.h>

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, set the desired threshold.

Section 3.4.1 FLASH option bytes of the RM0461 STM32WLEx Reference Manual shows the organization of the option bytes below. The BOR Level are 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(). The desired threshold for BOR can be added to OptionBytes.UserConfig.

We then call HAL_FLASH_OB_Launch() to reload the option byte register. This generates a option byte reset, so the code should not execute past this point.

Trouble Shooting

If the above code compiles correctly, but fails to link i.e. you get a handful of undefined reference errors such as below, then it is likely you don’t have the kconfig file set up correctly with the correct USE_STM32_ options. Recheck this file and your USE_STM32_ settings.

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'

Complete Example

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




Be the first to comment

Leave a Reply

Your email address will not be published.


*