From d43977894909ad42aa11718d97d6ed84bd912382 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Mon, 8 Sep 2025 15:38:08 -0500 Subject: [PATCH] Add basic ADC support Planning on abstracting this behind hardware setup APIs, but this works for a simple demo until then --- CMakeLists.txt | 2 + boards/nrf52840dongle_nrf52840.overlay | 19 +++++++ core/hardware/adc.c | 75 ++++++++++++++++++++++++++ core/hardware/adc.h | 11 ++++ core/main.c | 2 + includes/hardware/adc.h | 10 ++++ prj.conf | 3 ++ routines/photosensor.c | 27 ++++++++++ routines/routine_list.c | 2 + 9 files changed, 151 insertions(+) create mode 100644 boards/nrf52840dongle_nrf52840.overlay create mode 100644 core/hardware/adc.c create mode 100644 core/hardware/adc.h create mode 100644 includes/hardware/adc.h create mode 100644 routines/photosensor.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9055fbb..8966607 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,10 +9,12 @@ target_sources(app PRIVATE core/main.c core/ble.c core/routine.c + core/hardware/adc.c ) # User routines to compile target_sources(app PRIVATE routines/routine_list.c routines/input_echo.c + routines/photosensor.c ) diff --git a/boards/nrf52840dongle_nrf52840.overlay b/boards/nrf52840dongle_nrf52840.overlay new file mode 100644 index 0000000..a0fa543 --- /dev/null +++ b/boards/nrf52840dongle_nrf52840.overlay @@ -0,0 +1,19 @@ +/ { + zephyr,user { + io-channels = <&adc 0>; + }; +}; + +&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 = ; + zephyr,input-positive = ; /* P0.02 */ + zephyr,resolution = <12>; + }; +}; diff --git a/core/hardware/adc.c b/core/hardware/adc.c new file mode 100644 index 0000000..9042a77 --- /dev/null +++ b/core/hardware/adc.c @@ -0,0 +1,75 @@ +#include +#include +#include + +#include +#include +#include +#include + +/** Setup code referenced from Zephyr ADC example **/ +#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \ + !DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) +#error "No suitable devicetree overlay specified" +#endif + +#define DT_SPEC_AND_COMMA(node_id, prop, idx) \ + ADC_DT_SPEC_GET_BY_IDX(node_id, idx), + +static const struct adc_dt_spec adc_channels[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_SPEC_AND_COMMA) +}; + +static int setup = 0; + +int +adc_init() +{ + for (int i = 0; i < ARRAY_SIZE(adc_channels); i++) { + int err; + const struct adc_dt_spec *chan = &adc_channels[i]; + + if (!adc_is_ready_dt(chan)) { + return (-1); + } + + err = adc_channel_setup_dt(chan); + if (err) { + return (err); + } + } + + setup = 1; + return (0); +} + +int +adc_get_mv(int channel) +{ + uint16_t val_raw; + int32_t val_mv; + struct adc_sequence seq = { + .buffer = &val_raw, + .buffer_size = sizeof(val_raw) + }; + const struct adc_dt_spec *chan; + + if ((channel >= ARRAY_SIZE(adc_channels)) || + (!setup)) { + return (INT32_MIN); + } + + chan = &adc_channels[channel]; + adc_sequence_init_dt(chan, &seq); + + if (adc_read_dt(chan, &seq) < 0) { + return (INT32_MIN); + } + + val_mv = (int32_t)val_raw; + if (adc_raw_to_millivolts_dt(chan, &val_mv)) { + return (INT32_MIN); + } + + return (val_mv); +} diff --git a/core/hardware/adc.h b/core/hardware/adc.h new file mode 100644 index 0000000..dad8a74 --- /dev/null +++ b/core/hardware/adc.h @@ -0,0 +1,11 @@ +#ifndef _ADC_H_ +#define _ADC_H_ + +#include "includes/hardware/adc.h" + +/** + * @brief Initialize ADC hardware + */ +int adc_init(void); + +#endif /* _ADC_H_ */ diff --git a/core/main.c b/core/main.c index dde5cfd..c99500d 100644 --- a/core/main.c +++ b/core/main.c @@ -5,10 +5,12 @@ #include "core/ble.h" #include "core/routine.h" +#include "core/hardware/adc.h" int main(void) { + adc_init(); routine_init(); ble_init(); diff --git a/includes/hardware/adc.h b/includes/hardware/adc.h new file mode 100644 index 0000000..859fc1b --- /dev/null +++ b/includes/hardware/adc.h @@ -0,0 +1,10 @@ +#ifndef _INCLUDES_HARDWARE_ADC_H_ +#define _INCLUDES_HARDWARE_ADC_H_ + +/** + * @brief Read the ADC value from the given channel index. See "adc-channels" in + * your board overlay file. + */ +int adc_get_mv(int channel); + +#endif /* _INCLUDES_HARDWARE_ADC_H_ */ diff --git a/prj.conf b/prj.conf index 0d402a5..1b2d9ed 100644 --- a/prj.conf +++ b/prj.conf @@ -7,4 +7,7 @@ CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_ADC=y + CONFIG_DEBUG=y +CONFIG_DEBUG_OPTIMIZATIONS=y diff --git a/routines/photosensor.c b/routines/photosensor.c new file mode 100644 index 0000000..238248c --- /dev/null +++ b/routines/photosensor.c @@ -0,0 +1,27 @@ +#include + +#include "includes/ble.h" +#include "includes/routine.h" +#include "includes/hardware/adc.h" + +routine_return_e +photosensor() +{ + while (routine_status() == ROUTINE_STATUS_RUNNING) { + int32_t adc_mv = adc_get_mv(0); + char buf[16]; + size_t print_len; + + if (adc_mv == INT32_MIN) { + ble_set_telemetry_data("Reading failed", 14); + } else { + print_len = snprintk(buf, sizeof(buf), "%dmV", adc_mv); + ble_set_telemetry_data(buf, print_len); + } + + routine_delay(500); + routine_yield(); + } + + return ROUTINE_RETURN_SUCCESS; +} diff --git a/routines/routine_list.c b/routines/routine_list.c index fab748c..b491ea3 100644 --- a/routines/routine_list.c +++ b/routines/routine_list.c @@ -4,8 +4,10 @@ /** Extern all of your routines here so they can be included in the list **/ extern routine_return_e input_echo(); +extern routine_return_e photosensor(); const routine_listing_t routine_list[] = { { .entry = input_echo, .name = "Input Echo" }, + { .entry = photosensor, .name = "Photosensor" }, { NULL, NULL }, };