The ADC (Analog-to-Digital Converter) is the bridge between the physical world and digital processing. Temperature sensors, light sensors, potentiometers, microphones, and pressure sensors all produce analog signals. The PY32F0xx's 12-bit ADC converts these analog voltages into digital values that firmware can process.
PY32F0xx ADC Features
| Feature | Detail |
|---|---|
| Resolution | 12-bit (output range: 0 to 4095) |
| Channels | Multiple ADC channels shared with GPIO pins |
| Reference voltage | Typically VDDA (3.3V) |
| Conversion modes | Single conversion, continuous conversion |
| Trigger sources | Software trigger, hardware trigger (timers) |
| Data alignment | Left or right alignment |
| Sampling time | Configurable per channel (1.5 to 239.5 cycles) |
ADC Resolution and Voltage Conversion
The 12-bit ADC produces values from 0 to 4095. To convert a raw ADC reading to voltage:
Voltage = (ADC_Value / 4095) × VDDA
Example with VDDA = 3.3V:
ADC = 2048 → Voltage = (2048 / 4095) × 3.3 ≈ 1.65V
ADC = 4095 → Voltage = 3.3V
ADC = 0 → Voltage = 0V
ADC Channel Mapping
ADC channels are multiplexed with GPIO pins. The GPIO pin must be configured in analog mode before it can be used as an ADC input:
| ADC Channel | GPIO Pin |
|---|---|
| ADC_CHANNEL_0 | PA0 |
| ADC_CHANNEL_1 | PA1 |
| ADC_CHANNEL_2 | PA2 |
| ADC_CHANNEL_3 | PA3 |
| ADC_CHANNEL_4 | PA4 |
| ADC_CHANNEL_5 | PA5 |
| ADC_CHANNEL_6 | PA6 |
| ADC_CHANNEL_7 | PA7 |
Step 1: Configure GPIO Pin as Analog Input
GPIO_InitTypeDef GPIO_InitStruct = {0};
// Configure PA5 as analog input (no pull-up/down)
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
In analog mode, the digital input/output buffers are disabled. This prevents the pin from drawing extra current and minimizes noise on the ADC reading.
Step 2: Initialize the ADC
ADC_HandleTypeDef AdcHandle;
// Enable ADC clock
__HAL_RCC_ADC_CLK_ENABLE();
AdcHandle.Instance = ADC1;
// Clock prescaler — ADC clock = PCLK / 1
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
// 12-bit resolution: values 0–4095
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
// Right-aligned: value appears in bits [11:0]
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
// Single channel scan (not multi-channel sequence)
AdcHandle.Init.ScanConvMode = DISABLE;
// End-of-conversion flag on single conversion
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
// No auto-wait for low-power operation
AdcHandle.Init.LowPowerAutoWait = DISABLE;
// Single conversion (not continuous)
AdcHandle.Init.ContinuousConvMode = DISABLE;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
// Software-triggered conversion
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
// No DMA
AdcHandle.Init.DMAContinuousRequests = DISABLE;
// Overwrite old data if not read in time
AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
// Handle initialization error
while(1);
}
Step 3: Configure the ADC Channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_5; // PA5
sConfig.Rank = 1; // First (and only) in sequence
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // Longest sampling for best accuracy
HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
Sampling Time Trade-off
Longer sampling times produce more accurate readings but reduce conversion speed:
| Setting | Sampling Time | Accuracy | Speed |
|---|---|---|---|
ADC_SAMPLETIME_1CYCLES_5 | 1.5 cycles | Lower | Fastest |
ADC_SAMPLETIME_7CYCLES_5 | 7.5 cycles | Medium | Fast |
ADC_SAMPLETIME_239CYCLES_5 | 239.5 cycles | Highest | Slowest |
For sensors with high source impedance (like the MQ303A alcohol sensor), use the longest sampling time.
Step 4: Read ADC Value
uint16_t Read_ADC_Value(void) {
HAL_ADC_Start(&AdcHandle); // Start conversion
HAL_ADC_PollForConversion(&AdcHandle, HAL_MAX_DELAY); // Wait for completion
return HAL_ADC_GetValue(&AdcHandle); // Return raw 12-bit value
}
Complete ADC Example
#include "py32f0xx_hal.h"
ADC_HandleTypeDef AdcHandle;
void ADC_Config(void) {
// Configure PA5 as analog input
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Configure ADC
__HAL_RCC_ADC_CLK_ENABLE();
AdcHandle.Instance = ADC1;
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ScanConvMode = DISABLE;
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
AdcHandle.Init.LowPowerAutoWait = DISABLE;
AdcHandle.Init.ContinuousConvMode = DISABLE;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
AdcHandle.Init.DMAContinuousRequests = DISABLE;
AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
HAL_ADC_Init(&AdcHandle);
// Configure ADC channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
}
uint16_t Read_ADC_Value(void) {
HAL_ADC_Start(&AdcHandle);
HAL_ADC_PollForConversion(&AdcHandle, HAL_MAX_DELAY);
return HAL_ADC_GetValue(&AdcHandle);
}
int main(void) {
HAL_Init();
ADC_Config();
while (1) {
uint32_t adcValue = Read_ADC_Value();
float voltage = (adcValue * 3.3f) / 4095.0f; // Convert to voltage
printf("ADC Value: %lu, Voltage: %.2f V\r\n", adcValue, voltage);
HAL_Delay(1000); // 1-second interval
}
}
ADC Conversion Formula
// Convert raw ADC reading to voltage (3.3V reference)
float voltage = (adc_raw * 3.3f) / 4095.0f;
// Convert raw ADC reading to voltage (custom reference)
float voltage = (adc_raw * vref) / 4095.0f;
Data Alignment
The 12-bit ADC result can be placed in the 16-bit data register in two ways:
Right-aligned (ADC_DATAALIGN_RIGHT):
Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 0 0 0 D D D D D D D D D D D D
^--- 12-bit result in bits [11:0]
Left-aligned (ADC_DATAALIGN_LEFT):
Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
D D D D D D D D D D D D 0 0 0 0
^--- 12-bit result in bits [15:4] (useful for 8-bit truncation)
Right alignment is standard for 12-bit reading. Left alignment is useful when you want to read only the top 8 bits for lower precision.
Final Thoughts
The PY32F0xx ADC is a capable 12-bit converter that enables the chip to interface with the full range of analog sensors. The HAL-based configuration flow is consistent with STM32 projects, making PY32 ADC knowledge directly transferable.
Key practices for accurate ADC readings:
- Use the longest sampling time for high-impedance sources
- Configure the GPIO in analog mode before using it as ADC input
- Use a stable VDDA reference for voltage accuracy
- Average multiple readings to reduce noise for precision measurements
The ADC is the window through which the PY32 sees the physical world in numbers.