From 7f070744a9b6a493530a48b9c57dc761cfc5c999 Mon Sep 17 00:00:00 2001 From: Hein Wessels Date: Fri, 27 Jan 2023 09:51:23 +0100 Subject: [PATCH 1/5] drivers: adc: stm32: dma support Sampling multiple adc channels at once using dma Only verified to be working on nucleo_h743zi Signed-off-by: Hein Wessels --- drivers/adc/Kconfig.stm32 | 15 ++- drivers/adc/adc_stm32.c | 245 +++++++++++++++++++++++++++++++++++++- 2 files changed, 253 insertions(+), 7 deletions(-) diff --git a/drivers/adc/Kconfig.stm32 b/drivers/adc/Kconfig.stm32 index 1998f051b5844..3752ea0af67f0 100644 --- a/drivers/adc/Kconfig.stm32 +++ b/drivers/adc/Kconfig.stm32 @@ -4,6 +4,7 @@ # Copyright (c) 2019 Endre Karlson # Copyright (c) 2019 Song Qiang # Copyright (c) 2021 Marius Scholtz, RIC Electronics +# Copyright (c) 2022 Hein Wessels, Nobleo Technology # SPDX-License-Identifier: Apache-2.0 config ADC_STM32 @@ -13,12 +14,24 @@ config ADC_STM32 help Enable the driver implementation for the stm32xx ADC +if ADC_STM32 + +config ADC_STM32_DMA + bool "STM32 MCU ADC DMA Support" + select DMA + help + Enable the ADC DMA mode for ADC instances + that enable dma channels in their device tree node. + if SOC_SERIES_STM32F2X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32F7X || SOC_SERIES_STM32G4X config ADC_STM32_SHARED_IRQS bool "STM32 ADC shared interrupts" default y - depends on ADC_STM32 + depends on ADC_STM32 && !ADC_STM32_DMA help Enable the use of shared interrupts for families that only have a single interrupt for all ADC's + +endif + endif diff --git a/drivers/adc/adc_stm32.c b/drivers/adc/adc_stm32.c index 5273fdb6bed59..df57613020f07 100644 --- a/drivers/adc/adc_stm32.c +++ b/drivers/adc/adc_stm32.c @@ -24,6 +24,13 @@ #include #endif /* CONFIG_SOC_SERIES_STM32U5X */ +#ifdef CONFIG_ADC_STM32_DMA +#include +#include +#include +#include +#endif + #define ADC_CONTEXT_USES_KERNEL_TIMER #define ADC_CONTEXT_ENABLE_ON_COMPLETE #include "adc_context.h" @@ -246,6 +253,18 @@ static const uint32_t table_samp_time[] = { /* External channels (maximum). */ #define STM32_CHANNEL_COUNT 20 +#ifdef CONFIG_ADC_STM32_DMA +struct stream { + const struct device *dma_dev; + uint32_t channel; + struct dma_config dma_cfg; + struct dma_block_config dma_blk_cfg; + uint8_t priority; + bool src_addr_increment; + bool dst_addr_increment; +}; +#endif /* CONFIG_ADC_STM32_DMA */ + struct adc_stm32_data { struct adc_context ctx; const struct device *dev; @@ -261,6 +280,11 @@ struct adc_stm32_data { defined(CONFIG_SOC_SERIES_STM32L0X) int8_t acq_time_index; #endif + +#ifdef CONFIG_ADC_STM32_DMA + volatile int dma_error; + struct stream dma; +#endif }; struct adc_stm32_cfg { @@ -277,7 +301,108 @@ struct adc_stm32_cfg { static bool init_irq = true; #endif -static int check_buffer_size(const struct adc_sequence *sequence, +#ifdef CONFIG_ADC_STM32_DMA +static int adc_stm32_dma_start(const struct device *dev, + void *buffer, size_t channel_count) +{ + const struct adc_stm32_cfg *config = dev->config; + ADC_TypeDef *adc = (ADC_TypeDef *)config->base; + struct adc_stm32_data *data = dev->data; + struct dma_block_config *blk_cfg; + int ret; + + struct stream *dma = &data->dma; + + blk_cfg = &dma->dma_blk_cfg; + + /* prepare the block */ + blk_cfg->block_size = channel_count * sizeof(int16_t); + + /* Source and destination */ + blk_cfg->source_address = (uint32_t)LL_ADC_DMA_GetRegAddr(adc, LL_ADC_DMA_REG_REGULAR_DATA); + blk_cfg->source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + blk_cfg->source_reload_en = 0; + + blk_cfg->dest_address = (uint32_t)buffer; + blk_cfg->dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + blk_cfg->dest_reload_en = 0; + + /* Manually set the FIFO threshold to 1/4 because the + * dmamux DTS entry does not contain fifo threshold + */ + blk_cfg->fifo_mode_control = 0; + + /* direction is given by the DT */ + dma->dma_cfg.head_block = blk_cfg; + dma->dma_cfg.user_data = data; + + ret = dma_config(data->dma.dma_dev, data->dma.channel, + &dma->dma_cfg); + if (ret != 0) { + LOG_ERR("Problem setting up DMA: %d", ret); + return ret; + } + + /* Allow ADC to create DMA request and set to one-shot mode, + * as implemented in HAL drivers, if applicable. + */ +#if defined(ADC_VER_V5_V90) + if (adc == ADC3) { + LL_ADC_REG_SetDMATransferMode(adc, + ADC3_CFGR_DMACONTREQ(LL_ADC_REG_DMA_TRANSFER_LIMITED)); + LL_ADC_EnableDMAReq(adc); + } else { + LL_ADC_REG_SetDataTransferMode(adc, + ADC_CFGR_DMACONTREQ(LL_ADC_REG_DMA_TRANSFER_LIMITED)); + } +#elif defined(ADC_VER_V5_X) + LL_ADC_REG_SetDataTransferMode(adc, LL_ADC_REG_DMA_TRANSFER_LIMITED); +#endif + + data->dma_error = 0; + ret = dma_start(data->dma.dma_dev, data->dma.channel); + if (ret != 0) { + LOG_ERR("Problem starting DMA: %d", ret); + return ret; + } + + LOG_DBG("DMA started"); + + return ret; +} +#endif /* CONFIG_ADC_STM32_DMA */ + +#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) +/* Returns true if given buffer is in a non-cacheable SRAM region. + * This is determined using the device tree, meaning the .nocache region won't work. + * The entire buffer must be in a single region. + * An example of how the SRAM region can be defined in the DTS: + * &sram4 { + * zephyr,memory-region-mpu = "RAM_NOCACHE"; + * }; + */ +static bool address_in_non_cacheable_sram(const uint16_t *buffer, const uint16_t size) +{ + /* Default if no valid SRAM region found or buffer+size not located in a single region */ + bool cachable = false; +#define IS_NON_CACHEABLE_REGION_FN(node_id) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, zephyr_memory_region_mpu), ({ \ + const uint32_t region_start = DT_REG_ADDR(node_id); \ + const uint32_t region_end = region_start + DT_REG_SIZE(node_id); \ + if (((uint32_t)buffer >= region_start) && \ + (((uint32_t)buffer + size) < region_end)) { \ + cachable = strcmp(DT_PROP(node_id, zephyr_memory_region_mpu), \ + "RAM_NOCACHE") == 0; \ + } \ + }), \ + (EMPTY)) + DT_FOREACH_STATUS_OKAY(mmio_sram, IS_NON_CACHEABLE_REGION_FN); + + return cachable; +} +#endif /* defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) */ + +static int check_buffer(const struct adc_sequence *sequence, uint8_t active_channels) { size_t needed_buffer_size; @@ -294,6 +419,14 @@ static int check_buffer_size(const struct adc_sequence *sequence, return -ENOMEM; } +#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) + /* Buffer is forced to be in non-cacheable SRAM region to avoid cache maintenance */ + if (!address_in_non_cacheable_sram(sequence->buffer, needed_buffer_size)) { + LOG_ERR("Supplied buffer is not in a non-cacheable region according to DTS."); + return -EINVAL; + } +#endif + return 0; } @@ -649,6 +782,43 @@ static void adc_stm32_teardown_channels(const struct device *dev) adc_stm32_enable(adc); } +#ifdef CONFIG_ADC_STM32_DMA +static void dma_callback(const struct device *dev, void *user_data, + uint32_t channel, int status) +{ + /* user_data directly holds the adc device */ + struct adc_stm32_data *data = user_data; + const struct adc_stm32_cfg *config = dev->config; + ADC_TypeDef *adc = (ADC_TypeDef *)config->base; + + LOG_DBG("dma callback"); + + if (channel == data->dma.channel) { + if (LL_ADC_IsActiveFlag_OVR(adc) || (status == 0)) { + data->samples_count = data->channel_count; + data->buffer += data->channel_count; + /* Stop the DMA engine, only to start it again when the callback returns + * ADC_ACTION_REPEAT or ADC_ACTION_CONTINUE, or the number of samples + * haven't been reached Starting the DMA engine is done + * within adc_context_start_sampling + */ + dma_stop(data->dma.dma_dev, data->dma.channel); + LL_ADC_ClearFlag_OVR(adc); + /* No need to invalidate the cache because it's assumed that + * the address is in a non-cacheable SRAM region. + */ + adc_context_on_sampling_done(&data->ctx, dev); + } else { + LOG_ERR("DMA sampling complete, but DMA reported error %d", status); + data->dma_error = status; + LL_ADC_REG_StopConversion(adc); + dma_stop(data->dma.dma_dev, data->dma.channel); + adc_context_complete(&data->ctx, status); + } + } +} +#endif /* CONFIG_ADC_STM32_DMA */ + static int start_read(const struct device *dev, const struct adc_sequence *sequence) { @@ -814,7 +984,7 @@ static int start_read(const struct device *dev, #endif } - err = check_buffer_size(sequence, data->channel_count); + err = check_buffer(sequence, data->channel_count); if (err) { return err; } @@ -917,6 +1087,9 @@ static int start_read(const struct device *dev, */ adc_stm32_enable(adc); + LL_ADC_ClearFlag_OVR(adc); + +#if !defined(CONFIG_ADC_STM32_DMA) #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ @@ -935,11 +1108,18 @@ static int start_read(const struct device *dev, #else LL_ADC_EnableIT_EOCS(adc); #endif +#endif /* CONFIG_ADC_STM32_DMA */ + /* This call will start the DMA */ adc_context_start_read(&data->ctx, sequence); int result = adc_context_wait_for_completion(&data->ctx); +#ifdef CONFIG_ADC_STM32_DMA + /* check if there's anything wrong with dma start */ + result = (data->dma_error ? data->dma_error : result); +#endif + return result; } @@ -950,6 +1130,9 @@ static void adc_context_start_sampling(struct adc_context *ctx) data->repeat_buffer = data->buffer; +#ifdef CONFIG_ADC_STM32_DMA + adc_stm32_dma_start(data->dev, data->buffer, data->channel_count); +#endif adc_stm32_start_conversion(data->dev); } @@ -964,6 +1147,7 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx, } } +#ifndef CONFIG_ADC_STM32_DMA static void adc_stm32_isr(const struct device *dev) { struct adc_stm32_data *data = dev->data; @@ -981,6 +1165,7 @@ static void adc_stm32_isr(const struct device *dev) LOG_DBG("%s ISR triggered.", dev->name); } +#endif /* !CONFIG_ADC_STM32_DMA */ static void adc_context_on_complete(struct adc_context *ctx, int status) { @@ -1147,7 +1332,7 @@ static int adc_stm32_init(const struct device *dev) ADC_TypeDef *adc = (ADC_TypeDef *)config->base; int err; - LOG_DBG("Initializing...."); + LOG_DBG("Initializing %s", dev->name); if (!device_is_ready(clk)) { LOG_ERR("clock control device not ready"); @@ -1184,6 +1369,14 @@ static int adc_stm32_init(const struct device *dev) LL_PWR_EnableVDDA(); #endif /* CONFIG_SOC_SERIES_STM32U5X */ +#ifdef CONFIG_ADC_STM32_DMA + if ((data->dma.dma_dev != NULL) && + !device_is_ready(data->dma.dma_dev)) { + LOG_ERR("%s device not ready", data->dma.dma_dev->name); + return -ENODEV; + } +#endif + #if defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ @@ -1402,9 +1595,47 @@ static void adc_stm32_irq_init(void) #define ADC_STM32_IRQ_CONFIG(index) #define ADC_STM32_IRQ_FUNC(index) \ - .irq_cfg_func = adc_stm32_irq_init, \ + .irq_cfg_func = adc_stm32_irq_init, +#define ADC_DMA_CHANNEL(id, dir, DIR, src, dest) + +#elif defined(CONFIG_ADC_STM32_DMA) /* !CONFIG_ADC_STM32_SHARED_IRQS */ + +#define ADC_DMA_CHANNEL_INIT(index, name, dir_cap, src_dev, dest_dev) \ + .dma = { \ + .dma_dev = DEVICE_DT_GET(STM32_DMA_CTLR(index, name)), \ + .channel = DT_INST_DMAS_CELL_BY_NAME(index, name, channel), \ + .dma_cfg = { \ + .dma_slot = STM32_DMA_SLOT(index, name, slot), \ + .channel_direction = STM32_DMA_CONFIG_DIRECTION( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + .source_data_size = STM32_DMA_CONFIG_##src_dev##_DATA_SIZE( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + .dest_data_size = STM32_DMA_CONFIG_##dest_dev##_DATA_SIZE( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + .source_burst_length = 1, /* SINGLE transfer */ \ + .dest_burst_length = 1, /* SINGLE transfer */ \ + .channel_priority = STM32_DMA_CONFIG_PRIORITY( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + .dma_callback = dma_callback, \ + .block_count = 2, \ + }, \ + .src_addr_increment = STM32_DMA_CONFIG_##src_dev##_ADDR_INC( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + .dst_addr_increment = STM32_DMA_CONFIG_##dest_dev##_ADDR_INC( \ + STM32_DMA_CHANNEL_CONFIG(index, name)), \ + } -#else +#define ADC_DMA_CHANNEL(id, dir, DIR, src, dest) \ + COND_CODE_1(DT_INST_DMAS_HAS_NAME(id, dir), \ + (ADC_DMA_CHANNEL_INIT(id, dir, DIR, src, dest)), \ + (EMPTY)) + +#define ADC_STM32_IRQ_CONFIG(index) \ +static void adc_stm32_cfg_func_##index(void){ EMPTY } +#define ADC_STM32_IRQ_FUNC(index) \ + .irq_cfg_func = adc_stm32_cfg_func_##index, + +#else /* CONFIG_ADC_STM32_DMA */ #define ADC_STM32_IRQ_CONFIG(index) \ static void adc_stm32_cfg_func_##index(void) \ @@ -1416,8 +1647,9 @@ static void adc_stm32_cfg_func_##index(void) \ } #define ADC_STM32_IRQ_FUNC(index) \ .irq_cfg_func = adc_stm32_cfg_func_##index, +#define ADC_DMA_CHANNEL(id, dir, DIR, src, dest) -#endif /* CONFIG_ADC_STM32_SHARED_IRQS */ +#endif /* CONFIG_ADC_STM32_DMA && CONFIG_ADC_STM32_SHARED_IRQS */ #define ADC_STM32_INIT(index) \ @@ -1443,6 +1675,7 @@ static struct adc_stm32_data adc_stm32_data_##index = { \ ADC_CONTEXT_INIT_TIMER(adc_stm32_data_##index, ctx), \ ADC_CONTEXT_INIT_LOCK(adc_stm32_data_##index, ctx), \ ADC_CONTEXT_INIT_SYNC(adc_stm32_data_##index, ctx), \ + ADC_DMA_CHANNEL(index, dmamux, NULL, PERIPHERAL, MEMORY) \ }; \ \ DEVICE_DT_INST_DEFINE(index, \ From 7128cc896a75be571c00262c0f005cd2dd41bcca Mon Sep 17 00:00:00 2001 From: Hein Wessels Date: Tue, 24 Jan 2023 10:46:21 +0100 Subject: [PATCH 2/5] tests: drivers: adc_dma: wrap nxp specifc functionality This test contained NXP specific functions, for example the counter trigger, which is not required for all ADC DMA implementations. Also moved NXP specific kconfigs to appropriate board files Signed-off-by: Hein Wessels --- .../drivers/adc/adc_dma/boards/frdm_k64f.conf | 3 +++ .../drivers/adc/adc_dma/boards/frdm_k82f.conf | 3 +++ tests/drivers/adc/adc_dma/prj.conf | 3 --- tests/drivers/adc/adc_dma/src/test_adc.c | 24 +++++++++++++++---- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf b/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf index 5692079a8ffa6..a0c2eeaa0db37 100644 --- a/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf +++ b/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf @@ -5,3 +5,6 @@ # CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA=y +CONFIG_ADC_MCUX_ADC16_HW_TRIGGER=y +CONFIG_COUNTER=y +CONFIG_ADC_ASYNC=y diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf b/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf index 5692079a8ffa6..a0c2eeaa0db37 100644 --- a/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf +++ b/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf @@ -5,3 +5,6 @@ # CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA=y +CONFIG_ADC_MCUX_ADC16_HW_TRIGGER=y +CONFIG_COUNTER=y +CONFIG_ADC_ASYNC=y diff --git a/tests/drivers/adc/adc_dma/prj.conf b/tests/drivers/adc/adc_dma/prj.conf index 921de12078f82..806d50a48d825 100644 --- a/tests/drivers/adc/adc_dma/prj.conf +++ b/tests/drivers/adc/adc_dma/prj.conf @@ -2,10 +2,7 @@ CONFIG_ZTEST=y CONFIG_ZTEST_NEW_API=y CONFIG_ADC=y -CONFIG_ADC_ASYNC=y CONFIG_ADC_LOG_LEVEL_INF=y CONFIG_HEAP_MEM_POOL_SIZE=1024 CONFIG_TEST_USERSPACE=y CONFIG_DMA=y -CONFIG_COUNTER=y -CONFIG_ADC_MCUX_ADC16_HW_TRIGGER=y diff --git a/tests/drivers/adc/adc_dma/src/test_adc.c b/tests/drivers/adc/adc_dma/src/test_adc.c index be5d2c4d61472..efbf6876af794 100644 --- a/tests/drivers/adc/adc_dma/src/test_adc.c +++ b/tests/drivers/adc/adc_dma/src/test_adc.c @@ -21,6 +21,8 @@ #define ADC_REFERENCE ADC_REF_INTERNAL #define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT #define ADC_1ST_CHANNEL_ID 26 +#define COUNTER_NODE_NAME pit0 +#define HW_TRIGGER_INTERVAL (2U) #elif defined(CONFIG_BOARD_FRDM_K82F) @@ -30,15 +32,19 @@ #define ADC_REFERENCE ADC_REF_INTERNAL #define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT #define ADC_1ST_CHANNEL_ID 26 +#define COUNTER_NODE_NAME pit0 +#define HW_TRIGGER_INTERVAL (2U) #endif -#define HW_TRIGGER_INTERVAL (2U) /* for DMA HW trigger interval need large than HW trigger interval*/ #define SAMPLE_INTERVAL_US (10000U) #define BUFFER_SIZE 24 +#ifndef ALIGNMENT #define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(test_dma)) +#endif + static ZTEST_BMEM __aligned(ALIGNMENT) int16_t m_sample_buffer[BUFFER_SIZE]; static ZTEST_BMEM __aligned(ALIGNMENT) int16_t m_sample_buffer2[2][BUFFER_SIZE]; static int current_buf_inx; @@ -78,6 +84,7 @@ const struct device *get_adc_device(void) const struct device *get_count_device(void) { +#if defined(COUNTER_NODE_NAME) const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(pit0)); if (!device_is_ready(dev)) { @@ -86,12 +93,16 @@ const struct device *get_count_device(void) } return dev; +#else + return NULL; +#endif } -static void init_pit(void) +#if defined(COUNTER_NODE_NAME) +static void init_counter(void) { int err; - const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(pit0)); + const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(COUNTER_NODE_NAME)); struct counter_top_cfg top_cfg = { .callback = NULL, .user_data = NULL, .flags = 0 }; @@ -104,6 +115,7 @@ static void init_pit(void) zassert_equal(0, err, "%s: Counter failed to set top value (err: %d)", dev->name, err); } +#endif static const struct device *init_adc(void) { @@ -126,7 +138,9 @@ static const struct device *init_adc(void) (void)memset(m_sample_buffer, 0, sizeof(m_sample_buffer)); - init_pit(); +#if defined(COUNTER_NODE_NAME) + init_counter(); +#endif return adc_dev; } @@ -245,7 +259,7 @@ static int test_task_asynchronous_call(void) const struct adc_sequence_options options = { .extra_samplings = 4, /* Start consecutive samplings as fast as possible. */ - .interval_us = HW_TRIGGER_INTERVAL, + .interval_us = 0, }; const struct adc_sequence sequence = { .options = &options, From 42dec4e0f6b674a7c947cd451cbd4729feb5a928 Mon Sep 17 00:00:00 2001 From: Hein Wessels Date: Mon, 13 Mar 2023 11:52:38 +0100 Subject: [PATCH 3/5] tests: drivers: adc_dma: remove test userspace These test uses DMA which sometimes require buffers to be placed in custom specific section. This is not compatible with ztest userspace, which requires variables to be placed in a specific partition. Signed-off-by: Hein Wessels --- tests/drivers/adc/adc_dma/prj.conf | 2 -- tests/drivers/adc/adc_dma/src/main.c | 19 +---------- tests/drivers/adc/adc_dma/src/test_adc.c | 42 ++++++------------------ 3 files changed, 11 insertions(+), 52 deletions(-) diff --git a/tests/drivers/adc/adc_dma/prj.conf b/tests/drivers/adc/adc_dma/prj.conf index 806d50a48d825..81a73e12de1a9 100644 --- a/tests/drivers/adc/adc_dma/prj.conf +++ b/tests/drivers/adc/adc_dma/prj.conf @@ -3,6 +3,4 @@ CONFIG_ZTEST_NEW_API=y CONFIG_ADC=y CONFIG_ADC_LOG_LEVEL_INF=y -CONFIG_HEAP_MEM_POOL_SIZE=1024 -CONFIG_TEST_USERSPACE=y CONFIG_DMA=y diff --git a/tests/drivers/adc/adc_dma/src/main.c b/tests/drivers/adc/adc_dma/src/main.c index a1f73c339d522..2e968001c9764 100644 --- a/tests/drivers/adc/adc_dma/src/main.c +++ b/tests/drivers/adc/adc_dma/src/main.c @@ -8,21 +8,4 @@ #include #include -extern const struct device *get_adc_device(void); -extern const struct device *get_count_device(void); -extern struct k_poll_signal async_sig; - -void *adc_dma_setup(void) -{ - k_object_access_grant(get_adc_device(), k_current_get()); - k_object_access_grant(get_count_device(), k_current_get()); -#ifdef CONFIG_ADC_ASYNC - k_object_access_grant(&async_sig, k_current_get()); - k_poll_signal_init(&async_sig); - k_thread_system_pool_assign(k_current_get()); -#endif - - return NULL; -} - -ZTEST_SUITE(adc_dma, NULL, adc_dma_setup, NULL, NULL, NULL); +ZTEST_SUITE(adc_dma, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/adc/adc_dma/src/test_adc.c b/tests/drivers/adc/adc_dma/src/test_adc.c index efbf6876af794..c1578022497a8 100644 --- a/tests/drivers/adc/adc_dma/src/test_adc.c +++ b/tests/drivers/adc/adc_dma/src/test_adc.c @@ -45,10 +45,14 @@ #define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(test_dma)) #endif -static ZTEST_BMEM __aligned(ALIGNMENT) int16_t m_sample_buffer[BUFFER_SIZE]; -static ZTEST_BMEM __aligned(ALIGNMENT) int16_t m_sample_buffer2[2][BUFFER_SIZE]; +static __aligned(ALIGNMENT) int16_t m_sample_buffer[BUFFER_SIZE]; +static __aligned(ALIGNMENT) int16_t m_sample_buffer2[2][BUFFER_SIZE]; static int current_buf_inx; +#if defined(CONFIG_ADC_ASYNC) +static struct k_poll_signal async_sig; +#endif + static const struct adc_channel_cfg m_1st_channel_cfg = { .gain = ADC_GAIN, .reference = ADC_REFERENCE, @@ -70,34 +74,6 @@ static const struct adc_channel_cfg m_2nd_channel_cfg = { }; #endif /* defined(ADC_2ND_CHANNEL_ID) */ -const struct device *get_adc_device(void) -{ - const struct device *const dev = DEVICE_DT_GET(ADC_DEVICE_NODE); - - if (!device_is_ready(dev)) { - printk("ADC device is not ready\n"); - return NULL; - } - - return dev; -} - -const struct device *get_count_device(void) -{ -#if defined(COUNTER_NODE_NAME) - const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(pit0)); - - if (!device_is_ready(dev)) { - printk("count device is not ready\n"); - return NULL; - } - - return dev; -#else - return NULL; -#endif -} - #if defined(COUNTER_NODE_NAME) static void init_counter(void) { @@ -138,6 +114,10 @@ static const struct device *init_adc(void) (void)memset(m_sample_buffer, 0, sizeof(m_sample_buffer)); +#if defined(CONFIG_ADC_ASYNC) + k_poll_signal_init(&async_sig); +#endif + #if defined(COUNTER_NODE_NAME) init_counter(); #endif @@ -251,8 +231,6 @@ ZTEST_USER(adc_dma, test_adc_sample_two_channels) * test_adc_asynchronous_call */ #if defined(CONFIG_ADC_ASYNC) -struct k_poll_signal async_sig; - static int test_task_asynchronous_call(void) { int ret; From 13f3d761fb013f078be8851cb0d52e26f5077853 Mon Sep 17 00:00:00 2001 From: Hein Wessels Date: Mon, 13 Mar 2023 12:54:25 +0100 Subject: [PATCH 4/5] tests: drivers: adc_dma: add nucleo_h743zi Adds nucleo_h732zi ADC DMA unit tests with multiple channels. The STM32 ADC DMA driver requires the buffers to be placed in a non-cacheable memory region as defined by the DMA. Therefore this adds configurability to the test to change the region the buffer is placed in. Signed-off-by: Hein Wessels --- .../adc/adc_dma/boards/nucleo_h743zi.conf | 8 +++++ .../adc/adc_dma/boards/nucleo_h743zi.overlay | 34 +++++++++++++++++++ tests/drivers/adc/adc_dma/src/test_adc.c | 31 +++++++++++++++-- 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tests/drivers/adc/adc_dma/boards/nucleo_h743zi.conf create mode 100644 tests/drivers/adc/adc_dma/boards/nucleo_h743zi.overlay diff --git a/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.conf b/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.conf new file mode 100644 index 0000000000000..675ebf0d5aa6f --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023 Nobleo Technology +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_ADC_STM32_DMA=y +CONFIG_ADC_ASYNC=y diff --git a/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.overlay b/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.overlay new file mode 100644 index 0000000000000..af004e793710c --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/nucleo_h743zi.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Nobleo Technology + * + * SPDX-License-Identifier: Apache-2.0 + */ +&adc1 { + dmas = < &dmamux1 0 9 (STM32_DMA_PERIPH_TO_MEMORY | + STM32_DMA_MEM_INC | STM32_DMA_MEM_16BITS | STM32_DMA_PERIPH_16BITS) >; + dma-names = "dmamux"; + + #address-cells = <1>; + #size-cells = <0>; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* ADC driver expects a buffer in a non-cachable memory region */ +&sram4 { + zephyr,memory-region-mpu = "RAM_NOCACHE"; +}; + +&dma1 { + status = "okay"; +}; + +test_dma: &dmamux1 { + status = "okay"; +}; diff --git a/tests/drivers/adc/adc_dma/src/test_adc.c b/tests/drivers/adc/adc_dma/src/test_adc.c index c1578022497a8..248f3546da906 100644 --- a/tests/drivers/adc/adc_dma/src/test_adc.c +++ b/tests/drivers/adc/adc_dma/src/test_adc.c @@ -35,6 +35,33 @@ #define COUNTER_NODE_NAME pit0 #define HW_TRIGGER_INTERVAL (2U) +#elif defined(CONFIG_BOARD_NUCLEO_H743ZI) + +#define ADC_DEVICE_NODE DT_INST(0, st_stm32_adc) +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 1 +#define ADC_2ND_CHANNEL_ID 7 +#define ALIGNMENT 32 +#define BUFFER_MEM_REGION __attribute__((__section__(".sram4"))) + +#endif + +/* Invalid value that is not supposed to be written by the driver. It is used + * to mark the sample buffer entries as empty. If needed, it can be overridden + * for a particular board by providing a specific definition above. + */ +#if !defined(INVALID_ADC_VALUE) +#define INVALID_ADC_VALUE SHRT_MIN +#endif + +/* Memory region where buffers will be placed. By default placed in ZTEST_BMEM + * but can be overwritten for a particular board. + */ +#if !defined(BUFFER_MEM_REGION) +#define BUFFER_MEM_REGION EMPTY #endif /* for DMA HW trigger interval need large than HW trigger interval*/ @@ -45,8 +72,8 @@ #define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(test_dma)) #endif -static __aligned(ALIGNMENT) int16_t m_sample_buffer[BUFFER_SIZE]; -static __aligned(ALIGNMENT) int16_t m_sample_buffer2[2][BUFFER_SIZE]; +static BUFFER_MEM_REGION __aligned(ALIGNMENT) int16_t m_sample_buffer[BUFFER_SIZE]; +static BUFFER_MEM_REGION __aligned(ALIGNMENT) int16_t m_sample_buffer2[2][BUFFER_SIZE]; static int current_buf_inx; #if defined(CONFIG_ADC_ASYNC) From 45934a11619cc143eda57a62143ab54d564e2f0d Mon Sep 17 00:00:00 2001 From: Hein Wessels Date: Mon, 13 Mar 2023 11:22:58 +0100 Subject: [PATCH 5/5] tests: drivers: adc_dma: fix buffer state not being verified Previously the contents of buffers after an ADC DMA read was simply printed, but not verified with an zassert that the value was updated. This commit updates it to be similar to the adc_api test that fills the buffer initially with a known value, which is then used to ensure the ADC DMA functioned successfully. Signed-off-by: Hein Wessels --- tests/drivers/adc/adc_dma/src/test_adc.c | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/drivers/adc/adc_dma/src/test_adc.c b/tests/drivers/adc/adc_dma/src/test_adc.c index 248f3546da906..2a65681f9d688 100644 --- a/tests/drivers/adc/adc_dma/src/test_adc.c +++ b/tests/drivers/adc/adc_dma/src/test_adc.c @@ -122,7 +122,7 @@ static void init_counter(void) static const struct device *init_adc(void) { - int ret; + int i, ret; const struct device *const adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); zassert_true(device_is_ready(adc_dev), "ADC device is not ready"); @@ -139,7 +139,11 @@ static const struct device *init_adc(void) ret); #endif /* defined(ADC_2ND_CHANNEL_ID) */ - (void)memset(m_sample_buffer, 0, sizeof(m_sample_buffer)); + for (i = 0; i < BUFFER_SIZE; ++i) { + m_sample_buffer[i] = INVALID_ADC_VALUE; + m_sample_buffer2[0][i] = INVALID_ADC_VALUE; + m_sample_buffer2[1][i] = INVALID_ADC_VALUE; + } #if defined(CONFIG_ADC_ASYNC) k_poll_signal_init(&async_sig); @@ -164,8 +168,16 @@ static void check_samples(int expected_count) if (i && i % 10 == 0) { TC_PRINT("\n"); } + + if (i < expected_count) { + zassert_not_equal(INVALID_ADC_VALUE, sample_value, + "[%u] should be filled", i); + } else { + zassert_equal(INVALID_ADC_VALUE, sample_value, + "[%u] should be empty", i); + } } - TC_PRINT("%d sampled\n", BUFFER_SIZE); + TC_PRINT("\n"); } static void check_samples2(int expected_count) @@ -180,8 +192,16 @@ static void check_samples2(int expected_count) if (i && i % 10 == 0) { TC_PRINT("\n"); } + + if (i < expected_count) { + zassert_not_equal(INVALID_ADC_VALUE, sample_value, + "[%u] should be filled", i); + } else { + zassert_equal(INVALID_ADC_VALUE, sample_value, + "[%u] should be empty", i); + } } - TC_PRINT("%d sampled\n", BUFFER_SIZE); + TC_PRINT("\n"); } /*