cancel
Showing results for 
Search instead for 
Did you mean: 

Problem with reading ADC channels group using DMA

pawwodko
Associate II
Posted on September 30, 2013 at 11:35

I am using STM32F407VG.

I've read documentation about ADC and groups and still don't know how to set up properly ADC to sample periodically fixed ADC channels. I've done this by following code but don't know if this is right. I want to use ADC2, sample group of channels 10, 11, 12, 13 and 0 in this order. Samples tranfered using DMA. Currently using TIM2 and update event to trigger sample conversion. DMA config:

static
const
DMA_InitTypeDef analog_dma_stream_conf =
{
.DMA_Channel = DMA_Channel_1,
.DMA_PeripheralBaseAddr = (uint32_t)&ANALOG_ADC->DR,
.DMA_Memory0BaseAddr = (uint32_t)analog_dma_buffers[0],
.DMA_BufferSize = 
sizeof
(analog_dma_buffers[0]),
.DMA_DIR = DMA_DIR_PeripheralToMemory,
.DMA_MemoryInc = DMA_MemoryInc_Enable,
.DMA_PeripheralInc = DMA_PeripheralInc_Disable,
.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord,
.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord,
.DMA_Priority = DMA_Priority_High,
.DMA_Mode = DMA_Mode_Normal,
.DMA_FIFOMode = DMA_FIFOMode_Disable,
.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull,
.DMA_MemoryBurst = DMA_MemoryBurst_Single,
.DMA_PeripheralBurst = DMA_PeripheralBurst_Single,
};
DMA_Init(DMA2_Stream3, &analog_dma_stream_conf);
DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream3, ENABLE);
void
ANALOG_DMA_Stream_IRQHandler( 
void
)
{
DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIFx);
struct
analog_sample_vector *next_buf = NULL, *cur_mem;
signed
portBASE_TYPE res = xQueueReceiveFromISR(analog_dma_free_queue, &next_buf, NULL);
if
( res == pdTRUE )
{
cur_mem = (
struct
analog_sample_vector *)ANALOG_DMA_Stream->M0AR;
DMA2_Stream3->M0AR = (uint32_t)next_buf;
DMA2_Stream3->NDTR = ANALOG_BUFFER_SIZE * 
sizeof
(
struct
analog_sample_vector);
DMA2_Stream3->CR |= DMA_SxCR_EN;
signed
portBASE_TYPE woken;
res = xQueueSendToBackFromISR(analog_dma_ready_queue, &cur_mem, &woken);
assert
( res == pdTRUE );
analog_thread_ready_waiting |= woken;
portYIELD_FROM_ISR( analog_thread_ready_waiting && xQueueIsQueueEmptyFromISR(analog_dma_free_queue) );
}
else
if
( analog_thread_ready_waiting )
portYIELD();
}
static
const
ADC_InitTypeDef adc2_conf =
{
.ADC_Resolution = ADC_Resolution_12b,
.ADC_ScanConvMode = DISABLE,
.ADC_ContinuousConvMode = DISABLE,
.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising,
.ADC_ExternalTrigConv = ANALOG_ADC_ExternalTrigConv,
.ADC_DataAlign = ADC_DataAlign_Right,
.ADC_NbrOfConversion = 5,
};
RCC_APB2PeriphClockCmd(RCC_APB2ENR_ANALOG_ADCEN, ENABLE);
ADC_Init(ANALOG_ADC, &adc2_conf);
ADC_RegularChannelConfig(ADC2, ADC_Channel_10, 1, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 3, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_13, 4, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_480Cycles);
ADC_DMACmd(ADC2, ENABLE);

Then the trigger config

TIM_TimeBaseInitTypeDef timer_conf;
timer_conf.TIM_Prescaler = presc ? presc - 1 : 0;
timer_conf.TIM_CounterMode = TIM_CounterMode_Up;
timer_conf.TIM_Period = period - 1;
timer_conf.TIM_ClockDivision = TIM_CKD_DIV1;
timer_conf.TIM_RepetitionCounter = 0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInit(TIM2, &timer_conf);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);

