Skip to main content
Associate
June 29, 2026
Question

BNO055 getQuat() timing very different on STM32 vs Arduino — is my STM32 code an equivalent implementation?

  • June 29, 2026
  • 3 replies
  • 39 views

I'm implementing getQuat() for the BNO055 on both STM32 and Arduino and getting very different times. On STM32H7 (HAL, 400kHz I2C) my equivalent code measures ~394µs typical with frequent spikes up to ~2573µs (mean ~912µs), but on Arduino with the Adafruit library getQuat() I get ~2ms. Is my STM32 HAL code below a fair equivalent of the Adafruit getQuat(), so that the times are actually comparable?







adfruit bno055 offical library




 

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* BNO055 I2C address (0x28 7-bit -> 0x50 8-bit for HAL) */
#define BNO055_ADDR (0x28 << 1)
#define BNO055_ID 0xA0 /* CHIP_ID should read this */

/* Registers (Page 0) */
#define BNO055_CHIP_ID_ADDR 0x00
#define BNO055_PAGE_ID_ADDR 0x07
#define BNO055_OPR_MODE_ADDR 0x3D
#define BNO055_PWR_MODE_ADDR 0x3E
#define BNO055_SYS_TRIGGER_ADDR 0x3F
#define BNO055_CALIB_STAT_ADDR 0x34
#define BNO055_QUAT_DATA_W_LSB 0x20

/* Operating modes */
#define OPERATION_MODE_CONFIG 0x00
#define OPERATION_MODE_NDOF 0x0C

/* Power mode */
#define POWER_MODE_NORMAL 0x00

/* DWT: CPU running at 64 MHz -> 64 cycles per microsecond */
#define CPU_MHZ 64
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

I2C_HandleTypeDef hi2c1;
I2C_HandleTypeDef hi2c2;
I2C_HandleTypeDef hi2c3;
I2C_HandleTypeDef hi2c4;

UART_HandleTypeDef huart3;

/* USER CODE BEGIN PV */
/* Sensor data buffers */
uint8_t quat_raw[8]; /* 8 raw bytes from 0x20..0x27 */
int16_t quat[4]; /* w, x, y, z as int16 */
float quat_f[4]; /* w, x, y, z as float (Q14) */
uint8_t calib_raw; /* CALIB_STAT byte */

/* Quaternion scale: Q14 -> divide by 2^14 */
const float quat_scale = (1.0f / (1 << 14));

/* UART print buffer */
char uart_buf[160];

/* Timing statistics (microseconds) */
uint32_t t_min = 0xFFFFFFFF;
uint32_t t_max = 0;
uint64_t t_sum = 0;
uint32_t t_count = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_I2C2_Init(void);
static void MX_I2C3_Init(void);
static void MX_I2C4_Init(void);
static void MX_USART3_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* ---- DWT cycle counter (our microsecond stopwatch) ---- */
static void DWT_Init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

