2026-04-29 2:08 AM
Hello,
at the moment i try to read correct accelerometer data with a STM32L010C6 Microcontroller from a accelerometer sensor BMA530. The connection is via I^2C (SDA and SCL) and i programm the Microcontroller with a ST-LINK V2. I took a code from gitHub Platform boschsensortec/BMA530_SensorAPI and changed it because in the common.c data i can´t use the coines commands because the STM32L010C6 needs STM32 HAL-Functions because Bosch use other Hardware as STMicroelectronics.
The dev_addr2 = (0x18 << 1) see at https://sourcevu.sysprogs.com/stm32/HAL/symbols/HAL_I2C_Mem_Read#:~:text=SourceVu%20STM32%20Libraries%20and%20Samples,information%20for%20the%20specified%20I2C
and i got it from the BMA530 Datasheet - The default 7 bits I^2C address is 0x18.
The Mem_addr = 0x18, i got it from the BMA530 Datasheet. 0x18->ACC_DATA_0 -> Read-only acc_x_7_0.
In the BMA530 Datasheet at the Connection Diagram there are two resistors which i don´t have at my board. Could the resistors be the troublemaker?
I´m thankful for any help!
Best regards
/**
* Copyright (c) 2024 Bosch Sensortec GmbH. All rights reserved.
*
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "bma5.h"
#include "common.h"
#include "stm32l0xx_hal.h"
#include "main.h"
//#include "coines.h" nicht inkludiert weil die Hardware anders beim STM32 Mikroprozessor ist!
/* Kontext Variablendeklaration, uint16_t ist Datentyp, dev_addr ist die Variable, wegen HAL muss die DevAddress um 1 nach links verschoben werden */
uint16_t dev_addr2 = (0x18 << 1);
uint16_t Mem_addr = 0x18;
// I2C_handle_Structure_definition
extern I2C_HandleTypeDef hi2c1;
extern SPI_HandleTypeDef hspi1;
//I2C LeseFunktion für die STM32 Platform
BMA5_INTF_RET_TYPE bma5_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
// Hol den Wert aus der Adresse 0x18 << 1 -> dev_addr = 0x30
uint16_t dev_addr2 = *(uint16_t*)intf_ptr;
if (HAL_I2C_Mem_Read(&hi2c1, dev_addr2, Mem_addr, I2C_MEMADD_SIZE_8BIT, reg_data, len, HAL_MAX_DELAY) != HAL_OK){
return BMA5_E_COM_FAIL;
}
return BMA5_OK;
}
BMA5_INTF_RET_TYPE bma5_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
uint16_t dev_addr2 = *(uint16_t*)intf_ptr;
if (HAL_I2C_Mem_Write(&hi2c1, dev_addr2, Mem_addr, I2C_MEMADD_SIZE_8BIT, reg_data, len, HAL_MAX_DELAY) != HAL_OK){
return BMA5_E_COM_FAIL;
}
return BMA5_OK;
}
BMA5_INTF_RET_TYPE bma5_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
HAL_StatusTypeDef status;
uint8_t addr = reg_addr | 0x80; // Read-Bit setzen
HAL_GPIO_WritePin(FRAM_CS_GPIO_Port, FRAM_CS_Pin, GPIO_PIN_RESET); // CS Aktiv
// 1. Adresse senden
status = HAL_SPI_Transmit(&hspi1, &addr, 1, 100);
// 2. Nur empfangen, wenn Senden OK war
if (status == HAL_OK) {
status = HAL_SPI_Receive(&hspi1, reg_data, len, 100);
}
HAL_GPIO_WritePin(FRAM_CS_GPIO_Port, FRAM_CS_Pin, GPIO_PIN_SET); // CS Deaktiv
return (status == HAL_OK) ? 0 : -1; // 0 für Erfolg (BMA5_OK)
//uint8_t dev_addr = *(uint8_t*)intf_ptr;
//return HAL_SPI_Receive(&hspi1, reg_data, len, HAL_MAX_DELAY);
// Abschnitt den ich ändern muss -> [return coines_read_spi(COINES_SPI_BUS_0, dev_addr, reg_addr, reg_data, (uint16_t)len);]
}
BMA5_INTF_RET_TYPE bma5_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
//uint8_t dev_addr = *(uint8_t*)intf_ptr;
return HAL_SPI_Transmit(&hspi1, reg_data, len, HAL_MAX_DELAY);
// Abschnitt den ich ändern muss -> [return coines_write_spi(COINES_SPI_BUS_0, dev_addr, reg_addr, (uint8_t *)reg_data, (uint16_t)len);]
}
void bma5_delay_us(uint32_t period, void *intf_ptr)
{
if (period < 1000)
HAL_Delay(1);
else
HAL_Delay(period / 1000);
}
void bma5_check_rslt(const char api_name[], int8_t rslt)
{
switch (rslt)
{
case BMA5_OK:
/* Do nothing */
break;
case BMA5_E_NULL_PTR:
printf("API name %s\t", api_name);
printf("Error [%d] : Null pointer\r\n", rslt);
break;
case BMA5_E_COM_FAIL:
printf("API name %s\t", api_name);
printf("Error [%d] : Communication failure\r\n", rslt);
break;
case BMA5_E_DEV_NOT_FOUND:
printf("API name %s\t", api_name);
printf("Error [%d] : Device not found\r\n", rslt);
break;
default:
printf("API name %s\t", api_name);
printf("Error [%d] : Unknown error code\r\n", rslt);
break;
}
}
int8_t bma5_interface_init(struct bma5_dev *bma5, uint8_t intf, enum bma5_context context)
{
int8_t rslt = BMA5_OK;
if (bma5 == NULL)
{
return BMA5_E_NULL_PTR;
}
/* Bus configuration : I2C */
if (intf == BMA5_I2C_INTF)
{
bma5->bus_read = bma5_i2c_read;
bma5->bus_write = bma5_i2c_write;
bma5->intf = BMA5_I2C_INTF;
bma5->intf_ptr = &dev_addr2;
}
/* Bus configuration : SPI */
else if (intf == BMA5_SPI_INTF)
{
bma5->bus_read = bma5_spi_read;
bma5->bus_write = bma5_spi_write;
bma5->intf = BMA5_SPI_INTF;
bma5->intf_ptr = NULL;
}
else
{
return BMA5_E_COM_FAIL;
}
/* Configure delay in microseconds */
bma5->delay_us = bma5_delay_us;
/* Assign context parameter */
bma5->context = context;
return BMA5_OK;
}Michael Wilkens
2026-04-29 2:37 AM
I2C always needs pull-up resistors. For slow speeds maybe the MCU's internal resistors might be good enough, but you must activate these when setting up the I2C GPIOs.
Next step: get an oscilloscope and check what's happening on the SDA & SCL lines.
Further, check if the 0x18 address actually must be shifted or not, meaning that usually the device datasheets give the actual address, and you must not shift that one when writing the slave address register bits.
2026-04-29 3:39 AM
The I2C Speed Frequenzy is 100kHz and the I2C Speed Mode is in Standard Mode. I changed the GPIO Pull-up/Pull-down to Pull-up in the STM32CubeMX and generate the code. I don´t have a Oscilloscope for testing here and it´s impossible to connect the wire because the componets are placed on a thin flex PCB.
I try 2 tests in the debugg session. The first with the dev_addr2 = (0x18 << 1) and Mem_addr = 0x19.
The sens_data values for x and y changed for every loop and the value for z doesn´t changed, the sensor is layed flat on the surface so normaly the value should be for sens_data_z -16350 lsb which is -9.81m/s^2.
The question is, are the sens_data_values random and not true and are they influenced by the missed resistors?
The second test in the debugg session is with dev_addr2 = 0x18 and Mem_addr = 0x19.
There the sen_data values doesn´t changed for every loop so i think the dev_addr2 = 0x18 is not the correct one.
2026-04-29 4:47 AM
> I changed the GPIO Pull-up/Pull-down to Pull-up in the STM32CubeMX and generate the code.
This will almost certainly not work. The internal pull-ups are too weak, by about an order of magnitude.
The I2C bus basically forms a "wired OR", the pull-ups are essential for the physical level. About 2k...10k should be fine. I would suggest 2.4k .. 4.7k.
> I don´t have a Oscilloscope for testing here and it´s impossible to connect the wire because the componets are placed on a thin flex PCB.
It doesn't matter where you place these resistors on the bus. If your MCU sits on a board, perhaps it has connectors where you could add them to.
> I don´t have a Oscilloscope for testing here ...
I would recommend to check the datasheet of the BMA530 sensor. Probably it has an internal register that returns a fixed value upon read (often called "WHO_AM_I").
Reading that register correctly would verify that your I2C code basically works.
However, a scope or logic analyser is highly recommended.
Even the cheap Chinese Salae clones for <20€/$ work fine with free logic analyser software.
2026-04-29 4:57 AM
Well yes, the STM32 internal pull-ups are super weak, so it might work with an I2C frequency up to 10 kHz, very probably not at 100 kHz.
(-> calculate the corner frequency of a simple RC low pass with the pull-up resistor value and an estimated capacitance (sum of IOs and PCB C))
Do you actually have an idea of how I2C hardware is working, what open drain / collector IOs and pull-up resistors are?
If not, read about that first.
And not having an oscilloscope when doing HW development is ... a bad idea.
2026-04-29 5:57 AM
Hello Ozone,
thanks for the reply and the informations you shared. I will buy a Shuttle Board 3.0 BMA530 with 2x 2.4k resistors and 2x 4.7k resistors and test it.
Best regards
Michael
2026-04-29 6:27 AM
> From a plain description of the DAC with individual 16-bit output registers and the DMA section stating byte, half-word and word capability, this restriction is neither obvious, nor makes it sense on this level.
Yes, as this pull-up resistors are responsible for the currents flowing on the bus, they define the bandwidth as well.
Thanks for pointing that out.
> And not having an oscilloscope when doing HW development is ... a bad idea.
Here the cheap alternative I mentioned, in this case an Australian seller.
https://www.phippselectronics.com/product/usb-8-channel-24mhz-logic-analyser/
Or a German one :
https://www.makershop.de/module/schnittstellen/8-kanal-usb-logikanalysator/
The free PulseView / Sigrok software packages support it out of the box (at least under Linux), and even come with protocol interpreters for I2C, amongst others.
2026-04-29 6:51 AM - edited 2026-04-29 6:51 AM
> From a plain description of the DAC with individual 16-bit output registers and the DMA section stating byte, half-word and word capability, this restriction is neither obvious, nor makes it sense on this level.
DAC? Where's this quotation from? Some copy / paste relict guess... :D
Anyway, all of my comments (pull-ups, bandwidth) were aimed at the OP, not you.
2026-04-30 5:37 AM
> DAC? Where's this quotation from? Some copy / paste relict guess... :D
Yes, sorry. That was from another thread, don't know how that got in ... ;-O.
Your statement my comment was referring to was :
> Well yes, the STM32 internal pull-ups are super weak, so it might work with an I2C frequency up to 10 kHz, very probably not at 100 kHz.
Which is most probably right. I never tried to run an I2C bus with ST's internal pull-ups.