diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/CMakeLists.txt b/zephyr/modules/owntech_data_acquisition/zephyr/CMakeLists.txt
index 315587d90c40d069fb643bf9dcb848224912114e..6e2db647db2dceaa0df4d24ec3564c19466a534c 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/CMakeLists.txt
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/CMakeLists.txt
@@ -7,9 +7,9 @@ if(CONFIG_OWNTECH_DATA_ACQUISITION)
 
   # Select source files to be compiled
   zephyr_library_sources(
-    ./adc_to_mem/dma.c
-    ./adc_to_mem/data_dispatch.c
-	./data_conversion/data_conversion.c
+    ./adc_to_mem/dma.cpp
+    ./adc_to_mem/data_dispatch.cpp
+    ./data_conversion/data_conversion.cpp
     ./public_api/DataAcquisition.cpp
    )
 endif()
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/Kconfig b/zephyr/modules/owntech_data_acquisition/zephyr/Kconfig
index e4c67659e001b3d76621817ab5055e1b5a3e5eab..6ac033e64399fd24035e0b857c71ba88216bee6f 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/Kconfig
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/Kconfig
@@ -3,3 +3,6 @@ config OWNTECH_DATA_ACQUISITION
 	default y
 	select DMA
 	depends on OWNTECH_ADC_DRIVER
