cancel
Showing results for 
Search instead for 
Did you mean: 

Fails in triggering ADC with TIM + DMA transfer (HAL lib)

ten
Associate II
Posted on March 15, 2016 at 11:41

Hi everyone,

I am currently working with a nucleo board f401re (featuring an stm32f401re micro). From a conceptual point of view, my challenge is simple: I need to output a CLK signal as fast as possible (500kHz would be great), and on a every falling edge of CLK, I need to trigger the ADC on 1 Analog Input pin. I need to do this for 40*42 = 1680 consecutive times (i.e. that corresponds to the number of periods of the CLK signal, or equivalently, to the number of samplings done by the adc) So far, I managed to output CLK at 125kHz via an interrupt on timer TIM2. At the same time, I managed to have the ADC working with DMA, and able to reach 2.6MS/s. The problem is that the conversions are done ''continuously'', so as fast as possible according to the configuration, and thus not synchronized with the falling edges of CLK. This configuration is represented by the code below. So, my wish is now to synchronize the ADC sampling on the CLK falling edges. In order to do that, I was thinking of 2 solutions:
  1. Have the ADC triggered by TIM2, but still readout via DMA (I need it for efficiency). I already attempted to do so, but as a matter of fact, the ADC did not sample anything. This attempt corresponds to the code below, with all comments applied in function ConfigureADC().
  2. Output the CLK using PWM, connect this output pin to an input pin, and have the ADC triggered on the falling edges of this pin.
My questions are: - Is this challenge achievable ? - What is wrong with solution 1. (i.e with the modif. in comment applied) - Which solution would be preferable, 1. or 2. ? P.s: I already went through a lot of topics on this forum, unfortunately I cannot use the libraries that most of people use on the discovery boards, so I am ''stuck'' with the HAL drivers. I use the mbed online compiler. Thanks a lot for your time ! BR, Thibaud


#include ''mbed.h''

#include ''mbed_rpc.h''

#include <numeric>


#define SIZE_BUFF 40*42 // ADC nof samples

#define NOF_CLOCK_CYCLES 2*SIZE_BUFF


AnalogIn analog_pin4(PC_1);

DigitalOut CLK(PA_5);


uint32_t sample_buffer[SIZE_BUFF]; 
// adc sampling buffer

int
clock_switch_counter = 0; 
// counter on the nof clk switches


void
RCC_Configuration(
void
){

__HAL_RCC_DMA2_CLK_ENABLE();

__HAL_RCC_GPIOC_CLK_ENABLE();

__HAL_RCC_ADC1_CLK_ENABLE();

__HAL_RCC_TIM2_CLK_ENABLE();

}


ADC_HandleTypeDef g_AdcHandle;

void
ConfigureADC(){

GPIO_InitTypeDef gpioInit;


gpioInit.Pin = PC_1;

gpioInit.Mode = GPIO_MODE_ANALOG;

gpioInit.Pull = GPIO_NOPULL;

HAL_GPIO_Init(GPIOC, &gpioInit);


HAL_NVIC_SetPriority(ADC_IRQn, 0, 1);

HAL_NVIC_EnableIRQ(ADC_IRQn);


ADC_ChannelConfTypeDef adcChannel;


g_AdcHandle.Instance = ADC1;


g_AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2;

g_AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;

g_AdcHandle.Init.ScanConvMode = DISABLE;

g_AdcHandle.Init.ContinuousConvMode = ENABLE; 
// DISABLE

g_AdcHandle.Init.DiscontinuousConvMode = DISABLE;

g_AdcHandle.Init.NbrOfDiscConversion = 0;

g_AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_FALLING;

g_AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
//ADC_EXTERNALTRIGCONV_T2_TRGO;

g_AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;

g_AdcHandle.Init.NbrOfConversion = 1;

g_AdcHandle.Init.DMAContinuousRequests = ENABLE;
//DISABLE;

g_AdcHandle.Init.EOCSelection = DISABLE;


HAL_ADC_Init(&g_AdcHandle);


adcChannel.Channel = ADC_CHANNEL_11;

adcChannel.Rank = 1;

adcChannel.SamplingTime = ADC_SAMPLETIME_3CYCLES;

adcChannel.Offset = 0;


if
(HAL_ADC_ConfigChannel(&g_AdcHandle, &adcChannel) != HAL_OK)

{

asm(
''bkpt 255''
);

}

}


