cancel
Showing results for 
Search instead for 
Did you mean: 

ADC Sample Rate

TonyFauci
Associate II

I am doing current control with PWM. The switch provides a current sense pin which is going to my ADC.

I am using STM32F4. I am also using FreeRTOS, not sure if this is important though.

My ADC is set to run at 84MHz with 4x prescaler. The conversion is 10bit and the cycles are 15. ADC into circular buffer.

The PWM frequency to be measured is 250Hz, very small. I am watching the ADC values using STM Studio trace using genuine stlinkv2 and I am unable to capture every PWM pulse when the duty cycle is below ~15%. This is clearly due to aliasing. I assume it's due to the ADC being too slow but I'm also under the impression that the ADC is supposed to be capturing 750k samples per second, 21MHz/(15+12)  >= 750k. Even 5000 samples per second should suffice so what could be happening?

My problem is similar to this but I am not currently concerned with gating, just being able to capture the low duty cycle PWM pulses.

https://community.st.com/t5/stm32-mcus-products/stm32-adc-sample-rate/td-p/240061#:~:text=the%20sample%20time%20is%20set,%2F(56%2B12)%3D397KHz.

 

 

@waclawek.jan @Jshan.1 

 

code: 

void ADC_Init(local_config* peripheral_config){
 
set_adc_order(peripheral_config);
 
uint16_t* ADC_Values = peripheral_config->ADC_Values;
uint8_t* adc_rank = peripheral_config->adc_rank;
uint8_t* input_array = peripheral_config->adc_input_array;
 
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
 
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
 
/*
* DMA Init End
****************************************************************/
 
/*
* ADC Init Start
****************************************************************/
 
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_10B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = peripheral_config->adc_num_conv;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; //ADC_EOC_SINGLE_CONV
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
 
/*
* ADC Init End
****************************************************************/
 
/*
* MSP Init Start
****************************************************************/
 
 
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
 
__HAL_RCC_GPIOA_CLK_ENABLE();
 
/*
* It appears GPIO pins have to be initialized together per PORTA
*
* Need to figure out what to do about other ports
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
for(int index = 0; index < 8; index++){
if(input_array[index]){
GPIO_InitStruct.Pin = GPIO_InitStruct.Pin|ADC_DI_PINS[index];
}
}
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
/* Feedback pins GPIO init ***************************************************/
 
GPIO_InitStruct.Pin = FB1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(FB1_GPIO_Port, &GPIO_InitStruct);
 
GPIO_InitStruct.Pin = FB2_Pin|FB3_Pin|FB4_Pin|FB5_Pin
  |FB6_Pin|FB7_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(FB2_GPIO_Port, &GPIO_InitStruct);
 
/* Feedback pins GPIO init ***************************************************/
 
/* ADC1 DMA Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //DMA_MDATAALIGN_WORD
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
 
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
 
/* ADC1 Interrupt Enable */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
 
/*
* MSP Init End
****************************************************************/
 
/*
* ADC Channel Rank Start
****************************************************************/
 
/*
* Configuration of ADC Channels
*
* Discontinuous ranks are not allowed.
* First loop assigns the rank to customizable input pins.
*
* Second loop assigns rank for feedback pins.
*/
ADC_ChannelConfTypeDef sConfig = {0};
for(int index = 0; index < 8; index++){
if(input_array[index]) {
sConfig.Channel = ADC_CHANNELS[index];
sConfig.Rank = adc_rank[index];
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
}
 
for(int index = 8; index < 15; index++) { // 15 was changed to 9
sConfig.Channel = ADC_CHANNELS[index];
sConfig.Rank = adc_rank[index];
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
 
/*
* ADC Channel Rank End
****************************************************************/
 
/*
* Starting ADC,
*
* There may be a problem with not accounting for pin/channel order.
*/
//peripheral_config->adc_num_conv
if(HAL_ADC_Start_DMA(&hadc1, ADC_Values, peripheral_config->adc_num_conv) != HAL_OK) Error_Handler();
}

 

1 ACCEPTED SOLUTION

Accepted Solutions

Isn't sampling 15 channels going to significantly decimate the attained rate?

How deep is your buffer? Make sure its several KB deep, filled once, so you can inspect the content at your leisure.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

5 REPLIES 5
TDK
Guru

> I am watching the ADC values using STM Studio trace

Is this your only method of watching data? Likely STM Studio is just not passing variables every time it updates. You're churning through quite a lot of samples and the trace can't keep up.

Also make sure adc_num_conv is large enough that you aren't swamping the CPU with interrupt requests every few samples.

750 ksps is going to require to you get out 7.5 Mbps of data. That's a large amount of data, what is you plan for getting that from the STM32 to your computer? Or how else will the data be processed?

If you feel a post has answered your question, please click "Accept as Solution".

Isn't sampling 15 channels going to significantly decimate the attained rate?

How deep is your buffer? Make sure its several KB deep, filled once, so you can inspect the content at your leisure.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

STM Studio is faster than the cube ide trace. I don't have a jtag debugger so that's what I'm working with.

Currently I'm trying out 7 conversions. My plan was bad but I haven't thought of another way. In any case, the actual data stream is only one of the many ADC conversions taking place so I would be outputting less than 7.5 Mbps, I'm accepting samples at 10khz then outputting that.

I'm doing 7 ADC conversions and putting one value for each pin into a buffer then at the end of conversion replacing them with the newly converted values using a DMA request.

What you're suggesting is making it lets say 1000 long and having the DMA place new values into the next position after a conversion sequence (1000/number of conversions) times before starting from the beginning again?

So I did this, the ADC was sampling correctly but the duty cycle coming out of the switches sense pin was 10% lower than the actual duty cycle.