Freertos and DMA /ADC conflict

Hi,
I have Freertos running on a STM32F411 with two tasks.
One handles websockets via spi to a W5500 ethernet chip and the other task does ADC measurements.
It all works nicely but for academic / learning purposes I’d like to speed up the ADC using DMA.
I have the ADC / DMA working nicely by its self but when I integrate the ADC / DMA solution into the task the ADC readings go awry.
I suspect there is a conflict between the RTOS and DMA - maybe because the ADC /DMA is running as fast as it can (rather than on a timer) and is flooding a bus or something ?
Any ideas on how to improve this ?
I’m thinking it needs a timer to trigger the ADC / DMA a little slower or some other mechanism ?
Or maybe it’s a priority problem between the RTOS and DMA.
I’ve configured the solution using bare metal register manipulation in an attempt to have a better untderstanding of what’s going on.
Regards Tim

Cortex-M devices, like the STM32F, have a FreeRTOSConfig.h setting named configMAX_SYSCALL_INTERRUPT_PRIORITY. Setting the DMA priority higher than that setting (which on an ARM MCU means giving it a numerically lower value) will prevent the kernel ever disabling the DMA interrupt. Do you have the DMA priority above configMAX_SYSCALL_INTERRUPT_PRIORITY?

FreeRTOS - The Free RTOS configuration constants and configuration options - FREE Open Source RTOS for small real time embedded systems (note the example here has higher numerical values meaning higher logical priority - which is not the case on Cortex-M).
RTOS for ARM Cortex-M

Hi Richard,
thanks for taking the time to reply - much appreciated.
Currently I’ve made no changes to any interupt priority settings, indeed I’ll have to figure out how to change the settings and to what value.
I also suspect I may have to reduce the number of DMA transfers to avoid resource hogging.
I’ll focus on this approach for now and see where it leads.
FYI the system still runs ok and updates one ADC DMA channel but the other ADC DMA value tracks the working one rather than reporting its own value - very odd ! Regards Tim

A likely problem is that both the ethernet chip and the ADC are competing for memory bandwidth, and their just isn’t enough, so you get lost readings.

It isn’t a conflict with the “RTOS”, but just that you are overloading the processor with DMA being used for both the ethernet and the ADC.

Depending on how the ethernet works, it might be able to buffer the packet to internal memory and then transfer at a lower DMA priority than the ADC.

How fast is that “As fast as it can” compared to the speed of the memory?
Have you looked at the memory access patterns needed by the ethernet?

Hi Richard,
thanks again, you’re thinking is in line with mine.
However just to clarify, without the DMA in operation but still using FreeRTOS both the ADC reads and ethernet work well and fast with plenty of spare CPU cycles (measured by how long it stays in the forever loop outside of the tasks).
It’s only when I add DMA into the mix that it goes awry.
The DMA code is started during setup before any RTOS stuff gets started - I’ll try moving the DMA set up code inside the task but before the for ;; loop and report pack.
I’ll post my DMA code but essentially it runs continually with speed set by ADC clock and sampling time. It has no IRQ’s associated with it as far as I can tell and is thus essentially purely hardware ? It’s doing what it should and off loading ADC reads from the CPU such that the CPU has to simply read the latest value from an array with no over head like irq handlers etc.
I’ll take another look with a fresh mind and see how I get on.

Regards Tim

Hi Richard - Success.

Again desribing my problem on here and talking with experts helps to clear my thoughts leading to success.
All I had to do was move the DMA set up to within the task as per below

void TaskTunerControl( void *pvParameters __attribute__((unused)) ) // This is a Task. { start_dma_mode () ; for (;;) { etc etc

Here is how I set up the ADC DMA.

void start_dma_mode ()
{

//Enabling peripheral clocks
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
 RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;  // Enable DMA2 clock  
                                                                                
//Pin A0-A1  as analog inputs
GPIOA->MODER |= GPIO_MODER_MODE0|GPIO_MODER_MODE1; 
 
// Configuring sequence for analog reading
ADC1->SQR1 |= 1<<20;                    // Configuring sequence length for 2 analog channels 
ADC1->SQR3 |= 0<<0;                     // A0 first in the sequence
ADC1->SQR3 |= 1<<5;                     // A1 second in the sequence

// set sampling times
MODIFY_REG(ADC1->SMPR2, ADC_SMPR2_SMP0, 0x03 << ADC_SMPR2_SMP0_Pos); // PA0 ch0 RF FWD analog input 
MODIFY_REG(ADC1->SMPR2, ADC_SMPR2_SMP1, 0x03 << ADC_SMPR2_SMP1_Pos); // PA1 ch1 RF RFL analog input

// Setting ADC1 
ADC1->CR1 |= ADC_CR1_SCAN;          // Enabling scanning mode 
ADC1->CR2 |= ADC_CR2_DMA;           // Enabling DMA mode

//Setting DMA channel
DMA2_Stream0->PAR  = (uint32_t)(&(ADC1->DR));           // DMA source address
DMA2_Stream0->M0AR = (uint32_t)(&(DMA_Data[0]));      // DMA destination address

DMA2_Stream0->NDTR = 2;                                     // # of transmissions
DMA2_Stream0->CR |= DMA_SxCR_CIRC                           // Enabling circular mode
                    | DMA_SxCR_MINC                         // Enabling memory (destiny address) increase after each transmission 
                    | DMA_SxCR_PSIZE_0                      // Peripheral (source) size set to 16 bits
                    | DMA_SxCR_MSIZE_0;                     // Memory (destiny) size set to 16 bits

DMA2_Stream0->CR |= DMA_SxCR_EN;                            // DMA channel enable   

//ADC1 A0 config 
ADC1->CR2 |= ADC_CR2_DDS ;
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->CR2 |= ADC_CR2_ADON;
ADC1->CR2 |= ADC_CR2_SWSTART;


}

Using ADC DMA has reduced the measurement time from 3.4mS (32 readings averaged) to 4.5uS (32 reading averaged). A huge increased in speed !

Thanks again and best regards Tim