configs/components/ads131m08/ads131m08.h

230 lines
9.1 KiB
C++

#pragma once
// using external voltage reference
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/optional.h"
#include "ads131m08_defs.h"
#include "frame.h"
#include <string>
#include <charconv>
#include <format>
//#include "esphome/core/component.h"
//#include <esphome/core/hal.h>
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
//
//#include <vector>
//#include <esphome/core/gpio.h>
//#include <freertos/FreeRTOS.h>
//#include <freertos/semphr.h>
//#include <atomic>
namespace esphome {
namespace ads131m08 {
static const int MAX_CHANNELS = 11; // for debugging
static const int ADC_CHANNELS = 8;
static const int SAMPLE_TIME_SENSOR = 8; // for debugging
static const int MAX_SAMPLES_SENSOR = 9; // for debugging
static const int CRC_ERRORS_SENSOR = 10; // for debugging
typedef union {
int32_t i;
float f;
uint16_t u[2];
uint8_t b[4];
} flex32_t;
/* Adc Structure. Ch can be read as int32 or float*/
struct AdcOutput
{
uint16_t status;
flex32_t ch[ADC_CHANNELS];
};
//#define SET_IRAM IRAM_ATTR
#define SET_IRAM
typedef std::vector<uint8_t> spiframe;
typedef uint_str<uint16_t>::Ty_string uint16_str; // we want to use the stack to pass small arrays
class ADS131M08Hub : public Component, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_8MHZ>
{
friend class ads131m08_select;
public:
enum crc_pos: uint8_t {
crc_after_frame,
crc_after_firstword,
};
// register config values used
static constexpr uint16_t reg_clock_cfg = MASK_CLOCK_EXTREF_EN | OSR_1024 | PM_HIGH_RESOLUTION;
static constexpr uint16_t reg_clock_allch_on = MASK_CLOCK_ALLCH | reg_clock_cfg;
static constexpr uint16_t reg_clock_allch_off = ~MASK_CLOCK_ALLCH & reg_clock_cfg;
static constexpr uint16_t reg_mode_cfg16 = WLENGTH_16_BITS | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
static constexpr uint16_t reg_mode_cfg24 = WLENGTH_24_BITS | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
static constexpr uint16_t reg_mode_cfg32 = WLENGTH_32_BITS_MSB_SIGN_EXTEND | DRDY_FMT_PULSE | TIMEOUT_ENABLED;
// masks for the above
static constexpr uint16_t reg_clock_cfg_mask = MASK_CLOCK_EXTREF_EN | MASK_CLOCK_OSR | MASK_CLOCK_PWR;
static constexpr uint16_t reg_clock_allch_mask = MASK_CLOCK_ALLCH_OFF | reg_clock_cfg_mask;
static constexpr uint16_t reg_mode_cfg_mask = MASK_MODE_WLENGTH | MASK_MODE_DRDY_FMT | MASK_MODE_TIMEOUT;
// Semaphore handles
SemaphoreHandle_t data_ready_semhandle = NULL;
//SemaphoreHandle_t spi_mutex = NULL;
// from datasheet pg. 93:
const int numFrameWords = 10; // Number of words in a full ADS131M08 SPI frame
spiframe base_frame;
bool firstRead = true; // Flag to tell us if we are reading ADC data for the first time
signed long adcData; // Location where DMA will store ADC data in memory, length defined elsewhere
ADS131M08Hub();
void txf_init();
bool adc_initialize(uint8_t word_length);
bool adc_set_word_length(uint8_t word_length);
// end of from datasheet
void set_channel_gain(uint8_t channel, ADS131M08_PGA_GAIN gain);
//virtual void spi_setup() override;
void setup() override;
void loop() override;
void set_drdy_pin(InternalGPIOPin *pin) { drdy_pin_ = pin; }
void set_sync_reset_pin(InternalGPIOPin *pin) { sync_reset_pin_ = pin; }
void set_clock_frequency(float clock_frequency) { this->clock_frequency_ = clock_frequency; }
void register_sensor_ac(int channel, sensor::Sensor *s) { sensors_ac[channel] = s; }
void register_sensor_dc(int channel, sensor::Sensor *s) { sensors_dc[channel] = s; }
void set_reference_voltage(float reference_voltage) { this->reference_voltage_ = reference_voltage; }
void set_osr(uint16_t osr) { this->osr_ = osr; }
void dump_config() override;
// from tpcorrea
ADS131M08_PGA_GAIN pgaGain[ADC_CHANNELS] = {PGA_1}; // Store the current PGA gain settings for each channel
int8_t is_data_ready_soft(uint8_t channel);
bool is_data_ready();
bool is_reset_status();
bool is_lock_spi();
bool set_drdy_format(uint8_t drdy_format);
bool set_drdy_idle_state(uint8_t drdy_state);
bool set_power_mode(uint8_t power_mode);
bool set_channel_enable(uint8_t channel, bool enable);
bool set_channel_pga(uint8_t channel, ADS131M08_PGA_GAIN pga);
ADS131M08_PGA_GAIN get_channel_pga(uint8_t channel);
void set_global_chop(uint16_t global_chop);
void set_global_chop_delay(uint16_t delay);
bool set_channel_input_selection(uint8_t channel, ADS131M08_INPUT_CHANNEL_MUX input);
bool set_channel_offset_calibration(uint8_t channel, int32_t offset);
bool set_channel_gain_calibration(uint8_t channel, uint32_t gain);
bool set_channel_phase_calibration(uint8_t channel, int16_t phase_offset);
bool set_dcblock_filter_disable(uint8_t channel, bool disable);
void set_measure_rms(uint8_t channel, bool enable);
float get_sampled_value(uint8_t channel) { return sampled_values_[channel]; }
bool set_reg_osr();
void set_full_scale(uint8_t channel, float scale);
float get_full_scale(uint8_t channel);
uint16_t get_id();
uint16_t get_mode_reg();
uint16_t get_clock_reg();
uint16_t get_cfg_reg();
AdcOutput read_adc_raw();
AdcOutput read_adc_float();
protected:
static void IRAM_ATTR isr_handler(ADS131M08Hub *arg);
//volatile bool data_ready_{false};
//static void isr(ADS131M08Hub *arg);
float reference_voltage_;
float clock_frequency_;
InternalGPIOPin *drdy_pin_;
InternalGPIOPin *sync_reset_pin_= {nullptr};
sensor::Sensor *sensors_ac[MAX_CHANNELS] = {nullptr};
sensor::Sensor *sensors_dc[MAX_CHANNELS] = {nullptr};
float sampled_values_[ADC_CHANNELS];
bool rms_enabled_[ADC_CHANNELS];
bool rms_calc_req_{false};
uint16_t osr_{3};
uint8_t update_adc_word_length();
void SET_IRAM read_single();
std::pair<uint32_t, uint32_t> SET_IRAM read_multi();
bool adc_lock(bool enable);
bool adc_register_write(uint16_t address, uint16_t data);
bool adc_register_write(uint16_t address, const uint16_str& data);
uint16_str adc_register_read(uint8_t address, uint8_t nregs);
bool adc_register_write_masked(uint8_t address, uint16_t value, uint16_t mask, int line);
bool adc_register_write_masked(uint8_t start_address, const uint16_str& values, const uint16_str& masks, int line);
uint16_t readRegister(uint8_t address);
bool adc_reset_retry();
bool adc_soft_reset();
void adc_hard_reset();
void adc_sync();
void write_byte(uint8_t byte);
bool write(uint32_t data, uint8_t adcWordLength);
uint8_t read_byte();
void write_array(const spiframe& data);
void SET_IRAM read_array(spiframe& buffer);
void transfer_array(const spiframe& data_out, spiframe& data_in);
uint16_t SET_IRAM get_crc(const spiframe& frame);
bool SET_IRAM check_crc(const spiframe& frame_with_crc);
size_t SET_IRAM add_crc(spiframe& frame, crc_pos crcpos);
uint16_t SET_IRAM read_frame_crc(const spiframe& frame);
uint16_t SET_IRAM crc(uint16_t crc_register, uint8_t data);
bool SET_IRAM set_frame_word(spiframe& frame, int w_index, uint16_t data); // write 16 bit data to first two bytes of word
bool SET_IRAM set_frame_word(spiframe& frame, int w_index, uint32_t data);
uint32_t SET_IRAM get_unsigned_frame_word(const spiframe& frame, int w_index, bool force_16bits = false);
int32_t SET_IRAM get_sign_ext_frame_word(const spiframe& frame, int w_index);
std::atomic<int> adc_init_{0};
std::atomic<int> cs_ctr_{0}; // Counter to track nested CS enable/disable calls for proper handling of multiple transfers in a row
std::atomic<uint16_t> isr_ctr_{0};
void enable(const char *txt);
void disable(const char *txt);
struct chipselect {
ADS131M08Hub* parent_{nullptr};
const char *txt_;
chipselect(ADS131M08Hub *parent, const char *txt = nullptr) {
parent_ = parent;
if(parent_ != nullptr)
txt_ = txt;
parent_->enable(txt_);
}
~chipselect() {
if(parent_ != nullptr) {
parent_->disable(txt_);
parent_ = nullptr;
}
}
};
private:
uint8_t adc_word_length_{24};
float conversion_factor_{1.2/8388608.0};
const uint32_t sample_time_ = 40000; // 250 ms
float sps_{0.0f};
uint32_t num_samples_[ADC_CHANNELS];
int64_t sample_sum_[ADC_CHANNELS];
float sample_squared_sum_[ADC_CHANNELS];
// for debug only
std::string frame_to_string(const spiframe& frame);
std::string conversion_frame_to_string(const spiframe& frame);
std::string command_to_string(uint16_t cmdadr);
void print_command_response_to_string(uint16_t cmdadr_sent, const spiframe& frame);
std::string rwreg_command_frame_to_string(const spiframe& frame);
std::string status_to_string(uint16_t response);
std::string reg_data_to_string(int address, uint16_t data, bool nameonly = false);
std::string reg_addr_to_string(int address);
std::string reg_config_to_string(int address, uint16_t data, bool nameonly = false);
//temp
int first_read_data {0};
};
#ifndef CHIP_SELECT
# if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
# define CHIP_SELECT volatile chipselect cs(this, __FUNCTION__);
# else
# define CHIP_SELECT volatile chipselect cs(this);
# endif
# define CHIP_SELECTx volatile chipselect cs(this);
#endif
} // namespace ads131m08
} // namespace esphome