diff --git a/examples/tests/ads1219/Makefile b/examples/tests/ads1219/Makefile new file mode 100644 index 000000000..cc254f5c1 --- /dev/null +++ b/examples/tests/ads1219/Makefile @@ -0,0 +1,13 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := main.c ads1219.c + +STACK_SIZE := 2048 + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ads1219/README.md b/examples/tests/ads1219/README.md new file mode 100644 index 000000000..a8212394a --- /dev/null +++ b/examples/tests/ads1219/README.md @@ -0,0 +1,3 @@ +# ADS1219 Testing + +The ADS1219 is the external ADC used on the ENTS board for high fidelity analog measurements. This tests the basic functionality of the ADS1219 using the userspace I2C driver. diff --git a/examples/tests/ads1219/ads1219.c b/examples/tests/ads1219/ads1219.c new file mode 100644 index 000000000..daec9da7d --- /dev/null +++ b/examples/tests/ads1219/ads1219.c @@ -0,0 +1,276 @@ +/** + * @file ads.c + * @author Stephen Taylor + * @brief Soil Power Sensor ADS12 library + * @date 11/27/2023 + * + * This file provides a function to read from the ADS1219. + * + * This file was copied and modified from the ENTS libraries. + * + * TODO (jmadden173): Add support for data ready pin + * TODO (jmadden173): Improve lib with big bang for regs + * TODO (jmadden173): Add offset calibration at startup + **/ + +#include "ads1219.h" + +#include +#include +#include + + +// Generic calibration values that give rough estimate +static const double voltage_calibration_m = -0.00039326; +static const double voltage_calibration_b = 4.92916378e-05; +static const double current_calibration_m = -1.18693164e-10; +static const double current_calibration_b = 4.14518594e-05; + +static const uint8_t addr = 0x40; +/** i2c address left shifted one bit for hal i2c funcs */ +static uint8_t addrls = addr << 1; + +/** i2c address */ +typedef enum { + /** Reset the device */ + cmd_reset = 0x06, + /** Start or restart conversion */ + cmd_start = 0x08, + /** Enter power-down mode */ + cmd_powerdown = 0x02, + /** Read data */ + cmd_rdata = 0x10, + /** Write to register */ + // prev 0x80 + cmd_rreg = 0x20, + /** Read from register */ + cmd_wreg = 0x40 +} ads1219_cmd_t; + +/** + * Control register breakdown. + * + * The implementation for the bit field uses the first value as the LSB. Aka + * vref is the LSB and mux is the MSB. + * + * 7:5 MUX (default) + * 4 Gain (default) + * 3:2 Data rate (default) + * 1 Conversion mode (default) + * 0 VREF (External reference 3.3V) + */ +typedef union { + uint8_t value; + struct { + uint8_t vref : 1; + uint8_t mode : 1; + uint8_t dr : 2; + uint8_t gain : 1; + uint8_t mux : 3; + } bits; +} ConfigReg; + +/** + * @brief GPIO port for adc data ready line + * + * @see data_ready_pin + */ +//const GPIO_TypeDef *data_ready_port = GPIOC; + +/** + * @brief GPIO pin for adc data ready line + * + */ +//const uint16_t data_ready_pin = GPIO_PIN_0; + +/** + * @brief Turn on power to analog circuit + * + * Has a blocking wait of 1 mS to account for the startup time of OpAmps. This + * should be an order of magnitude greater than the startup or turn-on time of + * the opamps used in the circuit. + * + * MAX9944 - N/A + * INA296 - 20 uS + * THS4532 - 420 nS + * + * @see data_ready_pin + */ +void power_on(void); + +/** + * @brief Turn off power to analog circuit + * + * @see data_ready_pin + */ +void power_off(void); + +/** + * @brief Measure from the adc + * + * @param mwas Raw measurement + * + * @return Raw measurement from adc + */ +ads1219_status_t measure(uint32_t *meas); + +/** + * @brief This function reconfigures the ADS1219 based on the parameter reg_data + * + * @param reg_data + * @return ads1219_status_t + */ +ads1219_status_t configure(const ConfigReg reg_data); + +ads1219_status_t ads1219_reset(void) { + int ret = RETURNCODE_SUCCESS; + + // Send the reset code + uint8_t write_buf[1] = {cmd_reset}; + ret = i2c_master_write_sync(addrls, write_buf, 1); + if (ret != RETURNCODE_SUCCESS) { + return ADS1219_ERROR; + } + + // wait minimum 500 us to reach steady state + libtocksync_alarm_delay_ms(1); + + return ADS1219_SUCCESS; +} + +ads1219_status_t configure(const ConfigReg reg_data) { + int ret = RETURNCODE_SUCCESS; + uint8_t i2c_data[2] = {cmd_wreg, reg_data.value}; + ret = i2c_master_write_sync(addrls, (uint8_t *)i2c_data, sizeof(i2c_data)); + if (ret != RETURNCODE_SUCCESS) { + return ADS1219_CONFIGURE; + } + + return ADS1219_SUCCESS; +} + +ads1219_status_t ads1219_voltage_raw(uint32_t *voltage) { + ads1219_status_t ret = ADS1219_SUCCESS; + + ConfigReg reg_data = {0}; + reg_data.bits.vref = 1; + + // 0x21 is single shot and 0x23 is continuos + // configure to read voltage + ret = configure(reg_data); + if (ret != ADS1219_SUCCESS) { + return ret; + } + + ret = measure(voltage); + if (ret != ADS1219_SUCCESS) { + return ret; + } + + return ret; +} + +ads1219_status_t ads1219_voltage(double *voltage) { + ads1219_status_t ret = ADS1219_SUCCESS; + + uint32_t raw = 0; + ret = ads1219_voltage_raw(&raw); + + *voltage = (voltage_calibration_m * (double) raw) + voltage_calibration_b; + *voltage /= 1000; + + return ret; +} + +ads1219_status_t ads1219_current_raw(uint32_t *current) { + ads1219_status_t ret = ADS1219_SUCCESS; + double meas = 0.0; + + ConfigReg reg_data = {0}; + reg_data.bits.mux = 0b001; + reg_data.bits.vref = 1; + + // 0x21 is single shot and 0x23 is continuos + // configure to read voltage + ret = configure(reg_data); + if (ret != ADS1219_SUCCESS) { + return -1; + } + + ret = measure(current); + if (ret != ADS1219_SUCCESS) { + return -1; + } + + return meas; +} + +ads1219_status_t ads1219_current(double *current) { + ads1219_status_t ret = ADS1219_SUCCESS; + + uint32_t raw = 0; + ret = ads1219_current_raw(&raw); + + *current = (current_calibration_m * (double) raw) + current_calibration_b; + return ret; +} + +void power_on(void) { + //// set high + //HAL_GPIO_WritePin(POWERDOWN_GPIO_Port, POWERDOWN_Pin, GPIO_PIN_SET); + //// delay for settling of analog components + //HAL_Delay(1); +} + +void power_off(void) { + //// set low + //HAL_GPIO_WritePin(POWERDOWN_GPIO_Port, POWERDOWN_Pin, GPIO_PIN_RESET); +} + +ads1219_status_t measure(uint32_t *meas) { + int ret = RETURNCODE_SUCCESS; + + uint8_t rx_data[3] = {0x00, 0x00, 0x00}; + + power_on(); + + // start conversion + uint8_t buf[1] = {cmd_start}; + ret = i2c_master_write_sync(addrls, buf, 1); + if (ret != RETURNCODE_SUCCESS) { + return ADS1219_CONVERSION; + } + + // constant delay + libtocksync_alarm_delay_ms(100); + + // TODO(jmadden173) implement data ready pin wait + // Wait for the DRDY pin on the ADS12 to go low, this means data is ready + //while (HAL_GPIO_ReadPin(data_ready_port, data_ready_pin)) {} + + // send read data command + buf[0] = cmd_rdata; + ret = i2c_master_write_sync(addrls, buf, 1); + if (ret != RETURNCODE_SUCCESS) { + return ADS1219_MEASURE; + } + + // read 3 bytes of data + ret = i2c_master_read_sync(addrls, rx_data, 3); + if (ret != RETURNCODE_SUCCESS) { + return ADS1219_MEASURE; + } + + power_off(); + + // Combine the 3 bytes into a 24-bit value + *meas = ((int32_t)rx_data[0] << 16) | ((int32_t)rx_data[1] << 8) | + ((int32_t)rx_data[2]); + // Check if the sign bit (24th bit) is set + if (*meas & 0x800000) { + // Extend the sign to 32 bits + *meas |= 0xFF000000; + } + + return ADS1219_SUCCESS; +} diff --git a/examples/tests/ads1219/ads1219.h b/examples/tests/ads1219/ads1219.h new file mode 100644 index 000000000..c15e49ee4 --- /dev/null +++ b/examples/tests/ads1219/ads1219.h @@ -0,0 +1,103 @@ +/** + ****************************************************************************** + * @file ads1219.h + * @author Stephen Taylor + * @brief This file contains all the function prototypes for + * the ads.c file + * @date 11/27/2023 + * + * + * This file was copied and modified from the ENTS libraries + ****************************************************************************** + * Copyright [2023] + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @ingroup stm32 + * @defgroup ads ADS1219 + * @brief Library for interfacing with the ADS1219 ADC + * + * This library is designed to read measurements from the ADS1219 ADC. When not + * actively taking measurements, the analog frontend is power down to reduce + * parasitic current. + * + * Library expected I2C and GPIO to be initialized before use. + * + * Example: @ref example_adc.c + * + * Datasheet: https://www.ti.com/product/ADS1219 + * + * + * @{ + */ + +typedef enum { + ADS1219_SUCCESS, + ADS1219_ERROR = -1, + ADS1219_CONFIGURE = -2, + ADS1219_MEASURE = -3, + ADS1219_CONVERSION = -4 +} ads1219_status_t; + + +/** +****************************************************************************** +* @brief This function starts up the ADS1219 +* +* This function is a wrapper for the STM32 HAl I2C library. The +*ADS1219 uses I2C the I2C communication protocol. This function configures then +*ADS1219 for single read mode. Note: the ADS1219 requires a minimum of 500us +*when it is powered on. +* +* @param void +* @return HAL_StatusTypeDef +********************************************\n********************************** +*/ +ads1219_status_t ads1219_reset(void); + +/** +****************************************************************************** +* @brief This function reads the current ADC voltage value. +* +* This function is a wrapper for the STM32 HAl I2C library. The +*ADS1219 uses I2C the I2C communication protocol. This version simply chops the +*noisy bits. +* +* @param void +* @return double, current ADC reading in microvolts +****************************************************************************** +*/ +ads1219_status_t ads1219_voltage(double *voltage); +ads1219_status_t ads1219_voltage_raw(uint32_t *voltage); + +/** +****************************************************************************** +* @brief This function reads the current ADC ampere value. +* +* This function is a wrapper for the STM32 HAl I2C library. The +*ADS1219 uses I2C the I2C communication protocol. This version simply chops the +*noisy bits. +* +* @param void +* @return double, current ADC reading in microamps +****************************************************************************** +*/ +ads1219_status_t ads1219_current(double *current); +ads1219_status_t ads1219_current_raw(uint32_t *current); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif diff --git a/examples/tests/ads1219/main.c b/examples/tests/ads1219/main.c new file mode 100644 index 000000000..61d7a8bc6 --- /dev/null +++ b/examples/tests/ads1219/main.c @@ -0,0 +1,52 @@ +#include +#include + +#include + +#include "ads1219.h" + +int main(void) { + printf("Example ADS1219\n"); + + // TODO Need to add support for userspace gpio + // setup gpio pin (PA9) + + //bool gpio_exists = libtock_gpio_exists(); + //printf("[Test] GPIO exists: %s\n", gpio_exists ? "true" : "false"); + + //int gpio_count = 0; + //libtock_gpio_count(&gpio_count); + //printf("[Test] GPIO count: %d\n", gpio_count); + + //uint32_t powerdown = 0; + //libtock_gpio_enable_output(powerdown); + //libtock_gpio_clear(powerdown); + + + ads1219_status_t ret = ADS1219_SUCCESS; + + ret = ads1219_reset(); + if (ret != ADS1219_SUCCESS) { + printf("ADS1219 reset failed\n"); + } + + for (int i = 0; ; i++) { + uint32_t voltage = 0; + ret = ads1219_voltage_raw(&voltage); + if (ret != ADS1219_SUCCESS) { + printf("ADS1219 voltage read failed\n"); + continue; + } + + uint32_t current = 0; + ret = ads1219_current_raw(¤t); + if (ret != ADS1219_SUCCESS) { + printf("ADS1219 current read failed\n"); + continue; + } + + printf("v: %ld, i: %ld\n", voltage, current); + + libtocksync_alarm_delay_ms(1000); + } +}