Customizing the ADC Sample Rate

So in the last post I talked about getting streaming data from an Analog/Digital Converter unit on the STM32F746. In that case I let the ADC run at a pretty high clock rate, based on the PCLK2 configuration and a single ADC prescaler.  Needless to say, the options for sample rate are pretty limited, even when you throw in the 8-setting "per-channel" delay option.

So, what if you want to sample at a more arbitrary rate, or even change the sample rate after booting?

One way is to switch from "Continuous" mode to triggered mode.

Timer Triggered ADC

So if you look closely at the ADC settings in CubeMX, you will see the "External Trigger Conversion Source" under the ADC_Regular_ConversionMode section. This allows you to specify a timer event that will initiate a conversion.  For this mode, we want to disable the Continuous mode of the ADC, leaving it in single conversion mode, so that each trigger event starts a single conversion.

How Does It Work With DMA?

When we setup the DMA in continuous mode, we didn't worry about how it worked so much. But we now that the ADC takes time to convert each sample, much more time than the DMA needs to move the data.  What happens is that the ADC engine sends a DMA request after each conversion, telling the DMA engine to move one conversion value.  This works exactly the same way in our triggered mode, the only difference is the next conversion doesn't start right away, but waits for the next trigger event.

You can use the regular HAL_ADC_Start_DMA function if you want to collect a single buffer's worth of data for analysis.  The HAL_ADC_ConvCpltCallback function will be called when the data is ready.  Or you can use the double-buffering function discussed in the last post.

Timer Setup

I used the TIM5 timer TRGO (TRiGger Output) event. I setup TIM5 by enabling the Internal Clock, which is the APB1 Timer Clocks value which I had running at 60 MHz in the Continuous mode example, so 60 MHz is the clock input.  I left the Prescaler set to 0 for no division. And under TRGO Parameters I set the Trigger Event Selection to "Update Event".  The default is "Reset" which doesn't work well, so make sure to change it to Update.

The Update Trigger will be triggered every time the timer reaches the AutoReload (or "Period") value. So to generate a 300 KHz sample rate, we want the period to be 60,000,000 / 300,000 or 200.  To get 200 timer cycles as the period, you need to set the Period value to 199 (it counts the 0 cycle as the first one.)



ADC Changes

As we discussed already, the changes to the ADC are to disable Continuous mode and set the External Trigger Conversion Source to "Timer 5 Trigger Out event".

Software Changes

A couple minor things need to be done on the software side.  First of all, the F746 and some of the other F family have an errata where it can lose conversion triggers depending on the clock ratios you have configured.  Fortunately, there is a simple workaround - you have to turn on power to the DAC unit as shown here:

  /* USER CODE BEGIN SysInit */
  RCC->APB1ENR |= RCC_APB1ENR_DACEN;
  /* USER CODE END SysInit */

As you see I put this code just after the System Clock setup call, which seemed like the appropriate place.

We also have to enable the timer, which I added to the MX_TIM5_Init function:

  /* USER CODE BEGIN TIM5_Init 2 */
  HAL_TIM_Base_Start (&htim5);
  /* USER CODE END TIM5_Init 2 */

That's it.

If you want to update the AutoReload value, I call this function while the timer is running but the DMA and ADC are not.

#define APB1_TIMER_CLOCK 60000000
extern TIM_HandleTypeDef htim5;
int adc_sample_rate = 250000;  
int adc_set_sample_rate (long rate) 

{
long period = APB1_TIMER_CLOCK / rate;
float actual = APB1_TIMER_CLOCK / period;
if (period * rate != APB1_TIMER_CLOCK) {
float error = actual / rate - 1.0;
output ("Slight error, actual rate of %8.3f KHz, error %5.2f%%\r\n", actual/1000.0, error * 100.0);
}
__HAL_TIM_SET_AUTORELOAD (&htim5, period-1);
adc_sample_rate = actual;
return actual;
}






Comments

Popular posts from this blog

FFT Analysis using CMSIS DSP Library

Basic Scan-Mode A/D DMA (STM32H743)

Double-Buffered DMA from ADC