STM32 I2C Library for INA260 power monitor

STM32 I2C library for INA260 power monitor

Please read Liability Disclaimer and License Agreement CAREFULLY

Before going further please read INA260 datasheet

 

INA260.h

 

#ifndef INA260_H_
#define INA260_H_

#ifdef __cplusplus
extern "C" {
#endif

    #include <stdint.h>
    #include "stm32f1xx_hal.h"
    // Number of samples to collect
    typedef enum {
        samples_1,        // = 0 (000b) -- default
        samples_4,        // = 1 (001b)
        samples_16,        // = 2 (010b)
        samples_64,        // = 3 (011b)
        samples_128,    // = 4 (100b)
        samples_256,    // = 5 (101b)
        samples_512,    // = 6 (110b)
        samples_1024,    // = 7 (111b)
    } ina260_sample_size_t;

    // Conversion time per sample for current and voltage
    typedef enum {
        time_140us,        // = 0 (000b)
        time_204us,        // = 1 (001b)
        time_332us,        // = 2 (010b)
        time_588us,        // = 3 (011b)
        time_1100us,    // = 4 (100b) -- default (voltage, current)
        time_2116us,    // = 5 (101b)
        time_4156us,    // = 6 (110b)
        time_8244us,    // = 7 (111b)
    } ina260_conversion_time_t;

    // Set measurement mode
    typedef enum {
        mode_Triggered,        // = 0 (000b)
        mode_Continuous,    // = 1 (001b) -- default
    } ina260_operating_mode_t;

    // specifies which measurements are performed for each conversion. you can
    // perform current-only, voltage-only, or both current and voltage. note the
    // bit patterns result in the following equalities:
    // iotShutdown    == 0
    // iotPower        == ( iotVoltage | iotCurrent )
    typedef enum {
        measure_Shutdown,    // = 0 (000b)
        measure_Current,    // = 1 (001b)
        measure_Voltage,    // = 2 (010b)
        measure_Power,        // = 3 (011b) -- default
    } ina260_operating_type_t;

    // format of the DEVICE_ID register (FFh)
    typedef union    {
        uint16_t u16;
        __packed struct {
            uint8_t revision :    4;
            uint16_t device    : 12;
        };
    } ina260_ID_t;
    
    typedef union {
        uint16_t u16;
        __packed struct {
            uint8_t  alert_latch_enable : 1; //  0
      uint8_t      alert_polarity : 1; //  1
      uint8_t       math_overflow : 1; //  2
      uint8_t    conversion_ready : 1; //  3
      uint8_t alert_function_flag : 1; //  4
      uint8_t                resv : 5; //  5 -  9
      uint8_t    alert_conversion : 1; // 10
      uint8_t    alert_over_power : 1; // 11
      uint8_t alert_under_voltage : 1; // 12
      uint8_t  alert_over_voltage : 1; // 13
      uint8_t alert_under_current : 1; // 14
      uint8_t  alert_over_current : 1; // 15
  };
} ina260_mask_enable_t;

// format of CONFIGURATION register (00h)
typedef union {
        uint16_t u16;
        __packed struct {
            ina260_operating_type_t   type : 2; //  0 -  1
            ina260_operating_mode_t   mode : 1; //  2
            ina260_conversion_time_t ctime : 3; //  3 -  5
            ina260_conversion_time_t vtime : 3; //  6 -  8
            ina260_sample_size_t     ssize : 3; //  9 - 11
            uint8_t                   resv : 3; // 12 - 14
            uint8_t                  reset : 1; // 15
  };
} ina260_configuration_t;

    typedef HAL_StatusTypeDef ina260_status_t;
    #define DEFAULT_CONF    0x6127
    #define BUFFER_LEN       2

    // 7-bit I2C addresses see Table 2 in datasheet
    #define INA_ADDR        (0x40 << 1)
    // Check 8.5.3.3.1 High-Speed I2C Mode in datasheet
    #define INA_FastMode    0x08

    ina260_status_t ina260_ready(void);
    ina260_status_t ina260_wait_until_ready(uint32_t timeout);
    ina260_status_t ina260_set_config(ina260_operating_type_t operating_type, ina260_operating_mode_t operating_mode, ina260_conversion_time_t current_ctime, ina260_conversion_time_t voltage_ctime, ina260_sample_size_t sample_size);
    ina260_status_t ina260_start(void);
    ina260_status_t ina260_set_operating_type(ina260_operating_type_t operating_type);
    ina260_status_t ina260_set_operating_mode(ina260_operating_mode_t operating_mode);
    ina260_status_t ina260_set_conversion_time(ina260_conversion_time_t time);
    ina260_status_t ina260_set_current_conversion_time(ina260_conversion_time_t time);
    ina260_status_t ina260_set_voltage_conversion_time(ina260_conversion_time_t time);
    ina260_status_t ina260_set_sample_size(ina260_sample_size_t sample_size);
    ina260_status_t ina260_reset(uint8_t init);
    ina260_status_t ina260_conversion_ready(void);
    ina260_status_t ina260_conversion_start(void);
    ina260_status_t ina260_get_current(float *current);
    ina260_status_t ina260_get_voltage(float *voltage);
    ina260_status_t ina260_get_power(float *power);
    ina260_status_t ina260_get_reg(uint8_t reg, uint16_t *conf);
    ina260_status_t ina260_set_int(void);
#ifdef __cplusplus
}
#endif