/* ---- Adafruit write8 equivalent: write one byte to a register ---- */
static HAL_StatusTypeDef bno_write8(uint8_t reg, uint8_t value)
{
return HAL_I2C_Mem_Write(&hi2c1, BNO055_ADDR, reg,
I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
}

/* ---- Adafruit read8 equivalent: read one byte from a register ---- */
static uint8_t bno_read8(uint8_t reg)
{
uint8_t value = 0;
HAL_I2C_Mem_Read(&hi2c1, BNO055_ADDR, reg,
I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
return value;
}

/* ---- Adafruit readLen equivalent: read N bytes from a register ---- */
static HAL_StatusTypeDef bno_readLen(uint8_t reg, uint8_t *buffer, uint16_t len)
{
return HAL_I2C_Mem_Read(&hi2c1, BNO055_ADDR, reg,
I2C_MEMADD_SIZE_8BIT, buffer, len, 100);
}

/* ---- Adafruit setMode equivalent ---- */
static void bno_setMode(uint8_t mode)
{
bno_write8(BNO055_OPR_MODE_ADDR, mode);
HAL_Delay(30);
}

/* ---- Adafruit begin() equivalent: full init sequence ---- */
/* Returns 1 on success, 0 if sensor not found. */
static uint8_t bno_begin(void)
{
/* Wait for boot (Adafruit waits up to 850ms) */
uint32_t timeout = 850;
while (timeout > 0) {
if (bno_read8(BNO055_CHIP_ID_ADDR) == BNO055_ID) break;
HAL_Delay(10);
timeout -= 10;
}

/* Verify chip ID, retry once after a boot delay (as Adafruit does) */
uint8_t id = bno_read8(BNO055_CHIP_ID_ADDR);
if (id != BNO055_ID) {
HAL_Delay(1000);
id = bno_read8(BNO055_CHIP_ID_ADDR);
if (id != BNO055_ID) {
return 0; /* still not found */
}
}

/* Switch to CONFIG mode (default, just to be safe) */
bno_setMode(OPERATION_MODE_CONFIG);

/* Reset */
bno_write8(BNO055_SYS_TRIGGER_ADDR, 0x20);
HAL_Delay(30);
while (bno_read8(BNO055_CHIP_ID_ADDR) != BNO055_ID) {
HAL_Delay(10);
}
HAL_Delay(50);

/* Normal power mode */
bno_write8(BNO055_PWR_MODE_ADDR, POWER_MODE_NORMAL);
HAL_Delay(10);

/* Page 0 */
bno_write8(BNO055_PAGE_ID_ADDR, 0x00);

/* Clear sys trigger */
bno_write8(BNO055_SYS_TRIGGER_ADDR, 0x00);
HAL_Delay(10);

/* Final operating mode: NDOF (9-DOF fusion, quaternion @ 100Hz) */
bno_setMode(OPERATION_MODE_NDOF);
HAL_Delay(20);

return 1;
}
/* USER CODE END 0 */

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

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MPU Configuration--------------------------------------------------------*/
MPU_Config();

/* 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 */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_I2C2_Init();
MX_I2C3_Init();
MX_I2C4_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
DWT_Init();

/* BNO055 power-on settle */
HAL_Delay(700);

if (bno_begin()) {
int n = snprintf(uart_buf, sizeof(uart_buf),
"BNO055 init OK (NDOF mode)\r\n");
HAL_UART_Transmit(&huart3, (uint8_t*)uart_buf, n, 100);
} else {
int n = snprintf(uart_buf, sizeof(uart_buf),
"BNO055 NOT FOUND - check wiring/address/pullups\r\n");
HAL_UART_Transmit(&huart3, (uint8_t*)uart_buf, n, 100);
Error_Handler(); /* stop here if no sensor */
}
HAL_Delay(500);
/* USER CODE END 2 */

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

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
/* ---- Timed quaternion read (this is our getQuat equivalent) ---- */
/* ===== EXACT Adafruit getQuat() equivalent (timed) ===== */
uint32_t t1 = DWT->CYCCNT;

uint8_t buffer[8];
memset(buffer, 0, 8); /* lib does this */

int16_t qx, qy, qz, qw;
qx = qy = qz = qw = 0;

HAL_StatusTypeDef st = HAL_I2C_Mem_Read(&hi2c1, BNO055_ADDR,
BNO055_QUAT_DATA_W_LSB,
I2C_MEMADD_SIZE_8BIT, buffer, 8, 100);

qw = (((uint16_t)buffer[1]) << 8) | ((uint16_t)buffer[0]);
qx = (((uint16_t)buffer[3]) << 8) | ((uint16_t)buffer[2]);
qy = (((uint16_t)buffer[5]) << 8) | ((uint16_t)buffer[4]);
qz = (((uint16_t)buffer[7]) << 8) | ((uint16_t)buffer[6]);

const double scale = (1.0 / (1 << 14)); /* double, exactly like Adafruit */
double qd_w = scale * qw;
double qd_x = scale * qx;
double qd_y = scale * qy;
double qd_z = scale * qz;

uint32_t t2 = DWT->CYCCNT;
/* ===== end timed window ===== */

uint32_t us = (t2 - t1) / CPU_MHZ;

if (us < t_min) t_min = us;
if (us > t_max) t_max = us;
t_sum += us;
t_count++;
uint32_t avg = (uint32_t)(t_sum / t_count);

/* calibration — not timed */
calib_raw = bno_read8(BNO055_CALIB_STAT_ADDR);
uint8_t cal_sys = (calib_raw >> 6) & 0x03;
uint8_t cal_gyr = (calib_raw >> 4) & 0x03;
uint8_t cal_acc = (calib_raw >> 2) & 0x03;
uint8_t cal_mag = calib_raw & 0x03;

int n = snprintf(uart_buf, sizeof(uart_buf),
"st:%d getQuat:%lu us | min:%lu max:%lu avg:%lu | "
"W:%.3f X:%.3f Y:%.3f Z:%.3f | CAL S:%d G:%d A:%d M:%d\r\n",
(int)st, (unsigned long)us,
(unsigned long)t_min, (unsigned long)t_max, (unsigned long)avg,
qd_w, qd_x, qd_y, qd_z,
cal_sys, cal_gyr, cal_acc, cal_mag);
HAL_UART_Transmit(&huart3, (uint8_t*)uart_buf, n, 100);

HAL_Delay(10); /* pace the loop (~100Hz); does NOT affect measured read time */
}
/* USER CODE END 3 */
}

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

/*AXI clock gating */
RCC->CKGAENR = 0xE003FFFF;

/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);

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

while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = 64;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 8;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 4;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}

/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;

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

/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{

/* USER CODE BEGIN I2C1_Init 0 */

/* USER CODE END I2C1_Init 0 */

/* USER CODE BEGIN I2C1_Init 1 */

/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00602173;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */

/* USER CODE END I2C1_Init 2 */

}

