Insights into Utilizing STM32 ADC

RT-Thread IoT OS
4 min readFeb 28, 2024

Author: Luosisongdiaoderen

Lately, I’ve been delving deeper into the intricacies of using STM32’s ADC, exploring their functionalities with more depth than ever before. While my previous experiences were fairly straightforward, I’ve stumbled upon some fascinating aspects and features that aren’t extensively covered online. I’m eager to share my journey of discovery and insights, recognizing that some of my methods may not be optimal and there might be better approaches out there. I’m open to discussions and exchanges with fellow enthusiasts.

Fundamental Functions

The STM32 ADC allows us to directly measure voltage levels, enabling us to convert these readings into digital results for analog signals such as current and temperature. Typically, STM32 boasts three ADCs, each with over ten channels. These channels can gather data from corresponding pins, although some are dedicated solely to internal data acquisition without external pinouts. Among these channels, regular ones can handle up to 16 channels, while injected channels can manage up to 4. Certain ADCs can also be configured for master-slave mode, facilitating complex operations like cross-channel sampling, often employed in motor control applications. It’s important to note that selecting channels directly for reading within an ADC is usually not feasible for multiple channels, unless channels are enabled for injection mode (to be discussed later). Initially, I only utilized a single channel in the ADC, oblivious to this limitation. However, for developing device drivers, ensuring universality is crucial, addressing such intricate issues for users.

Regular Mode

Regular mode is the standard operational mode and is widely used. Typically, three register sequences can be configured, allowing multiple regular channels to be assigned to specific positions within a sequence. Upon activating ADC sequence conversion, channels within the sequence are scanned and converted sequentially. It’s crucial to note that each ADC has only one regular data register, sufficient for storing data from a single channel, which may lead to data overwrite. Therefore, after completing the conversion for each channel, data retrieval and distinction in the correct sequence order are necessary. Using an array to store conversion data, along with pre-set sequences, facilitates associating data with specific channels.

Additionally, incorporating DMA can facilitate sequential data retrieval, allowing us to obtain data in the desired channel order.

Injected Mode

Injected mode, akin to interrupts, supports various trigger sources, including software triggers, and can interrupt ongoing regular channel conversions. Up to 4 channels can be enabled as injected channels, each equipped with its data register, thus eliminating data overwrite issues. When DMA resources are limited, or blocking is undesirable, and maintaining conversion sequences becomes challenging, this mode proves advantageous.

Cross-Channel Mode

Cross-channel mode involves configuring two ADCs into master-slave relationships, typically ADC1/2, as detailed in the official documentation.

Considerations

  • Regular channel data registers are singular, leading to potential data overwrite issues, necessitating diligent data maintenance.
  • Channels enabled for injected mode cannot utilize continuous conversion mode.
  • Each ADC can only accommodate 4 injected mode channels, requiring careful resource allocation.
  • ADC data registers eliminate the occurrence of reading dirty data.

Comparison of Device Drivers

Now, let’s delve into an analysis and comparison of how mainstream RTOS handles the development of ADC device drivers, and how they effectively shield users from the complexities of low-level details.

RT-Thread

The ADC initialization settings are configured as follows, with continuous sampling mode disabled:

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 channels are designated as regular channels and grouped within the same sequence:

1ADC_ChanConf.Rank = ADC_REGULAR_RANK_1;

The reading function is as follows: (Note: The parameter includes the channel, although it remains unused)

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 examine how the ADC device driver is maintained:

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’s evident that the approach described aligns with the first method mentioned, where all channels operate in regular mode, organized within the same conversion sequence, sequentially read, and stored in an array according to their order.

Copyright Notice: This article is an original creation by the RT-Thread Club user “Luosisongdiaoderen”, adhering to the CC 4.0 BY-SA copyright agreement. When reposting, please include the source link and this statement.

Original Link:

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

--

--

RT-Thread IoT OS

An Open-Source Community-Powered Real-Time Operating System (RTOS) Project! Let’s develop, DIY, create, share, and explore this new IoT World together!