+	depends on OWNTECH_HRTIM_DRIVER
+	# Force-select the following to avoid dependency loop
+	select OWNTECH_SCHEDULING
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.c b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.c
deleted file mode 100644
index 5ed0ca74692f264af79dabce616c974019d1e517..0000000000000000000000000000000000000000
--- a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (c) 2021-2023 LAAS-CNRS
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU Lesser General Public License as published by
- *   the Free Software Foundation, either version 2.1 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU Lesser General Public License for more details.
- *
- *   You should have received a copy of the GNU Lesser General Public License
- *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGLPV2.1
- */
-
-/**
- * @date   2023
- *
- * @author Clément Foucher <clement.foucher@laas.fr>
- */
-
-
-// Stdlib
-#include <stdlib.h>
-#include <stdint.h>
-
-//Zephyr
-#include <zephyr.h>
-
-// OwnTech API
-#include "adc.h"
-
-// Current module private functions
-#include "dma.h"
-
-
-/////
-// Local variables
-
-#define CHANNELS_BUFFERS_SIZE 32
-#define ADC_COUNT 4
-
-// Number of channels in each ADC (cell i is ADC number i+1)
-static uint8_t* enabled_channels_count = NULL;
-
-// Array of per-adc/per-channel buffers.
-// adc_channel_buffers[x][y][z][] is ADC x+1 channel y buffer z
-// with z either 0 or 1 as there are two buffers per channel (double buffering)
-static uint16_t**** adc_channel_buffers = NULL;
-
-// Number of readings stored in each channel.
-// buffers_data_count[x][y] is the current nuumber of
-// values stored in the currently written buffer of ADC x+1 Channel y
-static uint32_t** buffers_data_count = NULL;
-
-// Currently written buffer for each channel.
-// Either 0 or 1.
-// If current_buffer[x][y] is 0, the currently written buffer
-// for ADC x+1 Channel y is buffer 0 and the user buffer is buffer 1
-static uint8_t** current_buffer = NULL;
-
-// Small memory to retain latest value available to
-// the peek() function after a buffer swap.
-static uint16_t** peek_memory = NULL;
-
-// DMA buffers: data from the ADC 1/2 are stored in these
-// buffers until dispatch is done (ADC 3/4 wwon't use DMA).
-// Main buffers are always used, while secondary buffers
-// will only be used when double-buffering is activated.
-static uint16_t* dma_main_buffers[ADC_COUNT]      = {NULL};
-static uint16_t* dma_secondary_buffers[ADC_COUNT] = {NULL};
-static uint8_t   current_dma_buffer[ADC_COUNT]    = {0};
-
-
-/////
-// Private functions
-
-__STATIC_INLINE uint16_t* _data_dispatch_get_buffer(uint8_t adc_index, uint8_t channel_index)
-{
-	uint8_t active_buffer = current_buffer[adc_index][channel_index];
-	return adc_channel_buffers[adc_index][channel_index][active_buffer];
-}
-
-__STATIC_INLINE uint32_t _data_dispatch_get_count(uint8_t adc_index, uint8_t channel_index)
-{
-	return buffers_data_count[adc_index][channel_index];
-}
-
-__STATIC_INLINE void _data_dispatch_increment_count(uint8_t adc_index, uint8_t channel_index)
-{
-	uint32_t* current_count = &buffers_data_count[adc_index][channel_index];
-	if ( (*current_count) < CHANNELS_BUFFERS_SIZE)
-	{
-		(*current_count)++;
-	}
-}
-
-__STATIC_INLINE void _data_dispatch_swap_buffer(uint8_t adc_index, uint8_t channel_index)
-{
-	uint8_t* active_buffer = &current_buffer[adc_index][channel_index];
-
-	*active_buffer = ((*active_buffer) == 0) ? 1 : 0;
-	buffers_data_count[adc_index][channel_index] = 0;
-}
-
-/////
-// Public API
-
-void data_dispatch_init()
-{
-	// Prepare arrays for each ADC
-	enabled_channels_count = k_malloc(ADC_COUNT * sizeof(uint8_t));
-	adc_channel_buffers    = k_calloc(ADC_COUNT,  sizeof(uint16_t***));
-	buffers_data_count     = k_calloc(ADC_COUNT,  sizeof(uint32_t*));
-	current_buffer         = k_calloc(ADC_COUNT,  sizeof(uint8_t*));
-	peek_memory            = k_calloc(ADC_COUNT,  sizeof(uint16_t*));
-
-	// DMA 1 & 2: use DMA
-	for (uint8_t adc_num = 1 ; adc_num <= ADC_COUNT ; adc_num++)
-	{
-		uint8_t adc_index = adc_num-1;
-		enabled_channels_count[adc_index] = adc_get_enabled_channels_count(adc_num);
-
-		if (enabled_channels_count[adc_index] > 0)
-		{
-			// For now, stay on double buffering approach
-			bool enable_double_buffering = true;
-
-			// Prepare buffers for DMA
-			size_t dma_buffer_size = enabled_channels_count[adc_index] * sizeof(uint16_t);
-			if (enable_double_buffering == true)
-			{
-				dma_buffer_size = dma_buffer_size * 2;
-			}
-
-			dma_main_buffers[adc_index] = (uint16_t*)k_malloc(dma_buffer_size);
-			if (enable_double_buffering == true)
-			{
-				dma_secondary_buffers[adc_index] = dma_main_buffers[adc_index] + enabled_channels_count[adc_index];
-			}
-
-			// Initialize DMA
-			dma_configure_adc_acquisition(adc_num, enable_double_buffering, dma_main_buffers[adc_index], dma_buffer_size);
-
-			// Prepare arrays for each channel
-			adc_channel_buffers[adc_index] = k_malloc(enabled_channels_count[adc_index] * sizeof(uint16_t**));
-			buffers_data_count[adc_index]  = k_calloc(enabled_channels_count[adc_index],  sizeof(uint32_t));
-			current_buffer[adc_index]      = k_calloc(enabled_channels_count[adc_index],  sizeof(uint8_t));
-			peek_memory[adc_index]         = k_calloc(enabled_channels_count[adc_index],  sizeof(uint16_t));
-			for (int channel_index = 0 ; channel_index < enabled_channels_count[adc_index] ; channel_index++)
-			{
-				// Prepare double buffer
-				adc_channel_buffers[adc_index][channel_index]    = k_malloc(sizeof(uint16_t*) * 2);
-				adc_channel_buffers[adc_index][channel_index][0] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
-				adc_channel_buffers[adc_index][channel_index][1] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
-			}
-		}
-	}
-}
-
-void data_dispatch_do_dispatch(uint8_t adc_num)
-{
-	uint8_t adc_index = adc_num - 1;
-
-	uint16_t* dma_buffer = dma_main_buffers[adc_index];
-	if (dma_secondary_buffers[adc_index] != NULL)
-	{
-		if (current_dma_buffer[adc_index] == 0)
-		{
-			current_dma_buffer[adc_index] = 1;
-		}
-		else
-		{
-			dma_buffer = dma_secondary_buffers[adc_index];
-			current_dma_buffer[adc_index] = 0;
-		}
-	}
-
-	for (int channel_index = 0 ; channel_index < enabled_channels_count[adc_index] ; channel_index++)
-	{
-		// Get info on buffer
-		uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_index);
-		uint32_t  current_count = _data_dispatch_get_count(adc_index, channel_index);
-
-		// Copy data
-		active_buffer[current_count] = dma_buffer[channel_index];
-
-		// Increment count
-		_data_dispatch_increment_count(adc_index, channel_index);
-	}
-}
-
-
-/////
-// Accessors
-
-uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_rank, uint32_t* number_of_values_acquired)
-{
-	uint8_t adc_index = adc_number-1;
-	if (adc_index < ADC_COUNT)
-	{
-		// Get info on buffer
-		uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_rank);
-		uint32_t  current_count = _data_dispatch_get_count(adc_index, channel_rank);
-
-		// Swap buffers
-		_data_dispatch_swap_buffer(adc_index, channel_rank);
-
-		// Retain latest value for peek() functions
-		if (current_count > 0)
-		{
-			peek_memory[adc_number][channel_rank] = active_buffer[current_count-1];
-		}
-
-		// Return data
-		*number_of_values_acquired = current_count;
-		return active_buffer;
-	}
-	else
-	{
-		*number_of_values_acquired = 0;
-		return NULL;
-	}
-}
-
-uint16_t data_dispatch_peek_acquired_value(uint8_t adc_number, uint8_t channel_rank)
-{
-	uint8_t adc_index = adc_number-1;
-	if (adc_index < ADC_COUNT)
-	{
-		// Get info on buffer
-		uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_rank);
-		uint32_t  current_count = _data_dispatch_get_count(adc_index, channel_rank);
-
-		// Return data
-		if (current_count > 0)
-		{
-			return active_buffer[current_count-1];
-		}
-		else
-		{
-			return peek_memory[adc_number][channel_rank];
-		}
-	}
-	else
-	{
-		return 0;
-	}
-}
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.cpp b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c5636e28946835711256058ad096ebae6a65d9d
--- /dev/null
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2021-2023 LAAS-CNRS
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGLPV2.1
+ */
+
+/**
+ * @date   2023
+ *
+ * @author Clément Foucher <clement.foucher@laas.fr>
+ */
+
+
+// Stdlib
+#include <stdlib.h>
+#include <stdint.h>
+
+//Zephyr
+#include <zephyr.h>
+
+// OwnTech API
+#include "adc.h"
+#include "Scheduling.h"
+#include "scheduling_internal.h"
+#include "hrtim.h"
+#include "leg.h"
+
+// Current module header
+#include "DataAcquisition.h"
+#include "dma.h"
+
+// Current file header
+#include "data_dispatch.h"
+
+
+/////
+// Local variables
+
+#define CHANNELS_BUFFERS_SIZE 32
+#define ADC_COUNT 4
+
+// Number of channels in each ADC (cell i is ADC number i+1)
+static uint8_t* enabled_channels_count = nullptr;
+
+// Array of per-adc/per-channel buffers.
+// adc_channel_buffers[x][y][z][] is ADC x+1 channel y buffer z
+// with z either 0 or 1 as there are two buffers per channel (double buffering)
+static uint16_t**** adc_channel_buffers = nullptr;
+
+// Number of readings stored in each channel.
+// buffers_data_count[x][y] is the current nuumber of
+// values stored in the currently written buffer of ADC x+1 Channel y
+static uint32_t** buffers_data_count = nullptr;
+
+// Currently written buffer for each channel.
+// Either 0 or 1.
+// If current_buffer[x][y] is 0, the currently written buffer
+// for ADC x+1 Channel y is buffer 0 and the user buffer is buffer 1
+static uint8_t** current_buffer = nullptr;
+
+// Small memory to retain latest value available to
+// the peek() function after a buffer swap.
+static uint16_t** peek_memory = nullptr;
+
+// DMA buffers: data from the ADC 1/2 are stored in these
+// buffers until dispatch is done (ADC 3/4 won't use DMA).
+// Main buffers are always used, while secondary buffers
+// will only be used when double-buffering is activated.
+// Double buffering is activated in Interrupt mode,
+// while Task mode doesn't need it.
+static uint16_t* dma_main_buffers[ADC_COUNT]      = {nullptr};
+static uint16_t* dma_secondary_buffers[ADC_COUNT] = {nullptr};
+static uint8_t   current_dma_buffer[ADC_COUNT]    = {0};
+static size_t    dma_buffer_sizes[ADC_COUNT]      = {0};
+
+// Dispatch method
+static dispatch_t dispatch_type;
+
+
+/////
+// Private functions
+
+__STATIC_INLINE uint16_t* _data_dispatch_get_buffer(uint8_t adc_index, uint8_t channel_index)
+{
+	uint8_t active_buffer = current_buffer[adc_index][channel_index];
+	return adc_channel_buffers[adc_index][channel_index][active_buffer];
+}
+
+__STATIC_INLINE uint32_t _data_dispatch_get_count(uint8_t adc_index, uint8_t channel_index)
+{
+	return buffers_data_count[adc_index][channel_index];
+}
+
+__STATIC_INLINE void _data_dispatch_increment_count(uint8_t adc_index, uint8_t channel_index)
+{
+	uint32_t* current_count = &buffers_data_count[adc_index][channel_index];
+	if ( (*current_count) < CHANNELS_BUFFERS_SIZE)
+	{
+		(*current_count)++;
+	}
+}
+
+__STATIC_INLINE void _data_dispatch_swap_buffers(uint8_t adc_index, uint8_t channel_index)
+{
+	uint8_t* active_buffer = &current_buffer[adc_index][channel_index];
+
+	*active_buffer = ((*active_buffer) == 0) ? 1 : 0;
+	buffers_data_count[adc_index][channel_index] = 0;
+}
+
+/////
+// Public API
+
+void data_dispatch_init(dispatch_t dispatch_method)
+{
+	// Store dispatch method
+	dispatch_type = dispatch_method;
+
+	// Prepare arrays for each ADC
+	enabled_channels_count = (uint8_t*)    k_malloc(ADC_COUNT * sizeof(uint8_t));
+	adc_channel_buffers    = (uint16_t****)k_calloc(ADC_COUNT,  sizeof(uint16_t***));
+	buffers_data_count     = (uint32_t**)  k_calloc(ADC_COUNT,  sizeof(uint32_t*));
+	current_buffer         = (uint8_t**)   k_calloc(ADC_COUNT,  sizeof(uint8_t*));
+	peek_memory            = (uint16_t**)  k_calloc(ADC_COUNT,  sizeof(uint16_t*));
+
+	// Configure DMA 1 channels
+	for (uint8_t adc_num = 1 ; adc_num <= ADC_COUNT ; adc_num++)
+	{
+		uint8_t adc_index = adc_num-1;
+		enabled_channels_count[adc_index] = adc_get_enabled_channels_count(adc_num);
+
+		if (enabled_channels_count[adc_index] > 0)
+		{
+			// Prepare buffers for DMA
+			size_t dma_buffer_size;
+
+			if (dispatch_type == interrupt)
+			{
+				dma_buffer_size = enabled_channels_count[adc_index];
+
+				// DMA double-buffering
+				dma_buffer_size = dma_buffer_size * 2;
+			}
+			else
+			{
+				uint32_t repetition;
+				if (scheduling_get_uninterruptible_synchronous_task_interrupt_source() == source_hrtim)
+				{
+					repetition = hrtim_PeriodicEvent_GetRep(MSTR);
+				}
+				else
+				{
+					uint32_t hrtim_period_us = leg_get_period_us();
+					uint32_t task_period_us = scheduling_get_uninterruptible_synchronous_task_period_us();
+					repetition = task_period_us / hrtim_period_us;
+				}
+
+				dma_buffer_size = repetition;
+
+				// Make sure buffer size is a multiple of enabled channels count
+				// so that each channel data will always be at the same position
+				if (repetition % enabled_channels_count[adc_index] != 0)
+				{
+					dma_buffer_size += enabled_channels_count[adc_index] - (repetition % enabled_channels_count[adc_index]);
+				}
+				else
+				{
+					// Add room for one additional measure per channel.
+					// This prevents DMA buffer to do exactly one rotation
+					// between two tasks calls, to prevent edge cases in
+					// acquired data count computation.
+					dma_buffer_size += enabled_channels_count[adc_index];
+				}
+			}
+
+			dma_buffer_sizes[adc_index] = dma_buffer_size;
+			dma_main_buffers[adc_index] = (uint16_t*)k_malloc(dma_buffer_size * sizeof(uint16_t));
+			if (dispatch_type == interrupt)
+			{
+				dma_secondary_buffers[adc_index] = dma_main_buffers[adc_index] + enabled_channels_count[adc_index];
+			}
+
+			// Initialize DMA
+			bool disable_interrupts = false;
+			if (dispatch_type == task)
+			{
+				disable_interrupts = true;
+			}
+			dma_configure_adc_acquisition(adc_num, disable_interrupts, dma_main_buffers[adc_index], dma_buffer_size);
+
+			// Prepare arrays for each channel
+			adc_channel_buffers[adc_index] = (uint16_t***)k_malloc(enabled_channels_count[adc_index] * sizeof(uint16_t**));
+
+			buffers_data_count[adc_index] = (uint32_t*)k_calloc(enabled_channels_count[adc_index], sizeof(uint32_t));
+			current_buffer[adc_index]     = (uint8_t*) k_calloc(enabled_channels_count[adc_index], sizeof(uint8_t));
+			peek_memory[adc_index]        = (uint16_t*)k_calloc(enabled_channels_count[adc_index], sizeof(uint16_t));
+			for (int channel_index = 0 ; channel_index < enabled_channels_count[adc_index] ; channel_index++)
+			{
+				// Prepare double buffer
+				adc_channel_buffers[adc_index][channel_index] = (uint16_t**)k_malloc(sizeof(uint16_t*) * 2);
+				adc_channel_buffers[adc_index][channel_index][0] = (uint16_t*)k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
+				adc_channel_buffers[adc_index][channel_index][1] = (uint16_t*)k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
+
+				peek_memory[adc_index][channel_index] = PEEK_NO_VALUE;
+			}
+		}
+	}
+}
+
+void data_dispatch_do_dispatch(uint8_t adc_num)
+{
+	uint8_t adc_index = adc_num - 1;
+
+	if (enabled_channels_count[adc_index] == 0)
+		return;
+
+	uint16_t* dma_buffer = dma_main_buffers[adc_index];
+	if (dma_secondary_buffers[adc_index] != nullptr)
+	{
+		if (current_dma_buffer[adc_index] == 0)
+		{
+			current_dma_buffer[adc_index] = 1;
+		}
+		else
+		{
+			dma_buffer = dma_secondary_buffers[adc_index];
+			current_dma_buffer[adc_index] = 0;
+		}
+	}
+
+	size_t data_count_in_dma_buffer;
+	if (dispatch_type == interrupt)
+	{
+		data_count_in_dma_buffer = enabled_channels_count[adc_index];
+	}
+	else
+	{
+		data_count_in_dma_buffer = dma_get_retreived_data_count(adc_num);
+	}
+
+	for (size_t dma_index = 0 ; dma_index < data_count_in_dma_buffer ; dma_index++)
+	{
+		// Copy data
+		size_t dma_buffer_index;
+		if (dispatch_type == interrupt)
+		{
+			dma_buffer_index = dma_index % enabled_channels_count[adc_index];
+		}
+		else
+		{
+			static size_t next_dma_buffer_index[ADC_COUNT] = {0};
+
+			dma_buffer_index = next_dma_buffer_index[adc_index];
+			if (next_dma_buffer_index[adc_index] < dma_buffer_sizes[adc_index]-1)
+			{
+				next_dma_buffer_index[adc_index]++;
+			}
+			else
+			{
+				next_dma_buffer_index[adc_index] = 0;
+			}
+		}
+
+		// Get info on buffer
+		size_t channel_index = dma_buffer_index % enabled_channels_count[adc_index];
+		uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_index);
+		uint32_t  current_count = _data_dispatch_get_count(adc_index, channel_index);
+
+		active_buffer[current_count] = dma_buffer[dma_buffer_index];
+
+		// Increment count
+		_data_dispatch_increment_count(adc_index, channel_index);
+	}
+}
+
+void data_dispatch_do_full_dispatch()
+{
+	for (uint8_t adc_num = 1 ; adc_num <= ADC_COUNT ; adc_num++)
+	{
+		data_dispatch_do_dispatch(adc_num);
+	}
+}
+
+
+/////
+// Accessors
+
+uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_rank, uint32_t& number_of_values_acquired)
+{
+	// Prepare default value
+	number_of_values_acquired = 0;
+
+	// Check index
+	uint8_t adc_index = adc_number-1;
+	if (adc_index >= ADC_COUNT)
+		return nullptr;
+
+	// Get and check data count
+	uint32_t current_count = _data_dispatch_get_count(adc_index, channel_rank);
+	if (current_count == 0)
+		return nullptr;
+
+	// Get and swap buffer
+	uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_rank);
+	_data_dispatch_swap_buffers(adc_index, channel_rank);
+
+	// Retain latest value for peek() functions
+	if (current_count > 0)
+	{
+		peek_memory[adc_number][channel_rank] = active_buffer[current_count-1];
+	}
+
+	// Return data
+	number_of_values_acquired = current_count;
+	return active_buffer;
+}
+
+uint16_t data_dispatch_peek_acquired_value(uint8_t adc_number, uint8_t channel_rank)
+{
+	uint8_t adc_index = adc_number-1;
+	if (adc_index < ADC_COUNT)
+	{
+		// Get info on buffer
+		uint16_t* active_buffer = _data_dispatch_get_buffer(adc_index, channel_rank);
+		uint32_t  current_count = _data_dispatch_get_count(adc_index, channel_rank);
+
+		// Return data
+		if (current_count > 0)
+		{
+			return active_buffer[current_count-1];
+		}
+		else
+		{
+			return peek_memory[adc_number][channel_rank];
+		}
+	}
+	else
+	{
+		return 0;
+	}
+}
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.h b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.h
index ab953b2f17420087d5fc3259542b8f595a1e4694..e82fe44dde8325b695d0b85a1ea0f900ad5f4d4d 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.h
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/data_dispatch.h
@@ -35,18 +35,21 @@
 #define DATA_DISPATCH_H_
 
 
