ADC trong STM32: Hướng Dẫn Cấu Hình Analog-to-Digital Converter

ADC (Analog-to-Digital Converter) là một trong những ngoại vi quan trọng nhất trong vi điều khiển STM32, cho phép đọc các tín hiệu tương tự từ thế giới thực như nhiệt độ, điện áp, cảm biến áp suất… và chuyển đổi sang dạng số để xử lý. Bài viết này hướng dẫn toàn diện về ADC trong STM32F1: từ nguyên lý hoạt động, các khái niệm cơ bản đến cấu hình chi tiết bằng thư viện SPL.

ADC là gì?

ADC (Analog-to-Digital Converter) là mạch điện tử nhận điện áp tương tự làm đầu vào và chuyển đổi thành dữ liệu số – một giá trị nhị phân đại diện cho mức điện áp đó.

Hình 1. ADC nhận tín hiệu tương tự (Analog Input) và xuất ra dữ liệu số (Digital Output)

Về cơ bản, ADC hoạt động bằng cách chia mức tín hiệu tương tự thành nhiều mức rời rạc. Mỗi mức được biểu diễn bằng một giá trị nhị phân tương ứng. Bằng cách so sánh điện áp đầu vào tại mỗi thời điểm lấy mẫu với các mức đã chia, ADC mã hóa tín hiệu tương tự thành chuỗi bit nhị phân.

Hình 2. Tín hiệu analog (đường xanh) và tín hiệu số hóa bậc thang (đường đỏ) sau khi qua ADC

Các khái niệm cơ bản cần biết

Độ phân giải (Resolution)

Độ phân giải là số bit cần thiết để biểu diễn toàn bộ các mức giá trị số sau quá trình chuyển đổi. ADC của STM32F103 có độ phân giải mặc định là 12 bit, tức là có 2¹² = 4096 mức (từ 0 đến 4095).

Độ phân giải càng cao, dữ liệu số càng gần với tín hiệu gốc:

Hình 3. ADC 3-bit tạo ra bậc thang thô (ít mức), ADC 16-bit mịn hơn và gần với sóng analog gốc hơn
Độ phân giảiSố mứcĐiện áp mỗi bước (Vref = 3.3V)
8-bit256~12.9 mV
10-bit1024~3.2 mV
12-bit (STM32F1)4096~0.8 mV
16-bit65536~0.05 mV

Tần số lấy mẫu (Sampling Rate)

Tần số lấy mẫu cho biết ADC lấy mẫu và số hóa tín hiệu bao nhiêu lần mỗi giây. Thời gian giữa hai lần lấy mẫu càng ngắn, tín hiệu số hóa càng chính xác và khả năng tái tạo lại tín hiệu gốc càng cao (theo định lý Nyquist: tần số lấy mẫu phải ≥ 2 lần tần số tín hiệu).

Hình 4. Các điểm lấy mẫu (chấm đỏ) trên tín hiệu tương tự – tần số lấy mẫu càng cao càng chính xác

ADC trong STM32F1

STM32F103 tích hợp 2 bộ ADC (ADC1 và ADC2), mỗi bộ có tối đa 9 kênh (channel) với nhiều chế độ hoạt động linh hoạt.

Hình 5. Sơ đồ khối ADC trong STM32 – hỗ trợ nhiều kênh, trigger ngoài, DMA và ngắt

Đặc điểm chính ADC STM32F103

  • Độ phân giải 12-bit – giá trị tối đa là 4095.
  • Hỗ trợ Single mode và Continuous mode.
  • Có các ngắt hỗ trợ (End of Conversion, Watchdog…).
  • Tự động calibration – giảm sai số do điện dung bên trong.
  • Có thể kích hoạt bằng Trigger ngoài (Timer, EXTI…).
  • Thời gian chuyển đổi nhanh: 1µs tại 65 MHz.
  • Điện áp cấp cho ADC: 2.4V → 3.6V → điện áp đầu vào phải trong khoảng này.
  • Tích hợp DMA giúp tăng tốc độ xử lý (không chiếm CPU).
  • Có 2 kênh đặc biệt: Vref và Temperature Sensor nội.

Tính toán giá trị ADC

Giả sử cần đo điện áp từ 0V đến 3.3V với ADC 12-bit:

  • STM32 chia khoảng 0 → 3.3V thành 4096 mức (0 → 4095).
  • 0V → giá trị ADC = 0
  • 3.3V → giá trị ADC = 4095
  • Công thức quy đổi: Voltage = ADC_Value × 3.3 / 4095
/* Quy đổi giá trị ADC sang điện áp (V) */
float voltage = (float)adc_value * 3.3f / 4095.0f;

Cấu hình ADC trong STM32F103

Bước 1: Cấu hình GPIO (chân analog input)

ADC hỗ trợ nhiều kênh ngõ vào được cấu hình sẵn trên các chân GPIO. Các chân dùng làm ngõ vào ADC phải được cấu hình ở chế độ AIN (Analog Input).

Hình 6. Bảng mapping chân GPIO và kênh ADC – PA0 là ADC12_IN0, PA1 là ADC12_IN1…
/* Cấu hình PA1 làm Analog Input cho ADC1 Channel 1 */
GPIO_InitTypeDef GPIO_InitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;   /* Analog Input */
GPIO_Init(GPIOA, &GPIO_InitStruct);

