add basic adc

This commit is contained in:
Tomasz Duda 2024-05-11 14:02:22 +02:00
parent db750fa53b
commit 101046d5de
7 changed files with 167 additions and 9 deletions

View File

@ -193,4 +193,9 @@ def validate_adc_pin(value):
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
if CORE.is_nrf52:
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
raise NotImplementedError

View File

@ -18,6 +18,10 @@ ADC_MODE(ADC_VCC)
#include <hardware/adc.h>
#endif
#ifdef USE_ZEPHYR
static const struct adc_dt_spec adc_chan = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
#endif
namespace esphome {
namespace adc {
@ -45,7 +49,7 @@ extern "C"
void
ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040) && !defined(USE_ZEPHYR)
pin_->setup();
#endif
@ -90,6 +94,20 @@ extern "C"
}
#endif
#ifdef USE_ZEPHYR
adc_chan_ = &adc_chan;
if (!adc_is_ready_dt(adc_chan_)) {
ESP_LOGE(TAG, "ADC controller device %s not ready", adc_chan_->dev->name);
return;
}
auto err = adc_channel_setup_dt(adc_chan_);
if (err < 0) {
ESP_LOGE(TAG, "Could not setup channel %s (%d)", adc_chan_->dev->name, err);
return;
}
#endif
ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
}
@ -139,6 +157,10 @@ void ADCSensor::dump_config() {
}
#endif // USE_RP2040
#ifdef USE_ZEPHYR
ESP_LOGCONFIG(TAG, " Name: %s, channel_id: %d", adc_chan_->dev->name, adc_chan_->channel_id);
#endif
LOG_UPDATE_INTERVAL(this);
}
@ -298,5 +320,49 @@ float ADCSensor::sample() {
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif
#ifdef USE_ZEPHYR
float ADCSensor::sample() {
uint16_t buf;
struct adc_sequence sequence = {
.buffer = &buf,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(buf),
};
int32_t val_mv;
adc_sequence_init_dt(adc_chan_, &sequence);
auto err = adc_read(adc_chan_->dev, &sequence);
if (err < 0) {
ESP_LOGE(TAG, "Could not read %s (%d)", adc_chan_->dev->name, err);
return 0.0;
}
/*
* If using differential mode, the 16 bit value
* in the ADC sample buffer should be a signed 2's
* complement value.
*/
if (adc_chan_->channel_cfg.differential) {
val_mv = (int32_t) ((int16_t) buf);
} else {
val_mv = (int32_t) buf;
}
if (output_raw_) {
return val_mv;
}
err = adc_raw_to_millivolts_dt(adc_chan_, &val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
ESP_LOGE(TAG, "Value in mV not available %s (%d)", adc_chan_->dev->name, err);
return 0.0;
}
return val_mv / 1000.0f;
}
#endif
} // namespace adc
} // namespace esphome

View File

@ -10,6 +10,9 @@
#include "driver/adc.h"
#include <esp_adc_cal.h>
#endif
#ifdef USE_ZEPHYR
#include <zephyr/drivers/adc.h>
#endif
namespace esphome {
namespace adc {
@ -37,7 +40,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
void dump_config() override;
/// `HARDWARE_LATE` setup priority
float get_setup_priority() const override;
#ifndef USE_ZEPHYR
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
#endif
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
float sample() override;
@ -50,7 +55,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#endif
protected:
#ifndef USE_ZEPHYR
InternalGPIOPin *pin_;
#else
const struct adc_dt_spec *adc_chan_;
#endif
bool output_raw_{false};
#ifdef USE_RP2040

View File

@ -21,6 +21,10 @@ from . import (
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
validate_adc_pin,
)
from esphome.components.zephyr import (
zephyr_add_overlay,
zephyr_add_prj_conf,
)
AUTO_LOAD = ["voltage_sampler"]
@ -85,6 +89,52 @@ async def to_code(config):
cg.add_define("USE_ADC_SENSOR_VCC")
elif config[CONF_PIN] == "TEMPERATURE":
cg.add(var.set_is_temperature())
elif CORE.using_zephyr:
zephyr_add_prj_conf("ADC", True)
zephyr_add_overlay(
"""
/ {
zephyr,user {
io-channels = <&adc 0>, <&adc 1>, <&adc 7>;
};
};
&adc {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN1>; /* P0.03 */
zephyr,resolution = <12>;
};
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_VDD>;
zephyr,resolution = <14>;
zephyr,oversampling = <8>;
};
channel@7 {
reg = <7>;
zephyr,gain = "ADC_GAIN_1_5";
zephyr,reference = "ADC_REF_VDD_1_4";
zephyr,vref-mv = <750>;
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN6>; /* P0.30 */
zephyr,input-negative = <NRF_SAADC_AIN7>; /* P0.31 */
zephyr,resolution = <12>;
};
};
"""
)
else:
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))

View File

@ -7,6 +7,7 @@ from esphome.const import (
CONF_MODE,
CONF_INVERTED,
CONF_NUMBER,
CONF_ANALOG,
)
from esphome.components.zephyr.const import (
zephyr_ns,
@ -35,7 +36,23 @@ def _translate_pin(value):
raise cv.Invalid(f"Invalid pin: {value}")
ADC_INPUTS = [
"AIN0",
"AIN1",
"AIN2",
"AIN3",
"AIN4",
"AIN5",
"AIN6",
"AIN7",
"VDD",
"VDDH",
]
def validate_gpio_pin(value):
if value in ADC_INPUTS:
return value
value = _translate_pin(value)
if value < 0 or value > (32 + 16):
raise cv.Invalid(f"NRF52: Invalid pin number: {value}")
@ -43,7 +60,11 @@ def validate_gpio_pin(value):
NRF52_PIN_SCHEMA = cv.All(
pins.gpio_base_schema(GPIOPin, validate_gpio_pin, modes=pins.GPIO_STANDARD_MODES),
pins.gpio_base_schema(
GPIOPin,
validate_gpio_pin,
modes=pins.GPIO_STANDARD_MODES + (CONF_ANALOG,),
),
)

View File

@ -6,7 +6,7 @@
namespace esphome {
namespace zephyr {
static const char *const TAG = "nrf52";
static const char *const TAG = "zephyr";
static int flags_to_mode(gpio::Flags flags, uint8_t pin, bool inverted, bool value) {
int ret = 0;

View File

@ -79,9 +79,16 @@ zephyr_debug:
debug:
text_sensor:
- platform: debug
device:
name: "Device Info"
reset_reason:
name: "Reset Reason"
sensor:
- platform: adc
pin: VDDH
name: "VDDH Voltage"
update_interval: 5sec
- platform: adc
pin: VDD
name: "VDD Voltage"
update_interval: 5sec
- platform: adc
pin: AIN0
name: "AIN0 Voltage"
update_interval: 5sec