Add board 1.0.0, 1.1.0, 1.2.0 board revisions for nRF52840 variant

This commit is contained in:
rbaron 2022-12-03 10:46:48 +01:00
parent a93171b9bf
commit dc88916823
10 changed files with 135 additions and 42 deletions

View file

@ -13,7 +13,7 @@
};
zephyr,user {
io-channels = <&adc 0>, <&adc 1>, <&adc 2>;
io-channels = <&adc 0>, <&adc 2>;
};
leds {
@ -108,17 +108,6 @@
};
// Photo.
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
// P0.02.
zephyr,input-positive = <NRF_SAADC_AIN0>;
zephyr,resolution = <10>;
};
// Battery.
channel@2 {
reg = <2>;

View file

@ -0,0 +1,30 @@
/ {
// Light dependant resistor.
ldr: ldr {
compatible = "voltage-divider";
output-ohms = <10000>;
io-channels = <&adc 1>;
};
ctrl {
compatible = "gpio-keys";
ldr_enable: ldr_enable {
// P0.29.
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
label = "Fast discharge circuitry";
};
};
};
&adc {
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
// P0.02.
zephyr,input-positive = <NRF_SAADC_AIN0>;
zephyr,resolution = <10>;
};
};

View file

@ -0,0 +1,29 @@
/ {
photo_transistor: photo_transistor {
compatible = "voltage-divider";
output-ohms = <470>;
io-channels = <&adc 1>;
};
ctrl {
compatible = "gpio-keys";
photo_transistor_enable: photo_transistor_enable {
// P0.29.
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
label = "Fast discharge circuitry";
};
};
};
&adc {
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
// P0.02.
zephyr,input-positive = <NRF_SAADC_AIN0>;
zephyr,resolution = <10>;
};
};

View file

@ -0,0 +1 @@
board_check_revision(FORMAT MAJOR.MINOR.PATCH)

View file

@ -4,6 +4,7 @@
#include <drivers/gpio.h>
#include <drivers/pwm.h>
#include <logging/log.h>
#include <math.h>
#include <zephyr/zephyr.h>
#include "prstlib/macros.h"
@ -28,21 +29,34 @@ static struct adc_sequence sequence = {
static const struct adc_dt_spec adc_soil_spec =
ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
static const struct adc_dt_spec adc_lux_spec =
static const struct adc_dt_spec adc_batt_spec =
ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
static const struct adc_dt_spec adc_batt_spec =
ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 2);
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
static const struct adc_dt_spec adc_photo_transistor_spec =
ADC_DT_SPEC_GET(DT_NODELABEL(photo_transistor));
struct gpio_dt_spec photo_transistor_enable_dt =
GPIO_DT_SPEC_GET(DT_NODELABEL(photo_transistor_enable), gpios);
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
static const struct adc_dt_spec adc_ldr_spec =
ADC_DT_SPEC_GET(DT_NODELABEL(ldr));
struct gpio_dt_spec ldr_enable_dt =
GPIO_DT_SPEC_GET(DT_NODELABEL(ldr_enable), gpios);
#endif
// TODO: recalibrate with v2 hardware.
static inline float get_soil_moisture_percent(float battery_voltage,
int16_t raw_adc_output) {
const double x = battery_voltage;
const double dry = -11.7f * x * x + 101.0f * x + 306.0f;
const double wet = 3.42f * x * x - 4.98f * x + 19.0f;
LOG_DBG("Raw %u | Batt: %.2f | Dry: %.2f | Wet: %.2f", raw_adc_output, x, dry,
wet);
return (raw_adc_output - dry) / (wet - dry);
const float percent = (raw_adc_output - dry) / (wet - dry);
LOG_DBG("Read soil moisture: %.2f | Raw %u | Batt: %.2f | Dry: %.2f | Wet: %.2f",
100.0f * percent, raw_adc_output, x, dry, wet);
return percent;
}
static int read_adc_spec(const struct adc_dt_spec* spec, prst_adc_read_t* out) {
@ -60,17 +74,22 @@ static int read_adc_spec(const struct adc_dt_spec* spec, prst_adc_read_t* out) {
}
int prst_adc_init() {
const struct adc_dt_spec* all_specs[] = {
&adc_soil_spec,
&adc_lux_spec,
&adc_batt_spec,
};
for (int i = 0; i < sizeof(all_specs) / sizeof(all_specs[0]); i++) {
RET_IF_ERR(adc_channel_setup_dt(all_specs[i]));
}
RET_IF_ERR(adc_channel_setup_dt(&adc_soil_spec));
RET_IF_ERR(adc_channel_setup_dt(&adc_batt_spec));
RET_IF_ERR(!device_is_ready(fast_disch_dt.port));
return gpio_pin_configure_dt(&fast_disch_dt, GPIO_OUTPUT);
RET_IF_ERR(gpio_pin_configure_dt(&fast_disch_dt, GPIO_OUTPUT));
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
RET_IF_ERR(adc_channel_setup_dt(&adc_photo_transistor_spec));
RET_IF_ERR(!device_is_ready(photo_transistor_enable_dt.port));
RET_IF_ERR(gpio_pin_configure_dt(&photo_transistor_enable_dt, GPIO_OUTPUT));
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
RET_IF_ERR(adc_channel_setup_dt(&adc_ldr_spec));
RET_IF_ERR(!device_is_ready(ldr_enable_dt.port));
RET_IF_ERR(gpio_pin_configure_dt(&ldr_enable_dt, GPIO_OUTPUT));
#endif
return 0;
}
@ -82,38 +101,59 @@ int prst_adc_batt_read(prst_adc_read_t* out) {
int prst_adc_soil_read(float battery_voltage, prst_adc_soil_moisture_t* out) {
// Enable fast discharge circuit.
RET_IF_ERR(gpio_pin_set_dt(&fast_disch_dt, 1));
// Start PWM.
RET_IF_ERR(pwm_set_dt(&soil_pwm_dt, soil_pwm_dt.period, pulse));
k_msleep(30);
RET_IF_ERR(read_adc_spec(&adc_soil_spec, &out->adc_read));
// Stop PWM.
RET_IF_ERR(pwm_set_dt(&soil_pwm_dt, 0, 0));
// Turn off fast discharge circuit.
RET_IF_ERR(gpio_pin_set_dt(&fast_disch_dt, 0));
out->percentage =
MAX(0.0f, MIN(1.0f, get_soil_moisture_percent(battery_voltage, buf)));
return 0;
}
int prst_adc_photo_read(float battery_voltage, prst_adc_photo_sensor_t* out) {
RET_IF_ERR(read_adc_spec(&adc_soil_spec, &out->adc_read));
// The ALS-PT19 phototransistor is a device in which the current flow between
// its two terminals is controlled by how much light there is in the ambient.
// We measure that current by calculating the voltage across a resistor that
// is connected in series with the phototransistor.
const float phototransistor_resistor = 470.0f;
const float current_sun = 3.59e-3f;
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
RET_IF_ERR(gpio_pin_set_dt(&photo_transistor_enable_dt, 1));
k_msleep(10);
RET_IF_ERR(read_adc_spec(&adc_photo_transistor_spec, &out->adc_read));
RET_IF_ERR(gpio_pin_set_dt(&photo_transistor_enable_dt, 0));
const float phototransistor_resistor = DT_PROP(DT_NODELABEL(photo_transistor), output_ohms);
// Assuming 10000 lux for the saturation test. Calibration with a proper light
// meter would be better.
const float lux_sun = 10000.0f;
const float current_sun = 3.59e-3f;
const float current = out->adc_read.voltage / phototransistor_resistor;
out->brightness = MAX(0, MIN(lux_sun * current / current_sun, UINT16_MAX));
LOG_DBG("Read phototransistor: %u lx | %.2f V", out->brightness, out->adc_read.voltage);
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
RET_IF_ERR(gpio_pin_set_dt(&ldr_enable_dt, 1));
k_msleep(10);
RET_IF_ERR(read_adc_spec(&adc_ldr_spec, &out->adc_read));
RET_IF_ERR(gpio_pin_set_dt(&ldr_enable_dt, 0));
// The photo resistor forms a voltage divider with R.
// The voltage here is measured in the middle of the voltage divider.
// Vcc ---- (R_photo) ---|--- (R) ---- GND
// Vout
// So we can estimate R_photo = R * (Vcc - Vout) / Vout
const float r = DT_PROP(DT_NODELABEL(ldr), output_ohms);
const float photo_resistance =
r * (battery_voltage - out->adc_read.voltage) / out->adc_read.voltage;
// The relationship between the LDR resistance and the lux level is
// logarithmic. We need to solve a logarithmic equation to find the lux
// level, given the LDR resistance we just measured.
// These values work for the GL5528 LDR and were borrowed from
// https://github.com/QuentinCG/Arduino-Light-Dependent-Resistor-Library.
const float mult_value = 32017200.0f;
const float pow_value = 1.5832f;
out->brightness =
MAX(0, MIN(mult_value / powf(photo_resistance, pow_value), UINT16_MAX));
LOG_DBG("Read LDR: %u lx | %.2f V", out->brightness, out->adc_read.voltage);
#endif
return 0;
}

View file

@ -20,3 +20,5 @@ CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
# Application config - see all options in Kconfig.
# CONFIG_PRST_BLE_ENCODING_BTHOME_V1=y
# CONFIG_PRST_SLEEP_DURATION_SEC=1