Insights on Using STM32 ADC

Author: Loose Screw

Recently, I have been using the ADC of STM32 quite a lot, and I have delved a little deeper. Previously, I only used it simply without much research. I found that there are still many interesting aspects, and some functions are not well introduced or used on the internet, so I would like to share my exploration process and thoughts. Some functions might not be used correctly or there could be better ways to use them, and I welcome everyone to exchange ideas.

Basic Functions

Through the ADC, we can directly read the voltage level. By converting the read voltage, we can obtain the digital conversion results of current, temperature, and other analog signals. In STM32, there are usually 3 ADCs, each with more than a dozen channels, and each channel can correspond to a pin to collect data (some channels are only used to collect internal data and do not expose external pins). Among them, regular channels can have up to 16 paths, and injected channels can have up to 4 paths. Some ADCs can also be configured in a master-slave mode for cross-sampling and other more complex operations, which is commonly used in motor control. However, it is important to note that for multiple channels in the ADC, you generally cannot directly specify a channel to read unless you enable the channel in injected mode (which will be introduced later). Previously, I generally only used one channel in ADC and did not pay attention to this aspect because I could only read that one channel. However, to write device drivers, one must ensure generality and shield users from these more intricate issues.

Regular Mode

Regular mode is the ordinary mode, and it is also widely used. You can generally set three register sequences and allocate multiple regular channels to specified positions in the designated sequence according to your needs. After enabling the ADC sequence conversion, it will start scanning and converting the channels in the sequence in order. One should note that each ADC has only one regular data register, which is only enough to store one channel’s data, and the data will be overwritten. This means that after reading the data from each channel, we need to retrieve it in a timely manner and distinguish it in order. You can use an array to store the converted data, and by knowing the designated sequence, you can know the data corresponding to the specified channels.

Generally, you can also adopt a method combined with DMA. After reading the data, the data can be filled in order, and we can also obtain the data in the specified channel order.

Injected Mode

Injected mode is somewhat like an interrupt. You can choose multiple trigger sources, and you can also trigger it via user software, which can directly interrupt the ongoing regular channel conversion. Up to 4 channels can be enabled as injected channels, and each channel has its own data register, so there will be no data overwrite issue. If DMA resources are tight and you do not want to introduce blocking, making it inconvenient to maintain the conversion sequence, you can choose to use this mode.

Cross Mode

Cross mode requires configuring two ADCs in a master-slave relationship, generally ADC1/2, and specific details can refer to the official manual.

Notes

  • There is only one regular channel data register, which will have data overwrite issues, so proper data maintenance must be done;

  • Once a channel is enabled in injected mode, continuous conversion mode cannot be used;

  • Each ADC can only use 4 injected mode channels, so resource allocation must be noted;

  • There will be no situation where the ADC data register reads dirty data;

Device Driver Comparison

Next, we will analyze and compare how the ADC device drivers of mainstream RTOS are written and how they shield users from these intricate low-level details.

RT-Thread

The ADC initialization settings are as follows, disabling continuous sampling mode:

 1
 2
 3#define ADC1_CONFIG                                                \
 4
 5    {                                                              \
 6
 7       .Instance                   = ADC1,                         \
 8
 9       .Init.DataAlign             = ADC_DATAALIGN_RIGHT,          \
10
11       .Init.ScanConvMode          = ADC_SCAN_DISABLE,             \
12
13       .Init.ContinuousConvMode    = DISABLE,                      \
14
15       .Init.NbrOfConversion       = 1,                            \
16
17       .Init.DiscontinuousConvMode = DISABLE,                      \
18
19       .Init.NbrOfDiscConversion   = 1,                            \
20
21       .Init.ExternalTrigConv      = ADC_SOFTWARE_START,           \
22
23    }
24
25#endif /* ADC1_CONFIG */

All are set as regular channels and placed in the same sequence:

1ADC_ChanConf.Rank = ADC_REGULAR_RANK_1;

Reading function: (the parameter has channel, but it is not used)

 1static rt_err_t stm32_adc_get_value(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value)
 2{
 3    ADC_HandleTypeDef *stm32_adc_handler;
 4    RT_ASSERT(device != RT_NULL);
 5    RT_ASSERT(value != RT_NULL);
 6    stm32_adc_handler = device->parent.user_data;
 7    /* Wait for the ADC to convert */
 8    HAL_ADC_PollForConversion(stm32_adc_handler, 100);
 9    /* get ADC value */
10    *value = (rt_uint32_t)HAL_ADC_GetValue(stm32_adc_handler);
11    return RT_EOK;
12}

Let’s take a look at how the ADC device driver maintains the data:

 1static rt_ssize_t _adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
 2{
 3    rt_err_t result = RT_EOK;
 4    rt_size_t i;
 5    struct rt_adc_device *adc = (struct rt_adc_device *)dev;
 6    rt_uint32_t *value = (rt_uint32_t *)buffer;
 7    for (i = 0; i < size; i += sizeof(int))
 8    {
 9        result = adc->ops->convert(adc, pos + i, value);
10        if (result != RT_EOK)
11        {
12            return 0;
13        }
14        value++;
15    }
16    return i;
17}

It can be seen that this is the first method we mentioned, where all channels are in regular mode, in the same conversion sequence, and read one by one, storing them in an array in sequence.

References

  • STM32H723/733, STM32H725/735 and STM32H730 Value line advanced Arm®-based 32-bit MCUs – Reference manual

  • Wildfire] STM32 Library Development Practical Guide – Based on Wildfire Guide Development Board Documentation (embedfire.com)

————————————————

Copyright Statement: This article is the original work of RT-Thread forum user “Loose Screw” and follows the CC 4.0 BY-SA copyright agreement. Reprint must include the original source link and this statement.

Original link:

https://club.rt-thread.org/ask/article/3c37ce454d8fecc4.html

———————End——————

Insights on Using STM32 ADC

Insights on Using STM32 ADC

👇 Click to read the original text and enter the official website

Leave a Comment

×