mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
[adc] Refactor ADS1115 driver (#24428)
This commit is contained in:
parent
0ee592f67c
commit
2aecdfe116
@ -42,18 +42,7 @@ int ADS1115::init()
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t config[2] = {};
|
||||
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.
|
||||
readChannel(Channel::A0); // prepare for the first measure.
|
||||
|
||||
ScheduleOnInterval(SAMPLE_INTERVAL / 4, SAMPLE_INTERVAL / 4);
|
||||
|
||||
@ -62,152 +51,69 @@ int ADS1115::init()
|
||||
|
||||
int ADS1115::probe()
|
||||
{
|
||||
uint8_t buf[2] = {};
|
||||
int ret = readReg(ADDRESSPOINTER_REG_CONFIG, buf, 2);
|
||||
// The ADS1115 has no ID register, so we read out the threshold registers
|
||||
// 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) {
|
||||
DEVICE_DEBUG("readReg failed (%i)", ret);
|
||||
DEVICE_DEBUG("lo_thresh read failed (%i)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (buf[0] != CONFIG_RESET_VALUE_HIGH || buf[1] != CONFIG_RESET_VALUE_LOW) {
|
||||
DEVICE_DEBUG("ADS1115 not found");
|
||||
return PX4_ERROR;
|
||||
ret = readReg(ADDRESSPOINTER_REG_HI_THRESH, parts.high, 2);
|
||||
|
||||
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 next_mux_reg = CONFIG_HIGH_MUX_P0NG;
|
||||
|
||||
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;
|
||||
uint8_t buf[2];
|
||||
buf[0] = CONFIG_HIGH_OS_START_SINGLE | uint8_t(ch) | 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;
|
||||
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};
|
||||
|
||||
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};
|
||||
readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1); // Pull config register
|
||||
ChannelSelection channel;
|
||||
|
||||
switch ((buf[0] & (uint8_t) 0x70) >> 4) {
|
||||
case 0x04:
|
||||
channel = A0;
|
||||
break;
|
||||
if (readReg(ADDRESSPOINTER_REG_CONFIG, buf, 1) != PX4_OK) { return Channel::Invalid; }
|
||||
|
||||
case 0x05:
|
||||
channel = A1;
|
||||
break;
|
||||
const auto channel{Channel(buf[0] & CONFIG_HIGH_MUX_P3NG)};
|
||||
|
||||
case 0x06:
|
||||
channel = A2;
|
||||
break;
|
||||
if (readReg(ADDRESSPOINTER_REG_CONVERSATION, buf, 2) != PX4_OK) { return Channel::Invalid; }
|
||||
|
||||
case 0x07:
|
||||
channel = A3;
|
||||
break;
|
||||
*value = int16_t((buf[0] << 8) | buf[1]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -93,9 +93,6 @@
|
||||
#define CONFIG_LOW_COMP_QU_AFTER4 0x02
|
||||
#define CONFIG_LOW_COMP_QU_DISABLE 0x03
|
||||
|
||||
#define CONFIG_RESET_VALUE_HIGH 0x85
|
||||
#define CONFIG_RESET_VALUE_LOW 0x83
|
||||
|
||||
using namespace time_literals;
|
||||
|
||||
/*
|
||||
@ -127,35 +124,39 @@ protected:
|
||||
|
||||
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{};
|
||||
|
||||
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
|
||||
enum ChannelSelection {
|
||||
Invalid = -1, A0 = 0, A1, A2, A3
|
||||
enum class Channel : uint8_t {
|
||||
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 */
|
||||
int setChannel(ChannelSelection ch);
|
||||
/* return true if sample result is valid */
|
||||
bool isSampleReady();
|
||||
constexpr unsigned ch2u(Channel ch) { return (unsigned(ch) >> 4) & 0b11u; }
|
||||
constexpr Channel u2ch(unsigned ch) { return Channel((ch << 4) | CONFIG_HIGH_MUX_P0NG); }
|
||||
// Set the channel and start a conversion
|
||||
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.
|
||||
* Invalid indicates sample failure.
|
||||
*/
|
||||
ChannelSelection getMeasurement(int16_t *value);
|
||||
/*
|
||||
* get adc sample and automatically switch to next channel and start another measurement
|
||||
*/
|
||||
ChannelSelection cycleMeasure(int16_t *value);
|
||||
Channel getMeasurement(int16_t *value);
|
||||
|
||||
int readReg(uint8_t addr, uint8_t *buf, size_t len);
|
||||
|
||||
|
||||
@ -45,7 +45,8 @@
|
||||
ADS1115::ADS1115(const I2CSPIDriverConfig &config) :
|
||||
I2C(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.resolution = 32768;
|
||||
@ -61,6 +62,7 @@ ADS1115::~ADS1115()
|
||||
{
|
||||
ScheduleClear();
|
||||
perf_free(_cycle_perf);
|
||||
perf_free(_comms_errors);
|
||||
}
|
||||
|
||||
void ADS1115::exit_and_cleanup()
|
||||
@ -77,56 +79,55 @@ void ADS1115::RunImpl()
|
||||
|
||||
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 (!_reported_ready_last_cycle) {
|
||||
PX4_INFO("ADS1115: reported ready");
|
||||
_reported_ready_last_cycle = true;
|
||||
if (ready == 1) {
|
||||
// I2C transaction success and status register reported conversion as finished
|
||||
if (_ready_counter == 0) { PX4_INFO("ADS1115: reported ready"); }
|
||||
|
||||
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;
|
||||
ADS1115::ChannelSelection ch = cycleMeasure(&buf);
|
||||
++_channel_cycle_count;
|
||||
// Find the next unread channel in the bitmask
|
||||
uint8_t next_index{0};
|
||||
|
||||
switch (ch) {
|
||||
case ADS1115::A0:
|
||||
_adc_report.channel_id[0] = 0;
|
||||
_adc_report.raw_data[0] = buf;
|
||||
break;
|
||||
for (; next_index < 4 && (_channel_cycle_mask & (1u << next_index)); next_index++) {}
|
||||
|
||||
case ADS1115::A1:
|
||||
_adc_report.channel_id[1] = 1;
|
||||
_adc_report.raw_data[1] = buf;
|
||||
break;
|
||||
readChannel(u2ch(next_index));
|
||||
|
||||
case ADS1115::A2:
|
||||
_adc_report.channel_id[2] = 2;
|
||||
_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;
|
||||
if (_channel_cycle_mask == 0b1111) {
|
||||
_channel_cycle_mask = 0;
|
||||
_to_adc_report.publish(_adc_report);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (_reported_ready_last_cycle) {
|
||||
_reported_ready_last_cycle = false;
|
||||
PX4_ERR("ADS1115: not ready. Device lost?");
|
||||
}
|
||||
} else if (ready == 0) {
|
||||
// I2C transaction success but status register reported conversion still in progress
|
||||
perf_count(_comms_errors);
|
||||
// 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);
|
||||
@ -151,7 +152,7 @@ parameter, and is disabled by default.
|
||||
If enabled, internal ADCs are not used.
|
||||
|
||||
)DESCR_STR");
|
||||
|
||||
|
||||
PRINT_MODULE_USAGE_NAME("ads1115", "driver");
|
||||
PRINT_MODULE_USAGE_COMMAND("start");
|
||||
PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false);
|
||||
@ -163,6 +164,7 @@ void ADS1115::print_status()
|
||||
{
|
||||
I2CSPIDriverBase::print_status();
|
||||
perf_print_counter(_cycle_perf);
|
||||
perf_print_counter(_comms_errors);
|
||||
}
|
||||
|
||||
extern "C" int ads1115_main(int argc, char *argv[])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user