cancel
Showing results for 
Search instead for 
Did you mean: 

Multichannel (scan), single conversion mode

Rogers.Gary
Senior II

I need to implement a single channel software triggered ADC scan in an interrupt callback paced at 10ms intervals, since the rate cannot be controlled in DMA.

I never had an issue doing this with CMSIS, yet, HAL seems to be, well, difficult is the nicest word I will use.

I am confident my code is set up correctly, because before the scheduler starts, I have a "while" test loop that run 10 times, and the values come out correctly. Yet, in the callback, it chokes to death and causes faults. Why, that's just like HAL.

Here is the init code for the ADC.

void MX_ADC1_Init(void)

{

 ADC_MultiModeTypeDef multimode = {0};

 ADC_ChannelConfTypeDef sConfig = {0};

 /** Common config

 */

 hadc1.Instance = ADC1;

 hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;

 hadc1.Init.Resolution = ADC_RESOLUTION_12B;

 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

 hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;

 hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

 hadc1.Init.LowPowerAutoWait = DISABLE;

 hadc1.Init.ContinuousConvMode = DISABLE;

 hadc1.Init.NbrOfConversion = 2;

 hadc1.Init.DiscontinuousConvMode = DISABLE;

 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;

 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

 hadc1.Init.DMAContinuousRequests = DISABLE;

 hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;

 hadc1.Init.OversamplingMode = DISABLE;

 if (HAL_ADC_Init(&hadc1) != HAL_OK)

 {

   Error_Handler();

 }

 /** Configure the ADC multi-mode

 */

 multimode.Mode = ADC_MODE_INDEPENDENT;

 if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)

 {

   Error_Handler();

 }

 /** Configure Regular Channel

 */

 sConfig.Channel = ADC_CHANNEL_13;

 sConfig.Rank = ADC_REGULAR_RANK_1;

 sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;

 sConfig.SingleDiff = ADC_SINGLE_ENDED;

 sConfig.OffsetNumber = ADC_OFFSET_NONE;

 sConfig.Offset = 0;

 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

 {

   Error_Handler();

 }

 /** Configure Regular Channel

 */

 sConfig.Channel = ADC_CHANNEL_14;

 sConfig.Rank = ADC_REGULAR_RANK_2;

 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

 {

   Error_Handler();

 }

}

Here's the code before launch:

MX_ADC1_Init();

HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); 

And in the TIM2 callback @ 10ms intervals:

{

      HAL_ADC_Start(&hadc1);

       HAL_ADC_PollForConversion(&hadc1, 50);

       accumulator[TEMP] += HAL_ADC_GetValue(&hadc1);

       HAL_ADC_PollForConversion(&hadc1, 50);

       accumulator[VBAT] += HAL_ADC_GetValue(&hadc1);       

       HAL_ADC_Stop(&hadc1);       

//when count reaches 100, divide accumulator by count and get voltages/temps

}

It's BROKE as broke can be. I have been fighting with this thing for too many hours. Dear ST, HAL has not made things easier, at all.

Can anyone please provide a clue. Where could the problem be?

If I continue to have these problems, I will have the L4 processors removed from my boards and have F4's put in, and go back to CMSIS. Where things worked and I still had hair.

1 ACCEPTED SOLUTION

Accepted Solutions
Chris1
Senior III

To achieve consistent sampling, use a timer to trigger the ADC. For example setup TIM3 to update every 1 ms and generate a Trigger Output Event (TRGO) on each Update Event (UEV), set h_ADC1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO.

You can have the ADC interrupt at end of conversion and store the converted values in your interrupt handler, assuming your system can tolerate all the interrupts. (If you cannot service the interrupts in time, use DMA).

View solution in original post

22 REPLIES 22
raptorhal2
Lead

You are counting on HAL to get the first conversion out of the ADC register before the ADC starts filling it with the second conversion. I recommend using DMA to stuff a two word matrix, and then process the data into accumulator[ ].

Cheers, Hal

Rogers.Gary
Senior II

In other words, polling does not work. As I said, I need the data at specific intervals, not when DMA feels like jamming it at whatever rate it is running at. Amazing I was able to do this so easily in CMSIS, with the F407, an interrupt running at 1ms (1000Hz) and never had an issue. Why is it so difficult with this software to do something so simple? Can I not do it the way I need to, in an interrupt, and get the conversion when I need it?

Can anyone tell me if this is possible, yes or no....

raptorhal2
Lead

Not sure why you belive the timing is dictated by the DMA. The time between end of conversion and first variable DMA transfer to memory complete is less than the ADC conversion time for the second variable. And the ADC will begin second variable conversion as soon as the first variable conversion is complete.

Can you be more specific as to what timing you are expecting that can't be supported by DMA ?

Examine the ADC Sequencer example for a non-DMA implementation. Personaly highy dislike the Sequencer way of doing things. But, it does teach callbacks.

Cheers, Hal

Rogers.Gary
Senior II

I need samples at 1ms. Once DMA is launched, what controls the rate of transfer? How can I keep it at 1ms between samples?

Rogers.Gary
Senior II

So, polling does not appear to function using HAL at regular intervals in an interrupt, and the rate of DMA cannot be controlled. Hmmm. Interesting.

raptorhal2
Lead

See any ADC DMA example in the L4 HAL Library.

In ADC DMA mode, when the ADC is finished with each sample, DMA transfers that sample to memory. In yor case, the DMA counter goes to zero after storing the second variable. Then you can extract the two variables sample to your 100 or whatever count buffer using the appropriate call as illustrated in the ADC DMA example.

The rate of transfer is controlled by how often you start the ADC to start each set of two variables sample, DMA does not run amok on its own.

Cheers, Hal

raptorhal2
Lead

After rethinking my first response, there is a big difference in timing between F4 and L4 processors. The F4 can have system clock speeds much higher than the ADC clock. The L4 system and ADC clock speeds can be the same, making it more likely that the second variable conversion will fill the ADC register before you can read the first variable results.

Cheers, Hal

Chris1
Senior III

To achieve consistent sampling, use a timer to trigger the ADC. For example setup TIM3 to update every 1 ms and generate a Trigger Output Event (TRGO) on each Update Event (UEV), set h_ADC1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO.

You can have the ADC interrupt at end of conversion and store the converted values in your interrupt handler, assuming your system can tolerate all the interrupts. (If you cannot service the interrupts in time, use DMA).

Rogers.Gary
Senior II

Hi,

Yes, this is what I was doing in the F4, previously. I had set up a timer at 1ms rate, on the interrupt calling the sequence of ADC_Get_Value for each configured input. This way I know each sample was at the correct interval. I will also look at raptoHal's posts.

Thanks all.