STM32F0 ADC mini tutorial – Scan mode using interrupts and DMA

The ADC in the STM32F0 series of microcontrollers is pretty advanced compared to similar costing equivalents, offering features such as continuous conversion, internally and software triggered conversions, channel scanned conversions, amongst much more!

This isn’t a proper tutorial but I will be posting the code on how to achieve such a task and how I went about testing it! The main reason I wanted to figure out how to do it is in a variety of sampled systems, you want to have the ability to sample other analog inputs at the same time. The problem however comes with having clashes between consistently sampling one channel and being able to sample the other (most likely) less important channels at the same time, for example: In a digital guitar effects pedal, you want to consistently sample the audio to remove the audible effects and implications of digital jitter, whereas sampling the variable resistors – used to set the internal “slow changing” parameters, is less important as the relative frequency changes of these resistors is generally much lower than that of the audio signal and the sampled values generally stays pretty constant. Therefore, you want the ability to read these other analog channels when your main channel isn’t being read.

This is where the multiple ADC modes come in really useful. In my example, I’ve defined two different modes. The first of these is using the end of conversion interrupt from the ADC to store data in the correct memory locations with minimal overhead and the second is using DMA to move the data direct from the ADC data register to the correct memory locations. Obviously, DMA is the more efficient solution with the downsides of increased complexity.

To test each mode, I’ve connected three seperate potential dividers to my STM32F0 discovery board, a potential divider consisting of a 10k and 1k resistor, another of a 4.7k and 1k resistor and finally, one consisting of two 1k resistors. With the power supply set at a constant of 3V, the corresponding voltages at each point (under unloaded conditions – this will be explained later!) should be: 0.272V, 0.526V and 1.5V respectively. In digital terms (sampling at 12bits), these “digital” voltages should be 372, 719 and 2048, again respectively. After actually sampling, the returned values are 299, 644 and 1991! Obviously, this seems like a massive error, -19.7%, -10.4% and -2.8%, respectively. This might seem weird but is a common occurrence when you drive an analog input. In the datasheet, the internal structure of the ADC is actually shown and consists of a variety of resistors and capacitors, required for the ADCs functioning. The internal capacitances need charging up to the sampled voltage without applying a significant load to the analog input. Obviously, to achieve faster sampling rated, these internal capacitances need to be charged faster, presenting even more load on the analog input. This is generally mitigated by either buffering the input with a low impedance source, such as an op amp or if the input changes at a very slow rate, for example a potentiometer, adding a buffering capacitor between the analog input and ground. This capacitor supplies the load requirements as the input is sampled, reducing the voltage sag and reducing the error associated with it.

ADC Internal
Internal structure of the STM32F0 ADC, taken from the STM32F051R8 datasheet. Copyright STMicroelectronics 2014

The amount of sag can actually be calculated and used to mitigate this error. This however is not done in this tutorial.

Using the example in the interrupt driven mode. Note the converted values!

Using the example in DMA mode!

In both of the above examples, I’m using the debug mode to read the converted values and using a software defined flag.

The setup on the board, 4x 1k resistors, 1x 10k resistor and 1x 4.7k resistor. All 5% tolerance.

As ever, you can find my code on Github! In the example, if you comment out the DMA_ADCMode, the code example will work using the interrupt mode.

2 thoughts on “STM32F0 ADC mini tutorial – Scan mode using interrupts and DMA

  1. Hi Harry,

    i used your code in order to read 16 ADC channels. In order to measure the sampling rate I toggle a pin any time the DMA ISR is called. I realized that the sampling frequency is very low (42 Hz) and I changed the parameter of the function ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles ) with ADC_SampleTime_1_5Cycles but the sampling frequency remains the same. I tried all the available options but the result was the same.

    Do you have any idea how the sampling frequency could be increased?

    Thanks in advance,

    1. 42Hz seems really low! As far as I remember, ADC1 in the STM32F0 runs at the same frequency as the core clock (48MHz), with the ADCCLK running at /1, you should have a sample rate for 16 inputs with a sample time of 239.5 cycles of ~12.5kHz. Would you possibly be able to post your code and I can have a quick look for you?


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s