MCP3208 모듈을 이용한 아날로그 조도 센서 측정 > 스마트기기시스템

본문 바로가기

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

필기자
2026-04-07 13:17 447 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 연결
20260407135546_1e10e7c9650f8cf9ab1e6044b654fa7b_j5ud.png

20260414151019_485acd1c628ac79d6a9076cfe9fc17f7_r4jc.png
 
  • SPI 기능 활성화

pi@pi20221234:~ $ sudo raspi-config

3529946169_5MDQqcE2_57c24f82f831e3a20ef627bd9288d0a9ffa3ca8c.png

3529946169_b7KzdR9c_c98b92fce061a5a87a8317d3f1b0758b8180383a.png

3529946169_Re1AzdgP_92339a54a0f61b2f3c28e42bcd4413cf7c268978.png

3529946169_hFZLBDmf_615a6080524d68233a5aa1151810edf9a9d11893.png

3529946169_Rug7a2Y5_f2723c5a6f5a6d5ba9c36915705010f080d0607e.png


pi@pi20221234:~ $ sudo reboot


2. 아날로그 조도 센서 값 측정 프로그램
  • 기본 구동 프로그램 : 라즈베리파이 가상환경에서 파이썬 코딩
    • 라즈베리파이에서 SPI 설정
    • VSCode에서 light_sensor_analog.py 파일 생성
    • SPI 통신을 통해 조도 센서 아날로그 값을 디지털로 변환 후 측정하는 코드 실습
3529946169_JMoUnf3G_bd5f377fb418a622125b5c609b5c9edfce57cc47.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_GJAbCpRt_0743fbb398cc7c4b24b405a08986140e8cee4059.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 동작 방식
20260407180453_1e10e7c9650f8cf9ab1e6044b654fa7b_3yrf.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_fI58d40m_2102cae1badf0ce86335eebeac5945f956a0ad72.gif
 

댓글목록0

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