+// Stdlib
 #include <stdint.h>
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+const uint16_t PEEK_NO_VALUE = 0xFFFF;
 
+/**
+ * Dispatch method
+ */
+typedef enum {task, interrupt} dispatch_t;
 
 /**
  * Init function to be called first.
  */
-void data_dispatch_init();
+void data_dispatch_init(dispatch_t dispatch_method);
 
 /**
  * Dispatch function: gets the readings and store them
@@ -57,6 +60,12 @@ void data_dispatch_init();
  */
 void data_dispatch_do_dispatch(uint8_t adc_number);
 
+/**
+ * Function to proceed to all chanels dispatch when
+ * it is done at uninterruptible task start.
+ */
+void data_dispatch_do_full_dispatch();
+
 /**
  * Obtain data for a specific channel.
  * The data is provided as an array of values
@@ -75,7 +84,7 @@ void data_dispatch_do_dispatch(uint8_t adc_number);
  *         by further calls to the function with same
  *         adc number/channel rank.
  */
-uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_rank, uint32_t* number_of_values_acquired);
+uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_rank, uint32_t& number_of_values_acquired);
 
 /**
  * Peek data for a specific channel:
@@ -93,8 +102,4 @@ uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_
 uint16_t data_dispatch_peek_acquired_value(uint8_t adc_number, uint8_t channel_rank);
 
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // DATA_DISPATCH_H_
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.c b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.cpp
similarity index 65%
rename from zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.c
rename to zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.cpp
index 2701d171ab9c84c5cdc047e143828775db2eec62..f4687c954010fa174beb68547feddd51e57012a5 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.c
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.cpp
@@ -48,9 +48,9 @@ static const struct device* dma1 = DEVICE_DT_GET(DT_NODELABEL(dma1));
 
 
 /////
-// LL definitions
+// Local variables
 
-static uint32_t source_registers[4] =
+static const uint32_t source_registers[4] =
 {
 	(uint32_t)(&(ADC1->DR)),
 	(uint32_t)(&(ADC2->DR)),
@@ -58,7 +58,7 @@ static uint32_t source_registers[4] =
 	(uint32_t)(&(ADC4->DR))
 };
 
-static uint32_t source_triggers[4] =
+static const uint32_t source_triggers[4] =
 {
 	LL_DMAMUX_REQ_ADC1,
 	LL_DMAMUX_REQ_ADC2,
@@ -66,6 +66,8 @@ static uint32_t source_triggers[4] =
 	LL_DMAMUX_REQ_ADC4
 };
 
+static size_t buffers_sizes[4] = {0};
+
 
 /////
 // Private API
@@ -91,7 +93,7 @@ static void _dma_callback(const struct device* dev, void* user_data, uint32_t ch
 /////
 // Public API
 
-void dma_configure_adc_acquisition(uint8_t adc_number, bool enable_double_buffering, uint16_t* buffer, size_t buffer_size)
+void dma_configure_adc_acquisition(uint8_t adc_number, bool disable_interrupts, uint16_t* buffer, size_t buffer_size)
 {
 	// Check environment
 	if (device_is_ready(dma1) == false)
@@ -100,23 +102,22 @@ void dma_configure_adc_acquisition(uint8_t adc_number, bool enable_double_buffer
 	if (adc_get_enabled_channels_count(adc_number) == 0)
 		return;
 
-	uint8_t adc_index = adc_number - 1;
+	uint8_t dma_index = adc_number - 1;
+	uint32_t buffer_size_bytes = (uint32_t) buffer_size * sizeof(uint16_t);
+	buffers_sizes[dma_index] = buffer_size;
 
 	// Configure DMA
 	struct dma_block_config dma_block_config_s = {0};
-	dma_block_config_s.source_address   = source_registers[adc_index]; // Source: ADC DR register
+	dma_block_config_s.source_address   = source_registers[dma_index]; // Source: ADC DR register
 	dma_block_config_s.dest_address     = (uint32_t)buffer;            // Dest: buffer in memory
-	dma_block_config_s.block_size       = (uint32_t)buffer_size;       // Buffer size in bytes
+	dma_block_config_s.block_size       = buffer_size_bytes;           // Buffer size in bytes
 	dma_block_config_s.source_addr_adj  = DMA_ADDR_ADJ_NO_CHANGE;      // Source: no increment in ADC register
 	dma_block_config_s.dest_addr_adj    = DMA_ADDR_ADJ_INCREMENT;      // Dest: increment in memory
 	dma_block_config_s.dest_reload_en   = 1;                           // Reload destination address on block completion
-	if (enable_double_buffering == 1)
-	{
-		dma_block_config_s.source_reload_en = 1; // Reload source address on block completion; Enables Half-transfer interrupt
-	}
+	dma_block_config_s.source_reload_en = 1;                           // Reload source address on block completion; Enables Half-transfer interrupt
 
 	struct dma_config dma_config_s = {0};
-	dma_config_s.dma_slot            = source_triggers[adc_index]; // Trigger source: ADC
+	dma_config_s.dma_slot            = source_triggers[dma_index]; // Trigger source: ADC
 	dma_config_s.channel_direction   = PERIPHERAL_TO_MEMORY;       // From periph to mem
 	dma_config_s.source_data_size    = 2;                          // Source: 2 bytes (uint16_t)
 	dma_config_s.dest_data_size      = 2;                          // Dest: 2 bytes (uint16_t)
@@ -128,5 +129,38 @@ void dma_configure_adc_acquisition(uint8_t adc_number, bool enable_double_buffer
 
 	dma_config(dma1, adc_number, &dma_config_s);
 
+	if (disable_interrupts == true)
+	{
+		LL_DMA_DisableIT_HT(DMA1, dma_index);
+		LL_DMA_DisableIT_TC(DMA1, dma_index);
+	}
+
 	dma_start(dma1, adc_number);
 }
+
+size_t dma_get_retreived_data_count(uint8_t adc_number)
+{
+	// Permanent variable
+	// -1 is equivalent to (buffer size - 1) in modulo arithmetics
+	static int32_t previous_dma_latest_data_pointers[4] = {-1};
+
+	// Get data
+	uint32_t dma_index = adc_number - 1;
+	uint32_t dma_remaining_data = LL_DMA_GetDataLength(DMA1, dma_index);
+	int32_t previous_dma_latest_data_pointer = previous_dma_latest_data_pointers[dma_index];
+
+	// Compute pointers
+	int32_t dma_next_data_pointer = buffers_sizes[dma_index] - dma_remaining_data;
+	int32_t dma_latest_data_pointer = dma_next_data_pointer - 1;
+
+	int32_t corrected_dma_pointer = dma_latest_data_pointer;
+	if (dma_latest_data_pointer < previous_dma_latest_data_pointer)
+	{
+		corrected_dma_pointer += buffers_sizes[dma_index];
+	}
+
+	size_t retreived_data = corrected_dma_pointer - previous_dma_latest_data_pointer;
+	previous_dma_latest_data_pointers[dma_index] = dma_latest_data_pointer;
+
+	return retreived_data;
+}
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.h b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.h
index 385a86b1faac4878324746b800320b73afecfe94..576beceae1e6bf526e26a6106c5e9de40da52dcb 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.h
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/adc_to_mem/dma.h
@@ -36,34 +36,32 @@
 #include <stdint.h>
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
 /**
- * This function configures a channel from DMA 1
- * to transfer measures  from an ADC to buffers,
+ * @brief This function configures a channel from DMA 1
+ * to transfer measures from an ADC to buffers,
  * then starts the channels.
  * It must only be called after all the ADCs configuration
  * has been carried out, as it uses its channels
  * configuration to determine the size of the buffers.
  *
  * @param adc_number Number of the ADC to acquire measures from.
- * @param enable_double_buffering Boolean indicating whether
- *        double buffering is to be activated.
- *        - If false, the buffer will be treated as a single buffer with
- *          interrupt being triggered only when it is full.
- *        - If true the buffer will be treated as two consecutive sub-buffers,
- *          with interrupt being triggered as soon as a sub-buffer is full.
+ * @param disable_interrupts Boolean indicating whether interrupts
+ *        shoud be disabled. Warning: this override Zephyr DMA
+ *        driver defalt behavior.
  * @param buffer Pointer to buffer.
- * @param buffer_size Size of the buffer in bytes.
+ * @param buffer_size Number of uint16_t words the buffer can contain.
  */
