🔌
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)으로 설정한다
과제
- 읽은 ADC 값을 19편 USART로 "adc=NNNN mv=MMMM" 형식으로 출력
- 여러 번 읽어 평균을 내 노이즈를 줄이는 간단한 필터 추가
- 채널1(PA1)을 추가로 읽도록 SQR3/SMPR 설정 변경