STM32 ADC is pretty complex peripheral. It is designed to be flexible enough to accomplish complex tasks. We are going to dedicate a few posts where we will try to cover the main features and give working examples of code.
The block schematic may look scary at the first time, but if you look closer, it can be split into several pieces that are responsible for different functions. Will will go through them step by step to make it look brighter.
Let’s see what ADC is capable of doing. Generally speaking STM32 microcontroller, ADC has up to 18 multiplexed 12-bit channels where two of them are internal – a temperature sensor and reference (band gap) voltage. ADC module has a separate supply which generally can be connected to general MCU power supply. Also, ADC can have external reference source through VREF+ and VREF- that can be brought through dedicated pins. In no particular case, the analog supply voltage may be as a reference internally.
ADC can be performed in single or continuous mode for a single channel, scan through all channels, externally triggered conversion. There is also an analog watchdog which can detect if the voltage exceeds preset threshold values. And of course DMA request based conversions.
ADC has dedicated prescaller that scales down APB2 clock to /2, /4, /6 and /8. Sampling time can be set individually for each channel from 1.5 to 239.5 cycles by selecting SMP[2:0] bits in ADC_SMPR1 and ADC_SMPR2 registers.
Regular and Injected conversions
STM32 microcontrollers support two ADC conversion modes: regular and injected. The proper way is what we used to see in many types of microcontroller – all channels share the same data register. Various regular modes can be possible, like single, continuous, and group.
More interesting is the group conversion where channels are specified to convert in round robin mode – cycle through channels. It is up to you how you collect data from the register in time – through conversion complete interrupt or using DMA. Conversion group can be configured to cycle through max 16 channels and order of channels can also be programmed – they don’t have to be in order like 0,1,2,… some channels and order can be selected in ADC_SQRx registers. Group can be programmed to convert continuously, and discontinuously triggered by an external trigger. In discontinuous mode parts of the group can be triggered to convert. For instance, if we select group to be 16 channels then we can program to convert the first three channels on the trigger, then wait for another trigger and convert the next three channels and so on.
Another is injected conversion mode. This might look something new. There is nothing fancy about it. An injected group can perform conversion of selected four channels. Simply speaking, the injected group have a priority over normal conversion group scan.
If normal group conversion is going on and injected conversion is triggered, then normal conversion is halted, then injected conversion is performed, then normal conversion is resumed. Injected conversion channels have their result registers and offset registers. The offset can store a value that will be deducted from ADC result automatically.
Analog watchdog is a handy feature of the ADC module. It can save lots of processing time if used where needed. In some cases, we use ADC only to check if the analog voltage reached some level (or dropped). We usually would scan the ADC channel continuously to capture the moment when the level is reached. Analog watchdog can do this automatically in hardware level. All you need is to load low, and high threshold values set interrupt and forget.
The watchdog can be set to monitor any channel or all channels either from the regular group or injected. Why not use this feature to track sensor data and raise the alarm on some level. Might be the right choice for zero crossing detector. The processor could be put to sleep all time and awaken when analog watchdog interrupt occurs.
Next time we will get to ADC programming examples using GCC tools. There are several exciting modes we would like to try.