-void dma_configure_adc_acquisition(uint8_t adc_number, bool enable_double_buffering, uint16_t* buffer, size_t buffer_size);
+void dma_configure_adc_acquisition(uint8_t adc_number, bool disable_interrupts, uint16_t* buffer, size_t buffer_size);
 
+/**
+ * @brief Obtain the number of acquired data since
+ *        last time this function was called.
+ *
+ * @param adc_number Number of the ADC.
+ *
+ * @return Number of acquired data modulo buffer size.
+ */
+size_t dma_get_retreived_data_count(uint8_t adc_number);
 
-#ifdef __cplusplus
-}
-#endif
 
 #endif // DMA_H_
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.c b/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.cpp
similarity index 100%
rename from zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.c
rename to zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.cpp
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.h b/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.h
index ec6c74f89eb516eb5e5b6955191c3cd4f493d8c2..c59a082399195abbee2a32e8bb04325f63275541 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.h
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/data_conversion/data_conversion.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2022 LAAS-CNRS
+ * Copyright (c) 2021-2023 LAAS-CNRS
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU Lesser General Public License as published by
@@ -18,7 +18,7 @@
  */
 
 /**
- * @date   2022
+ * @date   2023
  * @author Antoine Boche <antoine.boche@laas.fr>
  * @author Clément Foucher <clement.foucher@laas.fr>
  * @author Luiz Villa <luiz.villa@laas.fr>
@@ -29,10 +29,6 @@
 
 #include <arm_math.h>   // adds all the CMSIS library
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 
 /**
  * @brief    Converts the values of the given raw_value into a physical unit
@@ -191,8 +187,4 @@ void data_conversion_set_extra_parameters(float32_t gain, float32_t offset);
 void data_conversion_set_analog_comm_parameters(float32_t gain, float32_t offset);
 
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // DATA_CONVERSION_H_
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.cpp b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.cpp
index aea7cb18bfc856f401a7bd58f57fe10c5fe82078..5fec1e775632775c4a466a50ce4990c1a7d46fa4 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.cpp
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.cpp
@@ -33,6 +33,7 @@
 
 // OwnTech Power API
 #include "adc.h"
+#include "scheduling_internal.h"
 
 // Current module private functions
 #include "../adc_to_mem/data_dispatch.h"
@@ -97,8 +98,23 @@ void DataAcquisition::setChannnelAssignment(uint8_t adc_number, const char* chan
 	}
 }
 
-void DataAcquisition::start()
+int8_t DataAcquisition::start(dispatch_method_t dispatch_method)
 {
+	if (this->is_started == true)
+		return -1;
+
+	scheduling_interrupt_source_t int_source;
+	if (dispatch_method == dispatch_method_t::at_uninterruptible_task_start)
+	{
+		int_source = scheduling_get_uninterruptible_synchronous_task_interrupt_source();
+		if (int_source == scheduling_interrupt_source_t::source_uninitialized)
+		{
+			return -1;
+		}
+
+		scheduling_set_data_dispatch_at_task_start(true);
+	}
+
 	for (uint8_t adc_num = 1 ; adc_num <= 4 ; adc_num++)
 	{
 		uint8_t channel_rank = 0;
@@ -120,12 +136,15 @@ void DataAcquisition::start()
 	}
 
 	// Initialize data dispatch
-	data_dispatch_init();
+	dispatch_t dispatch_type = dispatch_method == on_dma_interrupt ? interrupt : task;
+	data_dispatch_init(dispatch_type);
 
 	// Launch ADC conversion
 	adc_start();
 
 	this->is_started = true;
+
+	return 0;
 }
 
 bool DataAcquisition::started()
@@ -137,426 +156,149 @@ bool DataAcquisition::started()
 /////
 // Public static accessors
 
+// Get raw values
+
 uint16_t* DataAcquisition::getV1LowRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(v1_low_assignement.adc_number, v1_low_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(v1_low_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getV2LowRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(v2_low_assignement.adc_number, v2_low_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(v2_low_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getVHighRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(v_high_assignement.adc_number, v_high_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(v_high_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getI1LowRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(i1_low_assignement.adc_number, i1_low_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(i1_low_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getI2LowRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(i2_low_assignement.adc_number, i2_low_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(i2_low_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getIHighRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(i_high_assignement.adc_number, i_high_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(i_high_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getTemperatureRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(temp_sensor_assignement.adc_number, temp_sensor_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(temp_sensor_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getExtraRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(extra_sensor_assignement.adc_number, extra_sensor_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(extra_sensor_assignement, number_of_values_acquired);
 }
 
 uint16_t* DataAcquisition::getAnalogCommRawValues(uint32_t& number_of_values_acquired)
 {
-	if (this->is_started == true)
-	{
-		return data_dispatch_get_acquired_values(analog_comm_assignement.adc_number, analog_comm_assignement.channel_rank, &number_of_values_acquired);
-	}
-	else
-	{
-		number_of_values_acquired = 0;
-		return nullptr;
-	}
+	return _getRawValues(analog_comm_assignement, number_of_values_acquired);
 }
 
+// Peek
 
 float32_t DataAcquisition::peekV1Low()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(v1_low_assignement.adc_number, v1_low_assignement.channel_rank);
-		return data_conversion_convert_v1_low(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(v1_low_assignement, data_conversion_convert_v1_low);
 }
 
 float32_t DataAcquisition::peekV2Low()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(v2_low_assignement.adc_number, v2_low_assignement.channel_rank);
-		return data_conversion_convert_v2_low(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(v2_low_assignement, data_conversion_convert_v2_low);
 }
 
 float32_t DataAcquisition::peekVHigh()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(v_high_assignement.adc_number, v_high_assignement.channel_rank);
-		return data_conversion_convert_v_high(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(v_high_assignement, data_conversion_convert_v_high);
 }
 
 float32_t DataAcquisition::peekI1Low()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(i1_low_assignement.adc_number, i1_low_assignement.channel_rank);
-		return data_conversion_convert_i1_low(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(i1_low_assignement, data_conversion_convert_i1_low);
 }
 
 float32_t DataAcquisition::peekI2Low()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(i2_low_assignement.adc_number, i2_low_assignement.channel_rank);
-		return data_conversion_convert_i2_low(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(i2_low_assignement, data_conversion_convert_i2_low);
 }
 
 float32_t DataAcquisition::peekIHigh()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(i_high_assignement.adc_number, i_high_assignement.channel_rank);
-		return data_conversion_convert_i_high(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(i_high_assignement, data_conversion_convert_i_high);
 }
 
 float32_t DataAcquisition::peekTemperature()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(temp_sensor_assignement.adc_number, temp_sensor_assignement.channel_rank);
-		return data_conversion_convert_temp(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(temp_sensor_assignement, data_conversion_convert_temp);
 }
 
 float32_t DataAcquisition::peekExtra()
 {
-	if (this->is_started == true)
-	{
-		uint16_t rawValue = data_dispatch_peek_acquired_value(extra_sensor_assignement.adc_number, extra_sensor_assignement.channel_rank);
-		return data_conversion_convert_extra(rawValue);
-	}
-	else
-	{
-		return 0;
-	}
+	return _peek(extra_sensor_assignement, data_conversion_convert_extra);
 }
 
-float32_t DataAcquisition::getV1Low()
+float32_t DataAcquisition::peekAnalogComm()
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getV1LowRawValues(data_count);
+	return _peek(analog_comm_assignement, data_conversion_convert_analog_comm);
+}
 
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_v1_low(raw_value);
-		}
+// Get latest value
 
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+float32_t DataAcquisition::getV1Low(uint8_t* dataValid)
+{
+	return this->_getChannel(v1_low_assignement, data_conversion_convert_v1_low, dataValid);
 }
 
-float32_t DataAcquisition::getV2Low()
+float32_t DataAcquisition::getV2Low(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getV2LowRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_v2_low(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(v2_low_assignement, data_conversion_convert_v2_low, dataValid);
 }
 
-float32_t DataAcquisition::getVHigh()
+float32_t DataAcquisition::getVHigh(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getVHighRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_v_high(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(v_high_assignement, data_conversion_convert_v_high, dataValid);
 }
 
-float32_t DataAcquisition::getI1Low()
+float32_t DataAcquisition::getI1Low(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getI1LowRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_i1_low(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(i1_low_assignement, data_conversion_convert_i1_low, dataValid);
 }
 
-float32_t DataAcquisition::getI2Low()
+float32_t DataAcquisition::getI2Low(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getI2LowRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_i2_low(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(i2_low_assignement, data_conversion_convert_i2_low, dataValid);
 }
 
-float32_t DataAcquisition::getIHigh()
+float32_t DataAcquisition::getIHigh(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getIHighRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_i_high(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(i_high_assignement, data_conversion_convert_i_high, dataValid);
 }
 
-float32_t DataAcquisition::getTemperature()
+float32_t DataAcquisition::getTemperature(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getTemperatureRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_temp(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(temp_sensor_assignement, data_conversion_convert_temp, dataValid);
 }
 
-float32_t DataAcquisition::getExtra()
+float32_t DataAcquisition::getExtra(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getExtraRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_extra(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(extra_sensor_assignement, data_conversion_convert_extra, dataValid);
 }
 
-float32_t DataAcquisition::getAnalogComm()
+float32_t DataAcquisition::getAnalogComm(uint8_t* dataValid)
 {
-	if (this->is_started == true)
-	{
-		uint32_t data_count;
-		static float32_t converted_data = -10000; // Return an impossible value if no data has been acquired yet
-		uint16_t* buffer = getAnalogCommRawValues(data_count);
-
-		if (data_count > 0) // If data was received it gets converted
-		{
-			uint16_t raw_value = buffer[data_count - 1];
-			converted_data = data_conversion_convert_analog_comm(raw_value);
-		}
-
-		return converted_data;
-	}
-	else
-	{
-		return -10000; // Return an impossible value if no data has been acquired yet
-	}
+	return this->_getChannel(analog_comm_assignement, data_conversion_convert_analog_comm, dataValid);
 }
 
+// Convertion
+
 float32_t DataAcquisition::convertV1Low(uint16_t raw_value)
 {
 	return data_conversion_convert_v1_low(raw_value);
@@ -602,6 +344,7 @@ float32_t DataAcquisition::convertAnalogComm(uint16_t raw_value)
 	return data_conversion_convert_analog_comm(raw_value);
 }
 
+// Parameter setters
 
 void DataAcquisition::setV1LowParameters(float32_t gain, float32_t offset)
 {
@@ -647,3 +390,90 @@ void DataAcquisition::setAnalogCommParameters(float32_t gain, float32_t offset)
 {
 	data_conversion_set_analog_comm_parameters(gain, offset);
 }
+
+// Internal private functions
+
+float32_t DataAcquisition::_getChannel(channel_assignment_t assignment, float32_t(*convert)(uint16_t), uint8_t* dataValid)
+{
+	if (this->is_started == false)
+	{
+		if (dataValid != nullptr)
+		{
+			*dataValid = DATA_IS_MISSING;
+		}
+		return NO_VALUE;
+	}
+
+	uint32_t data_count;
+	uint16_t* buffer = data_dispatch_get_acquired_values(assignment.adc_number, assignment.channel_rank, data_count);
+
+	if (data_count > 0)
+	{
+		uint16_t raw_value = buffer[data_count - 1];
+		if (dataValid != nullptr)
+		{
+			*dataValid = DATA_IS_OK;
+		}
+		return convert(raw_value);
+	}
+	else
+	{
+		uint16_t rawValue = data_dispatch_peek_acquired_value(assignment.adc_number, assignment.channel_rank);
+
+		float32_t peekValue;
+		if (rawValue != PEEK_NO_VALUE)
+		{
+			peekValue = convert(rawValue);
+		}
+		else
+		{
+			peekValue = NO_VALUE;
+		}
+
+		if (dataValid != nullptr)
+		{
+			if (peekValue != NO_VALUE)
+			{
+				*dataValid = DATA_IS_OLD;
+			}
+			else
+			{
+				*dataValid = DATA_IS_MISSING;
+			}
+		}
+		return peekValue;
+	}
+}
+
+uint16_t* DataAcquisition::_getRawValues(channel_assignment_t assignment, uint32_t& number_of_values_acquired)
+{
+	if (this->is_started == true)
+	{
+		return data_dispatch_get_acquired_values(assignment.adc_number, assignment.channel_rank, number_of_values_acquired);
+	}
+	else
+	{
+		number_of_values_acquired = 0;
+		return nullptr;
+	}
+}
+
+float32_t DataAcquisition::_peek(channel_assignment_t assignment, float32_t(*convert)(uint16_t))
+{
+	if (this->is_started == true)
+	{
+		uint16_t rawValue = data_dispatch_peek_acquired_value(assignment.adc_number, assignment.channel_rank);
+		if (rawValue != PEEK_NO_VALUE)
+		{
+			return convert(rawValue);
+		}
+		else
+		{
+			return NO_VALUE;
+		}
+	}
+	else
+	{
+		return NO_VALUE;
+	}
+}
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.h b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.h
index 4e758cff2de0a02ab5246a9abb11975ad8a09e67..78c085cd0a09ffbf4d17d89754cac9ed3ac92d03 100644
--- a/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.h
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/DataAcquisition.h
@@ -35,22 +35,27 @@
 #include <arm_math.h>
 
 
+/////
+// Public definitions
+
+typedef enum
+{
+	on_dma_interrupt,
+	at_uninterruptible_task_start
+} dispatch_method_t;
+
+// Define "no value" as an impossible, out of range value
+const float32_t NO_VALUE = -10000;
+
+const uint8_t DATA_IS_OK = 0;
+const uint8_t DATA_IS_OLD = 1;
+const uint8_t DATA_IS_MISSING = 2;
+
 /////
 // Static class definition
 
 class DataAcquisition
 {
-private:
-	/**
-	 * This function is used to indicate to the DataAcquisition module
-	 * what the underlying ADC channel configuration is.
-	 *
-	 * @param adc_number ADC number
-	 * @param channel_name Channel name
-	 * @param channel_rannk Channel rank
-	 */
-	void setChannnelAssignment(uint8_t adc_number, const char* channel_name, uint8_t channel_rank);
-
 public:
 
 	/**
@@ -60,16 +65,38 @@ public:
 	 * If you're not sure how to initialize ADC, just use the Hardware
 	 * Configuration module API: hwConfig.configureAdcDefaultAllMeasurements()
 	 *
-	 * NOTE: This function must be called before accessing any dataAcquisition.get*()
-	 * or dataAcquisition.peek*() function. Other functions are safe to
-	 * use before starting the module.
+	 * NOTE 1: If your code uses an uninterruptible task, you do not need to start
+	 * Data Acquisition manually, it will automatically be started at the same
+	 * time as the task as their internal behavior are intrinsically linked.
+	 *
+	 * NOTE 2: Data Acquisition must be started before accessing any
+	 * dataAcquisition.get*() or dataAcquisition.peek*() function.
+	 * Other Data Acquisition functions are safe to use before starting
+	 * the module.
+	 *
+	 * @param dispatch_method Indicates when the dispatch should be done.
+	 *        Dispatch makes data from ADCs available to dataAcquisition.get*()
+	 *        functions, thus available to the user.
+	 *        You should not worry too much about this parameter, as if you
+	 *        call this function manually, the default value is what you want,
+	 *        so just call the function without any parameter.
+	 *        By default, Data Acquisition is started automatically when
+	 *        the uninterruptible task is started, with dispatch method
+	 *        set to at_uninterruptible_task_start. However, if you do not
+	 *        use an uninterrptible task in your application, default parameter
+	 *        on_dma_interrupt is the correct value.
+	 *        If for some reason you have an uninterruptible task in your code,
+	 *        but stoill want the dispatch to be done on DMA interrupt,
+	 *        you need to call this function prior to starting the task.
+	 *        Note that using DMA interrupts will consume a non-negligible
+	 *        amount of processor time and it is not advised.
 	 *
 	 * @return 0 if everything went well, -1 if there was an error.
 	 *         Error is triggered when dispatch method is set to
 	 *         uninterruptible task start, but the task has not been
 	 *         defined yet.
 	 */