/**
* @brief I2C2 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C2_Init(void)
{

/* USER CODE BEGIN I2C2_Init 0 */

/* USER CODE END I2C2_Init 0 */

/* USER CODE BEGIN I2C2_Init 1 */

/* USER CODE END I2C2_Init 1 */
hi2c2.Instance = I2C2;
hi2c2.Init.Timing = 0x00602173;
hi2c2.Init.OwnAddress1 = 0;
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c2.Init.OwnAddress2 = 0;
hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c2) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C2_Init 2 */

/* USER CODE END I2C2_Init 2 */

}

/**
* @brief I2C3 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C3_Init(void)
{

/* USER CODE BEGIN I2C3_Init 0 */

/* USER CODE END I2C3_Init 0 */

/* USER CODE BEGIN I2C3_Init 1 */

/* USER CODE END I2C3_Init 1 */
hi2c3.Instance = I2C3;
hi2c3.Init.Timing = 0x00602173;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C3_Init 2 */

/* USER CODE END I2C3_Init 2 */

}

/**
* @brief I2C4 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C4_Init(void)
{

/* USER CODE BEGIN I2C4_Init 0 */

/* USER CODE END I2C4_Init 0 */

/* USER CODE BEGIN I2C4_Init 1 */

/* USER CODE END I2C4_Init 1 */
hi2c4.Instance = I2C4;
hi2c4.Init.Timing = 0x00602173;
hi2c4.Init.OwnAddress1 = 0;
hi2c4.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c4.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c4.Init.OwnAddress2 = 0;
hi2c4.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c4.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c4.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c4) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c4, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c4, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C4_Init 2 */

/* USER CODE END I2C4_Init 2 */

}

/**
* @brief USART3 Initialization Function
* @param None
* @retval None
*/
static void MX_USART3_UART_Init(void)
{

/* USER CODE BEGIN USART3_Init 0 */

/* USER CODE END USART3_Init 0 */

/* USER CODE BEGIN USART3_Init 1 */

/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart3, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart3, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART3_Init 2 */

/* USER CODE END USART3_Init 2 */

}

/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */

/* USER CODE END MX_GPIO_Init_1 */

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */

/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/* MPU Configuration */

void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};

/* Disables the MPU */
HAL_MPU_Disable();

/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

3 replies

Andrew Neil
Super User
June 29, 2026

What Arduino ?

 

STM32H7 code measures ~394µs typical with frequent spikes up to ~2573µs (mean ~912µs),

on Arduino with the Adafruit library getQuat() I get ~2ms

So the mystery Arduino is mostly slower ?

 

Have you tried using a scope or logic analyser on the I2C to see where the differences lie?

 

so that the times are actually comparable

Why would that matter ?

All that really matters is that your STM32 code is keeping to the BNO055’s specifications - so long as that’s all within-spec, the execution time of the code is not important.

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Pavel A.
June 30, 2026

> Why would that matter ?

I guess the OP wants more uniform sampling time under 2 ms without spikes of 2.5 ms?

What else does the stm32 besides reading the IMU?

 

Ozone
Principal
June 30, 2026

I suppose this is true.

And on this note, I would recommend the OP to look at the HAL implementation of peripheral read/write functions.
And I’m especially suspicious of interrupt callbacks.

 

While execution times of 0.4 ...2.5ms are not a problem for a 10ms cycle (100Hz), something is wrong here. And experience tells me it’s most likely the HAL code.
The sensor measurements could easily be triggered by a timer, and handled in interrupts for speed and equidistance.
The quaternion calculations itself could be done in application context, although they probably consume just a few microseconds on any M4F core, let alone on the H7.

Andrew Neil
Super User
July 1, 2026

@dinesh_ee_bme_72 it seems that Bosch provide a driver:

BNO055 sensor driver with STM32CubeIDE

Did you try that ?

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
LCE
Principal II
July 2, 2026

Oh my, I get the creeps when I look at your main loop…

  1. I’d put some variable declarations somewhere else, out of the loop.
  2. Build some kind of state machine, with timed I2C reading (can be done with SysTick), setting a flag for the UART, etc… HAL_Delay() may have been the fastest to implement, but is probably the worst solution in all other aspects.
  3. You are using blocking I2C and UART functions - why that when you have a powerful MCU with interrupts and / or DMA
  4. You are using HAL functions - you know what these are doing exactly? I don’t like black boxes in my code…
  5. Your timing measurement with the cycle counter (that’s good!) include the double calculations, and couldn’t the computation time depend on the input numbers? And have you set up the compiler to use the FPU?
    At least take the maths out of the time measurement.
  6. Last but not least, check with a scope if there’s really a time difference between several I2C transfers.

Edit: and another common problem might be some interrupt handler with too much code, esp when calling some blocking functions.

Ozone
Principal
July 2, 2026

> Oh my, I get the creeps when I look at your main loop…

I call this the result of the Cube / HAL school.