Bước 2: Cấp xung clock cho ADC

ADC được cấp xung từ RCC APB2. Cần cấp xung cho cả bộ ADC và GPIO Port tương ứng.

Hình 6. Bảng mapping chân GPIO và kênh ADC – PA0 là ADC12_IN0, PA1 là ADC12_IN1…
/* Cấp xung clock cho ADC1 và GPIOA */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

/* Cấu hình prescaler cho ADC clock (tối đa 14 MHz) */
RCC_ADCCLKConfig(RCC_PCLK2_Div6);   /* 72MHz / 6 = 12MHz */

Bước 3: Cấu hình struct ADC_InitTypeDef

Các tham số ADC được cấu hình trong struct ADC_InitTypeDef:

Tham sốMô tảGiá trị thường dùng
ADC_ModeChế độ đơn kênh (Independent) hay đa kênhADC_Mode_Independent
ADC_NbrOfChannelSố kênh ADC sử dụng trong một lần quét1
ADC_ContinuousConvModeChuyển đổi liên tục hay một lần rồi dừngENABLE / DISABLE
ADC_ExternalTrigConvKích hoạt bằng trigger ngoài hay khôngADC_ExternalTrigConv_None
ADC_ScanConvModeBật chế độ quét lần lượt từng kênhDISABLE (1 kênh)
ADC_DataAlignCăn lề dữ liệu 12-bit trong thanh ghi 16-bitADC_DataAlign_Right

Bước 4: Calibration và khởi động ADC

Sau khi cấu hình, STM32 khuyến nghị thực hiện hiệu chuẩn (calibration) để giảm sai số do điện dung bên trong gây ra:

/* Bật ADC */
ADC_Cmd(ADC1, ENABLE);

/* Reset thanh ghi calibration */
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));

/* Thực hiện hiệu chuẩn */
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));

Ví dụ hoàn chỉnh: Đọc ADC1 Channel 1 (PA1)

#include "stm32f10x.h"

void ADC_Init_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    ADC_InitTypeDef   ADC_InitStruct;

    /* ── 1. Cấp clock ─────────────────────────────── */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);   /* ADC clock = 12 MHz */

    /* ── 2. GPIO PA1 → Analog Input ──────────────── */
    GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ── 3. Cấu hình ADC1 ────────────────────────── */
    ADC_InitStruct.ADC_Mode               = ADC_Mode_Independent;
    ADC_InitStruct.ADC_NbrOfChannel       = 1;
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStruct.ADC_ExternalTrigConv   = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_ScanConvMode       = DISABLE;
    ADC_InitStruct.ADC_DataAlign          = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    /* ── 4. Cấu hình kênh: CH1, rank 1, 55.5 cycles ─ */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);

    /* ── 5. Bật ADC ──────────────────────────────── */
    ADC_Cmd(ADC1, ENABLE);

    /* ── 6. Calibration ──────────────────────────── */
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
}

uint16_t ADC_Read(void)
{
    /* Bắt đầu chuyển đổi */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    /* Chờ chuyển đổi hoàn tất */
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));

    /* Trả về giá trị 12-bit (0–4095) */
    return ADC_GetConversionValue(ADC1);
}

int main(void)
{
    ADC_Init_Config();

    while (1) {
        uint16_t raw     = ADC_Read();
        float    voltage = (float)raw * 3.3f / 4095.0f;
        /* Sử dụng voltage... */
    }
}

Lưu ý khi sử dụng ADC trong STM32

  • ADC clock tối đa 14 MHz – cấu hình prescaler phù hợp (thường Div6 hoặc Div8 với SYSCLK 72 MHz).
  • Điện áp đầu vào phải trong khoảng 0V → VDDA (thường là 3.3V) – tuyệt đối không vượt quá, sẽ hỏng MCU.
  • Luôn thực hiện calibration sau khi bật nguồn để đảm bảo độ chính xác.
  • Với nhiều kênh đồng thời, dùng Scan mode + DMA để tránh chiếm CPU.
  • SampleTime càng dài độ chính xác càng cao nhưng tốc độ chậm hơn – chọn phù hợp với trở kháng nguồn tín hiệu.
  • ADC1 có thêm kênh nội: Temperature Sensor (CH16) và Vrefint (CH17).

Kết luận

ADC (Analog-to-Digital Converter) là cầu nối giữa thế giới vật lý và hệ thống số trong các ứng dụng nhúng. STM32F103 tích hợp ADC 12-bit với nhiều tính năng mạnh mẽ: 2 bộ ADC độc lập, nhiều kênh, hỗ trợ trigger ngoài, calibration tự động và tích hợp DMA.

Hiểu rõ các tham số trong ADC_InitTypeDef và quy trình cấu hình đúng thứ tự (GPIO → Clock → Init → Calibration → Start) sẽ giúp bạn đọc tín hiệu analog chính xác và ổn định trong các dự án automotive embedded như đọc cảm biến vị trí bướm ga, cảm biến áp suất, đo điện áp ắc-quy…

Leave a Reply