-	void start();
+	int8_t start(dispatch_method_t dispatch_method = on_dma_interrupt);
 
 	/**
 	 * Check if the module is already started.
@@ -133,6 +160,8 @@ public:
 	 * be called safely at any time.
 	 *
 	 * @return Latest available value available from the given channel.
+	 *         If there was no value acquired in this channel yet,
+	 *         return value is NO_VALUE.
 	 */
 	float32_t peekV1Low();
 	float32_t peekV2Low();
@@ -142,6 +171,7 @@ public:
 	float32_t peekIHigh();
 	float32_t peekTemperature();
 	float32_t peekExtra();
+	float32_t peekAnalogComm();
 
 	/**
 	 * These functions return the latest acquired measure expressed
@@ -152,17 +182,25 @@ public:
 	 *       functions, as dataAcquisition.get*() functions clear the buffers
 	 *       on each call.
 	 *
+	 * @param dataValid Pointer to an uint8_t variable. This parameter is
+	 *        facultative. If this parameter is provided, it will be updated
+	 *        to indicate information about data. Possible values for this
+	 *        parameter will be: DATA_IS_OK if returned data is a newly acquired
+	 *        data, DATA_IS_OLD if returned data has already been provided before
+	 *        (no new data available since latest time this function was called),
+	 *        DATA_IS_MISSING if returned data is NO_VALUE.
 	 * @return Latest acquired measure for the channel.
+	 *         If no value was acquired in this channel yet, return value is NO_VALUE.
 	 */
