Commit 55b750ba authored by Clément Foucher's avatar Clément Foucher
Browse files

Add double-buffering to data dispatch in order to avoid extraneous copy of data.

Use a more generic configuration for DMA.
Corrected error codes.
parent 5c85d374
......@@ -32,9 +32,11 @@
#include "adc_channels.h"
#include "adc_core.h"
/////
// Local variables
static uint32_t adc_trigger_sources[3];
static uint32_t adc_trigger_sources[3] = {0};
/////
......@@ -42,11 +44,6 @@ static uint32_t adc_trigger_sources[3];
void adc_init()
{
for (int i = 0 ; i < 3 ; i++)
{
adc_trigger_sources[i] = 0;
}
adc_core_init();
adc_channels_init();
}
......@@ -115,3 +112,8 @@ void adc_start()
adc_core_start(i+1);
}
}
char* adc_get_channel_name(uint8_t adc_number, uint8_t channel_rank)
{
return adc_channels_get_channel_name(adc_number, channel_rank);
}
......@@ -29,6 +29,11 @@
*
* To use this driver, first call adc_init(), then call
* required configuration functions, then call adc_start().
*
* This file is the entry point of the ADC management.
* Only this header file provides public functions for the
* ADC. No other header from this folder should be included
* in files outside this folder.
*/
......@@ -93,6 +98,21 @@ int8_t adc_configure_adc_channels(uint8_t adc_number, char* channel_list[], uint
*/
void adc_start();
/**
* This function returns the name of an enabled channel.
*
* This function must onle be called after
* adc_configure_adc_channels has been called.
*
* @param adc_number Number of the ADC
* @param channel_rank Rank of the ADC channel to query.
* Rank ranges from 0 to (number of enabled channels)-1
* @return Name of the channel as defined in the device tree, or
* NULL if channel configuration has not been made or
* channel_rank is over (number of enabled channels)-1.
*/
char* adc_get_channel_name(uint8_t adc_number, uint8_t channel_rank);
#ifdef __cplusplus
}
#endif
......
......@@ -19,6 +19,11 @@
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*
* @brief This file implements the channel management of the ADCs.
*
* THIS FILE SHOULD NOT BE INCLUDED ANYWHERE OUTSIDE FILES
* FROM THE SAME FOLDER.
*/
#ifndef ADC_CHANNELS_H_
......
......@@ -19,6 +19,11 @@
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*
* @brief This file implements the core management of the ADCs.
*
* THIS FILE SHOULD NOT BE INCLUDED ANYWHERE OUTSIDE FILES
* FROM THE SAME FOLDER.
*/
#ifndef ADC_CORE_H_
......
......@@ -19,6 +19,12 @@
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*
* @brief This file provides helper functions for the ADC management.
*
* THIS FILE SHOULD NOT BE INCLUDED ANYWHERE OUTSIDE FILES
* FROM THE SAME FOLDER.
*
*/
#ifndef ADC_HELPER_H_
......
......@@ -29,7 +29,6 @@
// OwnTech Power API
#include "dma/dma.h"
#include "adc/adc.h"
#include "adc/adc_channels.h"
#include "data_dispatch/data_dispatch.h"
#include "data_acquisition.h"
......@@ -86,7 +85,7 @@ int8_t data_acquisition_set_adc12_dual_mode(uint8_t dual_mode)
}
else if (data_acquisition_initialized == 0)
{
return EUNITITIALIZED;
return EUNINITIALIZED;
}
else
{
......@@ -144,7 +143,7 @@ int8_t data_acquisition_configure_adc_channels(uint8_t adc_number, char* channel
}
else if (data_acquisition_initialized == 0)
{
return EUNITITIALIZED;
return EUNINITIALIZED;
}
else
{
......@@ -161,7 +160,7 @@ int8_t data_acquisition_configure_adc_trigger_source(uint8_t adc_number, uint32_
}
else if (data_acquisition_initialized == 0)
{
return EUNITITIALIZED;
return EUNINITIALIZED;
}
else
{
......@@ -175,10 +174,10 @@ int8_t data_acquisition_start()
if ( (data_acquisition_initialized == 1) && (data_acquisition_started == 0) )
{
// DMAs
dma_configure_and_start();
dma_configure_and_start(2);
// Initialize data dispatch
data_dispatch_init();
data_dispatch_init(2);
// Launch ADC conversion
adc_start();
......@@ -189,7 +188,7 @@ int8_t data_acquisition_start()
}
else if (data_acquisition_initialized == 0)
{
return EUNITITIALIZED;
return EUNINITIALIZED;
}
else
{
......@@ -199,7 +198,7 @@ int8_t data_acquisition_start()
char* data_acquisition_get_channel_name(uint8_t adc_number, uint8_t channel_rank)
{
return adc_channels_get_channel_name(adc_number, channel_rank);
return adc_get_channel_name(adc_number, channel_rank);
}
uint16_t* data_acquisition_get_v1_low_values(uint32_t* number_of_values_acquired)
......
......@@ -25,7 +25,6 @@
// Stdlib
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
//Zephyr
#include <zephyr.h>
......@@ -35,124 +34,109 @@
/////
// General data
// Local variables
#define CHANNELS_BUFFERS_SIZE 32
// Number of channels in each ADC (cell i is ADC number i+1)
static uint8_t enabled_channels_count[3];
static uint8_t number_of_adcs = 0;
// Number of channels in each ADC (cell i is ADC number i+1)
static uint8_t* enabled_channels_count = NULL;
/////
// Per-channel buffers
// 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;
// Bidimentionnal arrays: each of these variables is an array
// of per-channel arrays holding the readings
static uint16_t** adc1_channels_buffers = NULL;
static uint16_t** adc2_channels_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;
// Arrays to store the read and write indexes in each channel array
static uint32_t* adc1_next_read_indexes = NULL;
static uint32_t* adc2_next_read_indexes = NULL;
static uint32_t* adc1_next_write_indexes = NULL;
static uint32_t* adc2_next_write_indexes = 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;
static uint16_t** adc1_user_buffers = NULL;
static uint16_t** adc2_user_buffers = NULL;
/////
// Private functions
__STATIC_INLINE void _increment_circular_index(uint32_t* buffer_index, uint32_t buffer_size)
__STATIC_INLINE uint16_t* _data_dispatch_get_buffer(uint8_t adc_index, uint8_t channel_index)
{
(*buffer_index) = ( (*buffer_index) < (buffer_size-1) ) ? ( (*buffer_index) + 1 ) : 0;
uint8_t active_buffer = current_buffer[adc_index][channel_index];
return adc_channel_buffers[adc_index][channel_index][active_buffer];
}
__STATIC_INLINE void _circular_buffer_copy(uint16_t* src_buffer, uint16_t* dest_buffer,
uint32_t src_buffer_next_read_index, uint32_t dest_buffer_next_write_index
)
__STATIC_INLINE uint32_t _data_dispatch_get_count(uint8_t adc_index, uint8_t channel_index)
{
dest_buffer[dest_buffer_next_write_index] = src_buffer[src_buffer_next_read_index];
return buffers_data_count[adc_index][channel_index];
}
static uint32_t _get_values_available_count_in_buffer(uint32_t next_read_index, uint32_t next_write_index, uint32_t buffer_size)
__STATIC_INLINE void _data_dispatch_increment_count(uint8_t adc_index, uint8_t channel_index)
{
if (next_read_index == next_write_index)
return 0;
uint32_t i_next_write_index = (next_write_index < next_read_index) ? (next_write_index + buffer_size) : next_write_index;
return i_next_write_index - next_read_index;
uint32_t* current_count = &buffers_data_count[adc_index][channel_index];
if ( (*current_count) < CHANNELS_BUFFERS_SIZE)
(*current_count)++;
}
static uint16_t _get_next_value_from_buffer(uint16_t* buffer, uint32_t* next_read_index, uint32_t buffer_size)
__STATIC_INLINE void _data_dispatch_swap_buffer(uint8_t adc_index, uint8_t channel_index)
{
uint16_t value = buffer[*next_read_index];
_increment_circular_index(next_read_index, buffer_size);
return value;
}
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()
void data_dispatch_init(uint8_t adc_count)
{
enabled_channels_count[0] = adc_channels_get_enabled_channels_count(1);
if (enabled_channels_count[0] > 0)
{
adc1_channels_buffers = k_malloc(sizeof(uint16_t*) * enabled_channels_count[0]);
adc1_next_read_indexes = k_malloc(sizeof(uint32_t) * enabled_channels_count[0]);
adc1_next_write_indexes = k_malloc(sizeof(uint32_t) * enabled_channels_count[0]);
adc1_user_buffers = k_malloc(sizeof(uint16_t*) * enabled_channels_count[0]);
for (int i = 0 ; i < enabled_channels_count[0] ; i++)
{
adc1_channels_buffers[i] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
adc1_next_read_indexes[i] = 0;
adc1_next_write_indexes[i] = 0;
adc1_user_buffers[i] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
}
}
// Store number on ADCs
number_of_adcs = adc_count;
enabled_channels_count[1] = adc_channels_get_enabled_channels_count(2);
if (enabled_channels_count[1] > 0)
// Prepare arrays for each ADC
enabled_channels_count = k_malloc(number_of_adcs * sizeof(uint8_t));
adc_channel_buffers = k_calloc(number_of_adcs, sizeof(uint16_t***));
buffers_data_count = k_calloc(number_of_adcs, sizeof(uint32_t*));
current_buffer = k_calloc(number_of_adcs, sizeof(uint8_t*));
for (int adc_index = 0 ; adc_index < number_of_adcs ; adc_index++)
{
adc2_channels_buffers = k_malloc(sizeof(uint16_t*) * enabled_channels_count[1]);
adc2_next_read_indexes = k_malloc(sizeof(uint32_t) * enabled_channels_count[1]);
adc2_next_write_indexes = k_malloc(sizeof(uint32_t) * enabled_channels_count[1]);
adc2_user_buffers = k_malloc(sizeof(uint16_t*) * enabled_channels_count[1]);
for (int i = 0 ; i < enabled_channels_count[1] ; i++)
enabled_channels_count[adc_index] = adc_channels_get_enabled_channels_count(adc_index+1);
if (enabled_channels_count[adc_index] > 0)
{
adc2_channels_buffers[i] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_SIZE);
adc2_next_read_indexes[i] = 0;
adc2_next_write_indexes[i] = 0;
adc2_user_buffers[i] = k_malloc(sizeof(uint16_t) * CHANNELS_BUFFERS_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));
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, uint16_t* dma_buffer)
{
if (adc_num == 1)
uint8_t adc_index = adc_num-1;
for (int channel_index = 0 ; channel_index < enabled_channels_count[adc_index] ; channel_index++)
{
for (int current_channel = 0 ; current_channel < enabled_channels_count[0] ; current_channel++)
{
_circular_buffer_copy(dma_buffer, adc1_channels_buffers[current_channel],
current_channel, adc1_next_write_indexes[current_channel]
);
// 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);
_increment_circular_index(&adc1_next_write_indexes[current_channel], CHANNELS_BUFFERS_SIZE);
}
}
else if (adc_num == 2)
{
for (int current_channel = 0 ; current_channel < enabled_channels_count[1] ; current_channel++)
{
_circular_buffer_copy(dma_buffer, adc2_channels_buffers[current_channel],
current_channel, adc2_next_write_indexes[current_channel]
);
// Copy data
active_buffer[current_count] = dma_buffer[channel_index];
_increment_circular_index(&adc2_next_write_indexes[current_channel], CHANNELS_BUFFERS_SIZE);
}
// Increment count
_data_dispatch_increment_count(adc_index, channel_index);
}
}
......@@ -162,43 +146,19 @@ void data_dispatch_do_dispatch(uint8_t adc_num, uint16_t* dma_buffer)
uint16_t* data_dispatch_get_acquired_values(uint8_t adc_number, uint8_t channel_rank, uint32_t* number_of_values_acquired)
{
if (adc_number == 1)
{
// Get number of available values
uint32_t number_of_values = _get_values_available_count_in_buffer(adc1_next_read_indexes[channel_rank],
adc1_next_write_indexes[channel_rank],
CHANNELS_BUFFERS_SIZE
);
for (uint32_t i = 0 ; i < number_of_values ; i++)
{
adc1_user_buffers[channel_rank][i] = _get_next_value_from_buffer(adc1_channels_buffers[channel_rank],
&adc1_next_read_indexes[channel_rank],
CHANNELS_BUFFERS_SIZE
);
}
*number_of_values_acquired = number_of_values;
return adc1_user_buffers[channel_rank];
}
else if (adc_number == 2)
uint8_t adc_index = adc_number-1;
if (adc_index < number_of_adcs)
{
// Get number of available values
uint32_t number_of_values = _get_values_available_count_in_buffer(adc2_next_read_indexes[channel_rank],
adc2_next_write_indexes[channel_rank],
CHANNELS_BUFFERS_SIZE
);
// 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);
for (uint32_t i = 0 ; i < number_of_values ; i++)
{
adc2_user_buffers[channel_rank][i] = _get_next_value_from_buffer(adc2_channels_buffers[channel_rank],
&adc2_next_read_indexes[channel_rank],
CHANNELS_BUFFERS_SIZE
);
}
// Swap buffers
_data_dispatch_swap_buffer(adc_index, channel_rank);
*number_of_values_acquired = number_of_values;
return adc2_user_buffers[channel_rank];
// Return data
*number_of_values_acquired = current_count;
return active_buffer;
}
else
{
......
......@@ -20,8 +20,13 @@
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*
* @brief This is a quick and dirty dispatch of ADC
* data in per-DMA channel independent buffers.
* @brief Data dispatch is intended at dispatching ADCs
* acquired data from DMA buffers to per-channel buffers.
* User can then request the data of a specific channel.
*
* It uses double-buffering, holding 2 buffers for each
* enabled channel of each ADC, one being filled and one
* made available to the user.
*/
#ifndef DATA_DISPATCH_H_
......@@ -36,15 +41,46 @@ extern "C" {
#endif
// Init function to be called first
void data_dispatch_init();
/**
* Init function to be called first.
*
* @param adc_count Number of enabled ADCs
*/
void data_dispatch_init(uint8_t adc_count);
// Dispatch function: gets the readings and store them in per-channel arrays
/**
* Dispatch function: gets the readings and store
* them in per-channel arrays. This functon is
* called by DMA callback when the DMA has filled
* one of its buffers.
*
* @param adc_num Number of the ADC from which data
* comes from.
* @param dma_buffer Buffer in which data is stored.
*/
void data_dispatch_do_dispatch(uint8_t adc_num, uint16_t* dma_buffer);
// Obtain buffer for a specific channel
/**
* Obtain data for a specific channel.
* The data is provided as an array of values
* and the count of data in this buffer is returned
* as an output parameter.
*
* @param adc_number Number of the ADC from which to
* obtain data.
* @param channel_rank Rank of the channel from which
* to obtain data.
* @param number_of_values_acquired Output parameter:
* address to a variable that will be updated
* by the function with the data count.
* @return Buffer containing the available data.
* Note that the returned buffer is invalidated
* 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);
#ifdef __cplusplus
}
#endif
......
......@@ -48,17 +48,38 @@
/////
// DT definitions
#define DMA1_NODELABEL DT_NODELABEL(dma1)
#define DMA1_LABEL DT_PROP(DMA1_NODELABEL, label)
const struct device* _dma1;
static const struct device* dma1;
/////
// LL definitions
static uint32_t source_registers[3] =
{
(uint32_t)(&(ADC1->DR)),
(uint32_t)(&(ADC2->DR)),
(uint32_t)(&(ADC3->DR))
};
static uint32_t source_triggers[3] =
{
LL_DMAMUX_REQ_ADC1,
LL_DMAMUX_REQ_ADC2,
LL_DMAMUX_REQ_ADC3
};
/////
// Arrays of buffers:
// half_buffer_*[i][] is an array whose size matches
// the number of enabled channels in ADC(i+1).
static uint16_t* half_buffer_1[3];
static uint16_t* half_buffer_2[3];
static uint16_t** half_buffer_1;
static uint16_t** half_buffer_2;
/////
......@@ -72,15 +93,16 @@ static uint16_t* half_buffer_2[3];
static void _dma_callback(const struct device* dev, void* user_data, uint32_t channel, int status)
{
static uint8_t current_half_buffer[3] = {0};
uint8_t adc_number = channel + 1;
if (current_half_buffer[channel] == 0)
{
data_dispatch_do_dispatch(channel+1, half_buffer_1[channel]);
data_dispatch_do_dispatch(adc_number, half_buffer_1[channel]);
current_half_buffer[channel] = 1;
}
else
{
data_dispatch_do_dispatch(channel+1, half_buffer_2[channel]);
data_dispatch_do_dispatch(adc_number, half_buffer_2[channel]);
current_half_buffer[channel] = 0;
}
}
......@@ -121,40 +143,31 @@ static void _dma_channel_init(uint8_t adc_num, uint32_t source_address, uint32_t
dma_config_s.head_block = &dma_block_config_s; // Above block config
dma_config_s.dma_callback = _dma_callback; // Callback
dma_config(_dma1, adc_num, &dma_config_s);
dma_config(dma1, adc_num, &dma_config_s);
}
/////
// Public API
void dma_configure_and_start()
void dma_configure_and_start(uint8_t adc_count)
{
_dma1 = device_get_binding(DMA1_LABEL);
// ADC 1
if (adc_channels_get_enabled_channels_count(1) > 0)
{
_dma_channel_init(1, (uint32_t)(&(ADC1->DR)), LL_DMAMUX_REQ_ADC1);
dma_start(_dma1, 1);
}
dma1 = device_get_binding(DMA1_LABEL);
// ADC 2
if (adc_channels_get_enabled_channels_count(2) > 0)
{
_dma_channel_init(2, (uint32_t)(&(ADC2->DR)), LL_DMAMUX_REQ_ADC2);
dma_start(_dma1, 2);
}
half_buffer_1 = k_malloc(adc_count * sizeof(uint16_t*));
half_buffer_2 = k_malloc(adc_count * sizeof(uint16_t*));
// ADC 3
if (adc_channels_get_enabled_channels_count(3) > 0)
for (uint8_t adc_index = 0 ; adc_index < adc_count ; adc_index++)
{
_dma_channel_init(3, (uint32_t)(&(ADC3->DR)), LL_DMAMUX_REQ_ADC3);