[실습] MCP3208 모듈을 이용한 아날로그 조도 센서 측정
필기자
2026-04-08 14:03
83
0
본문
MCP3208 모듈을 이용한 아날로그 조도 센서 측정
목 적
1. MCP3208 기반 조도 센서 회로 구성
2. 아날로그 조도 센서 값 측정 프로그램
1. MCP3208 기반 조도 센서 회로 구성





2. 아날로그 조도 센서 값 측정 프로그램
목 적
- 라즈베리파이에서 MCP3208 ADC 모듈을 통해 조도 센서(SSBH-011)의 아날로그 값을 측정하는 방법 이해한다.
1. MCP3208 기반 조도 센서 회로 구성
2. 아날로그 조도 센서 값 측정 프로그램
1. MCP3208 기반 조도 센서 회로 구성
- MCP3208
- 12비트 해상도의 8채널 아날로그-디지털 변환기(ADC)
- SPI 인터페이스로 라즈베리파이와 통신
- 라즈베리파이는 아날로그 입력 기능이 없어 외부 ADC 필요
- 기본 사양
- 입력 채널: CH0 ~ CH7
- 해상도: 12비트 (0~4095)
- 인터페이스: SPI
- 동작 전압: 2.7V ~ 5.5V
- 변환 속도: 최대 100ksps
- 조도 센서 회로 구성
- SSBH-011 조도 센서, MCP3208, GPIO 연결
- SPI 기능 활성화
pi@pi20221234:~ $ sudo raspi-config





pi@pi20221234:~ $ sudo reboot
2. 아날로그 조도 센서 값 측정 프로그램
- 기본 구동 프로그램 : 라즈베리파이 가상환경에서 파이썬 코딩
- 라즈베리파이에서 SPI 설정
- VSCode에서 light_sensor_analog.py 파일 생성
- SPI 통신을 통해 조도 센서 아날로그 값을 디지털로 변환 후 측정하는 코드 실습

