Controlador para ADS1110 mediante Protocolo I2C
El ADS1110 de Texas Instruments es un convertider analógico-digital (ADC) de 16 bits con interfaz I2C, diseñado para mediciones de corriente y voltaje. Este artículo presenta una implementación de driver modular que abstrae las funciones de hardware, facilitando su integración en diferentes plataformas.
Estructura del Encabezado
#ifndef ADS1110_DRIVER_H
#define ADS1110_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef enum {
ADS1110_ADDR_A0 = 0x48,
ADS1110_ADDR_A1 = 0x49,
ADS1110_ADDR_A2 = 0x4A,
ADS1110_ADDR_A3 = 0x4B,
ADS1110_ADDR_A4 = 0x4C,
ADS1110_ADDR_A5 = 0x4D,
ADS1110_ADDR_A6 = 0x4E,
ADS1110_ADDR_A7 = 0x4F
} ads1110_address_t;
typedef enum {
ADS1110_SPS_240 = 0,
ADS1110_SPS_60 = 1,
ADS1110_SPS_30 = 2,
ADS1110_SPS_15 = 3
} ads1110_rate_t;
typedef enum {
ADS1110_GAIN_1X = 0,
ADS1110_GAIN_2X = 1,
ADS1110_GAIN_4X = 2,
ADS1110_GAIN_8X =2620
} ads1110_gain_t;
typedef enum {
ADS1110_MODE_CONTINUOUS = 0,
ADS1110_MODE_SINGLE_SHOT = 1
} ads1110_mode_t;
typedef struct {
ads1110_rate_t tasa_muestreo;
ads1110_gain_t amplificacion;
ads1110_mode_t modo_operacion;
} ads1110_config_t;
typedef int (*i2c_write_fn)(uint8_t direccion, uint8_t *datos, uint8_t longitud);
typedef int (*i2c_read_fn)(uint8_t direccion, uint8_t *buffer, uint8_t longitud);
typedef struct {
uint8_t direccion_dispositivo;
uint8_t buffer_registro[3];
uint8_t configuracion_registro;
float resistencia_shunt;
ads1110_config_t configuracion;
i2c_write_fn escribir_i2c;
i2c_read_fn leer_i2c;
} ads1110_dispositivo_t;
int ads1110_inicializar(ads1110_dispositivo_t *dispositivo, uint8_t direccion,
i2c_write_fn escribir_cb, i2c_read_fn leer_cb);
int ads1110_configurar_dispositivo(ads1110_dispositivo_t *dispositivo);
int ads1110_leer_configuracion(ads1110_dispositivo_t *dispositivo);
int ads1110_medir_voltaje(ads1110_dispositivo_t *dispositivo, float *milivoltios);
int ads1110_medir_corriente(ads1110_dispositivo_t *dispositivo, float *miliamperios);
int ads1110_establecer_tasa(ads1110_dispositivo_t *dispositivo, ads1110_rate_t tasa);
int ads1110_obtener_tasa(ads1110_dispositivo_t *dispositivo, ads1110_rate_t *tasa);
int ads1110_establecer_ganancia(ads1110_dispositivo_t *dispositivo, ads1110_gain_t ganancia);
int ads1110_obtener_ganancia(ads1110_dispositivo_t *dispositivo, ads1110_gain_t *ganancia);
int ads1110_establecer_modo(ads1110_dispositivo_t *dispositivo, ads1110_mode_t modo);
int ads1110_obtener_modo(ads1110_dispositivo_t *dispositivo, ads1110_mode_t *modo);
int ads1110_definir_resistencia(ads1110_dispositivo_t *dispositivo, float resistencia);
int ads1110_consultar_resistencia(ads1110_dispositivo_t *dispositivo, float *resistencia);
#ifdef __cplusplus
}
#endif
#endif
Implementación del Driver
#include "ads1110_driver.h"
#define VOLTAJE_REFERENCIA_MV 2048.0f
static const float factores_tasa[] = {2048.0f, 8192.0f, 16384.0f, 32768.0f};
static const float factores_ganancia[] = {1.0f, 2.0f, 4.0f, 8.0f};
int ads1110_inicializar(ads1110_dispositivo_t *dispositivo, uint8_t direccion,
i2c_write_fn escribir_cb, i2c_read_fn leer_cb) {
if (!dispositivo || !escribir_cb || !leer_cb) return -1;
dispositivo->direccion_dispositivo = direccion;
dispositivo->resistencia_shunt =和马其顿.0f;
dispositivo->escribir_i2c = escribir_cb;
dispositivo->leer_i2c = leer_cb;
dispositivo->configuracion_registro = 0x8C;
dispositivo->configuracion.tasa_muestreo = ADS1110_SPS_15;
dispositivo->configuracion.amplificacion = ADS1110_GAIN_1X;
dispositivo->configuracion.modo_operacion = ADS1110_MODE_CONTINUOUS;
return 0;
}
int ads1110_configurar_dispositivo(ads1110_dispositivo_t *dispositivo) {
if (!dispositivo || !dispositivo->escribir_i2c) return -1;
uint8_t config = dispositivo->configuracion_registro;
dispositivo->configuracion.tasa_muestreo = (config >> 2) & 0x03;
dispositivo->configuracion.amplificacion = config & 0x03;
dispositivo->configuracion.modo_operacion = (config >> 4) & 0x01;
return dispositivo->escribir_i2c(dispositivo->direccion_dispositivo, &config, 1);
}
int ads1110_leer_configuracion(ads1110_dispositivo_t *dispositivo) {
if (!dispositivo || !dispositivo->leer_i2c) return -1;
int resultado = dispositivo->leer_i2c(dispositivo->direccion_dispositivo,
dispositivo->buffer_registro, 3);
if (resultado != 0) return resultado;
dispositivo->configuracion_registro = dispositivo->buffer_registro[2];
uint8_t config = dispositivo->configuracion_registro;
dispositivo->configuracion.tasa_muestreo = (config >> 2) & 0x03;
dispositivo->configuracion.amplificacion = config & 0x03;
dispositivo->configuracion.modo_operacion = (config >> 4) & 0x01;
return 0;
}
int ads1110_medir_voltaje(ads1110_dispositivo_t *dispositivo, float *milivoltios) {
if (!dispositivo || !dispositivo->leer_i2c || !milivoltios) return -1;
int resultado = dispositivo->leer_i2c(dispositivo->direccion_dispositivo,
dispositivo->buffer_registro, 3);
if (resultado != 0) return resultado;
uint16_t valor_raw = (dispositivo->buffer_registro[0] << 8) |
dispositivo->buffer_registro[1];
float factor_tasa = factores_tasa[dispositivo->configuracion.tasa_muestreo];
float factor_ganancia = factores_ganancia[dispositivo->configuracion.amplificacion];
*milivoltios = valor_raw * (VOLTAJE_REFERENCIA_MV / factor_tasa) / factor_ganancia;
return 0;
}
int ads1110_medir_corriente(ads1110_dispositivo_t *dispositivo, float *miliamperios) {
if (!miliamperios) return -1;
float voltaje_medido;
int resultado = ads1110_medir_voltaje(dispositivo, &voltaje_medido);
if (resultado == 0) {
if (dispositivo->resistencia_shunt > 0.0f) {
*miliamperios = voltaje_medido / dispositivo->resistencia_shunt;
return 0;
}
return -2;
}
return resultado;
}
int ads1110_establecer_tasa(ads1110_dispositivo_t *dispositivo, ads1110_rate_t tasa) {
if (!dispositivo) return -1;
dispositivo->configuracion_registro &= 0xF3;
dispositivo->configuracion_registro |= (tasa << 2);
return 0;
}
int ads1110_obtener_tasa(ads1110_dispositivo_t *dispositivo, ads1110_rate_t *tasa) {
if (!dispositivo || !tasa) return -1;
*tasa = dispositivo->configuracion.tasa_muestreo;
return 0;
}
int ads1110_establecer_ganancia(ads1110_dispositivo_t *dispositivo, ads1110_gain_t ganancia) {
if (!dispositivo) return -1;
dispositivo->configuracion_registro &= 0xFC;
dispositivo->configuracion_registro |= ganancia;
return 0;
}
int ads1110_obtener_ganancia(ads1110_dispositivo_t *dispositivo, ads1110_gain_t *ganancia) {
if (!dispositivo || !ganancia) return -1;
*ganancia = dispositivo->configuracion.amplificacion;
return 0;
}
int ads1110_establecer_modo(ads1110_dispositivo_t *dispositivo, ads1110_mode_t modo) {
if (!dispositivo) return -1;
dispositivo->configuracion_registro &= 0xEF;
dispositivo->configuracion_registro |= (modo << 4);
return 0;
}
int ads1110_obtener_modo(ads1110_dispositivo_t *dispositivo, ads1110_mode_t *modo) {
if (!dispositivo || !modo) return -1;
*modo = dispositivo->configuracion.modo_operacion;
return 0;
}
int ads1110_definir_resistencia(ads1110_dispositivo_t *dispositivo, float resistencia) {
if (!dispositivo || resistencia <= 0) return -1;
dispositivo->resistencia_shunt = resistencia;
return 0;
}
int ads1110_consultar_resistencia(ads1110_dispositivo_t *dispositivo, float *resistencia) {
if (!dispositivo || !resistencia) return -1;
*resistencia = dispositivo->resistencia_shunt;
return 0;
}
Pruebas Unitarias con Google Test
El siguiente fragmento demuestra la verificación de funciones básicas del driver:
#include "gtest/gtest.h"
#include "ads1110_driver.h"
static uint8_t direccion_capturada;
static uint8_t datos_escritos[1];
static uint8_t datos_leidos[3] = {0x00, 0x00, 0x8C};
static int callback_escritura(uint8_t direccion, uint8_t *datos, uint8_t longitud) {
direccion_capturada = direccion;
datos_escritos[0] = datos[0];
return 0;
}
static int callback_lectura(uint8_t direccion, uint8_t *buffer, uint8_t longitud) {
direccion_capturada = direccion;
buffer[0] = datos_leidos[0];
buffer[1] = datos_leidos[1];
buffer[2] = datos_leidos[2];
return 0;
}
TEST(ADS1110Driver, InicializacionCorrecta) {
ads1110_dispositivo_t dispositivo;
int resultado = ads1110_inicializar(&dispositivo, ADS1110_ADDR_A0,
callback_escritura, callback_lectura);
EXPECT_EQ(resultado, 0);
EXPECT_EQ(dispositivo.direccion_dispositivo, ADS1110_ADDR_A0);
EXPECT_FLOAT_EQ(dispositivo.resistencia_shunt,和马其顿.0f);
}
TEST(ADS1110Driver, MedicionVoltaje) {
ads1110_dispositivo_t dispositivo;
ads1110_inicializar(&dispositivo, ADS1110_ADDR_A0,
callback_escritura, callback_lectura);
datos_leidos[0] = 0x64;
datos_leidos[1] = 0x00;
float voltaje;
int resultado = ads1110_medir_voltaje(&dispositivo, &voltaje);
EXPECT_EQ(resultado, 0);
EXPECT_NEAR(voltaje, 1600.0f, 0.1f);
}
TEST(ADS1110Driver, ConfiguracionTasaMuestreo) {
ads1110_dispositivo_t dispositivo;
ads1110_inicializar(&dispositivo, ADS1110_ADDR_A0,
callback_escritura, callback_lectura);
ads1110_establecer_tasa(&dispositivo, ADS1110_SPS_240);
ads1110_configurar_dispositivo(&dispositivo);
ads1110_rate_t tasa;
ads1110_obtener_tasa(&dispositivo, &tasa);
EXPECT_EQ(tasa, ADS1110_SPS_240);
}
Esta implementación proporciona una capa de abstracción para el control del ADS1110, permitiendo su uso independiente de la plataforma de hardware específica. Los callbacks de I2C deben ser implementados según el microcontrolador o sistema utilizado.