[태그:] i2c

  • i2c 세부 사항

    I2C의 **중재(Arbitration)**는 여러 개의 마스터가 동시에 대화를 시작하려고 할 때, **”교통정리”**를 하는 아주 똑똑한 규칙입니다.

    앞서 배운 **오픈 드레인(Open-Drain)**과 **와이어드 AND(Wired-AND)**의 특징을 생각하면 아주 쉽게 이해할 수 있습니다. 핵심은 **”0(Low)을 보낸 사람이 1(High)을 보낸 사람을 이긴다”**는 것입니다.


    1. 중재의 핵심 원리: “말하면서 듣기”

    I2C 마스터들은 데이터를 보낼 때 그냥 던지기만 하는 게 아니라, **내가 보낸 데이터가 선(SDA)에 똑같이 나타나고 있는지 실시간으로 확인(모니터링)**합니다.

    • Logic 1을 보낼 때: 마스터는 스위치를 엽니다. (풀업 저항에 의해 선이 High가 되길 기대함)
    • Logic 0을 보낼 때: 마스터는 스위치를 닫아 선을 바닥(GND)으로 강제로 끌어내립니다.

    2. 중재 과정 (Step-by-Step)

    상황: 마스터 A마스터 B가 정확히 동시에 통신을 시작했다고 가정해봅시다.

    1. 비트 단위 비교: 두 마스터는 각자의 데이터를 한 비트씩 보냅니다.
    2. 공통 구간: 처음 몇 비트가 둘 다 ‘0’이거나 둘 다 ‘1’이라면, 두 마스터는 서로 다른 누군가가 같이 말하고 있다는 사실을 전혀 모른 채 계속 전송합니다.
    3. 충돌 발생 (결정적 순간):
      • 마스터 A: 이번 비트에서 **’1′(High)**을 보내려고 스위치를 열었습니다.
      • 마스터 B: 이번 비트에서 **’0′(Low)**을 보내려고 선을 꽉 붙잡아 내렸습니다.
      • 결과: SDA 선은 마스터 B 때문에 **’0′(Low)**이 됩니다. (Wired-AND 특성)
    4. 패배자 발견: ‘1’을 보냈던 마스터 A는 선을 확인해보니 ‘0’이 되어 있는 것을 발견합니다. “어? 난 1을 보냈는데 선은 0이네? 나보다 우선순위가 높은 누군가가 있구나!”라고 판단합니다.
    5. 즉시 항복: 마스터 A는 그 즉시 전송을 중단하고 손을 뗍니다. 이를 **’중재 패배’**라고 합니다.
    6. 승자 계속: 마스터 B는 자기가 보낸 ‘0’이 선에도 그대로 ‘0’으로 나타나므로, 방해받지 않았다고 생각하고 계속 데이터를 보냅니다. 이를 **’중재 승리’**라고 합니다.

    3. 왜 I2C 중재가 훌륭한가요?

    • 데이터가 깨지지 않음: 승리한 마스터(마스터 B)는 상대방이 중간에 포기했다는 사실조차 모를 정도로 신호에 아무런 간섭을 받지 않고 통신을 이어갑니다.
    • 하드웨어 자동 처리: 개발자가 코드로 “누가 먼저 보낼지” 정해줄 필요가 없습니다. 칩 내부의 로직이 알아서 처리합니다.
    • 자연스러운 우선순위: 주소 값이 낮은 장치(앞부분에 ‘0’이 더 많은 장치)가 자연스럽게 우선순위를 갖게 됩니다.

    요약하자면

    I2C 중재는 **”누가 더 먼저 선을 Low(0)로 당기느냐”**를 겨루는 게임입니다.

    ‘1’을 보내며 버스 상태를 관찰하던 마스터가, ‘0’을 보내며 선을 장악한 마스터에게 조용히 자리를 양보하는 질서 정연한 방식입니다.

    이해가 좀 더 되셨나요? 중재가 끝난 뒤 패배한 마스터가 어떻게 행동하는지 등 추가로 궁금한 점이 있으시면 말씀해 주세요!

  • sensehat 온도 + LED matrix(2)

    sensehat 온도 + LED matrix(2)

    제한된 화면에 온도, 습도를 어떻게 표시할까 고민하다, 디바이스를 2개를 만들로 했다. 사용자가 디바이스 개수를 제한 없이 만들 수 있는 점이 좋다. 2개를 만들어 온도로 한 개, 습도로 한 개를 할당 했다.

    위 그림에서 사용자가 rs-tmpre1를 읽어(read) module이 저장한 HTS221 온도 관련 레지스터 값을 얻는다. 사용자가 받은 값으로 온도를 계산한 후 LED Matrix를 빨강으로 설정하고, 커널로 쓴다(write). 사용자가 rs-tempre2를 읽어(read) module이 저장한 HTS221 습도 관련 레지스터 값을 얻는다. 역시 받은 값으로 습도를 계산하고, LED Matrix를 녹색으로 설정, 커널로 쓴다(write). 사용자 프로그램이 일정 주기로 계속한다.

    LED Matrix가 숫자를 보여주는 방식이 너무 촌스럽다. 좀 개선해야 겠다.

    1. 부팅
    2. 내가 만든 모듈 로딩
    3. 사용자 프로그램 실행

    1 ~ 3번을 자동으로 하고 싶었다. 이게 좀 까다롭다. 처음에는 간단하게 crontab으로 @reboot로 설정했으나, 실패했다. 모듈이 올라가기 전에 사용자 프로그램이 실행된다. 찾던 중 udev가 이벤트를 감지(디바이스 추가, 삭제)하여 사용자 정의 행동을 한다고 알았다.

    udev에 등록하려면 디바이스 정보를 알아야 된다. 왜 class로 추가하는지 느꼈다. 인터넷 검색으로 다음과 같이 알아냈다.

    pi@raspberrypi:~ $ udevadm info /dev/rs-tmpre1
    P: /devices/virtual/rpi-sensehat/rs-tmpre1
    N: rs-tmpre1
    L: 0
    E: DEVPATH=/devices/virtual/rpi-sensehat/rs-tmpre1
    E: DEVNAME=/dev/rs-tmpre1
    E: DEVMODE=0666
    E: MAJOR=235
    E: MINOR=0
    E: SUBSYSTEM=rpi-sensehat
    
    pi@raspberrypi:~ $ udevadm info /dev/rs-tmpre2
    P: /devices/virtual/rpi-sensehat/rs-tmpre2
    N: rs-tmpre2
    L: 0
    E: DEVPATH=/devices/virtual/rpi-sensehat/rs-tmpre2
    E: DEVNAME=/dev/rs-tmpre2
    E: DEVMODE=0666
    E: MAJOR=235
    E: MINOR=1
    E: SUBSYSTEM=rpi-sensehat
    
    pi@raspberrypi:/dev $ udevadm info -a -n /dev/rs-tmpre1
    
    Udevadm info starts with the device specified by the devpath and then
    walks up the chain of parent devices. It prints for every device
    found, all possible attributes in the udev rules key format.
    A rule to match, can be composed by the attributes of the device
    and the attributes from one single parent device.
    
      looking at device '/devices/virtual/rpi-sensehat/rs-tmpre1':
        KERNEL=="rs-tmpre1"
        SUBSYSTEM=="rpi-sensehat"
        DRIVER==""
        ATTR{power/control}=="auto"
        ATTR{power/runtime_active_time}=="0"
        ATTR{power/runtime_status}=="unsupported"
        ATTR{power/runtime_suspended_time}=="0"

    아래 스크립트와 같이 사용자 정의 rules를 만들면 전원이 들어가면서 실행됨을 확인 했다.

    pi@raspberrypi:/etc/udev/rules.d $ pwd
    /etc/udev/rules.d
    pi@raspberrypi:/etc/udev/rules.d $ cat 100-sensehat.rules 
    ACTION=="add", SUBSYSTEM=="rpi-sense-tmpre", KERNEL=="rs-tmpre2", RUN+="/bin/sensehat_display"

    아쉽게도 udev로 등록한 프로세가 오래 못산다. 이벤트가 끝나면 process가 죽는다. systemctl로 서비스 형식으로 등록해야 한다. 참조하여 아래와 같이 설정했다. 잘 된다. 이제 LED matrix를 어떻게 해 봐야겠다.

    pi@raspberrypi:/etc/systemd/system $ cat sensehat_display.service 
    [Unit]
    Description=SenseHat Temperatur & Humidity Monitor
    
    [Service]
    Type=simple
    ExecStart=/usr/bin/sensehat_display
    
    [Install]
    https://github.com/now0930/sensehat_toy

    참조 사이트

  • sensehat 온도 + LED matrix(1)

    sensehat 온도 + LED matrix(1)

    온도를 LED Matrix에 표시하기

    나름 쓸만한 디바이스를 만들기 위해 온도계를 구상해 봤다. 지마켓에서 온도, 습도, 시각을 측정하는 디바이스를 5,400원에 구매할 수 있다. 라즈베리 파이 + sensehat = 120,000원 정도 하려나? 약 20배 비싸지만 인터넷에 연결되어 싸다고 세뇌시키고 있다. ^^! 그런데, sensehat에 있는 hts221이 보드에 붙어 있다. 실내 온도를 측정하지 않고 보드 온도를 측정 하고 있다.

    HTS221로 받은 온도를 LED matrix에 표시했다. 숫자를 바로 LED matrix에 표시할 수 없어, 각 숫자에 맞춰 8 * 8 * 3가지 색을 정의 해야 한다. 이게 참 시간을 잡아먹는다. 왜 frame buffer를 써야 되는지 알 만했다.

    기본 설정으로는 온도를 바로바로 측정할 수 없다. 02h 레지스터 ODR을 1Hz로 설정해야 바로 측정할 수 있다. 12.5Hz로 측정하면 왜 안되는지 잘 모르겠다.

    kernel에서 float 형식을 사용할 수 없다. 모든 데이터를 받아 사용자 영역에서 float 형식으로 온도를 계산해야 할 듯 하다. 지금은 user program이 led matrix를 써 주면 바로 표시하지만, timer나 workqueue를 추가하여 필요할 때 표시하게 수정할 것 이다. 아니면 습도도 받아 그래프로 표시하던가. 온도, 습도를 표시하려면 그래프가 좋아 보인다.

    코드

    참조 사이트

    https://github.com/ControlEverythingCommunity/HTS221/blob/master/C/HTS221.c

  • sensehat i2c led2472g(2)

    pixel 제어하기

    8 * 8 LED matrix 색, 위치를 개별 제어하려면 픽셀 주소, 색을 알아야 한다. 가로 8, 세로 8, 1 점 당 rgb 3개 = 192개 주소가 rgb 값을 갖는다. 여러 삽질과 인터넷 검색으로 아래 정보를 확인 했다. 개별 소자를 다음과 같이 제어할 수 있다.

    $>i2cset 1(i2c 채널 번호) 0x47?(노드 번호) 0x1(점 주소) 0x10(색 값)

    각 점별 최대 0x63을 입력할 수 있다. 0x63을 넘어가면 다른 픽셀에 영향을 미치는지 깜박였던 것 같다. python으로 작성한 LED matrix display 코드를 그대로 사용해도 된다. 각 좌표에서 1을 빼야 된다.

    픽셀로 제어하다 보니 폰트, 그림 이런 거 없다. 왜 frame buffer로 제어했는지 이제 이해간다!!

    참조 사이트

  • sensehat i2c led2472g

    I2C에 연결된 LED Matrix 밝기 제어

    sensehat 모듈에 LED matrix가 i2c로 설치, 연결되어 있다. LED2472G가 각 연결된 LED를 제어하는데, AVR이 i2c로 라즈베리 파이와 LED2472G 를 중계한다. LED를 제어한다고 LED2472G datasheet를 찾으면 별 도움이 안된다. AVR로 led를 어떻게 제어하는지 잘 공개되지 않은 듯 하다. 아무리 찾아도 c로 작성한 코드를 찾을 수 없다. i2c 0x46에 0~191까지 밝기를 전송하면 led 밝기를 제어할 수 있다. 아래 game of life python 코드를 보면 대충 어떻게 돌아가는지 알 수 있다.

    shell로 i2cset으로 한 개씩 램프를 껐다. 0x46번은 LED matrix 노드 번이고, 0 ~ 191까지 led 램프 위치를 말하는 듯 하다. 뒤에 255, 0은 밝기다. 컬러 코드를 넣는 방법이 있는데, 아직 잘 모르겠다.

    pi@raspberrypi:~/rasp/sense_temperature $ i2cset 1 0x46 1 255
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will write to device file /dev/i2c-1, chip address 0x46,
    data address 0x01, data 0xff, mode byte.
    Continue? [Y/n] y
    pi@raspberrypi:~/rasp/sense_temperature $ i2cset 1 0x46 1 0
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will write to device file /dev/i2c-1, chip address 0x46,
    data address 0x01, data 0x00, mode byte.
    Continue? [Y/n] y
    pi@raspberrypi:~/rasp/sense_temperature $ i2cdetect 1
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will probe file /dev/i2c-1.
    I will probe address range 0x08-0x77.
    Continue? [Y/n] y
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:                         -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- 1c -- -- -- 
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    40: -- -- -- -- -- -- 46 -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- 5c -- -- 5f 
    60: -- -- -- -- -- -- -- -- -- -- 6a -- -- -- -- -- 
    70: -- -- -- -- -- -- -- --                   

    shell로 0부터 191까지 해보면 led가 꺼지지 않는데 루프를 만들어 돌리면 한 번에 꺼진다. 구매 5일만에 led를 껐다.

    고수는 frame buffer를 만들어서 하는 것 같은데, 아직 잘 모르겠다. 정말 필요한지도 모르겠다.

    참조 사이트