mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-11-01 03:31:37 +08:00
[adc] Refactor ADS1115 driver (#24428)
This commit is contained in:
@@ -42,18 +42,7 @@ int ADS1115::init()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t config[2] = {};
|
readChannel(Channel::A0); // prepare for the first measure.
|
||||||
config[0] = CONFIG_HIGH_OS_NOACT | CONFIG_HIGH_MUX_P0NG | CONFIG_HIGH_PGA_6144 | CONFIG_HIGH_MODE_SS;
|
|
||||||
config[1] = CONFIG_LOW_DR_250SPS | CONFIG_LOW_COMP_MODE_TRADITIONAL | CONFIG_LOW_COMP_POL_RESET |
|
|
||||||
CONFIG_LOW_COMP_LAT_NONE | CONFIG_LOW_COMP_QU_DISABLE;
|
|
||||||
ret = writeReg(ADDRESSPOINTER_REG_CONFIG, config, 2);
|
|
||||||
|
|
||||||
if (ret != PX4_OK) {
|
|
||||||
PX4_ERR("writeReg failed (%i)", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
setChannel(ADS1115::A0); // prepare for the first measure.
|
|
||||||
|
|
||||||
ScheduleOnInterval(SAMPLE_INTERVAL / 4, SAMPLE_INTERVAL / 4);
|
ScheduleOnInterval(SAMPLE_INTERVAL / 4, SAMPLE_INTERVAL / 4);
|
||||||
|
|
||||||
@@ -62,152 +51,69 @@ int ADS1115::init()
|
|||||||
|
|
||||||
int ADS1115::probe()
|
int ADS1115::probe()
|
||||||
{
|
{
|
||||||
uint8_t buf[2] = {};
|
// The ADS1115 has no ID register, so we read out the threshold registers
|
||||||
int ret = readReg(ADDRESSPOINTER_REG_CONFIG, buf, 2);
|
// and check their default values. We cannot use the config register, as
|
||||||
|
// this is changed by this driver. Note the default value is in BE.
|
||||||
|
static constexpr uint32_t DEFAULT{0xFF7F0080};
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t low[2];
|
||||||
|
uint8_t high[2];
|
||||||
|
} parts;
|
||||||
|
uint32_t threshold{};
|
||||||
|
};
|
||||||
|
int ret = readReg(ADDRESSPOINTER_REG_LO_THRESH, parts.low, 2);
|
||||||
|
|
||||||
if (ret != PX4_OK) {
|
if (ret != PX4_OK) {
|
||||||
DEVICE_DEBUG("readReg failed (%i)", ret);
|
DEVICE_DEBUG("lo_thresh read failed (%i)", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[0] != CONFIG_RESET_VALUE_HIGH || buf[1] != CONFIG_RESET_VALUE_LOW) {
|
ret = readReg(ADDRESSPOINTER_REG_HI_THRESH, parts.high, 2);
|
||||||
DEVICE_DEBUG("ADS1115 not found");
|
|
||||||
return PX4_ERROR;
|
if (ret != PX4_OK) {
|
||||||
|
DEVICE_DEBUG("hi_thresh read failed (%i)", ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PX4_OK;
|
if (threshold == DEFAULT) {
|
||||||
|
return PX4_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_DEBUG("ADS1115 not found");
|
||||||
|
return PX4_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ADS1115::setChannel(ADS1115::ChannelSelection ch)
|
int ADS1115::readChannel(ADS1115::Channel ch)
|
||||||
{
|
{
|
||||||
uint8_t buf[2] = {};
|
uint8_t buf[2];
|
||||||
uint8_t next_mux_reg = CONFIG_HIGH_MUX_P0NG;
|
buf[0] = CONFIG_HIGH_OS_START_SINGLE | uint8_t(ch) | CONFIG_HIGH_PGA_6144 | CONFIG_HIGH_MODE_SS;
|
||||||
|
|
||||||
switch (ch) {
|
|
||||||
case A0:
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P0NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case A1:
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P1NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case A2:
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P2NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case A3:
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P3NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[0] = CONFIG_HIGH_OS_START_SINGLE | next_mux_reg | CONFIG_HIGH_PGA_6144 | CONFIG_HIGH_MODE_SS;
|
|
||||||
buf[1] = CONFIG_LOW_DR_250SPS | CONFIG_LOW_COMP_MODE_TRADITIONAL | CONFIG_LOW_COMP_POL_RESET |
|
buf[1] = CONFIG_LOW_DR_250SPS | CONFIG_LOW_COMP_MODE_TRADITIONAL | CONFIG_LOW_COMP_POL_RESET |
|
||||||
CONFIG_LOW_COMP_LAT_NONE | CONFIG_LOW_COMP_QU_DISABLE;
|
CONFIG_LOW_COMP_LAT_NONE | CONFIG_LOW_COMP_QU_DISABLE;
|
||||||
return writeReg(ADDRESSPOINTER_REG_CONFIG, buf, 2); // must write whole register to take effect
|
return writeReg(ADDRESSPOINTER_REG_CONFIG, buf, 2); // must write whole register to take effect
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ADS1115::isSampleReady()
|
int ADS1115::isSampleReady()
|
||||||
{
|
{
|
||||||
uint8_t buf[1] = {0x00};
|
uint8_t buf[1] = {0x00};
|
||||||
|
|
||||||
if (readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1) != 0) { return false; } // Pull config register
|
if (readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1) != PX4_OK) { return -1; } // Pull config register
|
||||||
|
|
||||||
return (buf[0] & (uint8_t) 0x80);
|
return (buf[0] & (uint8_t) 0x80) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ADS1115::ChannelSelection ADS1115::getMeasurement(int16_t *value)
|
ADS1115::Channel ADS1115::getMeasurement(int16_t *value)
|
||||||
{
|
{
|
||||||
uint8_t buf[2] = {0x00};
|
uint8_t buf[2] = {0x00};
|
||||||
readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1); // Pull config register
|
|
||||||
ChannelSelection channel;
|
|
||||||
|
|
||||||
switch ((buf[0] & (uint8_t) 0x70) >> 4) {
|
if (readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1) != PX4_OK) { return Channel::Invalid; }
|
||||||
case 0x04:
|
|
||||||
channel = A0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x05:
|
const auto channel{Channel(buf[0] & CONFIG_HIGH_MUX_P3NG)};
|
||||||
channel = A1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x06:
|
if (readReg(ADDRESSPOINTER_REG_CONVERSATION, buf, 2) != PX4_OK) { return Channel::Invalid; }
|
||||||
channel = A2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x07:
|
*value = int16_t((buf[0] << 8) | buf[1]);
|
||||||
channel = A3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
readReg(ADDRESSPOINTER_REG_CONVERSATION, buf, 2);
|
|
||||||
uint16_t raw_adc_val = buf[0] * 256 + buf[1];
|
|
||||||
|
|
||||||
if (raw_adc_val & (uint16_t) 0x8000) { // Negetive value
|
|
||||||
raw_adc_val = ~raw_adc_val + 1; // 2's complement
|
|
||||||
*value = -raw_adc_val;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
*value = raw_adc_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
ADS1115::ChannelSelection ADS1115::cycleMeasure(int16_t *value)
|
|
||||||
{
|
|
||||||
uint8_t buf[2] = {0x00};
|
|
||||||
readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1); // Pull config register
|
|
||||||
ChannelSelection channel;
|
|
||||||
uint8_t next_mux_reg = CONFIG_HIGH_MUX_P0NG;
|
|
||||||
|
|
||||||
switch ((buf[0] & (uint8_t) 0x70) >> 4) {
|
|
||||||
case 0x04:
|
|
||||||
channel = A0;
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P1NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x05:
|
|
||||||
channel = A1;
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P2NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x06:
|
|
||||||
channel = A2;
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P3NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x07:
|
|
||||||
channel = A3;
|
|
||||||
next_mux_reg = CONFIG_HIGH_MUX_P0NG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
readReg(ADDRESSPOINTER_REG_CONVERSATION, buf, 2);
|
|
||||||
uint16_t raw_adc_val = buf[0] * 256 + buf[1];
|
|
||||||
|
|
||||||
if (raw_adc_val & (uint16_t) 0x8000) { // Negetive value
|
|
||||||
raw_adc_val = ~raw_adc_val + 1; // 2's complement
|
|
||||||
*value = -raw_adc_val;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
*value = raw_adc_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[0] = CONFIG_HIGH_OS_START_SINGLE | next_mux_reg | CONFIG_HIGH_PGA_6144 | CONFIG_HIGH_MODE_SS;
|
|
||||||
buf[1] = CONFIG_LOW_DR_250SPS | CONFIG_LOW_COMP_MODE_TRADITIONAL | CONFIG_LOW_COMP_POL_RESET |
|
|
||||||
CONFIG_LOW_COMP_LAT_NONE | CONFIG_LOW_COMP_QU_DISABLE;
|
|
||||||
writeReg(ADDRESSPOINTER_REG_CONFIG, buf, 2); // must write whole register to take effect
|
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,6 @@
|
|||||||
#define CONFIG_LOW_COMP_QU_AFTER4 0x02
|
#define CONFIG_LOW_COMP_QU_AFTER4 0x02
|
||||||
#define CONFIG_LOW_COMP_QU_DISABLE 0x03
|
#define CONFIG_LOW_COMP_QU_DISABLE 0x03
|
||||||
|
|
||||||
#define CONFIG_RESET_VALUE_HIGH 0x85
|
|
||||||
#define CONFIG_RESET_VALUE_LOW 0x83
|
|
||||||
|
|
||||||
using namespace time_literals;
|
using namespace time_literals;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -127,35 +124,39 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uORB::PublicationMulti<adc_report_s> _to_adc_report{ORB_ID(adc_report)};
|
uORB::PublicationMulti<adc_report_s> _to_adc_report{ORB_ID(adc_report)};
|
||||||
|
|
||||||
static const hrt_abstime SAMPLE_INTERVAL{50_ms};
|
static const hrt_abstime SAMPLE_INTERVAL{50_ms};
|
||||||
|
|
||||||
adc_report_s _adc_report{};
|
adc_report_s _adc_report{};
|
||||||
|
|
||||||
perf_counter_t _cycle_perf;
|
perf_counter_t _cycle_perf;
|
||||||
|
perf_counter_t _comms_errors;
|
||||||
|
|
||||||
int _channel_cycle_count{0};
|
uint8_t _channel_cycle_mask{0};
|
||||||
|
|
||||||
bool _reported_ready_last_cycle{false};
|
static constexpr uint8_t MAX_READY_COUNTER{20};
|
||||||
|
uint8_t _ready_counter{MAX_READY_COUNTER};
|
||||||
|
|
||||||
// ADS1115 logic part
|
// ADS1115 logic part
|
||||||
enum ChannelSelection {
|
enum class Channel : uint8_t {
|
||||||
Invalid = -1, A0 = 0, A1, A2, A3
|
A0 = CONFIG_HIGH_MUX_P0NG,
|
||||||
|
A1 = CONFIG_HIGH_MUX_P1NG,
|
||||||
|
A2 = CONFIG_HIGH_MUX_P2NG,
|
||||||
|
A3 = CONFIG_HIGH_MUX_P3NG,
|
||||||
|
Invalid = 0xff,
|
||||||
};
|
};
|
||||||
/* set multiplexer to specific channel */
|
constexpr unsigned ch2u(Channel ch) { return (unsigned(ch) >> 4) & 0b11u; }
|
||||||
int setChannel(ChannelSelection ch);
|
constexpr Channel u2ch(unsigned ch) { return Channel((ch << 4) | CONFIG_HIGH_MUX_P0NG); }
|
||||||
/* return true if sample result is valid */
|
// Set the channel and start a conversion
|
||||||
bool isSampleReady();
|
int readChannel(Channel ch);
|
||||||
|
// return 1 if sample result is valid else 0 or -1 if I2C transaction failed
|
||||||
|
int isSampleReady();
|
||||||
/*
|
/*
|
||||||
* get adc sample. return the channel being measured.
|
* get adc sample. return the channel being measured.
|
||||||
* Invalid indicates sample failure.
|
* Invalid indicates sample failure.
|
||||||
*/
|
*/
|
||||||
ChannelSelection getMeasurement(int16_t *value);
|
Channel getMeasurement(int16_t *value);
|
||||||
/*
|
|
||||||
* get adc sample and automatically switch to next channel and start another measurement
|
|
||||||
*/
|
|
||||||
ChannelSelection cycleMeasure(int16_t *value);
|
|
||||||
|
|
||||||
int readReg(uint8_t addr, uint8_t *buf, size_t len);
|
int readReg(uint8_t addr, uint8_t *buf, size_t len);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,8 @@
|
|||||||
ADS1115::ADS1115(const I2CSPIDriverConfig &config) :
|
ADS1115::ADS1115(const I2CSPIDriverConfig &config) :
|
||||||
I2C(config),
|
I2C(config),
|
||||||
I2CSPIDriver(config),
|
I2CSPIDriver(config),
|
||||||
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample"))
|
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample")),
|
||||||
|
_comms_errors(perf_alloc(PC_COUNT, MODULE_NAME": comms errors"))
|
||||||
{
|
{
|
||||||
_adc_report.device_id = this->get_device_id();
|
_adc_report.device_id = this->get_device_id();
|
||||||
_adc_report.resolution = 32768;
|
_adc_report.resolution = 32768;
|
||||||
@@ -61,6 +62,7 @@ ADS1115::~ADS1115()
|
|||||||
{
|
{
|
||||||
ScheduleClear();
|
ScheduleClear();
|
||||||
perf_free(_cycle_perf);
|
perf_free(_cycle_perf);
|
||||||
|
perf_free(_comms_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADS1115::exit_and_cleanup()
|
void ADS1115::exit_and_cleanup()
|
||||||
@@ -77,56 +79,55 @@ void ADS1115::RunImpl()
|
|||||||
|
|
||||||
perf_begin(_cycle_perf);
|
perf_begin(_cycle_perf);
|
||||||
|
|
||||||
_adc_report.timestamp = hrt_absolute_time();
|
const int ready = isSampleReady();
|
||||||
|
|
||||||
if (isSampleReady()) { // whether ADS1115 is ready to be read or not
|
if (ready == 1) {
|
||||||
if (!_reported_ready_last_cycle) {
|
// I2C transaction success and status register reported conversion as finished
|
||||||
PX4_INFO("ADS1115: reported ready");
|
if (_ready_counter == 0) { PX4_INFO("ADS1115: reported ready"); }
|
||||||
_reported_ready_last_cycle = true;
|
|
||||||
|
if (_ready_counter < MAX_READY_COUNTER) { _ready_counter++; }
|
||||||
|
|
||||||
|
int16_t value;
|
||||||
|
Channel ch = getMeasurement(&value);
|
||||||
|
|
||||||
|
if (ch != Channel::Invalid) {
|
||||||
|
// Store current readings and mark channel as read
|
||||||
|
const unsigned index{ch2u(ch)};
|
||||||
|
_adc_report.channel_id[index] = index;
|
||||||
|
_adc_report.raw_data[index] = value;
|
||||||
|
_channel_cycle_mask |= 1u << index;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// we will retry the same channel again
|
||||||
|
perf_count(_comms_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t buf;
|
// Find the next unread channel in the bitmask
|
||||||
ADS1115::ChannelSelection ch = cycleMeasure(&buf);
|
uint8_t next_index{0};
|
||||||
++_channel_cycle_count;
|
|
||||||
|
|
||||||
switch (ch) {
|
for (; next_index < 4 && (_channel_cycle_mask & (1u << next_index)); next_index++) {}
|
||||||
case ADS1115::A0:
|
|
||||||
_adc_report.channel_id[0] = 0;
|
|
||||||
_adc_report.raw_data[0] = buf;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADS1115::A1:
|
readChannel(u2ch(next_index));
|
||||||
_adc_report.channel_id[1] = 1;
|
|
||||||
_adc_report.raw_data[1] = buf;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADS1115::A2:
|
if (_channel_cycle_mask == 0b1111) {
|
||||||
_adc_report.channel_id[2] = 2;
|
_channel_cycle_mask = 0;
|
||||||
_adc_report.raw_data[2] = buf;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADS1115::A3:
|
|
||||||
_adc_report.channel_id[3] = 3;
|
|
||||||
_adc_report.raw_data[3] = buf;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
PX4_DEBUG("ADS1115: undefined behaviour");
|
|
||||||
setChannel(ADS1115::A0);
|
|
||||||
--_channel_cycle_count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_channel_cycle_count == 4) { // ADS1115 has 4 channels
|
|
||||||
_channel_cycle_count = 0;
|
|
||||||
_to_adc_report.publish(_adc_report);
|
_to_adc_report.publish(_adc_report);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else if (ready == 0) {
|
||||||
if (_reported_ready_last_cycle) {
|
// I2C transaction success but status register reported conversion still in progress
|
||||||
_reported_ready_last_cycle = false;
|
perf_count(_comms_errors);
|
||||||
PX4_ERR("ADS1115: not ready. Device lost?");
|
// Reset the channel to unstick the device
|
||||||
}
|
readChannel(Channel::A0);
|
||||||
|
|
||||||
|
} else if (ready == -1) {
|
||||||
|
if (_ready_counter == 1) { PX4_ERR("ADS1115: device lost"); }
|
||||||
|
|
||||||
|
if (_ready_counter > 0) { _ready_counter--; }
|
||||||
|
|
||||||
|
perf_count(_comms_errors);
|
||||||
|
// Reset the channel to unstick the device
|
||||||
|
readChannel(Channel::A0);
|
||||||
}
|
}
|
||||||
|
|
||||||
perf_end(_cycle_perf);
|
perf_end(_cycle_perf);
|
||||||
@@ -163,6 +164,7 @@ void ADS1115::print_status()
|
|||||||
{
|
{
|
||||||
I2CSPIDriverBase::print_status();
|
I2CSPIDriverBase::print_status();
|
||||||
perf_print_counter(_cycle_perf);
|
perf_print_counter(_cycle_perf);
|
||||||
|
perf_print_counter(_comms_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int ads1115_main(int argc, char *argv[])
|
extern "C" int ads1115_main(int argc, char *argv[])
|
||||||
|
|||||||
Reference in New Issue
Block a user