Add basic ADC support

Planning on abstracting this behind hardware setup APIs, but this works
for a simple demo until then
This commit is contained in:
Aaron Cook 2025-09-08 15:38:08 -05:00
commit d439778949
9 changed files with 151 additions and 0 deletions

View file

@ -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
)

View file

@ -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 = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN0>; /* P0.02 */
zephyr,resolution = <12>;
};
};

75
core/hardware/adc.c Normal file
View file

@ -0,0 +1,75 @@
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/kernel.h>
/** 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);
}

11
core/hardware/adc.h Normal file
View file

@ -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_ */

View file

@ -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();

10
includes/hardware/adc.h Normal file
View file

@ -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_ */

View file

@ -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

27
routines/photosensor.c Normal file
View file

@ -0,0 +1,27 @@
#include <zephyr/kernel.h>
#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;
}

View file

@ -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 },
};