diff --git a/ads131m08/ads131m08.cpp b/ads131m08/ads131m08.cpp index 8a316a4..fa36717 100644 --- a/ads131m08/ads131m08.cpp +++ b/ads131m08/ads131m08.cpp @@ -11,6 +11,15 @@ // using namespace esphome::spi; +/* +//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); + } +*/ + namespace esphome { namespace ads131m08 { @@ -100,10 +109,8 @@ void ADS131M08Hub::dump_config() { } else { ESP_LOGCONFIG(TAG, " Clock frequency: %.3fMHz", this->clock_frequency_ / 1000000); } - uint16_t osr = 1 << (7 + this->osr_); - if (osr == 16384) { - osr = 16256; - } + uint16_t osr = convert_adc_osr_to_value(this->osr_); + ESP_LOGCONFIG(TAG, " Oversampling ratio: %" PRId32, osr); ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_); ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_); @@ -158,19 +165,19 @@ float ADS131M08Hub::update_conversion_factor() { bool ADS131M08Hub::adc_initialize(uint8_t word_length) { this->adc_init_++; update_adc_word_length(); - if (!adc_register_write(REG_CLOCK, reg_clock_allch_off)) { + if (!adc_register_write(REG_CLOCK, (reg_clock_allch_off | this->osr_ << 2), __LINE__)) { ESP_LOGE(TAG, "CLOCK register write / read to turn off all channels failed"); 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)) { + if (!adc_register_write(REG_MODE, MODE_MASK_RESET_HAPPENED & reg_mode_cfg24, __LINE__)) { ESP_LOGE(TAG, "MODE register write / read to set word size to 24bits failed"); return false; } ESP_LOGV(TAG, "Written MODE register; Cleared the RESET flag, made DRDY active low pulse"); - if (!adc_register_write(REG_THRSHLD_LSB, 0x09)) { + if (!adc_register_write(REG_THRSHLD_LSB, 0x09, __LINE__)) { ESP_LOGE(TAG, "THRSHLD_LSB register write / read to set DBLOCK filter failed"); return false; } @@ -179,7 +186,7 @@ bool ADS131M08Hub::adc_initialize(uint8_t word_length) { if (!adc_set_word_length(word_length)) return false; // we leave should channels off, as the individual sensors should turn it on - if (!adc_register_write(REG_CLOCK, reg_clock_allch_off)) { + if (!adc_register_write(REG_CLOCK, (reg_clock_allch_off | this->osr_ << 2), __LINE__)) { ESP_LOGE(TAG, "CLOCK register write / read to turn-off all channels failed"); return false; } @@ -192,20 +199,44 @@ bool ADS131M08Hub::adc_initialize(uint8_t word_length) { return true; } +bool ADS131M08Hub::adc_restore_registers() { + uint8_t start_addr = REG_MODE; + uint16_str stored_regs(MAX_FRAME_SIZE - 2, 0); + bool success = true; + int j = 0; + success = adc_register_write(start_addr, settings.data[j], __LINE__, false); + j++; + start_addr++; + while(success && (j < sizeof(settings.data))) { + int i = 0; + stored_regs.resize(MAX_FRAME_SIZE - 2); + while((i < MAX_FRAME_SIZE - 2) && (j < sizeof(settings.data))) { + stored_regs[i++] = settings.data[j++]; + } + stored_regs.resize(i); + success = adc_register_write(start_addr, stored_regs, __LINE__, false); + ESP_LOGD(TAG, "start_addr: %02X i: %02X", start_addr, i); + start_addr += i; + } + return success; +} + bool ADS131M08Hub::adc_set_word_length(uint8_t word_length) { - uint16_t setting; + uint16_t value; switch (word_length) { case 32: - setting = reg_mode_cfg32; + value = reg_mode_cfg32; break; case 16: - setting = reg_mode_cfg16; + value = reg_mode_cfg16; break; default: - setting = reg_mode_cfg24; + value = reg_mode_cfg24; break; } - if (!adc_register_write_masked(REG_MODE, setting, reg_mode_cfg_mask, __LINE__)) { + uint16_t mask = reg_mode_cfg_mask; + //ESP_LOGD(TAG, "WL: %d stored value: %04X / %04X new value: %04X masked value %04X mask: %04X", word_length, this->settings.reg.mode, this->settings.data[REG_MODE-2], value, value & mask, mask ); + if (!adc_register_write_masked(REG_MODE, value, mask, __LINE__)) { ESP_LOGE(TAG, "MODE register write / read to set word size to %ubits failed", word_length); return false; } @@ -269,8 +300,8 @@ void ADS131M08Hub::loop() { CHIP_SELECTx uint64_t start = micros(); - this->txf_init(); // implementing datasheet recommended TXF init - //this->adc_sync(); + ///this->txf_init(); // implementing datasheet recommended TXF init + this->adc_sync(); if (rms_calc_req_) { float_str result = read_multi(); num_samples = result[0]; @@ -449,28 +480,54 @@ int32_t ADS131M08Hub::get_sign_ext_frame_word(const spiframe &frame, int w_index return result; } -void ADS131M08Hub::adc_hard_reset() { - float cycle_micros = 1000000 / this->clock_frequency_; // one cycle duration in micro seconds - uint32_t reset_pulse_duration = static_cast( - 1.05 * 2048 * cycle_micros); // 2048 clock cycles converted to microseconds, multiplied by 1.05 to ensure we meet - // the minimum pulse duration requirement - if (reset_pulse_duration < 1) { - reset_pulse_duration = 1; // set it to 1 microsecond - } +uint32_t ADS131M08Hub::adc_hard_reset() { + uint32_t pulse_duration = convert_adc_clock_cycles_to_micros(2150); sync_reset_pin_->digital_write(false); // Pull SYNC/RESET pin low to reset the ADC - delay_microseconds_safe(reset_pulse_duration); // Hold reset for the calculated duration + delay_microseconds_safe(pulse_duration); // Hold reset for the calculated duration sync_reset_pin_->digital_write(true); // Set SYNC/RESET pin high to allow normal operation + return convert_adc_clock_cycles_to_micros(T_SETTLE_OSR1024); } -void ADS131M08Hub::adc_sync() { - float cycle_micros = 1000000 / this->clock_frequency_; // one cycle duration in micro seconds - uint32_t sync_pulse_duration = - static_cast(std::round(50 * cycle_micros)); // minimum is 1/clock freq, max = 2047/clock freq - if (sync_pulse_duration < 1) { - sync_pulse_duration = 1; // set it to 1 microsecond - } + +uint32_t ADS131M08Hub::adc_sync() { + uint32_t pulse_duration = convert_adc_clock_cycles_to_micros(50); sync_reset_pin_->digital_write(false); // Pull SYNC/RESET pin low to sync the ADC - delay_microseconds_safe(sync_pulse_duration); // Hold sync for the calculated duration + delay_microseconds_safe(pulse_duration); // Hold sync for the calculated duration sync_reset_pin_->digital_write(true); // Set SYNC/RESET pin high to allow normal operation + return get_filter_settling_time(); +} + +uint32_t ADS131M08Hub::get_filter_settling_time() { + uint16_t cycles = T_SETTLE_OSR16384; + switch(this->osr_) { + case OSR_128: + cycles = T_SETTLE_OSR128; + break; + case OSR_256: + cycles = T_SETTLE_OSR256; + break; + case OSR_512: + cycles = T_SETTLE_OSR512; + break; + case OSR_1024: + cycles = T_SETTLE_OSR1024; + break; + case OSR_2048: + cycles = T_SETTLE_OSR2048; + break; + case OSR_4096: + cycles = T_SETTLE_OSR4096; + break; + case OSR_8192: + cycles = T_SETTLE_OSR8192; + break; + } + return convert_adc_clock_cycles_to_micros(cycles); +} + +uint32_t ADS131M08Hub::convert_adc_clock_cycles_to_micros(uint32_t cycles) +{ + float cycle_micros = 1000000 / this->clock_frequency_; // one cycle duration in micro seconds + return 1 + static_cast(std::round(cycle_micros * cycles)); } // ********************** from datasheet ************************ @@ -496,7 +553,7 @@ bool ADS131M08Hub::adc_reset_retry() { int i = 0; while (i < sizeof(retry_wordlengths) && !reset_ok) { adc_word_length_ = retry_wordlengths[i]; - // ESP_LOGW(TAG, "ADC word length: %d", adc_word_length_); + ESP_LOGW(TAG, "ADC word length: %d", adc_word_length_); reset_ok = adc_soft_reset(); i++; } @@ -506,7 +563,7 @@ bool ADS131M08Hub::adc_reset_retry() { i = 0; while (i < sizeof(retry_wordlengths) && !reset_ok) { adc_word_length_ = retry_wordlengths[i]; - // ESP_LOGW(TAG, "ADC word length: %d", adc_word_length_); + ESP_LOGW(TAG, "ADC word length: %d", adc_word_length_); reset_ok = adc_soft_reset(); i++; } @@ -690,6 +747,7 @@ float_str ADS131M08Hub::read_multi() { uint16_t drdy_status, status; int i, iteration = 0; uint32_t elapsed_time = 0; + uint32_t sample_time = this->sample_time_; uint32_t loop_start_time = micros(); int word_nbytes = adc_word_length_ >> 3; int frame_length = numFrameWords * word_nbytes; @@ -703,8 +761,8 @@ float_str ADS131M08Hub::read_multi() { sample_sum_[i] = 0; sample_squared_sum_[i] = 0; } - - while ((elapsed_time < this->sample_time_) && (iteration++ < 1000)) { + uint32_t settling_end_time = loop_start_time; + while ((elapsed_time < sample_time) && (iteration++ < 1000)) { result[2] = iteration; setup_frame(rx_frame, numFrameWords); this->transfer_array(rx_frame); @@ -715,39 +773,52 @@ float_str ADS131M08Hub::read_multi() { update_adc_word_length(status); 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]++; + if(status & MASK_STATUS_RESET) { + adc_reset_retry(); + adc_restore_registers(); + ESP_LOGW(TAG, "Mid data-read reset detected; restored registers"); + return result; + } + else { + if(status & MASK_STATUS_RESYNC) { + settling_end_time = elapsed_time + get_filter_settling_time() + 50; // add 50us for good measure } - for (i = 0; i < ADC_CHANNELS && drdy_status != 0; i++) { - data_ready = drdy_status & 1; - drdy_status = drdy_status >> 1; - if (data_ready) { - this->sampled_values_[i] = value; - ac_sensor_exist = this->sensors_ac[i] != nullptr; - dc_sensor_exist = this->sensors_dc[i] != nullptr; - if (ac_sensor_exist || dc_sensor_exist) { - raw = get_sign_ext_frame_word(rx_frame, i + 1); - value = this->conversion_factor_ * raw; - if (this->rms_enabled_[i]) { - (this->num_samples_[i])++; - (this->sample_sum_[i]) += value; - (this->sample_squared_sum_[i]) += value * value; - } else { - if(ac_sensor_exist) - this->sensors_ac[i]->publish_state(value); - if(dc_sensor_exist) - this->sensors_dc[i]->publish_state(value); - //update_averages(i, value, value); + if(elapsed_time > settling_end_time) { + 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; + if (data_ready) { + this->sampled_values_[i] = value; + ac_sensor_exist = this->sensors_ac[i] != nullptr; + dc_sensor_exist = this->sensors_dc[i] != nullptr; + if (ac_sensor_exist || dc_sensor_exist) { + raw = get_sign_ext_frame_word(rx_frame, i + 1); + value = this->conversion_factor_ * raw; + if (this->rms_enabled_[i]) { + (this->num_samples_[i])++; + (this->sample_sum_[i]) += value; + (this->sample_squared_sum_[i]) += value * value; + } else { + if(ac_sensor_exist) + this->sensors_ac[i]->publish_state(value); + if(dc_sensor_exist) + this->sensors_dc[i]->publish_state(value); + //update_averages(i, value, value); + } + } } } } } } - } else { + } + else { result[1]++; // ESP_LOGW(TAG, "iteration: %d, frame: %s CRC error", iteration, frame_to_string(frame).c_str()); } @@ -917,9 +988,19 @@ bool ADS131M08Hub::adc_register_write_masked(uint8_t address, uint16_t value, ui } // ESP_LOGW(TAG, "1: reg contents: 0x%04X, address: 0x%02X, value: 0x%04X, mask: 0x%04X, caller: %d", // reg_contents[0],address, value,mask, line ); - reg_contents[0] = (reg_contents[0] & ~mask) | (value & mask); - // ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[0], line ); - return adc_register_write(address, reg_contents); + auto new_value = (reg_contents[0] & ~mask) | (value & mask); + if(new_value == reg_contents[0]) { + ESP_LOGW(TAG, "1: No update needed. reg: %02X data: %04X / %04X Line: %d", address, value, new_value, line); + int bup_index = address - REG_MODE; + ESP_LOGW(TAG, "Storing: %-13s: data %04X stored val: %04X Line: %d", reg_addr_to_string(bup_index + REG_MODE).c_str(), value, settings.data[bup_index], line); + // we backup data irrespective + settings.data[bup_index] = value; + return true; // no need for an adc reg update + } + reg_contents[0] = new_value; + //ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[0], line ); + //ESP_LOGD(TAG, "adc_register_write_masked: address: %02X value: %04X reg_contents: %04X mask: %04X new value: %04X Line:%d", address, value, reg_contents[0], mask, new_value, line); + return adc_register_write(address, reg_contents, line); } /// @brief /// @param start_address - first register address @@ -943,34 +1024,54 @@ bool ADS131M08Hub::adc_register_write_masked(uint8_t start_address, const uint16 ESP_LOGW(TAG, "No contents read from adc. Expected %d values. Register write aborted.", nregs); return false; } + bool update_required = false; for (int i = 0; i < nregs; i++) { - // ESP_LOGW(TAG, "Before change %d: reg contents: 0x%04X, address: 0x%02X, value: 0x%04X, mask: 0x%04X, caller: %d", - // i, reg_contents[i], start_address + i, values[i], masks[i], line); - reg_contents[i] = (reg_contents[i] & ~masks[i]) | (values[i] & masks[i]); - // ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[i], line); + //ESP_LOGW(TAG, "Before change %d: reg contents: 0x%04X, address: 0x%02X, value: 0x%04X, mask: 0x%04X, caller: %d", + // i, reg_contents[i], start_address + i, values[i], masks[i], line); + auto new_value = (reg_contents[i] & ~masks[i]) | (values[i] & masks[i]); + if(reg_contents[i] != new_value) + update_required = true; + reg_contents[i] = new_value; + //ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[i], line); } - return adc_register_write(start_address, reg_contents); + if(!update_required) { + ESP_LOGW(TAG, "2: No update needed. start reg: %02X first data: %04X Line: %d", start_address, values[0], line); + // we backup data irrespective + int bup_index = start_address - REG_MODE; + for(auto i = values.begin(); i != values.end(); i++) { + ESP_LOGW(TAG, "Storing: %-13s: data %04X stored val: %04X Line: %d", reg_addr_to_string(bup_index + REG_MODE).c_str(), *i, settings.data[bup_index], line); + settings.data[bup_index++] = *i; + } + return true; + } + return adc_register_write(start_address, reg_contents, line); } -bool ADS131M08Hub::adc_register_write(uint16_t address, uint16_t data) { +bool ADS131M08Hub::adc_register_write(uint16_t address, uint16_t data, int line, bool backup_data) { uint16_str arg; arg += data; - return adc_register_write(address, arg); + return adc_register_write(address, arg, line, backup_data); } -/* -//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) { +bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str &data, int line, bool backup_data) { int nregs = data.size(); if (nregs == 0) { return false; // invalid } + if(backup_data) { + int bup_index = start_address - REG_MODE; + for(auto i = data.begin(); i != data.end(); i++) { + ESP_LOGW(TAG, "Storing: %-13s: data %04X stored val: %04X Line: %d", reg_addr_to_string(bup_index + REG_MODE).c_str(), *i, settings.data[bup_index], line); + settings.data[bup_index++] = *i; + } + } + else { + int bup_index = start_address - REG_MODE; + for(auto i = data.begin(); i != data.end(); i++) { + ESP_LOGD(TAG, "Restoring: %-13s: data %04X stored val: %04X Line: %d", reg_addr_to_string(bup_index + REG_MODE).c_str(), *i, settings.data[bup_index], line); + bup_index++; + } + } int wreg_nwords = 2 + nregs; // ensure room for crc int rreg_resp_framelength = 2 + (nregs == 1 ? -1 : nregs); // add room for CRC if nregs > 1 //spiframe frame(wreg_nwords, 0); @@ -1010,7 +1111,7 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str & return verified; } -// recommended not to read more than 20 registers at a time +// recommended not to read more than 8 registers at a time uint16_str ADS131M08Hub::adc_register_read(uint8_t address, uint8_t nregs) { uint16_str result; if (nregs == 0) { @@ -1045,6 +1146,7 @@ uint16_str ADS131M08Hub::adc_register_read(uint8_t address, uint8_t nregs) { if (nregs > 1) { bool crc_ok = check_crc(rx_frame); if (!crc_ok) { + ESP_LOGD(TAG, "Recv Frame: %s", frame_to_string(rx_frame).c_str()); ESP_LOGW(TAG, "CRC failed reading ADC registers!"); result.clear(); return result; // invalid @@ -1070,28 +1172,46 @@ uint16_str ADS131M08Hub::adc_register_read(uint8_t address, uint8_t nregs) { } bool ADS131M08Hub::set_reg_osr() { - uint16_t osr = 3; // default - if (this->osr_ <= 16256 && this->osr_ >= 128) { - if (this->osr_ == 16256) - osr = 7; + uint16_t mask = MASK_CLOCK_OSR; + uint16_t value = this->osr_ << 2; + // ESP_LOGD(TAG, "adc_register_write_masked(REG_CLOCK=%02X, value=%04X, mask=%04X, __LINE__)", REG_CLOCK, + // value, mask); + //this->settings.reg.clock = value & mask; + return adc_register_write_masked(REG_CLOCK, value, mask, __LINE__); +} + +uint16_t ADS131M08Hub::convert_value_to_adc_osr(uint16_t osr_value) { + uint16_t adc_osr = OSR_1024; // default + if (osr_value <= 16256 && osr_value >= 128) { + if (osr_value == 16256) + adc_osr = OSR_16256; else { - osr = 0; - auto tmp = this->osr_ / 128; + adc_osr = OSR_128; + auto tmp = osr_value / 128; while (tmp >>= 1) - osr++; + adc_osr++; } } - return adc_register_write_masked(REG_CLOCK, osr << 2, MASK_CLOCK_OSR, __LINE__); + return adc_osr; +} + +uint16_t ADS131M08Hub::convert_adc_osr_to_value(uint16_t adc_osr) { + uint16_t osr_value = 1 << (7 + adc_osr); + if (osr_value == 16384) { + osr_value = 16256; + } + return osr_value; } bool ADS131M08Hub::set_channel_enable(uint8_t channel, bool enable) { if (channel >= ADC_CHANNELS) return false; - uint16_t enable_mask = MASK_CLOCK_CH0 << channel; - uint16_t reg_value = (enable) ? enable_mask : 0; - // ESP_LOGD(TAG, "adc_register_write_masked(REG_CLOCK=%02X, reg_value=%04X, enable_mask=%04X, __LINE__)", REG_CLOCK, - // reg_value, enable_mask); - return adc_register_write_masked(REG_CLOCK, reg_value, enable_mask, __LINE__); + uint16_t mask = MASK_CLOCK_CH0 << channel; + uint16_t value = (enable) ? mask : 0; + // ESP_LOGD(TAG, "adc_register_write_masked(REG_CLOCK=%02X, value=%04X, mask=%04X, __LINE__)", REG_CLOCK, + // value, mask); + //this->settings.reg.clock = value & mask; + return adc_register_write_masked(REG_CLOCK, value, mask, __LINE__); } bool ADS131M08Hub::set_channel_gain(uint8_t channel, uint8_t gain) { @@ -1101,16 +1221,26 @@ bool ADS131M08Hub::set_channel_gain(uint8_t channel, uint8_t gain) { uint8_t shift = (channel % 4) << 2; uint16_t mask = MASK_GAIN_PGAGAIN0 << shift; uint16_t value = gain << shift; + //if(reg == REG_GAIN1) + //this->settings.reg.gain1 = value & mask; + //else + //this->settings.reg.gain2 = value & mask; // ESP_LOGW(TAG, "Ch%d: Set Gain: reg: %02X pga: %04X mask: %04X ", channel, reg, value, mask); return adc_register_write_masked(reg, value, mask, __LINE__); } -void ADS131M08Hub::set_global_chop(uint16_t global_chop) { - adc_register_write_masked(REG_CFG, global_chop << 8, MASK_CFG_GC_EN, __LINE__); +bool ADS131M08Hub::set_global_chop(uint16_t global_chop) { + uint16_t mask = MASK_CFG_GC_EN; + uint16_t value = global_chop << 8; + //this->settings.reg.cfg = value & mask; + return adc_register_write_masked(REG_CFG, value, mask, __LINE__); } -void ADS131M08Hub::set_global_chop_delay(uint16_t delay) { - adc_register_write_masked(REG_CFG, delay << 9, MASK_CFG_GC_DLY, __LINE__); +bool ADS131M08Hub::set_global_chop_delay(uint16_t delay) { + uint16_t mask = MASK_CFG_GC_DLY; + uint16_t value = delay << 9; + //this->settings.reg.cfg = value & mask; + return adc_register_write_masked(REG_CFG, value, mask, __LINE__); } bool ADS131M08Hub::set_channel_phase_calibration(uint8_t channel, int16_t phase_offset) { @@ -1119,22 +1249,31 @@ bool ADS131M08Hub::set_channel_phase_calibration(uint8_t channel, int16_t phase_ uint16_t reg_addr = 5 * channel + REG_CH0_CFG; if (phase_offset < -512 || phase_offset > 511) return false; - return adc_register_write_masked(reg_addr, (phase_offset << 6), MASK_CHX_CFG_PHASE, __LINE__); + uint16_t mask = MASK_CHX_CFG_PHASE; + uint16_t value = phase_offset << 6; + ////this->settings.reg.chan[channel].ch_cfg = value & mask; + return adc_register_write_masked(reg_addr, value, mask, __LINE__); } bool ADS131M08Hub::set_dcblock_filter_disable(uint8_t channel, bool disable) { if (channel > 7) return false; uint16_t reg_addr = 5 * channel + REG_CH0_CFG; - uint16_t reg_value = (disable) ? MASK_CHX_CFG_DCBLKX_DIS : 0; - return adc_register_write_masked(reg_addr, reg_value, MASK_CHX_CFG_DCBLKX_DIS, __LINE__); + uint16_t mask = MASK_CHX_CFG_DCBLKX_DIS; + uint16_t value = (disable) ? mask : 0; + ////this->settings.reg.chan[channel].ch_cfg = value & mask; + ESP_LOGI(TAG, "Setting DC BLOCK of Ch%d reg:%02X to %s [%04X]", channel, reg_addr, (disable ? "disable" : "enable"), value); + return adc_register_write_masked(reg_addr, value, mask, __LINE__); } bool ADS131M08Hub::set_channel_input_selection(uint8_t channel, ADC_INPUT_CHANNEL_MUX input) { if (channel > 7) return false; uint16_t reg_addr = 5 * channel + REG_CH0_CFG; - return adc_register_write_masked(reg_addr, input, MASK_CHX_CFG_MUX, __LINE__); + uint16_t mask = MASK_CHX_CFG_MUX; + uint16_t value = input; + ////this->settings.reg.chan[channel].ch_cfg = value & mask; + return adc_register_write_masked(reg_addr, value, mask, __LINE__); } bool ADS131M08Hub::set_channel_offset_calibration(uint8_t channel, int32_t offset) { @@ -1142,11 +1281,15 @@ bool ADS131M08Hub::set_channel_offset_calibration(uint8_t channel, int32_t offse return false; if (offset < -8388608 || offset > 8388607) return false; - uint16_t MSB = offset >> 8; uint16_t LSB = offset << 8; uint16_t reg_addr = 5 * channel + REG_CH0_OCAL_MSB; - return adc_register_write_masked(reg_addr, {MSB, LSB}, {MASK_CHX_OCAL_MSB, MASK_CHX_OCAL_LSB}, __LINE__); + + uint16_str masks = {MASK_CHX_OCAL_MSB, MASK_CHX_OCAL_LSB}; + uint16_str values = {MSB, LSB}; + ////this->settings.reg.chan[channel].ch_ocal_msb = values[0] & masks[0]; + //this->settings.reg.chan[channel].ch_ocal_lsb = values[1] & masks[1]; + return adc_register_write_masked(reg_addr, values, masks, __LINE__); } bool ADS131M08Hub::set_channel_gain_calibration(uint8_t channel, uint32_t gain) { @@ -1158,7 +1301,11 @@ bool ADS131M08Hub::set_channel_gain_calibration(uint8_t channel, uint32_t gain) uint16_t MSB = gain >> 8; uint8_t LSB = gain; uint16_t reg_addr = 5 * channel + REG_CH0_GCAL_MSB; - return adc_register_write_masked(reg_addr, {MSB, LSB}, {MASK_CHX_GCAL_MSB, MASK_CHX_GCAL_LSB}, __LINE__); + uint16_str masks = {MASK_CHX_GCAL_MSB, MASK_CHX_GCAL_LSB}; + uint16_str values = {MSB, LSB}; + //this->settings.reg.chan[channel].ch_gcal_msb = values[0] & masks[0]; + //this->settings.reg.chan[channel].ch_gcal_lsb = values[1] & masks[1]; + return adc_register_write_masked(reg_addr, values, masks, __LINE__); } // for debug only - remove references, declarations and definitions before submitting for production @@ -1336,10 +1483,11 @@ std::string ADS131M08Hub::reg_data_to_string(int address, uint16_t data, bool na str += " / EXT VREF="; str += ((data & MASK_CLOCK_EXTREF_EN) != 0) ? "EN" : "DIS"; { - uint16_t osr = 1 << (7 + ((data & MASK_CLOCK_OSR) >> 2)); - if (osr == 16384) { - osr = 16256; - } + //uint16_t osr = 1 << (7 + ((data & MASK_CLOCK_OSR) >> 2)); + //if (osr == 16384) { + // osr = 16256; + //} + uint16_t osr = convert_adc_osr_to_value((data & MASK_CLOCK_OSR) >> 2); snprintf(buffer, sizeof(buffer), " / OSR=%d", osr); str += buffer; } diff --git a/ads131m08/ads131m08.h b/ads131m08/ads131m08.h index 67d186f..0587223 100644 --- a/ads131m08/ads131m08.h +++ b/ads131m08/ads131m08.h @@ -51,7 +51,7 @@ class ADS131M08Hub : public Component, public: // 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_cfg = MASK_CLOCK_EXTREF_EN | PM_HIGH_RESOLUTION; // OSR must be added 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_cfg = DRDY_FMT_PULSE | TIMEOUT_ENABLED | CRC_REC_ENABLE | CRC_REG_ENABLE; @@ -62,6 +62,29 @@ class ADS131M08Hub : public Component, 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 | MASK_MODE_RX_CRC_EN | MASK_MODE_REG_CRC_EN; + struct adc_channel_settings { + uint16_t ch_cfg; + uint16_t ch_ocal_msb; + uint16_t ch_ocal_lsb; + uint16_t ch_gcal_msb; + uint16_t ch_gcal_lsb; + }; + struct adc_register_settings { + uint16_t mode; + uint16_t clock; + uint16_t gain1; + uint16_t gain2; + uint16_t cfg; + uint16_t threshold_msb; + uint16_t threshold_lsb; + adc_channel_settings chan[8]; + //uint16_t regmap_crc; + }; + union regmap { + adc_register_settings reg; + uint16_t data[sizeof(adc_register_settings)/2]; + }; + // Semaphore handles SemaphoreHandle_t data_ready_semhandle = NULL; SemaphoreHandle_t update_avg = NULL; @@ -70,8 +93,10 @@ class ADS131M08Hub : public Component, 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(); + regmap settings; void txf_init(); bool adc_initialize(uint8_t word_length); + bool adc_restore_registers(); bool adc_set_word_length(uint8_t word_length); void setup() override; @@ -86,8 +111,8 @@ class ADS131M08Hub : public Component, void dump_config() override; bool set_channel_enable(uint8_t channel, bool enable); - void set_global_chop(uint16_t global_chop); - void set_global_chop_delay(uint16_t delay); + bool set_global_chop(uint16_t global_chop); + bool set_global_chop_delay(uint16_t delay); bool set_channel_input_selection(uint8_t channel, ADC_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); @@ -96,8 +121,12 @@ class ADS131M08Hub : public Component, bool set_channel_gain(uint8_t channel, uint8_t gain); bool set_measure_rms(uint8_t channel, bool enable); float get_sampled_value(uint8_t channel) { return sampled_values_[channel]; } + uint32_t get_filter_settling_time(); //float get_average(uint8_t channel, bool read_ac); bool set_reg_osr(); + uint16_t convert_value_to_adc_osr(uint16_t osr_value); + uint16_t convert_adc_osr_to_value(uint16_t adc_osr); + uint32_t convert_adc_clock_cycles_to_micros(uint32_t cycles); protected: HighFrequencyLoopRequester high_freq_; @@ -117,15 +146,15 @@ class ADS131M08Hub : public Component, void SET_IRAM read_single(); float_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); + bool adc_register_write(uint16_t address, uint16_t data, int line, bool backup_date = true); + bool adc_register_write(uint16_t address, const uint16_str &data, int line, bool backup_date = true); 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); bool adc_reset_retry(); bool adc_soft_reset(); - void adc_hard_reset(); - void adc_sync(); + uint32_t adc_hard_reset(); + uint32_t adc_sync(); void SET_IRAM transfer_array(spiframe &frame); //void transfer_array(const spiframe &data_out, spiframe &data_in); uint16_t SET_IRAM get_crc(const spiframe &frame); @@ -179,7 +208,7 @@ class ADS131M08Hub : public Component, uint8_t update_state_[MAX_CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t adc_word_length_{24}; float conversion_factor_{1.25 / 8388608.0}; // updated when word length changes - const uint32_t sample_time_ = 20000; // 250 ms + const uint32_t sample_time_ = 40000; // 40 ms float sps_{0.0f}; uint32_t num_samples_[ADC_CHANNELS]; float sample_sum_[ADC_CHANNELS]; diff --git a/ads131m08/ads131m08_defs.h b/ads131m08/ads131m08_defs.h index a6792c1..7e3f8a0 100644 --- a/ads131m08/ads131m08_defs.h +++ b/ads131m08/ads131m08_defs.h @@ -13,6 +13,7 @@ enum ADC_DRDY_STATE : uint8_t { DS_HI_Z = 1 }; +// no shift enum ADC_POWERMODE : uint16_t { PM_VERY_LOW_POWER = 0, PM_LOW_POWER = 1, @@ -39,26 +40,27 @@ enum ADC_INPUT_CHANNEL_MUX : uint8_t { ICM_NEGATIVE_DC_TEST_SIGNAL = 3, }; +// must be shifted << 2 enum ADC_OVERSAMPLING_RATIO : uint16_t { OSR_128 = 0, OSR_256 = 1, OSR_512 = 2, - OSR_1024 = 3, // default + OSR_1024 = 3, OSR_2048 = 4, OSR_4096 = 5, OSR_8192 = 6, OSR_16256 = 7 }; -enum ADC_WAIT_TIME : uint16_t { - WT_128 = 856, - WT_256 = 1112, - WT_512 = 1624, - WT_1024 = 2648, - WT_2048 = 4696, - WT_4096 = 8792, - WT_8192 = 16984, - WT_16384 = 33368 +enum ADC_SETTLING_TIME : uint16_t { + T_SETTLE_OSR128 = 856, + T_SETTLE_OSR256 = 1112, + T_SETTLE_OSR512 = 1624, + T_SETTLE_OSR1024 = 2648, + T_SETTLE_OSR2048 = 4696, + T_SETTLE_OSR4096 = 8792, + T_SETTLE_OSR8192 = 16984, + T_SETTLE_OSR16384 = 33368, }; // delays in microseconds @@ -67,6 +69,7 @@ enum ADC_DELAY { }; + static constexpr uint16_t MODE_MASK_RESET_HAPPENED = ~0x0400; // DEFAULT static constexpr uint16_t MODE_RESET_HAPPENED = 0x0400; diff --git a/ads131m08/sensor/__init__.py b/ads131m08/sensor/__init__.py index 3a8a5da..682c07a 100644 --- a/ads131m08/sensor/__init__.py +++ b/ads131m08/sensor/__init__.py @@ -14,8 +14,10 @@ CONF_OFFSET_CALIBRATION = "offset_calibration" CONF_GAIN_CALIBRATION = "gain_calibration" CONF_PHASE_CALIBRATION = "phase_calibration" CONF_INPUT_SELECT = "input_select" +CONF_DC_BLOCK = "dc_block" ICON_CURRENT_DC = "mdi:current-dc" + DEPENDENCIES = ["ads131m08"] GAIN = ads131m08_ns.enum("ADC_PGA_GAIN") @@ -59,6 +61,7 @@ CONFIG_SCHEMA = CONFIG_SCHEMA_RMS_SENSORS.extend( cv.Optional(CONF_GAIN_CALIBRATION, default=1): cv.float_range(min=0, max=2), cv.Optional(CONF_PHASE_CALIBRATION, default=0): cv.int_range(min=-512, max=511), # should use degrees, but need to figure out conversion function first cv.Optional(CONF_INPUT_SELECT, default='normal'): cv.enum(ALLOWED_MUX_INP, int=False), + cv.Optional(CONF_DC_BLOCK, default=False): cv.boolean, } ).extend( { @@ -84,6 +87,8 @@ async def to_code(config): cg.add(channel_sensor.set_offset_calibration(offset_cal)) phase_cal = config[CONF_PHASE_CALIBRATION] cg.add(channel_sensor.set_phase_calibration(phase_cal)) + dc_block = config[CONF_DC_BLOCK] + cg.add(channel_sensor.set_dc_block(dc_block)) await cg.register_component(channel_sensor, config) if dc_sensor := await to_code_dc(config): await cg.register_parented(dc_sensor, channel_sensor) diff --git a/ads131m08/sensor/ads131m08_sensor.cpp b/ads131m08/sensor/ads131m08_sensor.cpp index 938a8f0..a9c0c03 100644 --- a/ads131m08/sensor/ads131m08_sensor.cpp +++ b/ads131m08/sensor/ads131m08_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ads131m08 { -static const char *const TAG = "adsm131m08.sensor"; +static const char *const TAG = "ads131m08.sensor"; void Channel::loop() { @@ -16,7 +16,7 @@ void Channel::loop() this->parent_->set_channel_phase_calibration(this->channel_, this->normalised_phase_calibration()); this->parent_->set_channel_offset_calibration(this->channel_, this->normalised_offset_calibration()); this->parent_->set_channel_gain_calibration(this->channel_, this->normalised_gain_calibration()); - this->parent_->set_dcblock_filter_disable(this->channel_, true); + this->parent_->set_dcblock_filter_disable(this->channel_, !this->dc_block_); this->parent_->set_channel_enable(this->channel_, true); this->first_reading_ = false; } @@ -53,6 +53,7 @@ void Channel::dump_config() { ESP_LOGCONFIG(TAG, " Gain calibration: %f", this->gain_cal_); ESP_LOGCONFIG(TAG, " Offset calibration: %" PRIu32, this->offset_cal_); ESP_LOGCONFIG(TAG, " Phase calibration: %" PRId32, this->phase_cal_); + ESP_LOGCONFIG(TAG, " DC block: %s", this->dc_block_ ? "YES" : "NO"); ESP_LOGCONFIG(TAG, " Input select: %s", (this->input_ == ICM_AIN0P_AIN0N) ? "normal" \ : (this->input_ == ICM_INPUT_SHORTED) ? "shorted" \ : (this->input_ == ICM_POSITIVE_DC_TEST_SIGNAL) ? "positive_dc" \ diff --git a/ads131m08/sensor/ads131m08_sensor.h b/ads131m08/sensor/ads131m08_sensor.h index 4d25cef..b4231b9 100644 --- a/ads131m08/sensor/ads131m08_sensor.h +++ b/ads131m08/sensor/ads131m08_sensor.h @@ -25,6 +25,7 @@ class Channel : public sensor::Sensor, void set_gain_calibration(float value) { this->gain_cal_ = value; } void set_offset_calibration(int value) { this->offset_cal_ = value; } void set_phase_calibration(int value) { this->phase_cal_ = value; } + void set_dc_block(bool enable) { this->dc_block_ = enable; } bool set_calc_rms(bool enable) { this->calc_rms_ = enable; return this->parent_ != nullptr ? this->parent_->set_measure_rms(this->channel_, enable) : false; } //float get_average(bool read_ac) { return this->parent_->get_average(this->channel_, read_ac); } void set_mux_input(ADC_INPUT_CHANNEL_MUX value) { this->input_ = value; } @@ -38,6 +39,7 @@ class Channel : public sensor::Sensor, int offset_cal_; float gain_cal_; bool calc_rms_; + bool dc_block_; uint8_t gain_; ADC_INPUT_CHANNEL_MUX input_; bool first_reading_{true};