#endif /* INA260_H_ */

 

INA260.c

 

#include "INA260.h"
#include "i2c.h"
#include <stdio.h>

// convert value at addr to little-endian (16-bit)
#define byteSwap(x) ((x << 8) | (x >> 8))
/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')
#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

static uint16_t const TIMEOUT_MS = 500;

// INA260 register addresses
static uint8_t const INA260_REG_CONFIG  = 0x00;
static uint8_t const INA260_REG_CURRENT = 0x01;
static uint8_t const INA260_REG_VOLTAGE = 0x02;
static uint8_t const INA260_REG_POWER   = 0x03;
static uint8_t const INA260_REG_MASK_EN = 0x06;
static uint8_t const INA260_REG_ALRTLIM = 0x07;
static uint8_t const INA260_REG_MFG_ID  = 0xFE;
static uint8_t const INA260_REG_DEV_ID  = 0xFF;

// INA260 register LSB values for A, V, W, not mA, MV, mW 
// Same for voltage and current
static float const INA260_LSB_CV    =  0.00125F; //1.25F;
static float const INA260_LSB_POWER    = 0.01F; //10.00F;

static ina260_configuration_t config, newConf;

// -- pre-defined device ID per datasheet */
static ina260_ID_t const INA260_DEVICE_ID = {
    .revision = 0x00,
    .device   = 0x227
};

static ina260_status_t ina260_i2c_read(uint8_t mem_addr, uint16_t *buff_dst);
static ina260_status_t ina260_write_config(void);
static ina260_status_t ina260_read_mask_enable(ina260_mask_enable_t *mask_en);

ina260_status_t ina260_ready(void) {
    ina260_ID_t id;
    ina260_status_t status = ina260_i2c_read(INA260_REG_DEV_ID, &(id.u16));
    if (status != HAL_OK){
        return status; }
    // INA260 returns MSB first
    id.u16 = byteSwap(id.u16);
    if (INA260_DEVICE_ID.u16 != id.u16) {
        return HAL_ERROR; }
    return HAL_OK;
}

ina260_status_t ina260_wait_until_ready(uint32_t timeout) {
    uint32_t start = HAL_GetTick();
    while (ina260_ready() != HAL_OK) {
            if (((HAL_GetTick() - start) > timeout)) {
                return HAL_TIMEOUT;
            }
    }
    return HAL_OK;
}

ina260_status_t ina260_set_config(ina260_operating_type_t operating_type, ina260_operating_mode_t operating_mode, ina260_conversion_time_t current_ctime, ina260_conversion_time_t voltage_ctime, ina260_sample_size_t sample_size) {
    config.type = operating_type;
    config.mode  = operating_mode;
    config.ctime  = current_ctime;
    config.vtime  = voltage_ctime;
    config.ssize  = sample_size;
    return ina260_write_config();
}

ina260_status_t ina260_start(void) {
    config.u16  = DEFAULT_CONF;
    ina260_ID_t id;
    //Initiate High speed - see 8.5.3.3.1 High-Speed I2C Mode in datasheet
    HAL_I2C_Master_Transmit(&hi2c1, INA_ADDR, (uint8_t *)INA_FastMode, 1, TIMEOUT_MS);
    HAL_Delay(1);
    if (ina260_i2c_read(INA260_REG_DEV_ID, &(id.u16)) != HAL_OK){
        return HAL_ERROR;
    } else {
        id.u16 = byteSwap(id.u16);
        if (INA260_DEVICE_ID.u16 != id.u16){
            return HAL_ERROR;
        }
    }
    ina260_write_config();
    return HAL_OK;
}

ina260_status_t ina260_set_operating_type(ina260_operating_type_t operating_type) {
    config.type = operating_type;
    return ina260_write_config();
}

ina260_status_t ina260_set_operating_mode(ina260_operating_mode_t operating_mode) {
    config.mode = operating_mode;
    return ina260_write_config();
}

ina260_status_t ina260_set_conversion_time(ina260_conversion_time_t time) {
    config.ctime = time;
    config.vtime = time;
    return ina260_write_config();
}

ina260_status_t ina260_set_current_conversion_time(ina260_conversion_time_t time) {
    config.ctime = time;
    return ina260_write_config();
}

ina260_status_t ina260_set_voltage_conversion_time(ina260_conversion_time_t time) {
    config.vtime = time;
    return ina260_write_config();
}

