아날로그 전압을 디지털 값으로 변환하는 주변장치(Analog-to-Digital Converter).
기본 개념
분해능 12비트 → 0 ~ 4095 (4096단계) 기준 전압 3.3V
1 LSB = 3.3V / 4096 = 0.806mV 읽은 값 2048 → 2048 × 0.806mV ≈ 1.65V
float voltage = (float)adc_raw * VREF / 4095.0f;
4096이 아니라 4095로 나누는 이유 — 최댓값 4095가 VREF에 대응하기 때문이다(정확히는 구현마다 다르므로 데이터시트 확인).
변환 방식
| 방식 | 속도 | 분해능 | 용도 |
|---|---|---|---|
| SAR (축차 비교) | 빠름 | 8~16비트 | MCU 내장 대부분 |
| Σ-Δ (시그마 델타) | 느림 | 16~24비트 | 정밀 측정, 계량 |
| Flash | 매우 빠름 | 낮음(6~8비트) | 고속 오실로스코프 |
| 이중 적분 | 매우 느림 | 높음 | 멀티미터 |
SAR은 이진 탐색과 같다 — 최상위 비트부터 하나씩 "이 값보다 큰가?"를 비교해 결정한다. 12비트면 12번 비교한다.
샘플링 시간 — 가장 중요한 설정
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; // 짧다
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; // 길다
왜 필요한가
ADC 입력에는 샘플 커패시터가 있다 스위치를 닫아 이 커패시터를 입력 전압까지 충전해야 한다
충전 시간 ∝ 소스 임피던스 × 커패시턴스
소스 임피던스가 높으면 충전이 느리다.
저항 분압기로 만든 신호 (임피던스 10kΩ) → 긴 샘플링 시간 필요
- 버퍼 앰프 출력 (임피던스 수십 Ω) — → 짧아도 된다
샘플링 시간이 부족하면 값이 실제보다 낮게 나오고, 직전에 변환한 다른 채널의 값이 섞여 들어온다(크로스토크).
"채널을 여러 개 쓰는데 값이 서로 영향을 준다" 면 이것이 원인이다. → 샘플링 시간을 늘리거나, 소스 임피던스를 낮추거나(버퍼), 입력에 커패시터(0.1μF)를 달아 순간 충전원을 만든다.
나이퀴스트와 에일리어싱
샘플링 주파수 > 신호 최고 주파수 × 2
위반하면 → 에일리어싱: 높은 주파수가 낮은 주파수로 둔갑한다
- 1kHz로 샘플링하는데 신호에 600Hz 성분이 있으면
- 400Hz 신호로 나타난다 (실제로 없는 신호!)
디지털 필터로는 제거할 수 없다. 이미 잘못 샘플링됐기 때문이다. → ADC 앞에 아날로그 저역 통과 필터(안티 에일리어싱 필터) 가 필요하다.
노이즈 대책
- ① VREF 를 안정화 — 전용 레퍼런스 IC 또는 잘 필터링된 전원
- ★ VREF가 흔들리면 모든 측정값이 흔들린다
- ② VDDA/VSSA 를 디지털 전원과 분리, 페라이트 비드 + 커패시터
- ③ 아날로그 그라운드를 한 점에서 연결
- ④ 입력에 RC 필터
- ⑤ 오버샘플링 + 평균 — N배 샘플링하면 SNR이 √N 배 개선
- ⑥ 4^n 배 오버샘플링 + 시프트 → n비트 분해능 증가 (디더링 필요)
⑤가 가장 손쉬운 개선책이다. 16회 평균하면 노이즈가 1/4로 준다.
DMA + 타이머 트리거 — 실무 표준
// 타이머가 정확한 간격으로 ADC를 트리거하고, DMA가 결과를 버퍼에 채운다
sConfig.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
HAL_ADC_Start_DMA(&hadc, buf, LENGTH);
왜 소프트웨어 트리거가 나쁜가 — HAL_ADC_Start()를 루프에서
부르면 인터럽트나 다른 작업 때문에 샘플 간격이 흔들린다(지터).
FFT 같은 신호 처리를 하면 결과가 번진다.
타이머 트리거는 하드웨어가 정확한 간격을 보장한다.
캘리브레이션
HAL_ADCEx_Calibration_Start(&hadc); // 기동 시 1회
내부 오프셋과 게인 오차를 보정한다. 안 하면 수십 LSB의 오차가 남는다.
내부 기준 전압 채널을 이용하면 VREF를 역산할 수 있다. 실제 VDDA = 3.3V × VREFINT_CAL / VREFINT_measured 배터리 전압이 변하는 기기에서 VDDA를 모르는 상태로도 정확한 측정이 가능하다. 매우 유용한 기법이다.
흔한 문제
- 값이 항상 0 또는 4095 — → 배선, GPIO를 아날로그 모드로 설정했는지
- 값이 흔들린다 — → 노이즈, 샘플링 시간, VREF 안정성
- 채널끼리 영향을 준다 — → 샘플링 시간 부족 (크로스토크)
- 값이 실제보다 낮다 — → 샘플링 시간 부족, 소스 임피던스 높음
- DMA가 안 온다 — → 스캔 모드, DMA 연속 모드 설정