DMA_HandleTypeDef g_DmaHandle;

void
ConfigureDMA(){

__DMA2_CLK_ENABLE(); 


g_DmaHandle.Instance = DMA2_Stream4;

g_DmaHandle.Init.Channel = DMA_CHANNEL_0;

g_DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;

g_DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;

g_DmaHandle.Init.MemInc = DMA_MINC_ENABLE;

g_DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;

g_DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;

g_DmaHandle.Init.Mode = DMA_NORMAL;

g_DmaHandle.Init.Priority = DMA_PRIORITY_HIGH;

g_DmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 

g_DmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;

g_DmaHandle.Init.MemBurst = DMA_MBURST_SINGLE;

g_DmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE;


HAL_DMA_Init(&g_DmaHandle);


__HAL_LINKDMA(&g_AdcHandle, DMA_Handle, g_DmaHandle);


HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 2); 

HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);

}



TIM_HandleTypeDef htim2;

void
TIM2_Configuration(
void
){

TIM_Base_InitTypeDef TIM_TimeBaseStructure;


// Time base configuration 

TIM_TimeBaseStructure.Prescaler = 0;

TIM_TimeBaseStructure.Period = (84000000 / 42000000) - 1;

TIM_TimeBaseStructure.ClockDivision = TIM_CLOCKDIVISION_DIV1;

TIM_TimeBaseStructure.CounterMode = TIM_COUNTERMODE_UP;


htim2.Instance = TIM2;

htim2.Init = TIM_TimeBaseStructure;

HAL_TIM_Base_MspInit(&htim2);


__TIM2_CLK_ENABLE();

HAL_TIM_Base_Init(&htim2);


HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);

HAL_TIM_Base_Start_IT(&htim2);

}


extern
''C''

{

void
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){

if
(clock_switch_counter++ < NOF_CLOCK_CYCLES){

if
(CLK == 0)

CLK = 1;

else

CLK = 0;

}

else
{

HAL_NVIC_DisableIRQ(TIM2_IRQn);

}

}


void
TIM2_IRQHandler(
void
){

HAL_TIM_IRQHandler(&htim2); 

}


void
HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle){

HAL_ADC_Stop_DMA(&g_AdcHandle);

}


void
DMA2_Stream4_IRQHandler(){

HAL_DMA_IRQHandler(&g_DmaHandle);

} 


void
ADC_IRQHandler(){

HAL_ADC_IRQHandler(&g_AdcHandle);

}

}



int
main(
void
)

{ 

RCC_Configuration(); 

ConfigureADC();

ConfigureDMA();

TIM2_Configuration();


// initialize output clock

CLK = 0;


HAL_ADC_Start_DMA(&g_AdcHandle, sample_buffer, SIZE_BUFF); 
// start ADC configured on DMA

HAL_NVIC_EnableIRQ(TIM2_IRQn); 
// start Timer


while
(1) {}

}

#adc-dma-tim-hal
3 REPLIES 3
TDK
Guru
Posted on March 15, 2016 at 16:05

No need to use interrupts here.  Have the ADC be triggered off of the TIM directly, by using the TRGO signal.

Set the TIM period to whatever you want.

Set the ADC to be triggered off of that, using DMA.

If you feel a post has answered your question, please click "Accept as Solution".
ten
Associate II
Posted on March 16, 2016 at 14:38

Hi,

Thanks for your answer.

However, although I think I understand what you mean from a conceptual point of view, I have a hard time translating that into code.

May I kindly ask for your help about translating what you said into code instructions ? Thanks!

ten
Associate II
Posted on March 21, 2016 at 10:30

Does anyone have a suggestion ?

It would be very much appreciated.