2023-05-20 12:45 AM
I'm completely lost in ADC working modes. And it happens EACH time I start a new project with internal ADC. So it ends up taking a lot of time to get the ADC do what I want.
I need the following behaviour: 2 channels of single adc runs forever.
By my requests I collect N last saved conversions for each channel.
Again for example, I press the button and collect last 16 conversions for both channels.
Intervals for that conversions are defined by timer or it would be fine if they are running at whatever rate.
I guess it's free-running-scan-continious-forever mode
If someone can help with settings in CubeMX and code to set this up it would be great!
Solved! Go to Solution.
2023-05-23 12:23 AM
Hi VL.3 (Community Member)
The scan mode is already configured in your application (ADC3_IN1 and IN12) but the conversion is discontinuous because you need to trigger ADC3 with TIM1 TRGO.
When ADC is configured in continuous mode, it is started once (by software or timer) and it does conversion infinitely until it is stopped. Continuous is also suitable.
You'll find in an attachment a STM32CubeIDE project on Nucleo-G474 that perform the ADC3 IN1 and IN12 conversion triggered with TIM1 (trigger period is 100ms)
As explained yesterday, the ADC3 and the DMA store IN1 and IN12 rank in a array of 32 elements (adcBuffer[0] = IN1, adcBuffer[2] = IN12.....adcBuffer[30] = IN1, adcBuffer[31] = IN12)
and split the 32 samples into 2 independant arrays of 16 samples each (IN1 and IN12).
Can you try it, and tell me if it help you?
BR
Romain,
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-05-22 12:23 AM
Hi @VL.3 (Community Member),
Could you indicates which STM32 product you are using?
BR,
Romain,
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-05-22 02:36 AM - edited 2023-11-20 05:03 AM
It is STM32G4.
I managed to make it work more or less how I wanted. CubeMX setup attached.
In the code I start conversions like this once:
#define NUM_ADC_CONVERSIONS 16
#define NUM_ADC_CHANNELS 2
...
uint16_t adc[NUM_ADC_CHANNELS];
...
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc3, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc3, (uint32_t*) adc, NUM_ADC_CHANNELS);
And then in the interrupt I collect adc values for each channel:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC3)
{
static uint8_t i = 0;
adc_array[0][i] = adc[0];
adc_array[1][i] = adc[1];
if (++i >= NUM_ADC_CONVERSIONS)
{
i = 0;
flags.int_adc_flag = 1;
}
}
}
2023-05-22 04:38 AM - edited 2023-11-20 05:03 AM
I've looked your CubeMx setup.
It seems TIM1 is only used to trigger ADC3 using TRGO internal event. In this case, do you really need TIM1 interrupt?
ADC3 in 8bit resolution in scan Mode for IN1 with IN12 in single-ended channels and using DMA request is also ok.
Concerning DMA Settings, I don't understand why Channel = ADC3 ?
It should normally corresponds to the physical DMA (like DMA1 Channel 1) (see my image below)
Can you check it?
The DMA is set in Circular mode. As your code start ADC3 conversion once, maybe it will be better to set DMA in normal mode.
Now concerning the code, I suggest the following:
After ADC Start, wait for the end of ADC conversion and DMA transfer like this:
if (HAL_TIM_Base_Start_IT(&htim1) != HAL_OK)
{
/* TIMER1 Error */
Error_Handler();
}
if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_SINGLE_ENDED) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
if (HAL_ADC_Start_DMA(&hadc3, (uint32_t*) adc, NUM_ADC_CHANNELS) != HAL_OK)
{
/* ADC3 Error */
Error_Handler();
}
/* Wait ADC - DMA Transfert complete */
while (flags.int_adc_flag == 0) {;}
/* Reset the flag */
flags.int_adc_flag = 0;
/* Stop ADC3 Conversion */
if (HAL_ADC_Stop_DMA(&hadc3) != HAL_OK)
{
/* ADC3 Error */
Error_Handler();
}
You could also do post compute the ADC3 conversion results array after this code and outside of callback function.
I hope it will helps you.
BR
Romain,
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-05-22 07:03 AM
Thanks for the detailed answer!
It would be fine if I manage to do this: software starts N adc conversions of 2 channels with TIM period - defined interval between conversions.
Callback on complete and collect N conversions for both channels.
2023-05-22 07:23 AM
Here my answers after >:
>Yes TIM1 TRGO Update is an internal event and happens during overflow of counter. It does not needs to generate an interrupt. Yes only HAL_TIM_Base_Start() is needed.
>It doesn't matter, sometimes some thing can't be explained !
>CubeMx keeps the user code written into following tags:
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
And many other...Your code is thus preserved if you regenerate it with CubeMx.
Continuous/Discontinuous mode are specific to ADC, not to the DMA.
DMA perform only request to peripheral (here ADC) and transfer each data to the memory (here the SRAM)
I can suggest to you the AN3116 to better understand STM32 ADC operating mode.
> Normally, it's what I proposed in my previous code.
Let me know if you can implement my proposal. And if it works as you need ?
BR
Romain,
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-05-22 10:38 PM
From the AN I see that closest mode is "Multichannel (scan) continuous conversion mode".
As I understand your code it starts 'number of channels' conversions; then waits for a flag which is set in callback; then stops adc.
However what I would like to do is:
Start Nchannels * Nconversions (so 2 channel, 16 conversions each would be 32 conversions total); then wait for Callback after all 32 conversions done; then stop ADC.
What CubeMX settings should be in this case?
2023-05-23 12:23 AM
Hi VL.3 (Community Member)
The scan mode is already configured in your application (ADC3_IN1 and IN12) but the conversion is discontinuous because you need to trigger ADC3 with TIM1 TRGO.
When ADC is configured in continuous mode, it is started once (by software or timer) and it does conversion infinitely until it is stopped. Continuous is also suitable.
You'll find in an attachment a STM32CubeIDE project on Nucleo-G474 that perform the ADC3 IN1 and IN12 conversion triggered with TIM1 (trigger period is 100ms)
As explained yesterday, the ADC3 and the DMA store IN1 and IN12 rank in a array of 32 elements (adcBuffer[0] = IN1, adcBuffer[2] = IN12.....adcBuffer[30] = IN1, adcBuffer[31] = IN12)
and split the 32 samples into 2 independant arrays of 16 samples each (IN1 and IN12).
Can you try it, and tell me if it help you?
BR
Romain,
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2023-05-23 03:53 AM
Thanks for the project!
I think that this is quite common ADC mode. Scenarios where there is more than one channel and a series of measurements is required (to implement basic filtering).
Few more questions.
Should calibration be started every time before adc start or only once after power-up?
I attached logic analyzer and see that timer overflows 16 times. It means that timer triggers 2 conversions: channel 1 and channel 12, not each channel separately, am I right?
Can I somehow not wait for flag, but instead start ADC and then somewhere in loop check for completion flag, collect data and be sure that 32 measurements are not overwritten? It means that I wouldn't be able to stop adc and timer (only in interrupt though). Maybe I should run timer forever?