Working with STM32 and Acceleration Sensor: ADXL345 in I2C mode

In this guide, we shall see how to interface STM32F4 with acceleration sensor ADXL345 using I2C mode.

In this guide, we shall cover the following:

  • Feature of ADXL345.
  • Developing the driver.
  • Code.
  • Results.

The ADXL345 is a small, thin, ultralow power, 3-axis accelerometer with high resolution (13-bit) measurement at up to ±16 g. Digital output data is formatted as 16-bit twos complement and is accessible through either a SPI (3- or 4-wire) or I2 C digital interface.

The ADXL345 is well suited for mobile device applications. It measures the static acceleration of gravity in tilt-sensing applications, as well as dynamic acceleration resulting from motion or shock. Its high resolution (3.9 mg/LSB) enables measurement of inclination changes less than 1.0°.

Several special sensing functions are provided. Activity and inactivity sensing detect the presence or lack of motion by comparing the acceleration on any axis with user-set thresholds. Tap sensing detects single and double taps in any direction. Freefall sensing detects if the device is falling. These functions can be mapped individually to either of two interrupt output pins. An integrated memory management system with a 32-level first in, first out (FIFO) buffer can be used to store data to minimize host processor activity and lower overall system power consumption.

Low power modes enable intelligent motion-based power management with threshold sensing and active acceleration measurement at extremely low power dissipation.

The ADXL345 is supplied in a small, thin, 3 mm × 5 mm × 1 mm, 14-lead, plastic package.

2. Developing the Driver:

Since the utilize the I2C communication, the I2C pins of STM32F411 Nucleo-64 is used as following:

Hence, the required function as as following:

  • I2C write single byte (from here).
  • I2C read multiple bytes (from here).

After acquiring the required drivers for I2C, start off by creating a new header file with name of adxl345.h.

Within the header file, declare enum which holds the acceleration range:

typedef enum
{
	accl_2g=0,
	accl_4g=1,
	accl_8g=2,
	accl_16g=3
}adxl345Parameters;

The ranges as follows which they have been taken from the datasheet of adxl345.:

Also, declare struct which will hold the acceleration results:

typedef struct
{

	float ax;
	float ay;
	float az;

}accleration_values_t;

Also, the following functions:

void adxl345_init(adxl345Parameters param);
void adxl345_update();
void adxl345_get_values(accleration_values_t * values);

Hence, the header file as following:

#ifndef ADXL345_H_
#define ADXL345_H_

#include "stdint.h"

typedef enum
{
	accl_2g=0,
	accl_4g=1,
	accl_8g=2,
	accl_16g=3
}adxl345Parameters;

typedef struct
{

	float ax;
	float ay;
	float az;

}accleration_values_t;
Se
void adxl345_init(adxl345Parameters param);
void adxl345_update();
void adxl345_get_values(accleration_values_t * values);






#endif /* ADXL345_H_ */

Now, create adxl345.c source file and include adxl345.h and the i2c header file:

#include "adxl345.h"
#include "i2c.h"

Also define some registers and the address of adxl345:

#define DEVICE_ADDR    			(0x53) /*adxl345 address*/
#define DATA_FORMAT_R   		(0x31) /*data format register*/
#define POWER_CTL_R 			(0x2D) /*Power control register*/
#define DATA_START_ADDR			(0x32) /*Start address to read the values*/

#define	RESET					(0x00) /*Reset value*/
#define SET_MEASURE_B		    (0x08) /*Put adxl345 into measurement mode*/

Variables that hold the values:

uint8_t data_rec[6];
uint8_t acc_range;
int16_t x,y,z;

Now, to initialize the adxl345, the following:

  • Set the acceleration range.
  • Reset the power register.
  • Set the sensor in measurement mode.
