cancel
Showing results for 
Search instead for 
Did you mean: 

Problem: Bus Fault error when using ADC + DMA on STM32H747i with Zephyr OS

HuongNg
Associate

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));
	}
}
Please let me know any configuration/source code is miss.
Thanks in Advance!
 
1 REPLY 1
T_Hamdi
ST Employee

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

 

 

 

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Hamdi Teyeb