Added spiframe with DMA_ATTR to enable DMA transfers. Much quicker now. However, now plaque with what appears to be noise / errors on the channels. Will revert to direct publishing from main loop to see if it makes a difference.

This commit is contained in:
Chris Stuurman 2026-05-02 16:31:14 +02:00
parent c100502519
commit 678312a27d
4 changed files with 163 additions and 109 deletions

View File

@ -15,6 +15,11 @@ namespace esphome {
namespace ads131m08 {
static const char *const TAG = "ads131m08";
static const char *const white_space = " \t\n\r\f\v"; // Defines whitespace
//TODO: RREG might be longer than allocated frame size. work out max length and update following accordingly
DRAM_ATTR static spiframe tx_frame(ADS131M08Hub::numFrameWords * 4, 0);
DRAM_ATTR static spiframe rx_frame(ADS131M08Hub::numFrameWords * 4, 0);
// ADS131M08Hub::ADS131M08Hub()
// : base_frame(40, 0)
@ -76,7 +81,7 @@ void ADS131M08Hub::setup() {
// cs_ctr_=1000;
// SPIDevice::enable(); // leave CS low
// Request a high loop() execution interval
// this->high_freq_.start();
this->high_freq_.start();
}
void ADS131M08Hub::dump_config() {
@ -150,6 +155,7 @@ float ADS131M08Hub::update_conversion_factor() {
}
return this->conversion_factor_;
}
bool ADS131M08Hub::adc_initialize(uint8_t word_length) {
this->adc_init_++;
update_adc_word_length();
@ -158,17 +164,19 @@ bool ADS131M08Hub::adc_initialize(uint8_t word_length) {
return false;
}
ESP_LOGV(TAG, "Turned off all channels so short frames can be written during config");
if (!adc_register_write(REG_MODE, MODE_MASK_RESET_HAPPENED & reg_mode_cfg24)) {
ESP_LOGE(TAG, "MODE register write / read to set word size to 24bits failed");
return false;
}
// update_adc_word_length();
ESP_LOGV(TAG, "Written MODE register; Cleared the RESET flag, made DRDY active low pulse");
if (!adc_register_write(REG_THRSHLD_LSB, 0x09)) {
ESP_LOGE(TAG, "THRSHLD_LSB register write / read to set DBLOCK filter failed");
return false;
}
ESP_LOGV(TAG, "Written THRSHLD_LSB register; Set DCBLOCK filter to have a corner frequency of 622 mHz");
if (!adc_set_word_length(word_length))
return false;
// we leave should channels off, as the individual sensors should turn it on
@ -177,7 +185,6 @@ bool ADS131M08Hub::adc_initialize(uint8_t word_length) {
return false;
}
// ESP_LOGV(TAG, "Turned on all ADC channels; Re-wrote defaults for other bits in CLOCK register");
if (!drdy_pin_->digital_read()) {
ESP_LOGE(TAG, "DRDY pin is low after initialization!");
return false;
@ -237,24 +244,28 @@ void ADS131M08Hub::disable(const char *txt) {
// MUST be fast and non-blocking
void ADS131M08Hub::isr_handler(ADS131M08Hub *arg) {
{
InterruptLock lock;
// InterruptLock lock;
if (arg != nullptr) {
arg->enable_loop_soon_any_context();
arg->isr_ctr_++;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(arg->data_ready_semhandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR(); // Switch to the waiting task immediately
}
// BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// xSemaphoreGiveFromISR(arg->data_ready_semhandle, &xHigherPriorityTaskWoken);
// if (xHigherPriorityTaskWoken == pdTRUE) {
// portYIELD_FROM_ISR(); // Switch to the waiting task immediately
// }
}
}
}
void ADS131M08Hub::loop() {
void ADS131M08Hub::loop()
{
// Check the semaphore (0 timeout means non-blocking)
if (xSemaphoreTake(data_ready_semhandle, 0) == pdTRUE) {
//if (xSemaphoreTake(data_ready_semhandle, 0) == pdTRUE) {
if (this->adc_init_ == 0) {
uint32_t num_samples = 0;
uint32_t crc_errors = 0;
uint32_t iterations = 0;
uint32_t not_ready = 0;
{
CHIP_SELECTx
// Perform the read here safely outside of ISR
@ -262,15 +273,19 @@ void ADS131M08Hub::loop() {
// this->txf_init(); // implementing datasheet recommended TXF init in ISR
this->adc_sync();
if (rms_calc_req_) {
std::pair<uint32_t, uint32_t> result = read_multi();
num_samples = result.first;
crc_errors = result.second;
uint16_str result = read_multi();
num_samples = result[0];
crc_errors = result[1];
iterations = result[2];
not_ready = result[3];
uint64_t end = micros();
update_averages(SAMPLE_TIME_SENSOR, 1, static_cast<float>(end - start) / 1000.0);
update_averages(MAX_SAMPLES_SENSOR, 1, static_cast<float>(num_samples));
update_averages(CRC_ERRORS_SENSOR, 1, static_cast<float>(crc_errors));
uint16_t ctr = isr_ctr_;
update_averages(ISR_COUNT_SENSOR, 1, static_cast<float>(ctr));
update_averages(ITERATIONS_SENSOR, 1, static_cast<float>(iterations));
update_averages(NOT_READY_SENSOR, 1, static_cast<float>(not_ready));
// if(this->sensors_ac[SAMPLE_TIME_SENSOR] != nullptr)
// this->sensors_ac[SAMPLE_TIME_SENSOR]->publish_state(static_cast<float>(end - start)/1000.0);
@ -289,7 +304,8 @@ void ADS131M08Hub::loop() {
}
if (crc_errors > 30) {
ESP_LOGW(TAG, "High CRC error rate.");
adc_set_word_length(DEFAULT_WORD_LENGTH); // for some reason the adc occasionally reverts to 24bits; this will
int i = 4;
while(i-- > 0 && !adc_set_word_length(DEFAULT_WORD_LENGTH)); // for some reason the adc occasionally reverts to 24bits; this will
// reset the word length to what it should be
// update_adc_word_length();
// spiframe recoverframe(40, 0);
@ -298,14 +314,15 @@ void ADS131M08Hub::loop() {
}
// ESP_LOGW(TAG, "%llu ms (%llu us), max samples: %u", (end - start)/1000, (end - start), num_samples);
}
}
//}
}
/// @brief Set 16 bit frameword
/// @param frame
/// @param w_index
/// @param data uint16_t denotes command or CRC
/// @return
bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint16_t data) {
bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint16_t data)
{
size_t word_nbytes = adc_word_length_ >> 3;
size_t frame_words = frame.size() / word_nbytes;
int b_index = w_index * word_nbytes;
@ -334,7 +351,8 @@ bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint16_t data) {
return true;
}
bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint32_t data) {
bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint32_t data)
{
size_t word_nbytes = adc_word_length_ >> 3;
size_t frame_words = frame.size() / word_nbytes;
int b_index = w_index * word_nbytes;
@ -364,7 +382,8 @@ bool ADS131M08Hub::set_frame_word(spiframe &frame, int w_index, uint32_t data) {
}
// index(i) = zero-reference, returns i'th word, LSB aligned
uint32_t ADS131M08Hub::get_unsigned_frame_word(const spiframe &frame, int w_index, bool force_16bits) {
uint32_t ADS131M08Hub::get_unsigned_frame_word(const spiframe &frame, int w_index, bool force_16bits)
{
uint32_t result = 0;
size_t word_nbytes = adc_word_length_ >> 3;
int b_index = w_index * word_nbytes;
@ -388,7 +407,8 @@ uint32_t ADS131M08Hub::get_unsigned_frame_word(const spiframe &frame, int w_inde
return result;
}
int32_t ADS131M08Hub::get_sign_ext_frame_word(const spiframe &frame, int w_index) {
int32_t ADS131M08Hub::get_sign_ext_frame_word(const spiframe &frame, int w_index)
{
int32_t result = 0;
size_t word_nbytes = adc_word_length_ >> 3;
int b_index = w_index * word_nbytes;
@ -396,27 +416,29 @@ int32_t ADS131M08Hub::get_sign_ext_frame_word(const spiframe &frame, int w_index
ESP_LOGW(TAG, "Word index of %d out of bounds", w_index);
return result; // invalid
}
uint32_t raw = 0;
switch (adc_word_length_) {
case 32:
raw = (frame[b_index] << 24) | (frame[b_index + 1] << 16) | (frame[b_index + 2]) << 8 | frame[b_index + 3];
result = static_cast<int32_t>(raw);
result = (frame[b_index] << 24) | (frame[b_index + 1] << 16) | (frame[b_index + 2]) << 8 | frame[b_index + 3];
break;
case 24:
raw = (frame[b_index] << 16) | (frame[b_index + 1] << 8) | frame[b_index + 2];
if (raw & 0x800000)
result = static_cast<int32_t>(raw | 0xFF000000); // Sign extend
else
result = static_cast<int32_t>(raw);
result = (frame[b_index] << 24) | (frame[b_index + 1] << 16) | (frame[b_index + 2] << 8);
result = result >> 8;
break;
case 16:
raw = ((frame[b_index] << 8) | frame[b_index + 1]);
result = static_cast<int16_t>(raw);
{
int16_t raw = (frame[b_index] << 8) | frame[b_index + 1];
result = static_cast<int16_t>(raw);
}
break;
default:
ESP_LOGW(TAG, "Invalid adc word length of %d", adc_word_length_);
break;
}
//if(w_index == 1 || w_index ==2)
//if(result > 7000000 || result < -7000000) {
//ESP_LOGD(TAG, "frame: %s", frame_to_string(rx_frame).c_str());
// ESP_LOGD(TAG, "frame_word%d: %s result: [0x%08X] %d", w_index, frame_words_to_string(frame, w_index, 1).c_str(), result, result);
//}
return result;
}
@ -617,9 +639,7 @@ uint8_t ADS131M08Hub::read_byte() {
void ADS131M08Hub::read_array(spiframe &frame) {
CHIP_SELECT
for (size_t i = 0; i < frame.size(); i++) {
frame[i] = this->transfer_byte(0x00); // Send dummy byte to read data
}
this->transfer_array(frame.data(), frame.size());
}
void ADS131M08Hub::write_array(const spiframe &frame) {
@ -629,15 +649,15 @@ void ADS131M08Hub::write_array(const spiframe &frame) {
}
}
void ADS131M08Hub::transfer_array(const spiframe &tx_frame, spiframe &rx_frame) {
CHIP_SELECT
auto frame_length = tx_frame.size();
if (rx_frame.size() < frame_length)
rx_frame.resize(frame_length);
for (size_t i = 0; i < frame_length; i++) {
rx_frame[i] = this->transfer_byte(tx_frame[i]);
}
}
//void ADS131M08Hub::transfer_array(const spiframe &tx_frame, spiframe &rx_frame) {
// CHIP_SELECT
// auto frame_length = tx_frame.size();
// if (rx_frame.size() < frame_length)
// rx_frame.resize(frame_length);
// for (size_t i = 0; i < frame_length; i++) {
// rx_frame[i] = this->transfer_byte(tx_frame[i]);
// }
//}
bool ADS131M08Hub::set_measure_rms(uint8_t channel, bool enable) {
if (channel >= MAX_CHANNELS)
@ -681,20 +701,20 @@ void ADS131M08Hub::read_single() {
// hopefully reliably calculate RMS value, phase offsets, etc. the best the read_multi procedure does is to read about
// 66x 16bit / 33x 32bit word frames in 40ms which is far from ideal Once we ge the ISR and loop setup to respond fast
// enough, the following procedure should be completely re-designed
std::pair<uint32_t, uint32_t> ADS131M08Hub::read_multi() {
uint16_str ADS131M08Hub::read_multi() {
int32_t raw;
float value;
std::pair<uint32_t, uint32_t> result;
result.first = 0;
result.second = 0;
uint16_str result(4, 0);
bool crc_ok, data_ready, ac_sensor_exist, dc_sensor_exist;
uint16_t drdy_status, status;
int i, max_iters = 250; // going above 255 will result in overflows
int i, iteration = 0; // going above 255 will result in overflows
uint32_t elapsed_time = 0;
uint32_t loop_start_time = micros();
int word_nbytes = adc_word_length_ >> 3;
int frame_length = numFrameWords * word_nbytes;
spiframe frame(frame_length, 0);
//spiframe frame(frame_length, 0);
tx_frame.resize(frame_length);
rx_frame.resize(frame_length);
// integrate for rms values over #iterations or sample_time depending on what comes first
for (int i = 0; i < ADC_CHANNELS; i++) {
// last_publish_time_[i] = 0;
@ -702,29 +722,37 @@ std::pair<uint32_t, uint32_t> ADS131M08Hub::read_multi() {
sample_sum_[i] = 0;
sample_squared_sum_[i] = 0;
}
while (elapsed_time < this->sample_time_ && max_iters-- > 0) {
this->read_array(frame);
// ESP_LOGD(TAG, "max_iters: %d, frame: %s", max_iters, frame_to_string(frame).c_str());
status = get_unsigned_frame_word(frame, 0, true);
while (elapsed_time < this->sample_time_ && iteration++ < 1000) {
result[2] = iteration;
std::fill(rx_frame.begin(), rx_frame.end(), 0);
this->read_array(rx_frame);
//ESP_LOGD(TAG, "frame: %s", frame_to_string(rx_frame).c_str());
//ESP_LOGD(TAG, "iteration: %d, frame: %s", iteration, frame_to_string(rx_frame).c_str());
//ESP_LOGD(TAG, "frame_word%d: %s", 0, frame_words_to_string(rx_frame, 0, 1).c_str());
status = get_unsigned_frame_word(rx_frame, 0, true);
update_adc_word_length(status);
crc_ok = check_crc(frame);
crc_ok = check_crc(rx_frame);
if (crc_ok) {
// skip frame immediately afte resync
if ((status & MASK_STATUS_RESYNC) == 0) {
drdy_status = status & MASK_STATUS_DRDY;
// sample channels
if(drdy_status == 0){
result[3]++;
}
for (i = 0; i < ADC_CHANNELS && drdy_status != 0; i++) {
data_ready = drdy_status & 1;
drdy_status = drdy_status >> 1;
ac_sensor_exist = this->sensors_ac[i] != nullptr;
dc_sensor_exist = this->sensors_dc[i] != nullptr;
if (data_ready && (ac_sensor_exist || dc_sensor_exist)) {
raw = get_sign_ext_frame_word(frame, i + 1);
raw = get_sign_ext_frame_word(rx_frame, i + 1);
value = this->conversion_factor_ * raw;
this->sampled_values_[i] = value;
if (this->rms_enabled_[i]) {
(this->num_samples_[i])++;
(this->sample_sum_[i]) += static_cast<int64_t>(raw);
(this->sample_sum_[i]) += value;
(this->sample_squared_sum_[i]) += value * value;
} else {
// if(ac_sensor_exist)
@ -737,8 +765,8 @@ std::pair<uint32_t, uint32_t> ADS131M08Hub::read_multi() {
}
}
} else {
result.second++;
// ESP_LOGW(TAG, "max_iters: %d, frame: %s CRC error", max_iters, frame_to_string(frame).c_str());
result[1]++;
// ESP_LOGW(TAG, "iteration: %d, frame: %s CRC error", iteration, frame_to_string(frame).c_str());
}
elapsed_time = micros() - loop_start_time;
}
@ -748,20 +776,20 @@ std::pair<uint32_t, uint32_t> ADS131M08Hub::read_multi() {
ac_sensor_exist = this->sensors_ac[i] != nullptr;
dc_sensor_exist = this->sensors_dc[i] != nullptr;
auto num_samples = this->num_samples_[i];
if (num_samples > result.first) {
result.first = num_samples;
if (num_samples > result[0]) {
result[0] = num_samples;
}
if (num_samples == 0) {
// should never happen, but let's play safe and avoid dividing by zero
ESP_LOGW(TAG, "Num samples: %d", num_samples);
// should not happen, but let's play safe and avoid dividing by zero
// ESP_LOGW(TAG, "Num samples: %d", num_samples);
update_averages(i, NAN, NAN);
// if(ac_sensor_exist)
// this->sensors_ac[i]->publish_state(NAN);
// if(dc_sensor_exist)
// this->sensors_dc[i]->publish_state(NAN);
} else {
int64_t sample_sum = sample_sum_[i];
float rms_dc = this->conversion_factor_ * static_cast<float>(sample_sum) / num_samples;
float sample_sum = sample_sum_[i];
float rms_dc = sample_sum / num_samples;
float rms_ac_squared = sample_squared_sum_[i] / num_samples - rms_dc * rms_dc;
float rms_ac = 0;
if (rms_ac_squared > 0) {
@ -791,30 +819,29 @@ bool ADS131M08Hub::update_averages(uint8_t channel, float rms_ac, float rms_dc)
bool ac_sensor_exist = this->sensors_ac[channel] != nullptr;
bool dc_sensor_exist = this->sensors_dc[channel] != nullptr;
if (!ac_sensor_exist && !dc_sensor_exist) {
ESP_LOGW(TAG, "channel %d does not exist", channel);
//ESP_LOGW(TAG, "channel %d does not exist", channel);
return false;
}
// we treat update state as a critical resource that must not be clobbered
// we treat update_state as a critical resource that must not be clobbered
if (xSemaphoreTake(update_avg, (TickType_t) 50) == pdTRUE) {
uint8_t update_state = update_state_[channel];
update_state_[channel] &= 0xF0;
if (ac_sensor_exist) {
// if this is the first value, then we just store it with no averaging
if ((update_state & 0x0F) == 0) {
avg_ac_[channel] = rms_ac;
update_state |= 0x01;
update_state_[channel] = update_state;
} else {
// if we have NAN (no samples), we store it, but also also treat next valid value as the first
if (rms_ac == NAN) {
avg_ac_[channel] = NAN;
update_state &= 0xF0;
} else {
ESP_LOGI(TAG, "Setting AC avg: %f %f", avg_ac_[channel], rms_ac);
avg_ac_[channel] = 0.5 * (avg_ac_[channel] + rms_ac);
update_state_[channel] = update_state;
}
}
update_state_[channel] = update_state;
} else {
update_state_[channel] &= 0xF0;
}
update_state = update_state_[channel];
if (dc_sensor_exist) {
@ -942,6 +969,14 @@ bool ADS131M08Hub::adc_register_write(uint16_t address, uint16_t data) {
return adc_register_write(address, arg);
}
/*
//https://github.com/espressif/esp-idf/blob/v6.0.1/examples/peripherals/spi_master/lcd/main/spi_master_example_main.c
//Allocate memory for the pixel buffers
for (int i = 0; i < 2; i++) {
lines[i] = spi_bus_dma_memory_alloc(LCD_HOST, 320 * PARALLEL_LINES * sizeof(uint16_t), mem_cap);
assert(lines[i] != NULL);
}
*/
bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str &data) {
int nregs = data.size();
if (nregs == 0) {
@ -950,24 +985,25 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str &
int word_nbytes = adc_word_length_ >> 3;
int wreg_framelength = word_nbytes * (2 + nregs); // ensure room for crc
int rreg_resp_framelength = word_nbytes * (2 + (nregs == 1 ? -1 : nregs)); // add room for CRC if nregs > 1
spiframe frame(wreg_framelength, 0);
spiframe rxframe(wreg_framelength, 0);
//spiframe frame(wreg_framelength, 0);
//spiframe rxframe(wreg_framelength, 0);
uint16_t addr_regcnt_mask = (start_address << 7) | ((nregs - 1) & MASK_CMD_RW_REG_COUNT);
uint16_t wreg_addr_opcode = CMD_WREG | addr_regcnt_mask; // Combine WREG command, start_address and register count
uint16_t rreg_addr_opcode = CMD_RREG | addr_regcnt_mask; // Combine RREG command, start_address and register count
bool has_mode_reg =
(start_address == REG_MODE) || ((start_address < REG_MODE) && ((start_address + nregs) > REG_MODE));
tx_frame.resize(wreg_framelength);
rx_frame.resize(wreg_framelength);
{
CHIP_SELECT
set_frame_word(frame, 0, wreg_addr_opcode);
CHIP_SELECT set_frame_word(tx_frame, 0, wreg_addr_opcode);
for (int i = 0; i < nregs; i++) {
set_frame_word(frame, i + 1, data[i]);
set_frame_word(tx_frame, i + 1, data[i]);
}
add_crc(frame);
ESP_LOGVV(TAG, "%s\n", rwreg_command_frame_to_string(frame).c_str());
transfer_array(frame, rxframe); // WREG
add_crc(tx_frame);
//ESP_LOGD(TAG, "%s\n", rwreg_command_frame_to_string(tx_frame).c_str());
//ESP_LOGD(TAG, "Send Frame: %s", frame_to_string(tx_frame).c_str());
transfer_array(tx_frame.data(), tx_frame.size()); // WREG
}
ESP_LOGVV(TAG, "Sent Frame: %s", frame_to_string(frame).c_str());
if (has_mode_reg) {
update_adc_word_length();
}
@ -977,7 +1013,9 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str &
for (int i = 0; i < rxdata.size() && verified; i++) {
verified = rxdata[i] == data[i];
if (!verified) {
ESP_LOGE(TAG, "Write register failed: tx data= 0x%04X, rx data= 0x%04X", data[i], rxdata[i]);
ESP_LOGE(TAG, "Write \'%s\' register failed: tx data: 0x%04X, rx data: 0x%04X", reg_addr_to_string(start_address + i).c_str(), data[i], rxdata[i]);
// ESP_LOGE(TAG, "Write: %s", reg_data_to_string(start_address + i, data[i]).c_str());
// ESP_LOGE(TAG, "Ack : %s", reg_data_to_string(start_address + i, rxdata[i]).c_str());
}
}
delay_microseconds_safe(T_REGACQ);
@ -1033,7 +1071,6 @@ uint16_str ADS131M08Hub::adc_register_read(uint8_t address, uint8_t nregs) {
if (nregs > 1) {
offset = 1;
rreg_ack = get_unsigned_frame_word(frame, 0, true);
;
}
// uint16_t rreg_ack = (nregs == 1) ? rreg_addr_opcode : get_unsigned_frame_word(frame, 0, true);
nregs = (rreg_ack & MASK_CMD_RW_REG_COUNT) + 1;
@ -1139,6 +1176,24 @@ bool ADS131M08Hub::set_channel_gain_calibration(uint8_t channel, uint32_t gain)
}
// for debug only - remove references, declarations and definitions before submitting for production
std::string ADS131M08Hub::frame_words_to_string(const spiframe &frame, int index, int count) {
std::string str;
char buffer[20];
const std::string::size_type new_cap(768);
str.reserve(new_cap);
int word_nbytes = adc_word_length_ >> 3;
int frame_nwords = frame.size() / word_nbytes;
if(index >= frame_nwords)
return "";
for (int w = index; w < (index + count) && w < frame_nwords; w++) {
for (int i = 0; i < word_nbytes; i++) {
snprintf(buffer, sizeof(buffer), " %02X", frame[w * word_nbytes + i]);
str += buffer;
}
}
return str;
}
std::string ADS131M08Hub::frame_to_string(const spiframe &frame) {
std::string str;
char buffer[20];
@ -1189,12 +1244,10 @@ std::string ADS131M08Hub::rwreg_command_frame_to_string(const spiframe &frame) {
int nregs = 1 + (cmdadr & MASK_CMD_RW_REG_COUNT);
str = command_to_string(cmdadr);
uint16_t command = (cmdadr & MASK_CMD_RW_REG) ? (cmdadr & MASK_CMD_RW_REG) : cmdadr;
const char *ws = " \t\n\r\f\v"; // Defines whitespace
if (command == CMD_RREG) {
str += " : regs ";
for (int i = 0; i < nregs; i++) {
str += reg_addr_to_string(address + i);
str.erase(str.find_last_not_of(ws) + 1);
str += (i < (nregs - 1) ? ", " : "");
}
}
@ -1209,14 +1262,18 @@ std::string ADS131M08Hub::rwreg_command_frame_to_string(const spiframe &frame) {
uint16_t data = get_unsigned_frame_word(frame, i + 1, true);
str += "\n ";
str += reg_data_to_string(address + i, data);
str.erase(str.find_last_not_of(ws) + 1);
str += (i < (nregs - 1) ? ", " : "");
}
}
return str;
}
std::string ADS131M08Hub::reg_addr_to_string(int address) { return reg_data_to_string(address, 0, true); }
std::string ADS131M08Hub::reg_addr_to_string(int address)
{
auto str = reg_data_to_string(address, 0, true);
str.erase(str.find_last_not_of(white_space) + 1);
return str;
}
std::string ADS131M08Hub::reg_data_to_string(int address, uint16_t data, bool nameonly) {
std::string str;

View File

@ -22,13 +22,16 @@
namespace esphome {
namespace ads131m08 {
static const uint8_t DEFAULT_WORD_LENGTH = 32;
static const int MAX_CHANNELS = 12; // for debugging
static const uint8_t DEFAULT_WORD_LENGTH = 32; // we use 32 bit to allow for DMA transfers
static const int ADC_CHANNELS = 8;
static const int MAX_CHANNELS = 14; // for debugging
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
static const int ISR_COUNT_SENSOR = 11; // for debugging
static const int ITERATIONS_SENSOR = 12; // for debugging
static const int NOT_READY_SENSOR = 13; // for debugging
// #define SET_IRAM IRAM_ATTR
#define SET_IRAM
@ -46,18 +49,19 @@ class ADS131M08Hub : public Component,
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;
static constexpr uint16_t reg_mode_cfg = DRDY_FMT_PULSE | TIMEOUT_ENABLED | CRC_REC_ENABLE | CRC_REG_ENABLE;
static constexpr uint16_t reg_mode_cfg16 = WLENGTH_16_BITS | reg_mode_cfg;
static constexpr uint16_t reg_mode_cfg24 = WLENGTH_24_BITS | reg_mode_cfg;
static constexpr uint16_t reg_mode_cfg32 = WLENGTH_32_BITS_MSB_SIGN_EXTEND | reg_mode_cfg;
// 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;
static constexpr uint16_t reg_mode_cfg_mask = MASK_MODE_WLENGTH | MASK_MODE_DRDY_FMT | MASK_MODE_TIMEOUT | MASK_MODE_RX_CRC_EN | MASK_MODE_REG_CRC_EN;
// Semaphore handles
SemaphoreHandle_t data_ready_semhandle = NULL;
SemaphoreHandle_t update_avg = NULL;
const int numFrameWords = 10; // Number of words in a full ADS131M08 SPI frame
static constexpr int numFrameWords = 10; // Number of words in a full ADS131M08 SPI 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();
@ -106,7 +110,7 @@ class ADS131M08Hub : public Component,
uint8_t update_adc_word_length();
uint8_t update_adc_word_length(uint16_t status);
void SET_IRAM read_single();
std::pair<uint32_t, uint32_t> SET_IRAM read_multi();
uint16_str 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);
@ -122,7 +126,7 @@ class ADS131M08Hub : public Component,
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);
//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);
@ -165,12 +169,13 @@ class ADS131M08Hub : public Component,
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_sum_[ADC_CHANNELS];
float sample_squared_sum_[ADC_CHANNELS];
float avg_dc_[MAX_CHANNELS];
float avg_ac_[MAX_CHANNELS];
// for debug only
std::string frame_to_string(const spiframe &frame);
std::string frame_words_to_string(const spiframe &frame, int index, int count);
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);

View File

@ -67,25 +67,20 @@ enum ADC_DELAY {
};
// MODE Register
// enum ADC_RESET : uint16_t
//{
static constexpr uint16_t MODE_MASK_RESET_HAPPENED = ~0x0400; // DEFAULT
static constexpr uint16_t MODE_RESET_HAPPENED = 0x0400;
//};
enum ADC_CRC_TYPE : uint16_t {
CRC_CCITT_16BIT = 0x0000, // DEFAULT
CRC_ANSI_16BIT = 0x0800
enum ADC_CRC: uint16_t {
CRC_TYPE_CCITT = 0x0000, // DEFAULT CRC type
CRC_TYPE_ANSI = 0x0800,
CRC_REC_ENABLE = 0x1000,
CRC_REG_ENABLE = 0x2000,
};
// enum ADC_WORD_LENGTH : uint16_t
//{
static constexpr uint16_t WLENGTH_16_BITS = 0x0000;
static constexpr uint16_t WLENGTH_24_BITS = 0x0100; // DEFAULT
static constexpr uint16_t WLENGTH_32_BITS_LSB_ZERO_PADDING = 0x0200;
static constexpr uint16_t WLENGTH_32_BITS_MSB_SIGN_EXTEND = 0x0300;
//};
enum ADC_TIMEOUT : uint16_t {
TIMEOUT_DISABLED = 0x0000,
@ -98,12 +93,9 @@ enum ADC_DRDY_SELECTION : uint16_t {
DRDY_SEL_MOST_LEADING_CHAN = 0x0008,
DRDY_SEL_MOST_LEADING_CHAN2 = 0x000C
};
// enum ADC_DRDY_FORMAT : uint16_t
//{
static constexpr uint16_t DRDY_FMT_LEVEL = 0x0000; // Logic low (default)
static constexpr uint16_t DRDY_FMT_PULSE = 0x0001; // Low pulse with a fixed duration
//};
// end of MODE Register
// Commands
enum ADC_COMMANDS : uint16_t {

View File

@ -9,12 +9,12 @@ AUTO_LOAD = [
"sensor", "voltage_sampler",
]
MAX_CHANNELS = 14
CONF_OFFSET_CALIBRATION = "offset_calibration"
CONF_GAIN_CALIBRATION = "gain_calibration"
CONF_PHASE_CALIBRATION = "phase_calibration"
CONF_INPUT_SELECT = "input_select"
ICON_CURRENT_DC = "mdi:current-dc"
MAX_CHANNELS = 12
DEPENDENCIES = ["ads131m08"]