The problem is I am getting only one DMA interrupt. It seams that ADC stops requesting DMA transfers, but looked at register it seems all is set ok. Does anyone have any hints or code snippet that uses ADC channels group periodically sampled and and is using DMA. Maby better datasheet? #tim #group #dma #adc
5 REPLIES 5
Posted on September 30, 2013 at 15:26

Surely you'd need to be doing a scan conversion with multiple channels specified?

The DMA length seems too short, at least in the initialization. Salient code/definitions absent.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
pawwodko
Associate II
Posted on October 01, 2013 at 09:07

I don't know if scan mode conversion is what I need because I am confused with ADC description found in pdf. I am moving my project from XMega platform and there I used sweep mode to sample all specified channels on every trigger. I need is to scan 5 channels at fixed frequency and put those samples into buffer (using DMA). If this mode can't be set I can trigger every conversion by timer at higher frequency, but I need to understand how ADC works and datasheet is unclear in some places.

Can you clarify some thing?

Does timer configuration I've presented is ok?

Target is to generate update event on timer 2 at some frequency.

Does ADC configuration is ok?

Target is to sample channels 10, 11, 12, 13 and 0 at every trigger - update event from timer 2 and transfer every sample using DMA.

Does setting ADC_SQR1 bits L[3:0] to 5 means that only channels defined in ADC_SQR3 bits SQ1, SQ2, SQ3, SQ4 and SQ5 will be converted. On next trigger, does the conversion sequence restarts from begining or it continues for next 5 channels defined in SQ6 ... SQ10?

Does DMA settings are correct?

assume sizeof(analog_dma_buffers[0]) if 500 (checked in debuger) and ANALOG_ADC is ADC2
Posted on October 01, 2013 at 10:52

Scan will enumerate through the list you specify at each trigger. The samples will not all occur at the trigger time, to do that you'd need to use multiple ADC.

Your trigger rate will be limited by the time it takes to do the 5 samples here, and the DMA needs to be a multiple of 5. Not a sizeof(), rather # elements. A buffer twice the normal size will allow for ping-pong operation using HT and TC interrupts. If you are concerned about the mechanics/speed, add a GPIO toggle in the DMA TC interrupt and confirm the sample rate for your array of samples. ie a 500 16-bit word array, sampling 5 channels at 200 Hz will generate a GPIO signal of 1 Hz (TC at 2 Hz) What you don't want is continuous mode, as that is free running.

.ADC_ScanConvMode = ENABLE,

#define ELEMENTS (5 * 100) // 5 Channels, 100 Samples uint16_t Samples[ELEMENTS];

.DMA_Memory0BaseAddr = (uint32_t)&Samples[0],
.DMA_BufferSize = 
ELEMENTS
,

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
pawwodko
Associate II
Posted on October 01, 2013 at 11:22

clive1 - i have no doubts that solution you are posting is good, but speed is not an issue right now. Problem is in that this does not work at all. DMA is not transfering data after first TC interrupt (where i reinitialize and enable it to the next buffer).

ADC is working - I have checked this using debbuger by stopping program, enabling TCIE and placed breakopoint at TC handler (do not worry now about sampling frequency). For clarification

struct analog_sample_vector
{
uint16_t ekg;
uint16_t dz;
uint16_t dz_dt;
uint16_t z;
uint16_t br;
};
...
#define ANALOG_BUFFER_SIZE 50 // 50 próbek w buforze
...
static struct analog_sample_vector analog_dma_buffers[4][ ANALOG_BUFFER_SIZE ] __attribute(( section(''.dma_static'') ));

So you can see that sizeof(analog_dma_buffers[0]) == 500 (5 * 2 * 50 ) and

analog_dma_buffers[0]

points to first buffer :p
Posted on October 01, 2013 at 11:41

Your code is complex and incomplete, I can't help you with that. I have posted simpler complete examples for ADC/TIM/DMA.

If the capture is continuous, consider circular DMA. If the subsequent DMA operations aren't functioning you'll need to do more work to reinitialize/restart the DMA controller. I can't tell you what the minimal changes are, I haven't pondered on it, take the library initialization and work backward.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..