I have a very simple program trying to handle asynchronous pin events via a callback function while also reading analog input (on different pins). If the callback is installed, and I get a pin event while in the middle of an adc_read() call, get a USAGE FAULT:
Reading ADCs
- channel 0: 1586 mV
- channel 1: 1679 mV
- channel 2: 97 mV
- channel 3: 210 mV
Reading ADCs
- channel 0: 1590 mV
- channel 1: 1677 mV
- channel 2: 94 mV
- channel 3: 206 mV
[00:02:25.057,922] <err> os: ***** USAGE FAULT *****
[00:02:25.057,922] <err> os: Illegal use of the EPSR
[00:02:25.057,952] <err> os: r0/a1: 0x00013750 r1/a2: 0x20001f74 r2/a3: 0x00001000
[00:02:25.057,983] <err> os: r3/a4: 0x00000008 r12/ip: 0xaaaaaaaa r14/lr: 0x0000fdd7
[00:02:25.057,983] <err> os: xpsr: 0x20000016
[00:02:25.057,983] <err> os: Faulting instruction address (r15/pc): 0x00000008
[00:02:25.058,044] <err> os: >>> ZEPHYR FATAL ERROR 35: Unknown error on CPU 0
[00:02:25.058,044] <err> os: Fault during interrupt handling
[00:02:25.058,074] <err> os: Current thread: 0x200006d0 (idle)
�*** Booting Zephyr OS build v3.3.99-ncs1-1 *** system
*** Booting Zephyr OS build v3.3.99-ncs1-1 ***
Replicating source code below. If I comment out the adc_read() call, the GPIO callback works as expected.
#include <stddef.h> #include <stdio.h> #include <string.h> #include <inttypes.h> #include <stdint.h> #include <math.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/adc.h> #include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/sys/util.h> #include <zephyr/kernel.h> const struct gpio_dt_spec button_dt = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios); #define DT_SPEC_AND_COMMA(node_id, prop, idx) \ ADC_DT_SPEC_GET_BY_IDX(node_id, idx), /* Data of ADC io-channels specified in devicetree. */ static const struct adc_dt_spec adc_channels[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_SPEC_AND_COMMA) }; static void _gpio_setup(void); static void _adc_setup(void); static void _button_pressed_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins); K_SEM_DEFINE(s_inputs_updated_sema, 0, 1); static bool button_pressed = false; //----------------------------------------------------------------------------- static void _gpio_setup(void) { struct gpio_callback gpio_cb_data; if (!gpio_is_ready_dt(&button_dt)) { printk("Button dev not ready\n"); return; } if (gpio_pin_configure_dt(&button_dt, GPIO_INPUT)) { printk("Configure Button failed\n"); return; } if (gpio_pin_interrupt_configure_dt(&button_dt, GPIO_INT_EDGE_BOTH)) { printk("Error: failed to configure interrupt\n"); } gpio_init_callback(&gpio_cb_data, _button_pressed_cb, BIT(button_dt.pin)); gpio_add_callback(button_dt.port, &gpio_cb_data); } //----------------------------------------------------------------------------- static void _button_pressed_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { button_pressed = gpio_pin_get_dt(&button_dt); k_sem_give(&s_inputs_updated_sema); } //----------------------------------------------------------------------------- static void _adc_setup(void) { int err; for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { if (!device_is_ready(adc_channels[i].dev)) { printk("ADC controller device %s not ready\n", adc_channels[i].dev->name); return; } err = adc_channel_setup_dt(&adc_channels[i]); if (err < 0) { printk("Could not setup channel #%d (%d)\n", i, err); return; } } } //----------------------------------------------------------------------------- static void _adc_read(void) { printk("Reading ADCs\n"); int err; uint16_t buf; struct adc_sequence sequence = { .buffer = &buf, /* buffer size in bytes, not number of samples */ .buffer_size = sizeof(buf), }; for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { int32_t val_mv; adc_sequence_init_dt(&adc_channels[i], &sequence); if (adc_read(adc_channels[i].dev, &sequence) == 0) { val_mv = (int32_t)((int16_t)buf); err = adc_raw_to_millivolts_dt(&adc_channels[i], &val_mv); printk("- channel %d: %i mV\n", adc_channels[i].channel_id, val_mv); } } } //----------------------------------------------------------------------------- int main(void) { printk("main()\n"); _gpio_setup(); _adc_setup(); while (1) { if (!k_sem_take(&s_inputs_updated_sema, K_MSEC(250))) printk("Handle button presses here\n"); _adc_read(); } }
Associated overlay:
/ { zephyr,user { io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>; }; }; &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>; 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_AIN2>; zephyr,resolution = <12>; }; channel@2 { reg = <2>; zephyr,gain = "ADC_GAIN_1_6"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; zephyr,input-positive = <NRF_SAADC_AIN3>; zephyr,resolution = <12>; }; channel@3 { reg = <3>; zephyr,gain = "ADC_GAIN_1_6"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; zephyr,input-positive = <NRF_SAADC_AIN4>; zephyr,resolution = <12>; }; };
And prj.conf:
CONFIG_SERIAL=y CONFIG_LOG=y CONFIG_LOG_PRINTK=y # Redirect PRINTK to the logging subsystem CONFIG_PRINTK=y CONFIG_DISABLE_FLASH_PATCH=y # Disable Cortex-M4 Flash Patch capabilities CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_GPIO=y CONFIG_ADC=y # This option instructs the kernel to initialize stack areas with a known value (0xaa) before they are first used, so that the high # water mark can be easily determined. CONFIG_INIT_STACKS=y CONFIG_THREAD_NAME=y # This option allows to set a name for a thread. CONFIG_THREAD_MONITOR=y # This option instructs the kernel to maintain a list of all threads CONFIG_THREAD_STACK_INFO=y