MCP3208 모듈을 이용한 아날로그 조도 센서 측정 > IoT 유무선 제어

본문 바로가기

[실습] MCP3208 모듈을 이용한 아날로그 조도 센서 측정

필기자
2026-04-08 14:03 83 0

본문

MCP3208 모듈을 이용한 아날로그 조도 센서 측정

목 적
  • 라즈베리파이에서 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 연결
3529946169_CHKkzWNf_347cb58ffbbb6cde121d3ed082699d0d26f87b2f.png
 
  • SPI 기능 활성화

pi@pi20221234:~ $ sudo raspi-config

3529946169_rmKZautG_78b534772e1d867b15462e764e58fd3dd4828fdb.png

3529946169_noPlOIKf_a01272a8bdbd60f005ce26a8271abbd00215bddb.png

3529946169_fEhklIC4_9411382d801a10204e1ebd34aa11f4f9e7f3b42a.png

3529946169_pTvQoX5U_fbca38a0e67c884a666d8c1bb0ce18b19fab44e7.png

3529946169_8bpzrl6A_d8af2d323c89214f36cae91c325c12959c436446.png


pi@pi20221234:~ $ sudo reboot


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

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 기준, 라즈베리파이)
3529946169_bARUCz38_b60380b8e01281b3749a8e4c14363b641289115f.png
  • 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 동작 방식
3529946169_AGDWaZkz_cbe75a5b4503f8480126fed7f52ff08543d3960c.png
  • 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 장치가 많을 때만 사용



3529946169_nSFkYGQB_6757980624014a9624db3b0759e3a775e7bcb720.gif
 

댓글목록0

등록된 댓글이 없습니다.
게시판 전체검색