pi@pi20221234:~ $ sudo apt update
pi@pi20221234:~ $ sudo apt install python3-spidev
pi@pi20221234:~ $ iot_
(iot) pi@pi20221234:~/iot $ pip install git+https://github.com/doceme/py-spidev
import spidev
import time
# ==============================
# 설정값
# ==============================
VREF = 3.3 # ← 하드웨어 기준 전압 (3.3 또는 5.0)
# ==============================
# SPI 초기화
# ==============================
spi = spidev.SpiDev() # 하드웨어 SPI 컨트롤러
spi.open(0, 0) # bus=0, CE0
spi.max_speed_hz = 1000000 # ~125MHz, 전용 하드웨어가 처리
spi.mode = 0
def read_adc(channel):
if not 0 <= channel <= 7:
return -1
# ==============================
# [1] 채널 분해 (D2 D1 D0)
# ==============================
# CH5 = 5 = 0b00000101
# ↑↑↑
# D2 D1 D0
# >> 비트 오른쪽 시프트, & 0b1 로 최하위 1비트만 추출
d2 = (channel >> 2) & 0b1
d1 = (channel >> 1) & 0b1
d0 = (channel >> 0) & 0b1
# ==============================
# [2] cmd1 (Start + SGL + D2)
# ==============================
# 0b00000110
# ↑↑
# │└ SGL=1 (단일 모드)
# └─ Start=1 (통신 시작)
# CH0 → 0b00000110 (D2=0)
# CH5 → 0b00000111 (D2=1)
cmd1 = 0b00000110 | d2
# ==============================
# [3] cmd2 (D1, D0)
# ==============================
# D1을 7번 비트로, D0을 6번 비트로 올림
# 나머지 6비트(하위)는 don't care → 0으로 채움
# CH0 → 0b00000000 (D1=0, D0=0)
# CH5 → 0b01000000 (D1=1, D0=0)
cmd2 = (d1 << 7) | (d0 << 6)
# ==============================
# [4] SPI 전송
# ==============================
# 보낸 바이트 수 = 받는 바이트 수 (SPI 규칙)
# 3바이트 보내면 3바이트 돌아옴
adc = spi.xfer2([cmd1, cmd2, 0])
print(f"Raw SPI response: {adc}")
# ==============================
# [5] 12bit 데이터 복원
# ==============================
# adc[0] = 더미 (명령 전송 구간에서 수신된 무의미 데이터)
# adc[1] = xxxx B11 B10 B9 B8
# adc[2] = B7 B6 B5 B4 B3 B2 B1 B0
# Step 1: 상위 4비트 추출 (앞쪽 xxxx null 4비트 마스킹)
high = adc[1] & 0b00001111
# 10101100
# & 00001111
# ----------
# 00001100 ← B11~B8
# Step 2: 자리 이동 (하위 8비트 자리를 비워둠)
high = high << 8
# 00001100
# → 00001100 00000000
# Step 3: 하위 결합
data = high | adc[2]
# 00001100 00000000
# | 00000000 11110000
# -------------------
# 00001100 11110000
# 최종 결과 : 0 ~ 4095 사이의 12비트 정수
return data
def adc_to_voltage(value):
# MCP3208은 12비트 ADC → 최댓값 2^12 - 1 = 4095
# value=0 → 0V
# value=4095 → VREF(3.3V)
return value * VREF / 4095
try:
print("조도센서 값 읽기 시작...")
print(f"VREF = {VREF}V 기준")
print("Ctrl+C로 종료")
while True:
val0 = read_adc(0)
#val5 = read_adc(5)
volt0 = adc_to_voltage(val0)
#volt5 = adc_to_voltage(val5)
print(f"[CH0] 값: {val0:4d} | 전압: {volt0:.3f}V")
#print(f"[CH5] 값: {val5:4d} | 전압: {volt5:.3f}V")
print("-" * 50)
time.sleep(1)
except KeyboardInterrupt:
print("\n프로그램 종료")
finally:
spi.close()
print("SPI 연결 종료")
- 아날로그 조도 센서 데이터 SPI전송과정(spidev 기준, 라즈베리파이)
- 1st : Master -> MCP3208
- 0b00000110 = 00000(Dummy), 1(Start bit), 1(SGL), 0(D2)
| 비트 위치 | 7 | 6 | 5 | 4 | 3 | 2 (S) | 1 (SGL/DIFF) | 0 (D2) |
|---|---|---|---|---|---|---|---|---|
| 의미 | x | x | x | x | x | Start | 단일/차동 | 채널 상위비트 |
| 값 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
- 2nd : Master -> MCP3208
- 예: CH0 → D1=0, D0=0 → 0b00000000 (0x00)
- 예: CH7 → D1=1, D0=1 → 0b11000000 (0xC0), 1st의 D2 1로 세팅
| 비트 위치 | 7 (D1) | 6 (D0) | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 의미 | 채널 중간비트 | 채널 하위비트 | 무시 | 무시 | 무시 | 무시 | 무시 | 무시 |
| 값 (CH0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 값 (CH7) | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
- 3rd : Master -> MCP3208
| 비트 위치 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 의미 | 더미 | 더미 | 더미 | 더미 | 더미 | 더미 | 더미 | 더미 |
| 값 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 바이트 | 비트 번호 | 필드 이름 | 설명 |
|---|---|---|---|
| 1st (8bit) | 7 ~ 3 | Dummy | 항상 0, 의미 없음 |
| 2 | Start bit | 항상 1 (전송 시작 신호) | |
| 1 | SGL/DIFF | 1 = 단일 입력, 0 = 차동 입력 | |
| 0 | D2 | 채널 선택 상위 비트 (채널 번호의 3비트 중 상위) | |
| 2nd (8bit) | 7 | D1 | 채널 선택 하위 비트 |
| 6 | D0 | 채널 선택 최하위 비트 | |
| 5 ~ 0 | Don't care | 아무 값이나 무관 (무시됨) | |
| 3rd (8bit) | 7 ~ 0 | Dummy Read | 클럭 유지를 위한 더미 비트 (응답 데이터 읽기 목적) |
- MCP3208 -> Master(3byte cmd 기준)
| 비트 순서 | 의미 | 설명 |
|---|---|---|
| 23 ~ 16 bit | Null / Don't care | 무시됨, 값 없음 (대기 상태), 0번지 값 |
| 15 ~ 12 bit | Null | 무시됨, 1번지 앞 4자리 값 |
| 11 ~ 0 bit | B11 ~ B0 (12bit) | 변환된 디지털 값 (MSB부터 전송), 1번지 뒷 4자리+2번지 값 |
- 센서 SSBH-011의 AO 출력 특성
- 밝아지면 LDR(Light Dependent Resistor,광저항) 저항이 감소되면서 LDR 쪽으로 흐르는 전류가 많아짐
- AO 측정되는 전압이 감소됨(즉, LDR이 소비한 전압을 측정)
- 밝음 -> LDR 저항 작음 -> 전류 잘 흐름 -> LDR 소비 전압 작음 -> AO 낮음
- 어둠 -> LDR 저항 큼 -> 전류 못 흐름 -> LDR 소비 전압 큼 -> AO 높음
| 방식 | 조도 증가 시 | AO 전압 변화 | 설명 |
|---|---|---|---|
| 일반형 (반비례) | 밝아지면 | 낮아짐 | LDR(저항 감소) → 전압 감소 |
| 역형 (비례) | 밝아지면 | 높아짐 | 회로 반대로 구성됨 |
- 단일/차동에 차동의 노이즈 보정
- 단일, 노이즈 없음 : CH0 = 2.5V
- 단일, 노이즈 있음 : CH0 = 2.5 + 0.3 = 2.8V, 측정값 = 2.8V (2.8V에 노이즈가 섞인지 구분 불가)
- 차동, 노이즈 있음 : CH0 = 2.5 + 0.3 = 2.8V, CH1 = 0.0 + 0.3 = 0.3V, 측정값 = 2.8 - 0.3 = 2.5V (노이즈 제거)
- SPI 통신에서 CLK(Clock) 신호의 역할
- CLK(Clock)는 SPI 통신에서 데이터 전송 타이밍을 맞추는 기준 신호
- 클럭 한 주기마다 1비트씩 데이터가 이동함
- 데이터는 상승 또는 하강 엣지 중 한 번만 읽음
- 라즈베리파이(Master)가 CLK 신호를 생성하며, MCP3208은 이 신호에 맞춰 데이터를 송수신함
| 구분 | 설명 |
|---|---|
| CLK | 데이터를 언제 읽고 보내는지 결정하는 타이밍 신호 |
| 1클럭 주기 | 1비트 데이터 전송 (High + Low 한 사이클) |
- CLK 동작 방식
- SPI 모드에 따른 데이터 읽기 타이밍
| SPI Mode | 읽는 시점 | 설명 |
|---|---|---|
| Mode 0 | 상승엣지 (↑) | CLK가 Low → High로 변할 때 데이터 읽음 |
| Mode 1 | 하강엣지 (↓) | CLK가 High → Low로 변할 때 데이터 읽음 |
| Mode 2 | 하강엣지 (↓) | 클럭 시작 상태가 반대인 구조 |
| Mode 3 | 상승엣지 (↑) | 클럭 시작 상태가 반대인 구조 |
- 현재 실습 코드 설정
- spi.mode = 0
- Mode 0에서는 CLK 상승엣지(↑)에서만 데이터를 읽고, 하강엣지에서는 데이터를 준비
- SPI bus 모드
| 구분 | SPI0 (bus 0) | SPI1 (bus 1) |
|---|---|---|
| 기본 상태 | 기본 활성화 | 비활성 (설정 필요) |
| MOSI | GPIO10 (핀 19) | GPIO20 (핀 38) |
| MISO | GPIO9 (핀 21) | GPIO19 (핀 35) |
| CLK | GPIO11 (핀 23) | GPIO21 (핀 40) |
| CS 핀 | CE0(GPIO8, 핀 24) CE1(GPIO7, 핀 26) |
CS0(GPIO18, 핀 12) CS1(GPIO17, 핀 11) CS2(GPIO16, 핀 36) |
| CS 개수 | 2개 | 3개 이상 |
| 코드 사용 | spi.open(0, 0) spi.open(0, 1) |
spi.open(1, 0) spi.open(1, 1) spi.open(1, 2) |
| 사용 난이도 | 쉬움 | 설정 필요 (config.txt) |
| 사용 목적 | 기본 SPI 통신 | 장치 확장용 |
| 추천 사용 | 일반적인 경우 대부분 사용 | SPI 장치가 많을 때만 사용 |
댓글목록0