2026-01-20 9:19 PM - last edited on 2026-01-24 11:14 AM by mƎALLEm
I'm trying to read ADC1 on channel 12 using ADC with DMA in a Zephyr OS environment, but I'm getting a BUS FAULT error when using the adc_read_async() API.
This is my prj.conf file
CONFIG_LOG=y
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_ADC=y
CONFIG_ADC_ASYNC=y
CONFIG_DMA=y
CONFIG_ADC_STM32_DMA=y
CONFIG_DMA_STM32=y
CONFIG_DMA_STM32U5=n
CONFIG_ARM_MPU=n
CONFIG_DCACHE=n
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_CACHE_MANAGEMENT=y
CONFIG_NOCACHE_MEMORY=y
CONFIG_FAULT_DUMP=2
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_ISR_STACK_SIZE=2048
CONFIG_ADC_LOG_LEVEL_DBG=y
CONFIG_DMA_LOG_LEVEL_DBG=y
Overlay
/ {
zephyr,user {
io-channels = <&adc1 0>;
};
aliases {
adc1 = &adc1;
};
};
&dma1 {
status = "okay";
};
&dma2 {
status = "okay";
};
&dmamux1 {
status = "okay";
};
&adc1 {
status = "okay";
compatible = "st,stm32-adc";
st,adc-clock-source = "SYNC";
st,adc-prescaler = <4>;
// pinctrl-0 = <&adc1_in12_pc0>;
// pinctrl-names = "default";
dmas = <&dmamux1 0 9 (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_MEM_INC | STM32_DMA_PRIORITY_HIGH | STM32_DMA_MEM_16BITS | STM32_DMA_PERIPH_16BITS)>;
// dma-names = "adc";
dma-names = "rx";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>; /* Channel */
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <16>;
zephyr,oversampling = <0>; /* Oversampling setting to be used for the channel. When specified, each sample is averaged from 2^N conversion results (where N is the provided value). */
};
};
main.c
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/dma.h>
#define LOG_LEVEL LOG_LEVEL_INF
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(app);
#define ADC_NODE DT_PATH(zephyr_user)
static const struct adc_dt_spec adc_chan0 = ADC_DT_SPEC_GET_BY_IDX(ADC_NODE, 0);
const struct device *dma_dev = DEVICE_DT_GET(DT_NODELABEL(dmamux1));
struct k_poll_signal async_sig = K_POLL_SIGNAL_INITIALIZER(async_sig);
struct k_poll_event async_evt = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &async_sig);
static int16_t sample_buffer[16] __aligned(32) __nocache;
static const struct adc_sequence_options options = {
.interval_us = 0,
.extra_samplings = 15,
.callback = NULL,
};
static struct adc_sequence sequence = {
.options = NULL,
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 16,
};
int adc_dma_init(void) {
int err;
if (!adc_is_ready_dt(&adc_chan0.dev) || !device_is_ready(dma_dev)) {
LOG_INF("ADC controller device %s not ready\n",
adc_chan0.dev->name);
return 0;
}
if (adc_chan0.dev->api == NULL) {
LOG_ERR("ADC API is NULL - driver init/DMA binding failed!");
return -ENODEV;
}
err = adc_sequence_init_dt(&adc_chan0, &sequence);
if (err != 0) {
LOG_ERR("adc_read_async failed: %d", err);
return err;
}
LOG_INF("ADC initialized");
return 0;
}
int adc_dma_read_data(void) {
int err;
struct device *dev = adc_chan0.dev;
const struct adc_driver_api *api_ptr = (const struct adc_driver_api *)dev->api;
LOG_INF("API address: %p", api_ptr);
LOG_INF("read function: %p", api_ptr->read);
LOG_INF("read_async function: %p", api_ptr->read_async);
if (api_ptr->read_async == NULL) {
LOG_ERR("read_async is NULL! DMA not supported.");
return -ENOTSUP;
}
// LOG_INF("Starting blocking ADC read...");
// err = adc_read_dt(&adc_chan0, &sequence);
// if (err == 0) {
// LOG_INF("Blocking read OK");
// // sys_cache_data_invd_range(sample_buffer, sizeof(sample_buffer)); // safety
// LOG_HEXDUMP_INF(sample_buffer, sizeof(sample_buffer), "Blocking samples:");
// } else {
// LOG_ERR("adc_read_dt failed: %d", err);
// }
LOG_INF("Calling read_async...");
err = adc_read_async(&adc_chan0, &sequence, &async_sig); // note: use &adc_chan0 (dt_spec), not &adc_chan0.dev
if (err != 0) {
LOG_ERR("adc_read_async failed: %d", err);
return err;
}
LOG_INF("ADC conversion started, waiting for completion...");
// Wait for the signal (timeout 2 seconds)
struct k_poll_event events[1] = { async_evt };
err = k_poll(events, 1, K_MSEC(2000));
if (err == 0 && events[0].signal->signaled) {
// Optional: invalidate cache if you ever enable it later
// sys_cache_data_invd_range(sample_buffer, sizeof(sample_buffer));
LOG_INF("ADC conversion COMPLETE!");
LOG_HEXDUMP_INF(sample_buffer, sizeof(sample_buffer), "ADC samples:");
// Optional: convert first sample to mV (assuming single-ended, Vref=3300 mV)
int32_t raw = (int32_t)sample_buffer[0];
int32_t mv = (raw * 3300) / 65536; // for 16-bit resolution
LOG_INF("Channel 0: %d raw %d mV", raw, mv);
} else {
LOG_ERR("Timeout waiting for ADC completion: %d", err);
if (err == -EAGAIN) {
LOG_ERR("No signal raised (DMA/callback issue?)");
}
}
return err;
}
int main(void)
{
int err;
LOG_INF("Main app");
LOG_INF("DMA (Buffer Address): %p\n", (void *)sample_buffer);
LOG_INF("sequence Address: %p\n", &sequence);
adc_dma_init();
adc_dma_read_data();
while (1) {
k_sleep(K_MSEC(500));
}
}
2026-04-02 7:13 AM - edited 2026-04-03 6:34 AM
hello @HuongNg
you are getting a BUS FAULT when calling adc_read_async() in Zephyr with STM32 ADC + DMA. After reviewing your code, the most likely cause is not the ADC/DMA hardware itself, but an incorrect use of the Zephyr ADC API.
The main issue is this call:
adc_read_async(&adc_chan0, &sequence, &async);
adc_read_async() expects its first argument to be a const struct device *, but &adc_chan0 is a pointer to struct adc_dt_spec . Because of that, the ADC driver receives an invalid pointer type, interprets unrelated memory as a device structure, and may access an invalid address, which can trigger a BUS FAULT.
The correct call is, for example:
adc_read_async(adc_chan0.dev, &sequence, &async);
There is a similar issue here:
adc_is_ready_dt(adc_dev);
adc_is_ready_dt() expects a pointer to struct adc_dt_spec , so this should be:
adc_is_ready_dt(&adc_chan0);
In addition, the ADC channel should be configured before starting a conversion. The code should call:
adc_channel_setup(adc_chan0.dev, &channel_cfg);
before invoking adc_read_async().
There is also a DeviceTree inconsistency. The application says it wants to use ADC1 channel 12, but the overlay defines: io-channels = <&adc1 0>;
Also, if ADC1 channel 12 is mapped to pc0 on the target, the ADC pinctrl configuration should not remain commented out.
Please verify all the above‑mentioned configuration in your application.
Best regards,
Hamdi