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 = ¤t_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 = ¤t_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_