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:
parent
17b3f0a89f
commit
d439778949
9 changed files with 151 additions and 0 deletions
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
19
boards/nrf52840dongle_nrf52840.overlay
Normal file
19
boards/nrf52840dongle_nrf52840.overlay
Normal 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
75
core/hardware/adc.c
Normal 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
11
core/hardware/adc.h
Normal 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_ */
|
||||
|
|
@ -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
10
includes/hardware/adc.h
Normal 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_ */
|
||||
3
prj.conf
3
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
|
||||
|
|
|
|||
27
routines/photosensor.c
Normal file
27
routines/photosensor.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue