cancel
Showing results for 
Search instead for 
Did you mean: 

ADC sample rate

Ngai yuk kong
Associate II
Posted on May 04, 2018 at 07:47

Hi all,

I have seven adc channels to convert using STM32F051. I set the ADC to be triggered by timer 15. I also setup DMA to put all the converted results to a memory buffer and have a Transfer complete interrupt set. 

Here is the setting: 

Timer 15 using 100KHz

ADC set to complete whole sequence 

DMA in circular mode

Everything work except that when I toggle a I/O inside the DMA interrupt, I got the frequency around 5KHz.

Any direct correlation between this 5KHz with my timer 100KHz? The MCU specification has a diagram showing that during the conversion the timer trigger will be ignored until all the conversion is complete. Does it mean that there is no way to control the sampling rate????

Many thanks..

20 REPLIES 20
Posted on May 04, 2018 at 10:03

still 1uS per channel at the fastest and stable, you can play around after you have ot running.

There is a ADC cycles selection, 1.5 to 239.5 cycles,

you can select the 2nd or 3rd fastest, you have to test the stability.

so at 13.5 Cycles, its 1uS, 7uS for all 7 channels,

just run it flat out, get rid of the timer if you can. make the dma run continuously.

you should check the cube, did you check any examples ?

I haven't used an 'F051, so you should check the reference manual.

Posted on May 04, 2018 at 11:44

I understand that the DMA is not able to catch up the very high timer speed ...

DMA is not your problem I think.Rather your method.

When triggering the ADC, it triggers the configured sequence. And I see two problems here. Seven channels in less than 10us (1/100kHz) is challenging, at least accuracy-wise.

But more important, the samples are not equidistant. The sequence start depends on the timer setting, but the delay between channels on your ADC sample time configuration.

Perhaps you can get away with one channel and continuous mode, instead of time-triggered sequences.

What frequency does the input waveform have, 50/60Hz mains ?

Posted on May 04, 2018 at 11:57

 ,

 ,

Yes, below is my code:

//20us

 ,

♯ define TIMER15_PERIOD_TICKS 20

 ,

static hwtmr_handle_t timer15,

 ,

♯ define HWTMR15_CK_CNT 1000000.0 /* 1us tick */

 ,

♯ define HWTMR15_CLK \

 ,

(cpu_GetSpeed() / CONFIG_CPU_PCLK1_PRESCALER * \

 ,

((CONFIG_CPU_PCLK1_PRESCALER == 1) ? 1 : 2))

♯ define HWTMR15_PRESCALER (unsigned) (HWTMR15_CLK / HWTMR15_CK_CNT - 1)

void adcApp_Init(void)

 ,

{

 ,

 ,

DMA_Channel_TypeDef *dma = DMA1_Channel1,

timer15.device = TIM15,

 ,

timer15.config.clock_prescaler = HWTMR15_PRESCALER,

 ,

hwtmr_Init(&,timer15),

TIM15->,CR2 |= TIM_CR2_MMS_1,

 ,

hwtmr_StartRecurring(&,timer15, TIMER15_PERIOD_TICKS),

ADC1->,CFGR1 =

 ,

_VAL2FLD(ADC_CFGR1_DISCEN, 0) | // whole sequence at once

 ,

_VAL2FLD(ADC_CFGR1_AUTOFF, 0) |

 ,

_VAL2FLD(ADC_CFGR1_WAIT, 0) |

 ,

_VAL2FLD(ADC_CFGR1_CONT, 0) |

 ,

_VAL2FLD(ADC_CFGR1_OVRMOD, 0) |

 ,

_VAL2FLD(ADC_CFGR1_EXTEN, 1) |

 ,

_VAL2FLD(ADC_CFGR1_EXTSEL, 4) |

 ,

_VAL2FLD(ADC_CFGR1_ALIGN, 0) | // data right aligned

 ,

_VAL2FLD(ADC_CFGR1_RES, 0) | // 12 bit resolution

 ,

_VAL2FLD(ADC_CFGR1_SCANDIR, 0) | // upward scan

 ,

_VAL2FLD(ADC_CFGR1_DMACFG, 1) | // DMA circular mode

 ,

_VAL2FLD(ADC_CFGR1_DMAEN, 1), // DMA enabled

ADC1->,CFGR2 = _VAL2FLD(ADC_CFGR2_CKMODE, 1),

 ,

// sampling time set to maximum:

 ,

ADC1->,SMPR = _VAL2FLD(ADC_SMPR_SMP, 7),

 ,

 ,

ADC1->,CHSELR =

 ,

ADC_CHSELR_CHSEL0 //PA0

 ,

| ADC_CHSELR_CHSEL2 //PA2

 ,

| ADC_CHSELR_CHSEL3 //PA3

 ,

| ADC_CHSELR_CHSEL6 //PA6

 ,

| ADC_CHSELR_CHSEL7 //PA7

 ,

| ADC_CHSELR_CHSEL8 //PB0

 ,

| ADC_CHSELR_CHSEL9, //PB1

 ,

// Set up DMA transfer from ADC to program data memory.

 ,

dma->,CCR =

 ,

_VAL2FLD(DMA_CCR_MEM2MEM, 0) |

 ,

_VAL2FLD(DMA_CCR_PL, 0) | // low priority

 ,

_VAL2FLD(DMA_CCR_MSIZE, 2) | // 32 bit memory size

 ,

_VAL2FLD(DMA_CCR_PSIZE, 2) | // 32 bit peripheral size

 ,

_VAL2FLD(DMA_CCR_MINC, 1) | // memory increment enabled

 ,

_VAL2FLD(DMA_CCR_PINC, 0) | // peripheral increment disabled

 ,

_VAL2FLD(DMA_CCR_CIRC, 1) | // circular mode

 ,

_VAL2FLD(DMA_CCR_DIR, 0) | // from peripheral to memory

 ,

_VAL2FLD(DMA_CCR_TCIE, 1),

 ,

 ,

 ,

dma->,CNDTR = NO_OF_ADC,

 ,

dma->,CPAR = (uintptr_t) &,ADC1->,DR,

 ,

dma->,CMAR = (uintptr_t) adc_raw,

 ,

 ,

cfp_SetBit(&,(DMA1_Channel1)->,CCR, DMA_CCR_EN),

 ,

cpu_EnableInterrupt(DMA1_Channel1_IRQn),

 ,

enableAdc(),

 ,

startAdcConversion(),

}

void DMA1_Channel1_IRQHandler(void)

 ,

{

 ,

if(DMA1->,ISR &, DMA_ISR_TCIF1)

 ,

{

 ,

DMA1->,IFCR = DMA_IFCR_CTCIF1 | DMA_IFCR_CHTIF1 | DMA_IFCR_CGIF1,

 ,

digio_ToggleOutputValue(GPIOB, 14),

 ,

}

 ,

}
Posted on May 04, 2018 at 12:08

the Cube does this for me...

/* ADC init function */
void MX_ADC_Init(void)
{
 ADC_ChannelConfTypeDef sConfig;
 /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
 */
 hadc.Instance = ADC1;
 hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
 hadc.Init.Resolution = ADC_RESOLUTION_12B;
 hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
 hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
 hadc.Init.LowPowerAutoWait = DISABLE;
 hadc.Init.LowPowerAutoPowerOff = DISABLE;
 hadc.Init.ContinuousConvMode = DISABLE;
 hadc.Init.DiscontinuousConvMode = DISABLE;
 hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T15_TRGO;
 hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
 hadc.Init.DMAContinuousRequests = ENABLE;
 hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
 if (HAL_ADC_Init(&hadc) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_0;
 sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
 sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_5;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_6;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_7;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_8;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_9;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_13;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_14;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_15;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_VREFINT;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 /**Configure for the selected ADC regular channel to be converted. 
 */
 sConfig.Channel = ADC_CHANNEL_VBAT;
 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 if(adcHandle->Instance==ADC1)
 {
 /* USER CODE BEGIN ADC1_MspInit 0 */
 /* USER CODE END ADC1_MspInit 0 */
 /* ADC1 clock enable */
 __HAL_RCC_ADC1_CLK_ENABLE();
 
 /**ADC GPIO Configuration 
 PC3 ------> ADC_IN13
 PA0 ------> ADC_IN0
 PA5 ------> ADC_IN5
 PA6 ------> ADC_IN6
 PA7 ------> ADC_IN7
 PC4 ------> ADC_IN14
 PC5 ------> ADC_IN15
 PB0 ------> ADC_IN8
 PB1 ------> ADC_IN9 
 */
 GPIO_InitStruct.Pin = CurrentSense_Pin|RS6_Pin|RS7_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 GPIO_InitStruct.Pin = RS8_Pin|RS5_Pin|RS1_Pin|RS2_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 GPIO_InitStruct.Pin = RS3_Pin|RS4_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 /* ADC1 DMA Init */
 /* ADC Init */
 hdma_adc.Instance = DMA1_Channel1;
 hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
 hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
 hdma_adc.Init.Mode = DMA_CIRCULAR;
 hdma_adc.Init.Priority = DMA_PRIORITY_LOW;
 if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 __HAL_DMA1_REMAP(HAL_DMA1_CH1_ADC);
 __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc);
 /* USER CODE BEGIN ADC1_MspInit 1 */
 /* USER CODE END ADC1_MspInit 1 */
 }
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
 if(adcHandle->Instance==ADC1)
 {
 /* USER CODE BEGIN ADC1_MspDeInit 0 */
 /* USER CODE END ADC1_MspDeInit 0 */
 /* Peripheral clock disable */
 __HAL_RCC_ADC1_CLK_DISABLE();
 
 /**ADC GPIO Configuration 
 PC3 ------> ADC_IN13
 PA0 ------> ADC_IN0
 PA5 ------> ADC_IN5
 PA6 ------> ADC_IN6
 PA7 ------> ADC_IN7
 PC4 ------> ADC_IN14
 PC5 ------> ADC_IN15
 PB0 ------> ADC_IN8
 PB1 ------> ADC_IN9 
 */
 HAL_GPIO_DeInit(GPIOC, CurrentSense_Pin|RS6_Pin|RS7_Pin);
 HAL_GPIO_DeInit(GPIOA, RS8_Pin|RS5_Pin|RS1_Pin|RS2_Pin);
 HAL_GPIO_DeInit(GPIOB, RS3_Pin|RS4_Pin);
 /* ADC1 DMA DeInit */
 HAL_DMA_DeInit(adcHandle->DMA_Handle);
 /* USER CODE BEGIN ADC1_MspDeInit 1 */
 /* USER CODE END ADC1_MspDeInit 1 */
 }
} �?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Posted on May 04, 2018 at 12:38

From a superficial look, I find the following a bit supsicious:

_VAL2FLD(DMA_CCR_MSIZE, 2) | // 32 bit memory size

_VAL2FLD(DMA_CCR_PSIZE, 2) | // 32 bit peripheral size

Source and target items (ADC conversion values) are 16 bit, not 32 bit.

There is a SPL available for the F051, with examples for the ADC/DMA initialization. Perhaps you can cross-check with those.

Posted on May 07, 2018 at 08:41

After changing the ADC clock to be PCLK/2 (24MHz) and the ADC sampling time to be 13.5 ADC clock cycles, 100KHz seems to be achievable. However, another concern about the accuracy is really unknown to me. The range can be selected from 1.5 ADC cycle  up to 239.5 ADC cycles. How to choose the correct one? 

Posted on May 07, 2018 at 08:54

good question,

what I found was the Voltage on the pin didn't calculate from the ADC result.

I had to run the calibrate function, then I got to within 2mV.

Choose the best one for your work, Fastest doesn't work very well.

if you need accuracy, you will have to test it thoroughly

to be sure that you are reading an accurate pin Voltage.

Posted on May 07, 2018 at 09:03

I really want someone from ST can explain a bit more on this ADC sampling rate selection. 

Posted on May 07, 2018 at 09:06

The sampling time is the time needed to charge up all the capacitors for sampling purposes inside the ADC module. This sampling time must be enough for the input voltage source to charge the sample and hold capacitor to the input voltage level. Therefore, choosing this sampling time will mostly depend on the input resistance of the input voltage source, the lower the resistance, the lower the sampling time and vice versa.

Posted on May 07, 2018 at 09:26

Yes, you are right,

You should use a cap on the pin.

That helps stabilize DC levels, much smaller cap for AC signals depending on your drive impedance.