-	float32_t getV1Low();
-	float32_t getV2Low();
-	float32_t getVHigh();
-	float32_t getI1Low();
-	float32_t getI2Low();
-	float32_t getIHigh();
-	float32_t getTemperature();
-	float32_t getExtra();
-	float32_t getAnalogComm();
+	float32_t getV1Low(uint8_t* dataValid = nullptr);
+	float32_t getV2Low(uint8_t* dataValid = nullptr);
+	float32_t getVHigh(uint8_t* dataValid = nullptr);
+	float32_t getI1Low(uint8_t* dataValid = nullptr);
+	float32_t getI2Low(uint8_t* dataValid = nullptr);
+	float32_t getIHigh(uint8_t* dataValid = nullptr);
+	float32_t getTemperature(uint8_t* dataValid = nullptr);
+	float32_t getExtra(uint8_t* dataValid = nullptr);
+	float32_t getAnalogComm(uint8_t* dataValid = nullptr);
 
 	/**
 	 * Use these functions to convert values obtained using
@@ -193,7 +231,6 @@ public:
 	void setExtraParameters(float32_t gain, float32_t offset);
 	void setAnalogCommParameters(float32_t gain, float32_t offset);
 
-
 private:
 	typedef struct
 	{
@@ -201,6 +238,21 @@ private:
 		uint8_t channel_rank;
 	} channel_assignment_t;
 
+private:
+	/**
+	 * This function is used to indicate to the DataAcquisition module
+	 * what the underlying ADC channel configuration is.
+	 *
+	 * @param adc_number ADC number
+	 * @param channel_name Channel name
+	 * @param channel_rannk Channel rank
+	 */
+	void setChannnelAssignment(uint8_t adc_number, const char* channel_name, uint8_t channel_rank);
+
+	float32_t _getChannel(channel_assignment_t assignment, float32_t(*convert)(uint16_t), uint8_t* dataValid);
+	uint16_t* _getRawValues(channel_assignment_t assignment, uint32_t& number_of_values_acquired);
+	float32_t _peek(channel_assignment_t assignment, float32_t(*convert)(uint16_t));
+
 private:
 	bool is_started = false;
 
@@ -223,5 +275,4 @@ private:
 extern DataAcquisition dataAcquisition;
 
 
-
 #endif // DATAACQUISITION_H_
diff --git a/zephyr/modules/owntech_data_acquisition/zephyr/public_api/data_acquisition_internal.h b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/data_acquisition_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..6051e654b6d5167327e55c271787def05f1bc7cb
--- /dev/null
+++ b/zephyr/modules/owntech_data_acquisition/zephyr/public_api/data_acquisition_internal.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 LAAS-CNRS
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGLPV2.1
+ */
+
+/**
+ * @date   2023
+ * @author Clément Foucher <clement.foucher@laas.fr>
+ *
+ * Header to give access to scheduling internal API
+ * to other OwnTech modules.
+ *
+ * Only for use in OwnTech modules.
+ */
+
+#ifndef DATA_ACQUISITION_INTERNAL_H_
+#define DATA_ACQUISITION_INTERNAL_H_
+
+
+/**
+ * @brief Force full dispatch.
+ *
+ * For internal use only, do not call in user code.
+ */
+void data_dispatch_do_full_dispatch();
+
+
+#endif // DATA_ACQUISITION_INTERNAL_H_
diff --git a/zephyr/modules/owntech_scheduling/zephyr/Kconfig b/zephyr/modules/owntech_scheduling/zephyr/Kconfig
index 2ee522e6e1761411ffcfbadddd8bdd2ae47be177..f2a3fc2912cdf5b95906d4c6e469c26fa032d505 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/Kconfig
+++ b/zephyr/modules/owntech_scheduling/zephyr/Kconfig
@@ -3,6 +3,7 @@ config OWNTECH_SCHEDULING
 	default y
 	depends on OWNTECH_TIMER_DRIVER
 	depends on OWNTECH_HRTIM_DRIVER
+	depends on OWNTECH_DATA_ACQUISITION
 
 if OWNTECH_SCHEDULING
 
diff --git a/zephyr/modules/owntech_scheduling/zephyr/public_api/Scheduling.h b/zephyr/modules/owntech_scheduling/zephyr/public_api/Scheduling.h
index fe0abae4ca750dc5f67b60c51ce9323f1816979c..a051316cb579c47308e6ff4e54e93194d476d63d 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/public_api/Scheduling.h
+++ b/zephyr/modules/owntech_scheduling/zephyr/public_api/Scheduling.h
@@ -80,6 +80,10 @@ public:
 	/**
 	 * @brief Use this function to start the previously defined
 	 * uninterruptible synchronous task.
+	 * If Data Acquisition was not started previously,
+	 * starting the uninterruptible task will start it.
+	 * Thus, make sure all ADC configuration has been carried
+	 * out before starting the uninterruptible task.
 	 */
 	void startUninterruptibleSynchronousTask();
 
diff --git a/zephyr/modules/owntech_scheduling/zephyr/public_api/scheduling_internal.h b/zephyr/modules/owntech_scheduling/zephyr/public_api/scheduling_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..57eb4922b55eb60e04d75ded977c3ae752434ebd
--- /dev/null
+++ b/zephyr/modules/owntech_scheduling/zephyr/public_api/scheduling_internal.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 LAAS-CNRS
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGLPV2.1
+ */
+
+/**
+ * @date   2023
+ * @author Clément Foucher <clement.foucher@laas.fr>
+ *
+ * Header to give access to scheduling internal API
+ * to other OwnTech modules.
+ *
+ * Only for use in OwnTech modules.
+ */
+
+#ifndef SCHEDULING_INTERNAL_H_
+#define SCHEDULING_INTERNAL_H_
+
+
+#include <stdint.h>
+
+#include "Scheduling.h"
+
+
+/**
+ * @brief Get the period of the uninterruptible task in µs.
+ *
+ * For internal use only, do not call in user code.
+ */
+uint32_t scheduling_get_uninterruptible_synchronous_task_period_us();
+
+/**
+ * @brief Obtain the configured interrupt source for
+ * uninterruptible synchronous task.
+ *
+ * For internal use only, do not call in user code.
+ */
+scheduling_interrupt_source_t scheduling_get_uninterruptible_synchronous_task_interrupt_source();
+
+/**
+ * @brief Set uninterruptible task in charge of data dispatch.
+ *
+ * For internal use only, do not call in user code.
+ */
+void scheduling_set_data_dispatch_at_task_start(bool enable);
+
+
+#endif // SCHEDULING_INTERNAL_H_
diff --git a/zephyr/modules/owntech_scheduling/zephyr/src/asynchronous_tasks.hpp b/zephyr/modules/owntech_scheduling/zephyr/src/asynchronous_tasks.hpp
index 0e216d00a827609a871aeafa09b93ea75bebf9d1..77a45e5095f51d1a3761fa0bbe8b26b417107011 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/src/asynchronous_tasks.hpp
+++ b/zephyr/modules/owntech_scheduling/zephyr/src/asynchronous_tasks.hpp
@@ -26,13 +26,17 @@
 #ifndef ASYNCHRONOUSTASKS_HPP_
 #define ASYNCHRONOUSTASKS_HPP_
 
