← Embedded C 강의 목록으로
🔌
STM32
STM32 · 선수: 19강

20. ADC 아날로그 입력

지금까지 다룬 핀은 모두 디지털(0/1)이었습니다. 세상의 신호 대부분은 온도·밝기·소리처럼 연속적인 아날로그입니다. ADC(Analog-to-Digital Converter)는 아날로그 전압을 숫자로 바꿔 MCU가 다룰 수 있게 합니다. STM32F103의 12비트 ADC로 PA0의 전압을 0~4095로 읽고 밀리볼트로 환산합니다. STM32 주변장치 시리즈(13~20편)의 마지막으로, 클럭·핀·폴링이 한 번에 쓰입니다.

ADC12비트EOC보정ADCPRE밀리볼트
소요 시간
약 1.5~2시간
난이도
📊 중급
선수 조건
🎯 19강
결과물
ADC 클럭(≤14MHz)·채널·샘플시간을 설정하고 F1의 보정(RSTCAL/CAL) 후 EOC 폴링으로 12비트 값을 읽어 밀리볼트로 환산할 수 있습니다.

이 강의에서 배우는 것

  • 1ADC의 12비트 분해능(0~4095)과 기준 전압 개념을 이해한다
  • 2ADC 클럭 제약(≤14MHz)과 프리스케일러를 설정한다
  • 3채널·샘플 시간·변환 시퀀스를 설정한다
  • 4F1 ADC의 보정(RSTCAL/CAL)과 변환 완료(EOC) 폴링을 구현한다
  • 5ADC 카운트를 밀리볼트로 환산한다

소개

12비트 ADC는 입력 전압 범위(0~Vref, 보통 3.3V)를 4096단계로 나눕니다. 한 단계(LSB)는 약 0.806mV. mV = adc × 3300 / 4095. 가변저항·조도센서·온도센서가 모두 ADC로 들어옵니다.

핵심 개념

1) 클럭 제약과 채널

c
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;   /* ADC 클럭 ≤14MHz: 72/6 = 12MHz */
ADC1->SQR3  = 0u;                    /* 1차 변환 채널 0 (PA0) */
ADC1->SMPR2 |= (7u << 0);            /* 채널0 샘플시간 최대(239.5 cycle) */
/* ADC 핀은 아날로그 입력(0x0) — 디지털 버퍼/풀업 끄기 */

2) F1 보정과 변환(EOC)

c
ADC1->CR2 |= ADC_CR2_ADON;            /* 전원 ON (tSTAB 대기) */
ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL){}
ADC1->CR2 |= ADC_CR2_CAL;    while(ADC1->CR2 & ADC_CR2_CAL){}   /* 보정 */

ADC1->CR2 |= ADC_CR2_ADON;            /* ADON 재설정 = 변환 시작 */
while (!(ADC1->SR & ADC_SR_EOC)) { }  /* 변환 완료 대기 */
uint16_t v = ADC1->DR & 0x0FFFu;      /* 12비트 결과 (읽으면 EOC 클리어) */
⚠️

F1은 켠 뒤 보정(RSTCAL→CAL)을 한 번 해야 정확합니다. 밀리볼트 환산은 반드시 곱한 뒤 나누고 32비트로 계산하세요: (uint32_t)adc * 3300 / 4095.

핵심 예제

c
#include "stm32f10x.h"
static void adc1_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
    RCC->CFGR    |= RCC_CFGR_ADCPRE_DIV6;          /* 12MHz */
    GPIOA->CRL &= ~(0xFu << 0);                    /* PA0 아날로그 입력(0x0) */
    ADC1->SQR3 = 0u; ADC1->SMPR2 |= (7u << 0);
    ADC1->CR2 |= ADC_CR2_ADON; for(volatile int i=0;i<10000;i++){}
    ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL){}
    ADC1->CR2 |= ADC_CR2_CAL;    while(ADC1->CR2 & ADC_CR2_CAL){}
}
static uint16_t adc_read(void){
    ADC1->CR2 |= ADC_CR2_ADON; while(!(ADC1->SR & ADC_SR_EOC)){}
    return (uint16_t)(ADC1->DR & 0x0FFFu);
}
int main(void){
    adc1_init();
    while (1) {
        uint16_t adc = adc_read();
        volatile uint32_t mv = (uint32_t)adc * 3300u / 4095u;
        (void)mv;
    }
}

환산 검산: ADC=0→0mV, 2048→1650mV, 4095→3300mV.

자주 하는 실수

Q. EOC 대기에서 멈춰요.

A. ADC 전원(ADON)을 켰는지, 변환을 시작(ADON 재설정/SWSTART)했는지, ADC 클럭(ADC1EN)이 활성인지 확인하세요.

Q. 값이 들쭉날쭉하고 부정확해요.

A. ① 보정(RSTCAL/CAL)을 빠뜨림, ② 샘플 시간이 짧음(고임피던스 소스), ③ ADC 클럭이 14MHz 초과 — ADCPRE를 확인하세요.

Q. mV 계산이 0이 나와요.

A. adc/4095*3300 순서면 정수에서 0이 됩니다. 반드시 곱한 뒤 나누기: (uint32_t)adc * 3300 / 4095.

정리

  • ADC는 아날로그 전압을 12비트 정수(0~4095)로 바꾼다
  • mV = adc × 3300 / 4095 (곱한 뒤 나누고 32비트로 계산)
  • ADC 클럭은 ≤14MHz — ADCPRE로 나눈다
  • F1은 보정(RSTCAL/CAL) 후 변환, EOC 폴링 뒤 DR을 읽는다
  • ADC 핀은 아날로그 입력(0x0)으로 설정한다

과제

  1. 읽은 ADC 값을 19편 USART로 "adc=NNNN mv=MMMM" 형식으로 출력
  2. 여러 번 읽어 평균을 내 노이즈를 줄이는 간단한 필터 추가
  3. 채널1(PA1)을 추가로 읽도록 SQR3/SMPR 설정 변경
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드(과제·정답 포함)는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