void adxl345_init(adxl345Parameters param)
	{
		acc_range=param;
		i2c_writeByte(DEVICE_ADDR, DATA_FORMAT_R, param);
		i2c_writeByte(DEVICE_ADDR, POWER_CTL_R, RESET);
		i2c_writeByte(DEVICE_ADDR,POWER_CTL_R, SET_MEASURE_B);
	}

To update the sensor data read multiple data starting from address of DATAX0 up to DATAZ1:

void adxl345_update()
	{



		i2c_ReadMulti(DEVICE_ADDR, DATA_START_ADDR, 6, (char*)data_rec);

		x = ((data_rec[1]<<8)|data_rec[0]);
		y = ((data_rec[3]<<8)|data_rec[2]);
		z = ((data_rec[5]<<8)|data_rec[4]);

	}

To get the new values:

void adxl345_get_values(accleration_values_t * values)
	{
	float divider;
	switch(acc_range)
	{
		case accl_2g: divider=0.003906; /*1/256*/	break;
		case accl_4g: divider=0.0078125;/*1/128*/	break;
		case accl_8g: divider=0.01563;	/*1/64*/	break;
		case accl_16g: divider=0.03125;	/*1/32*/	break;
	}

		values->ax=x*divider;
		values->ay=y*divider;
		values->az=z*divider;
	}

To get the divider, you need to divide the result by the sensitivity:

Then store the values to the structure passed by the user.

Hence, the source file as following:

#include "adxl345.h"
#include "i2c.h"



#define DEVICE_ADDR    			(0x53) /*adxl345 address*/
#define DATA_FORMAT_R   		(0x31) /*data format register*/
#define POWER_CTL_R 			(0x2D) /*Power control register*/
#define DATA_START_ADDR			(0x32) /*Start address to read the values*/

#define	RESET					(0x00) /*Reset value*/
#define SET_MEASURE_B		    (0x08) /*Put adxl345 into measurement mode*/

/*Variables which hold some variable*/
uint8_t data_rec[6];
uint8_t acc_range;
int16_t x,y,z;
void adxl345_init(adxl345Parameters param)
	{
		acc_range=param;
		i2c_writeByte(DEVICE_ADDR, DATA_FORMAT_R, param);
		i2c_writeByte(DEVICE_ADDR, POWER_CTL_R, RESET);
		i2c_writeByte(DEVICE_ADDR,POWER_CTL_R, SET_MEASURE_B);
	}


void adxl345_update()
	{



		i2c_ReadMulti(DEVICE_ADDR, DATA_START_ADDR, 6, (char*)data_rec);

		x = ((data_rec[1]<<8)|data_rec[0]);
		y = ((data_rec[3]<<8)|data_rec[2]);
		z = ((data_rec[5]<<8)|data_rec[4]);

	}

void adxl345_get_values(accleration_values_t * values)
	{
	float divider;
	switch(acc_range)
	{
		case accl_2g: divider=0.003906; /*1/256*/	break;
		case accl_4g: divider=0.0078125;/*1/128*/	break;
		case accl_8g: divider=0.01563;	/*1/64*/	break;
		case accl_16g: divider=0.03125;	/*1/32*/	break;
	}

		values->ax=x*divider;
		values->ay=y*divider;
		values->az=z*divider;
	}

In main.c file:

#include "i2c.h"
#include "delay.h"
#include "adxl345.h"
#include "stdio.h"
accleration_values_t accleration_values;
extern void uart2_rxtx_init(void);
int main(void)
{
	uart2_rxtx_init();
	i2c_init();
	adxl345_init(accl_4g);
	while(1)
		{
			adxl345_update();
			adxl345_get_values(&accleration_values);
			printf("ax=%0.5f\tay=%0.5f\taz=%0.5f\r\n",accleration_values.ax,accleration_values.ay,accleration_values.az);
			delay(20);

		}

}

3. Code:

You may download the code from here:

4. Results:

Open your serial terminal application and set the baudrate to 115200 and you should get the following:

Happy coding 🙂

Add Comment

Your email address will not be published. Required fields are marked *