Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save HectorCarreno/4dc91b84112d4b9f1da9965ea5959069 to your computer and use it in GitHub Desktop.
Save HectorCarreno/4dc91b84112d4b9f1da9965ea5959069 to your computer and use it in GitHub Desktop.
Using the I2C driver provided by Espressif was developed this git for an ADLX345 accelerometer and ESP WROOM 32 MCU
/* I2C ADXL345 and Espressif ESP WROOM 32
This is an input for developers world, it's a code checked and work for this accelerometer.
Any doubt, contact with the provider of this git.
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
/**
* BIKE TRACKER DEVELOPMENT
*
* This is a development for bikes tracker application using BG96 from Quectel company,
* ADLX345 accelerometer, SD card reader and ESP32 from Espressif .
*
*
*
* Pin assignment:
*
* - Master pins I2C protocol:
* GPIO21 is assigned as the data signal of i2c master port
* GPIO22 is assigned as the clock signal of i2c master port
*
* Connection:
*
* - Connect with the SDA and SCL pins of sensor ADXL345
* - Isn't necessary add the external pull-up resistors, this driver will enable
* internal pull-up resistors, but for a major performance use it.
*
*/
#define SAMPLE_PERIOD_MS 100 // Sample period delay
#define I2C_SCL_IO 22 // GPIO pin for I2C Wire Master Clock
#define I2C_SDA_IO 21 // GPIO pin for I2C Wire Data Input/Output
#define I2C_FREQ_HZ 100000 // Clock Frequency for I2C
#define I2C_PORT_NUM I2C_NUM_1 // Master Port Enable Macro
#define I2C_TX_BUF_DISABLE 0 // I2C Master don't need buffer
#define I2C_RX_BUF_DISABLE 0 // I2C master don't need buffer
// I2C common protocol defines
#define WRITE_BIT I2C_MASTER_WRITE // Macro with Write bit to configure
#define READ_BIT I2C_MASTER_READ // Macro with Read bit to configure
#define ACK_CHECK_EN 0x1 // Acknowledge bit Enable
#define ACK_CHECK_DIS 0x0 // Acknowledge bit Disable
#define ACK_VAL 0x0 // Acknowledge Value
#define NACK_VAL 0x1 // Non Acknowledge Value
// ADXL345 defines
#define ADXL345_I2C_ADDR 0x53 // ADXL35 default address
#define DEVICE_ID 0x00 // Device ID, 0 by default
#define PWR_CTL_REG 0x2D // POWER Control Register
#define STANDBY_MODE_CONF 0x08 // Standby mode bit
#define SLEEP_MODE_CONF 0x04 // Sleep Mode bit
#define BW_RATE_REG 0x2C // Bandwidth Rate Register
#define DATA_RATE 0x07 // Output data rate 12.5Hz by default
#define LOW_POWER_MODE 0x10 // Bit LOW POWER configuration
#define DATA_FORMAT_REG 0x31
#define LEFT_JUSTIFIED 0X04 // Data x,y,z Left justified
#define GRAVITY_RANGE_BITS 0x01 // Gravity range of ±4g
#define RANGE_FULL_RES 0x08 // Full resolution for ±4 g
#define OFSX 0x1E // X axis offset
#define OFSY 0x1F // Y axis offset
#define OFSZ 0x20 // Z axis offset
#define INT_ENABLE 0x2E //Interrupts
#define INT_MAP 0x2F //Interrupts
#define INT_SOURCE 0x30 //Interrupts
#define OVERRUN 0x01 // Overrun mode
#define DATAX0 0x32 // LSB X axis... it is only needed
#define DATAX1 0x33 // MSB X axis
#define DATAY0 0x34 // LSB y axis
#define DATAY1 0x35 // MSB y axis
#define DATAZ0 0x36 // LSB z axis
#define DATAZ1 0x37 // MSB z axis
#define GRAVITY 9.80665 // Gravity value [m/s2]
#define DEVIAT_SENSIVILITY_X_Y 0.0040 // 4mg/LSB for x and y axis
#define DEVIAT_SENSIVILITY_Z 0.0043 // 4.3mg/LSB for z axis
#define SENSIVILITY 256 // LSB/g
// Structure to hold accelerometer bits
typedef struct ACCEL_DATA_BITS {
int16_t X0;
int16_t X1;
int16_t Y0;
int16_t Y1;
int16_t Z0;
int16_t Z1;
}ACCEL_DATA_BITS_t;
// Structure to hold acceleration working data
typedef struct ACCEL_DATA
{
int16_t X;
int16_t Y;
int16_t Z;
}ACCEL_DATA_t;
// Structure to hold acceleration output values
typedef struct ACCEL_VAL
{
double X;
double Y;
double Z;
}ACCE_VAL_t;
// Structure to configure the offset mode
typedef struct OFFSET
{
uint8_t X;
uint8_t Y;
uint8_t Z;
}OFFSET_VAL_t;
static const char *TAG = "I2C_BICKER_TRACKER";
/**
* @ Sequence to read I2C slave device
* | START | SLAVE ADDRESS + READ BIT + ACK | REGISTER + ACK | READ << 1 + ACK | READ 1 Byte + NACK | STOP |
*/
static esp_err_t I2C_Master_Read_Slave_Reg(i2c_port_t i2c_num, uint8_t i2c_addr, uint8_t i2c_reg, uint8_t* data_rd, size_t size)
{
if (size == 0) {
return ESP_OK;
}
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// first, send device address (indicating write) & register to be read
i2c_master_write_byte(cmd, ( i2c_addr << 1 ), ACK_CHECK_EN);
// send register we want
i2c_master_write_byte(cmd, i2c_reg, ACK_CHECK_EN);
// Send repeated start
i2c_master_start(cmd);
// now send device address (indicating read) & read data
i2c_master_write_byte(cmd, ( i2c_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
if (size > 1) {
i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
}
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/**
* @ Sequence to read I2C slave device
* | START | Slave Address + Write Bit + Acknowledge | Register + Acknowledge | Write N Bytes + Acknowledge | STOP |
*/
static esp_err_t I2C_Master_Write_Slave_Reg(i2c_port_t i2c_num, uint8_t i2c_addr, uint8_t i2c_reg, uint8_t* data_wr, size_t size)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// first, send device address (indicating write) & register to be written
i2c_master_write_byte(cmd, ( i2c_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
// send register we want
i2c_master_write_byte(cmd, i2c_reg, ACK_CHECK_EN);
// write the data
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/* Read contents of a ADXL345 register
---------------------------------------------------------------------------*/
esp_err_t ADXL345_REG_READER( uint8_t REG, uint8_t *P_DATA, uint8_t COUNT )
{
return( I2C_Master_Read_Slave_Reg( I2C_PORT_NUM, ADXL345_I2C_ADDR, REG, P_DATA, COUNT ) );
}
/* Write value to specified ADXL345 register
---------------------------------------------------------------------------*/
esp_err_t ADXL345_REG_WRITER( uint8_t reg, uint8_t *pdata, uint8_t count )
{
return( I2C_Master_Write_Slave_Reg( I2C_PORT_NUM, ADXL345_I2C_ADDR, reg, pdata, count ) );
}
/****************************
* ADXL345 Initialize *
****************************/
static void ADXL345_Initialize()
{
uint8_t val; // Work Value on the function
// Before re-configuring, must enter 'STANDBY' mode
ADXL345_REG_READER( PWR_CTL_REG, &(val), 1 );
val &= ~(STANDBY_MODE_CONF);
ADXL345_REG_WRITER(PWR_CTL_REG, &(val), 1 );
// check if the ID by default is the correct for the ADXL345 sensor.
ADXL345_REG_READER(DEVICE_ID, &(val), 1);
if (val == 0xE5) {
ESP_LOGI( TAG, "ADXL345 ID:0x%X (OK)", val );
} else {
ESP_LOGE( TAG, "ADXL345 ID:0x%X !!!! (NOT correct; should be 0xE5)", val );
}
// All is good about the ADXL345 configuration until here ---> OK
/*
** Configure accelerometer for:
** - Sleep Mode (125ms)
** - Low Power System
** - Full Scale of +/-4g
*/
// Configuring the "SLEEP" mode operation
val = SLEEP_MODE_CONF; // val take the value for into sleep mode operation to 8 Hz
ADXL345_REG_WRITER(PWR_CTL_REG, &(val), 1 ); // write in the Power Control Register the...
// sleep mode operation.
// All is good about the ADXL345 configuration until here ---> OK
// Configure the data operation with low power consumption
// Configure the data rate operation to 100Hz
val = (LOW_POWER_MODE | DATA_RATE );
ADXL345_REG_WRITER(BW_RATE_REG, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
// Configure the data format to ±4 g full resolution
val = (GRAVITY_RANGE_BITS | RANGE_FULL_RES );
ADXL345_REG_WRITER(DATA_FORMAT_REG, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
/*******************************
* Configure the interrupts *
*******************************/
val = (OVERRUN );
ADXL345_REG_WRITER(INT_ENABLE, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
val = (OVERRUN );
ADXL345_REG_WRITER(INT_MAP, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
val = (OVERRUN );
ADXL345_REG_WRITER(INT_SOURCE, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
/***********************************************
* Re-Configure the Sleep and Standby modes *
***********************************************/
// Reconfigure done by "SLEEP" mode disable
ADXL345_REG_READER( PWR_CTL_REG, &(val), 1 );
val &= ~(SLEEP_MODE_CONF);
ADXL345_REG_WRITER(PWR_CTL_REG, &(val), 1 );
// Reconfigure done by "STANDBY" mode disable
val |= (STANDBY_MODE_CONF);
ADXL345_REG_WRITER(PWR_CTL_REG, &(val), 1 );
// All is good about the ADXL345 configuration until here ---> OK
}
/****************************
* MASTER Mode Initialize *
****************************/
static void I2C_Master_Initialize()
{
int i2c_master_port = I2C_PORT_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode,
I2C_RX_BUF_DISABLE, I2C_TX_BUF_DISABLE, 0);
}
static void Offset_configuration()
{
OFFSET_VAL_t offset_axis;
offset_axis.X = 0xFC;
offset_axis.Y = 0x02;
offset_axis.Z = 0xFF;
ADXL345_REG_WRITER(OFSX, (uint8_t *)&offset_axis, sizeof(offset_axis) );
}
/***************************************
* Make the task desired function *
***************************************/
static void I2C_Acceleration_Calc_Task(void* arg)
{
esp_err_t err;
ACCEL_DATA_BITS_t acc;
ACCEL_DATA_t accel;
ACCE_VAL_t acc_val;
ESP_LOGI( TAG, "ESP I2C - ADXL345 Accelerometer" );
while (1) {
// Note: as configured, reading data from the output registers will start next acquisition
Offset_configuration();
err = ADXL345_REG_READER( DATAX0, (uint8_t *)&acc.X0, sizeof(acc.X0) );
err = ADXL345_REG_READER( DATAX1, (uint8_t *)&acc.X1, sizeof(acc.X1) );
// Swap Big Endian
acc.X1 = acc.X1 << 8;
accel.X = acc.X0 | acc.X1;
err = ADXL345_REG_READER( DATAY0, (uint8_t *)&acc.Y0, sizeof(acc.Y0) );
err = ADXL345_REG_READER( DATAY1, (uint8_t *)&acc.Y1, sizeof(acc.Y1) );
// Swap Big Endian
acc.Y1 = acc.Y1 << 8;
accel.Y = acc.Y0 | acc.Y1;
err = ADXL345_REG_READER( DATAZ0, (uint8_t *)&acc.Z0, sizeof(acc.Z0) );
err = ADXL345_REG_READER( DATAZ1, (uint8_t *)&acc.Z1, sizeof(acc.Z1) );
// Swap Big Endian
acc.Z1 = acc.Z1 << 8;
accel.Z = acc.Z0 | acc.Z1;
// acc.X = BYTE_SWAP( acc.X );
// acc.Y = BYTE_SWAP( acc.Y );
// acc.Z = BYTE_SWAP( acc.Z );
// byte-swap values to make little-endian
acc_val.X = -accel.X * DEVIAT_SENSIVILITY_X_Y * GRAVITY;
acc_val.Y = -accel.Y * DEVIAT_SENSIVILITY_X_Y * GRAVITY;
acc_val.Z = -accel.Z * DEVIAT_SENSIVILITY_Z * GRAVITY;
// shift each value to align 14-bits in 16-bit ints
ESP_LOGI( TAG, "Accelerometer err:%d x:%2f y:%2f z:%2f", err, acc_val.X, acc_val.Y, acc_val.Z );
vTaskDelay( pdMS_TO_TICKS( SAMPLE_PERIOD_MS ) );
}
}
void app_main()
{
I2C_Master_Initialize(); // Initialize I2C master communication
ADXL345_Initialize(); // Initialize I2C configuration
// Make the task
xTaskCreate(I2C_Acceleration_Calc_Task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment