LP-AM243: LP-AM243 ETHERNET/IP WITH I2C ADS1115

Part Number: LP-AM243
Other Parts Discussed in Thread: ADS1115

Tool/software:

Hi . Please review my code.

#include <stdbool.h>
#include <stdint.h>
#include "osal_error.h"
#include "ti_board_open_close.h"
#include "board/led.h"
#include "app8AI.h"
#include "appMutex.h"
#include "ti_drivers_config.h"
#include "drivers/i2c.h"
#include "kernel/dpl/ClockP.h"
#include "EI_API.h"

// ADS1115 I2C Addresses
#define ADS1115_ADDR_1 (0x48U) // Single ADS1115 (48H)
// ADS1115 Register Pointers
#define ADS1115_POINTER_CONVERT (0x00U)
#define ADS1115_POINTER_CONFIG (0x01U)
// ADS1115 Configuration Bits
#define ADS1115_CONFIG_OS_SINGLE (0x8000U)
#define ADS1115_CONFIG_MODE_SINGLE (0x0100U)
#define ADS1115_CONFIG_DR_860SPS (0x00E0U) // 860 SPS for speed

// PGA/FSR Settings for different input ranges (bits [11:9])
#define ADS1115_CONFIG_PGA_MASK (0x0E00U) // Mask for PGA bits
#define ADS1115_CONFIG_PGA_6_144V (0x0000U) // ±6.144 V
#define ADS1115_CONFIG_PGA_4_096V (0x0200U) // ±4.096 V
#define ADS1115_CONFIG_PGA_2_048V (0x0400U) // ±2.048 V (power-up default)
#define ADS1115_CONFIG_PGA_1_024V (0x0600U) // ±1.024 V (0-5V range)
#define ADS1115_CONFIG_PGA_0_512V (0x0800U) // ±0.512 V
#define ADS1115_CONFIG_PGA_0_256V (0x0A00U) // ±0.256 V (4-20mA / 0-20mA)

// Default configuration for 0-5V range
#define ADS1115_CONFIG_DEFAULT (ADS1115_CONFIG_OS_SINGLE | ADS1115_CONFIG_PGA_1_024V | \
ADS1115_CONFIG_MODE_SINGLE | ADS1115_CONFIG_DR_860SPS)
// Channel to MUX mapping for ADS1115 - 4 Single-Ended Channels
#define ADS1115_MUX_AIN0_GND (0x4000U) // Channel 0: AIN0 vs GND
#define ADS1115_MUX_AIN1_GND (0x5000U) // Channel 1: AIN1 vs GND
#define ADS1115_MUX_AIN2_GND (0x6000U) // Channel 2: AIN2 vs GND
#define ADS1115_MUX_AIN3_GND (0x7000U) // Channel 3: AIN3 vs GND

typedef struct EI_APP8AI_Industrial
{
uint32_t instance;
uint32_t value[4]; // 4 channels
I2C_Handle i2cHandle;
bool i2cInitialized;
bool ads1_present;
uint8_t ads1_addr;
} EI_APP8AI_Industrial_t;

static EI_APP8AI_Industrial_t EI_APP_8AI_industrial_s;

// Conversion functions for accurate voltage/current calculations
static inline uint32_t adc_code_to_mV(uint16_t code, uint16_t fsr_mV)
{
// code is 0..32767 for single-ended
return ((uint32_t)code * fsr_mV) / 32767U; // at ADS1115 pins
}

static inline uint32_t board_input_mV(uint16_t code, uint16_t fsr_mV)
{
// Front-end ~0.2 gain → external voltage is 5x the ADC pin voltage
return (adc_code_to_mV(code, fsr_mV) * 5U);
}

static inline uint32_t board_input_uA(uint16_t code, uint16_t fsr_mV)
{
// Current = Vext / 62Ω. mV → uA : (mV*1000)/62
uint32_t mv_ext = board_input_mV(code, fsr_mV);
return (mv_ext * 1000U) / 62U;
}

static bool probe_ads1115_device(uint8_t address)
{
uint8_t wrData[1] = {ADS1115_POINTER_CONFIG}, rdData[2];
I2C_Transaction i2cTransaction;
I2C_Transaction_init(&i2cTransaction);
i2cTransaction.writeBuf = wrData;
i2cTransaction.writeCount = 1;
i2cTransaction.readBuf = rdData;
i2cTransaction.readCount = 2;
i2cTransaction.targetAddress = address;
if (I2C_transfer(EI_APP_8AI_industrial_s.i2cHandle, &i2cTransaction)) {
uint16_t config = (rdData[0] << 8) | rdData[1];
return (config != 0x0000 && config != 0xFFFF);
}
return false;
}

static void App_adcConfig(void)
{
I2C_Params params;
I2C_Params_init(&params);
// Use default I2C parameters - no need to set bitRate explicitly
EI_APP_8AI_industrial_s.i2cHandle = I2C_open(CONFIG_I2C1, &params);
if (!EI_APP_8AI_industrial_s.i2cHandle) {
OSAL_printf("I2C open failed\n");
return;
}
OSAL_printf("I2C opened successfully\n");
EI_APP_8AI_industrial_s.ads1_addr = ADS1115_ADDR_1;
EI_APP_8AI_industrial_s.ads1_present = probe_ads1115_device(ADS1115_ADDR_1);
EI_APP_8AI_industrial_s.i2cInitialized = EI_APP_8AI_industrial_s.ads1_present;
if (!EI_APP_8AI_industrial_s.i2cInitialized) {
OSAL_printf("No ADS1115 device found at 0x%02X\n", ADS1115_ADDR_1);
} else {
OSAL_printf("ADS1115 found at 0x%02X\n", ADS1115_ADDR_1);
}
}

static void App_adcInit(void)
{
App_adcConfig();
}

static void App_adcStart(void)
{
OSAL_printf("ADS1115 I2C ADC started\n");
}

static void App_adcStop(void)
{
OSAL_printf("ADS1115 I2C ADC stopped\n");
}

static void App_adcDeInit(void)
{
if (EI_APP_8AI_industrial_s.i2cInitialized && EI_APP_8AI_industrial_s.i2cHandle != NULL) {
I2C_close(EI_APP_8AI_industrial_s.i2cHandle);
EI_APP_8AI_industrial_s.i2cInitialized = false;
OSAL_printf("I2C closed\n");
}
}

uint32_t EI_APP_8AI_init_i2c(EI_APP_8AI_SInit_t* pParams)
{
uint32_t result = OSAL_GENERAL_ERROR;
EI_APP_8AI_industrial_s.instance = pParams->industrial8AIsInst;
EI_APP_8AI_industrial_s.i2cInitialized = false;
for (uint32_t i = 0; i < 4; i++) {
EI_APP_8AI_industrial_s.value[i] = 0;
}
App_adcInit();
if (EI_APP_8AI_industrial_s.i2cInitialized) {
OSAL_printf("I2C ADS1115 initialization successful\n");
result = OSAL_NO_ERROR;
} else {
OSAL_printf("I2C ADS1115 initialization failed\n");
result = OSAL_GENERAL_ERROR;
}
return result;
}

uint32_t EI_APP_8AI_deInit_i2c(void)
{
App_adcStop();
App_adcDeInit();
return OSAL_NO_ERROR;
}

void EI_APP_8AI_industrialGet_i2c(uint32_t channel, uint32_t *value)
{
uint8_t addr;
uint16_t mux, config, ads1115Value;
uint8_t wrData[3], rdData[2];
I2C_Transaction i2cTransaction;
uint32_t timeout_count;

if (!EI_APP_8AI_industrial_s.i2cInitialized || channel >= 4 || value == NULL) {
OSAL_printf("Invalid state or channel %d\n", channel);
*value = 0;
return;
}

if (!EI_APP_8AI_industrial_s.ads1_present) {
OSAL_printf("ADS1115 not present for channel %d\n", channel);
*value = 0;
return;
}
addr = EI_APP_8AI_industrial_s.ads1_addr;

switch(channel) {
case 0: mux = ADS1115_MUX_AIN0_GND; break;
case 1: mux = ADS1115_MUX_AIN1_GND; break;
case 2: mux = ADS1115_MUX_AIN2_GND; break;
case 3: mux = ADS1115_MUX_AIN3_GND; break;
default: *value = 0; return;
}

config = ADS1115_CONFIG_DEFAULT | mux;
wrData[0] = ADS1115_POINTER_CONFIG;
wrData[1] = (config >> 8) & 0xFF;
wrData[2] = config & 0xFF;
I2C_Transaction_init(&i2cTransaction);
i2cTransaction.writeBuf = wrData;
i2cTransaction.writeCount = 3;
i2cTransaction.targetAddress = addr;

if (!I2C_transfer(EI_APP_8AI_industrial_s.i2cHandle, &i2cTransaction)) {
OSAL_printf("I2C config write failed for channel %d, addr 0x%02X\n", channel, addr);
*value = 0;
return;
}

timeout_count = 10; // 10ms timeout for 860 SPS
while (timeout_count--) {
wrData[0] = ADS1115_POINTER_CONFIG;
I2C_Transaction_init(&i2cTransaction);
i2cTransaction.writeBuf = wrData;
i2cTransaction.writeCount = 1;
i2cTransaction.readBuf = rdData;
i2cTransaction.readCount = 2;
i2cTransaction.targetAddress = addr;

if (I2C_transfer(EI_APP_8AI_industrial_s.i2cHandle, &i2cTransaction)) {
config = (rdData[0] << 8) | rdData[1];
if (config & 0x8000U) {
break;
}
} else {
OSAL_printf("I2C config read failed for channel %d, addr 0x%02X\n", channel, addr);
}
ClockP_usleep(1000);
}

if (timeout_count == 0) {
OSAL_printf("Conversion timeout for channel %d, addr 0x%02X\n", channel, addr);
*value = 0;
return;
}

wrData[0] = ADS1115_POINTER_CONVERT;
I2C_Transaction_init(&i2cTransaction);
i2cTransaction.writeBuf = wrData;
i2cTransaction.writeCount = 1;
i2cTransaction.readBuf = rdData;
i2cTransaction.readCount = 2;
i2cTransaction.targetAddress = addr;

if (I2C_transfer(EI_APP_8AI_industrial_s.i2cHandle, &i2cTransaction)) {
ads1115Value = (rdData[0] << 8) | rdData[1];
OSAL_printf("Channel %d raw value: 0x%04X\n", channel, ads1115Value);
int16_t raw = (int16_t)ads1115Value;
if (raw < 0) raw = 0;

// Determine FSR from current configuration using mask
uint16_t pga = (config & ADS1115_CONFIG_PGA_MASK);
uint16_t fsr_mV =
(pga == ADS1115_CONFIG_PGA_0_256V) ? 256 :
(pga == ADS1115_CONFIG_PGA_0_512V) ? 512 :
(pga == ADS1115_CONFIG_PGA_1_024V) ? 1024 :
(pga == ADS1115_CONFIG_PGA_2_048V) ? 2048 :
(pga == ADS1115_CONFIG_PGA_4_096V) ? 4096 : 6144; // default case 6.144 V

// Convert to external voltage (accounting for board's 5x scale factor)
uint32_t mv_ext = board_input_mV(raw, fsr_mV);
OSAL_printf("Channel %d voltage: %dmV (external)\n", channel, mv_ext);
*value = mv_ext;
EI_APP_8AI_industrial_s.value[channel] = mv_ext; // Store in struct
} else {
OSAL_printf("I2C conversion read failed for channel %d, addr 0x%02X\n", channel, addr);
*value = 0;
}
}

  • Hi,

    While we assign this to the relevant expert, can you please point to any specific areas of the code that you'd like us to review/evaluate?

    A bit of background on the use-case could be helpful as well.

    Regards
    Archit