ina260_status_t ina260_set_sample_size(ina260_sample_size_t sample_size) {
    config.ssize = sample_size;
    return ina260_write_config();
}

ina260_status_t ina260_reset(uint8_t init) {
    config.reset = 1;
    ina260_status_t status = ina260_write_config();
    if (status != HAL_OK) {
        return status;
    }
    if (init == 0U) {
        config.u16  = DEFAULT_CONF;
        return HAL_OK;
    }
    else {
        return ina260_write_config();
    }
}

ina260_status_t ina260_conversion_ready(void) {
    ina260_mask_enable_t mask_en;
    ina260_status_t status = ina260_read_mask_enable(&mask_en);
    if (status != HAL_OK) {
        return status;
    }
    if (mask_en.conversion_ready == 0U) {
        return HAL_BUSY;
    } else {
        return HAL_OK;
    }
}

ina260_status_t ina260_conversion_start(void) {
//    if (config.mode == mode_Continuous) {
//        return HAL_BUSY;
//    }
    return ina260_write_config();
}

ina260_status_t ina260_get_current(float *current) {
    uint16_t u;
    int16_t c, tmp;
    ina260_status_t status = ina260_i2c_read(INA260_REG_CURRENT, &u);
    if (status != HAL_OK) {
        return status;
    }
    tmp = u;
    tmp = byteSwap(tmp);
    c = (int16_t)byteSwap(u);
    *current = (float)c * INA260_LSB_CV;
    return HAL_OK;
}

ina260_status_t ina260_get_voltage(float *voltage) {
    uint16_t v;
    ina260_status_t status = ina260_i2c_read(INA260_REG_VOLTAGE, &v);
    if (status != HAL_OK) {
        return status;
    }
    v = byteSwap(v);
    *voltage = (float)v * INA260_LSB_CV;
    return HAL_OK;
}

ina260_status_t ina260_get_power(float *power) {
    uint16_t p;
    ina260_status_t status = ina260_i2c_read(INA260_REG_POWER, &p);
    if (status != HAL_OK) {
        return status;
    }
    p = byteSwap(p);
    *power = (float)p * INA260_LSB_POWER;
    return HAL_OK;
}

ina260_status_t ina260_get_reg(uint8_t reg, uint16_t *conf) {
    uint16_t r = 0;
    ina260_status_t status = ina260_i2c_read(reg, &r);
    if (status != HAL_OK) {
        return status;
    }
    *conf = byteSwap(r);
    return HAL_OK;
}

ina260_status_t ina260_set_int(void) {
    ina260_status_t status = ina260_wait_until_ready(TIMEOUT_MS);
    uint16_t p = INA_FastMode;
    newConf.u16 = byteSwap(p);
    if (HAL_OK == status) {
        //status = ina260_i2c_write(INA260_REG_CONFIG, &(config));
        status = HAL_I2C_Mem_Write(&hi2c1, INA_ADDR, (uint16_t)INA260_REG_MASK_EN, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&newConf, BUFFER_LEN, TIMEOUT_MS);
    }
    return status;
}

static ina260_status_t ina260_i2c_read(uint8_t mem_addr, uint16_t *buff_dst) {
    ina260_status_t status;
    while (HAL_BUSY == (status = HAL_I2C_Mem_Read(&hi2c1, INA_ADDR, (uint16_t)mem_addr, I2C_MEMADD_SIZE_8BIT, (uint8_t *)buff_dst, BUFFER_LEN, TIMEOUT_MS))) {
        // should not happen, unless during IRQ routine
        HAL_I2C_DeInit(&hi2c1);
        HAL_I2C_Init(&hi2c1);
    }
    return status;
}

static ina260_status_t ina260_write_config(void) {
    ina260_status_t status = ina260_wait_until_ready(TIMEOUT_MS);
    //printf("Config " PRINTF_BINARY_PATTERN_INT16 "\r\n", PRINTF_BYTE_TO_BINARY_INT16(config.u16));
    newConf.u16 = byteSwap(config.u16);
    //printf("Con--- " PRINTF_BINARY_PATTERN_INT16 "\r\n", PRINTF_BYTE_TO_BINARY_INT16(newConf.u16));
    if (HAL_OK == status) {
        //status = ina260_i2c_write(INA260_REG_CONFIG, &(config));
        status = HAL_I2C_Mem_Write(&hi2c1, INA_ADDR, (uint16_t)INA260_REG_CONFIG, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&newConf, BUFFER_LEN, TIMEOUT_MS);
    }
    return status;
}

static ina260_status_t ina260_read_mask_enable(ina260_mask_enable_t *mask_en) {
  ina260_mask_enable_t me;
  ina260_status_t status = ina260_i2c_read(INA260_REG_MASK_EN, &(me.u16));
  if (status != HAL_OK) {
        return status;
    }
  mask_en->u16 = byteSwap(me.u16);
  return HAL_OK;
}

 

Comments powered by CComment

Who’s online

We have 30 guests and no members online