-#ifdef CONFIG_OWNTECH_SCHEDULING_ENABLE_ASYNCHRONOUS_TASKS
 
+// Stdlib
+#include <stdint.h>
 
 // OwnTech Power API
 #include "Scheduling.h"
 
 
+#ifdef CONFIG_OWNTECH_SCHEDULING_ENABLE_ASYNCHRONOUS_TASKS
+
+
 int8_t scheduling_define_asynchronous_task(task_function_t routine);
 void scheduling_start_asynchronous_task(uint8_t task_number);
 void scheduling_stop_asynchronous_task(uint8_t task_number);
diff --git a/zephyr/modules/owntech_scheduling/zephyr/src/scheduling_common.hpp b/zephyr/modules/owntech_scheduling/zephyr/src/scheduling_common.hpp
index 0a902cd98432a9d9d22df67091116468ecd80ed3..b63457ca66ef0859768c4fc03cd04c7004561056 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/src/scheduling_common.hpp
+++ b/zephyr/modules/owntech_scheduling/zephyr/src/scheduling_common.hpp
@@ -26,9 +26,17 @@
 #ifndef SCHEDULING_COMMON_HPP_
 #define SCHEDULING_COMMON_HPP_
 
+
+// Stdlib
+#include <stdint.h>
+
+// Zephyr
+#include <zephyr.h>
+
 // OwnTech Power API
 #include "Scheduling.h"
 
+
 enum class task_status_t
 {
 	inexistent,
@@ -53,4 +61,5 @@ void scheduling_common_start_task(task_information_t& task_info, k_thread_entry_
 void scheduling_common_suspend_task(task_information_t& task_info);
 void scheduling_common_resume_task(task_information_t& task_info);
 
+
 #endif // SCHEDULING_COMMON_HPP_
diff --git a/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.cpp b/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.cpp
index 39cb10fc21cfcdca1f8bf4a16330b0acf00f5860..a8cc1cb2ad79ee4dfbbe15b90ddd28878e26aaae 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.cpp
+++ b/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.cpp
@@ -30,6 +30,8 @@
 #include "timer.h"
 #include "leg.h"
 #include "hrtim.h"
+#include "DataAcquisition.h"
+#include "data_acquisition_internal.h"
 
 
 /////
@@ -45,9 +47,27 @@ static task_status_t uninterruptibleTaskStatus = task_status_t::inexistent;
 static scheduling_interrupt_source_t interrupt_source = source_uninitialized;
 
 // For HRTIM interrupts
-static uint32_t repetition = 0;
 static task_function_t user_periodic_task = NULL;
 
+// Data dispatch
+static bool do_data_dispatch = false;
+static uint32_t task_period = 0;
+
+
+/////
+// Private API
+
+void user_task_proxy()
+{
+	if (user_periodic_task == NULL) return;
+
+	if (do_data_dispatch == true)
+	{
+		data_dispatch_do_full_dispatch();
+	}
+
+	user_periodic_task();
+}
 
 /////
 // Public API
@@ -58,6 +78,11 @@ void scheduling_set_uninterruptible_synchronous_task_interrupt_source(scheduling
 	interrupt_source = int_source;
 }
 
+scheduling_interrupt_source_t scheduling_get_uninterruptible_synchronous_task_interrupt_source()
+{
+	return interrupt_source;
+}
+
 int8_t scheduling_define_uninterruptible_synchronous_task(task_function_t periodic_task, uint32_t task_period_us)
 {
 	if ( (uninterruptibleTaskStatus != task_status_t::inexistent) && (uninterruptibleTaskStatus != task_status_t::suspended))
@@ -71,10 +96,13 @@ int8_t scheduling_define_uninterruptible_synchronous_task(task_function_t period
 		if (device_is_ready(timer6) == false)
 			return -1;
 
+		task_period = task_period_us;
+		user_periodic_task = periodic_task;
+
 		// Everything OK, go on with timer configuration
 		struct timer_config_t timer_cfg = {0};
 		timer_cfg.timer_enable_irq   = 1;
-		timer_cfg.timer_irq_callback = periodic_task;
+		timer_cfg.timer_irq_callback = user_task_proxy;
 		timer_cfg.timer_irq_t_usec   = task_period_us;
 
 		timer_config(timer6, &timer_cfg);
@@ -87,15 +115,20 @@ int8_t scheduling_define_uninterruptible_synchronous_task(task_function_t period
 	{
 		uint32_t hrtim_period_us = leg_get_period_us();
 
+		if (hrtim_period_us == 0)
+			return -1;
+
 		if (task_period_us % hrtim_period_us != 0)
 			return -1;
 
-		repetition = task_period_us / hrtim_period_us;
+		uint32_t repetition = task_period_us / hrtim_period_us;
 
 		if (repetition == 0)
 			return -1;
 
+		task_period = task_period_us;
 		user_periodic_task = periodic_task;
+		hrtim_PeriodicEvent_configure(MSTR, repetition, user_task_proxy);
 
 		uninterruptibleTaskStatus = task_status_t::defined;
 
@@ -110,6 +143,11 @@ void scheduling_start_uninterruptible_synchronous_task()
 	if ( (uninterruptibleTaskStatus != task_status_t::defined) && (uninterruptibleTaskStatus != task_status_t::suspended) )
 		return;
 
+	if (dataAcquisition.started() == false)
+	{
+		dataAcquisition.start(at_uninterruptible_task_start);
+	}
+
 	if (interrupt_source == source_tim6)
 	{
 		if (device_is_ready(timer6) == false)
@@ -121,10 +159,9 @@ void scheduling_start_uninterruptible_synchronous_task()
 	}
 	else if (interrupt_source == source_hrtim)
 	{
-		if ( (repetition == 0) || (user_periodic_task == NULL) )
+		if (user_periodic_task == NULL)
 			return;
 
-		hrtim_PeriodicEvent_configure(MSTR, repetition, user_periodic_task);
 		hrtim_PeriodicEvent_en(MSTR);
 
 		uninterruptibleTaskStatus = task_status_t::running;
@@ -152,3 +189,13 @@ void scheduling_stop_uninterruptible_synchronous_task()
 		uninterruptibleTaskStatus = task_status_t::suspended;
 	}
 }
+
+void scheduling_set_data_dispatch_at_task_start(bool enable)
+{
+	do_data_dispatch = enable;
+}
+
+uint32_t scheduling_get_uninterruptible_synchronous_task_period_us()
+{
+	return task_period;
+}
diff --git a/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.hpp b/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.hpp
index e64ac15d00aeba242d3d9f750ab19475f8613ae8..c84654b638876ab1c5702f62b46f140b7e4c60e9 100644
--- a/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.hpp
+++ b/zephyr/modules/owntech_scheduling/zephyr/src/uninterruptible_synchronous_task.hpp
@@ -26,14 +26,20 @@
 #ifndef UNINTERRUPTIBLESYNCHRONOUSTASK_HPP_
 #define UNINTERRUPTIBLESYNCHRONOUSTASK_HPP_
 
+// Stdlib
+#include <stdint.h>
+
 // OwnTech Power API
 #include "Scheduling.h"
 
 
 void scheduling_set_uninterruptible_synchronous_task_interrupt_source(scheduling_interrupt_source_t int_source);
+scheduling_interrupt_source_t scheduling_get_uninterruptible_synchronous_task_interrupt_source();
 int8_t scheduling_define_uninterruptible_synchronous_task(task_function_t periodic_task, uint32_t task_period_us);
 void scheduling_start_uninterruptible_synchronous_task();
 void scheduling_stop_uninterruptible_synchronous_task();
+void scheduling_set_data_dispatch_at_task_start(bool enable);
+uint32_t scheduling_get_uninterruptible_synchronous_task_period_us();
 
 
 #endif // UNINTERRUPTIBLESYNCHRONOUSTASK_HPP_