임베디드 관련 카테고리/Embedded System

GPIO, I2C, SPI 통신 프로토콜 이해하기

CBJH 2024. 9. 26.
728x90
반응형

1. GPIO (General-Purpose Input/Output)

GPIO는 임베디드 시스템에서 입력출력 신호를 제어하는 디지털 핀을 의미합니다. 이 핀은 여러 장치를 연결하고 제어할 수 있는 범용 입출력 포트로, 마이크로컨트롤러나 라즈베리 파이 같은 보드에서 많이 사용됩니다.

특징:

  • 단순한 신호 제어: GPIO는 단순한 HIGH(1) 또는 LOW(0) 신호로 동작합니다.
    • HIGH는 전압이 있음을 의미하고, LOW는 전압이 없는 상태를 의미합니다.
  • 입력/출력 모드: 각 핀을 입력(Input) 모드 또는 출력(Output) 모드로 설정할 수 있습니다.
    • 입력 모드: 센서 같은 장치에서 신호를 받아들일 때 사용됩니다.
    • 출력 모드: LED나 모터와 같은 장치를 제어할 때 사용됩니다.

GPIO 사용 예:

  • LED 제어: GPIO 핀을 출력 모드로 설정하여 LED를 켜고 끄는 간단한 제어.
  • 버튼 입력: GPIO 핀을 입력 모드로 설정하여 버튼을 누르는 신호를 받아 동작을 수행.

GPIO 코드 예시 (C++):

#include <wiringPi.h>
#include <iostream>

using namespace std;

int main() {
    int LED_PIN = 0;  // GPIO 핀 번호 (WiringPi 기준 핀 번호)
    
    // wiringPi 설정
    wiringPiSetup();
    
    // GPIO 핀을 출력 모드로 설정
    pinMode(LED_PIN, OUTPUT);
    
    // LED 켜기
    cout << "LED ON" << endl;
    digitalWrite(LED_PIN, HIGH);
    delay(1000);  // 1초 대기
    
    // LED 끄기
    cout << "LED OFF" << endl;
    digitalWrite(LED_PIN, LOW);
    
    return 0;
}

 

2. I2C (Inter-Integrated Circuit)

I2C는 여러 장치가 두 개의 와이어(SCL, SDA)를 사용해 서로 데이터를 주고받는 양방향 직렬 통신 프로토콜입니다. 마스터-슬레이브 구조로 여러 장치를 동시에 연결할 수 있어, 센서나 메모리 장치 등과 자주 사용됩니다.

특징:

  • 두 개의 핀 사용: I2C는 **SCL(Serial Clock)**과 SDA(Serial Data) 두 개의 핀을 사용합니다.
    • SCL은 클럭 신호를, SDA는 데이터 신호를 전달합니다.
  • 마스터-슬레이브 구조: I2C는 한 개의 마스터 장치가 슬레이브 장치들을 제어합니다.
    • 마스터는 데이터를 요청하고, 슬레이브는 데이터를 전송하거나 수신합니다.
  • 다중 장치 연결 가능: 여러 슬레이브 장치를 하나의 I2C 버스에 연결할 수 있습니다. 각 슬레이브는 고유 주소를 가집니다.

I2C 사용 예:

  • 센서 연결: 온도 센서나 가속도 센서와 같은 장치를 I2C 버스를 통해 연결하고 데이터를 읽어올 수 있습니다.
  • 메모리 장치 제어: EEPROM 같은 메모리 장치를 I2C로 제어하여 데이터를 읽고 씁니다.

I2C 코드 예시 (C++):

#include <iostream>
#include <fcntl.h>      // open()
#include <unistd.h>     // read(), write()
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

using namespace std;

int main() {
    const char *filename = "/dev/i2c-1";  // I2C 버스 번호 (라즈베리 파이의 경우 /dev/i2c-1)
    int addr = 0x48;  // 슬레이브 장치 주소

    // I2C 버스 열기
    int file;
    if ((file = open(filename, O_RDWR)) < 0) {
        cout << "Failed to open the bus.\n";
        return 1;
    }

    // 슬레이브 장치 선택
    if (ioctl(file, I2C_SLAVE, addr) < 0) {
        cout << "Failed to acquire bus access and/or talk to slave.\n";
        return 1;
    }

    // 슬레이브 장치에서 데이터 읽기
    char buffer[1];
    if (read(file, buffer, 1) != 1) {
        cout << "Failed to read from the I2C bus.\n";
    } else {
        cout << "Received data: " << (int)buffer[0] << endl;
    }

    close(file);
    return 0;
}

 

3. SPI (Serial Peripheral Interface)

SPI풀-듀플렉스 방식으로 동작하는 고속 직렬 통신 프로토콜입니다. I2C와는 다르게, SPI는 더 많은 핀을 사용하지만 훨씬 더 높은 속도로 데이터를 주고받을 수 있습니다. 마스터-슬레이브 구조로 장치 간에 통신이 이루어집니다.

특징:

  • 4개의 핀 사용: SPI는 네 개의 주요 핀을 사용합니다.
    • MOSI (Master Out Slave In): 마스터에서 슬레이브로 데이터를 보낼 때 사용.
    • MISO (Master In Slave Out): 슬레이브에서 마스터로 데이터를 보낼 때 사용.
    • SCK (Serial Clock): 마스터가 슬레이브에 제공하는 클럭 신호.
    • SS (Slave Select): 마스터가 제어할 슬레이브 장치를 선택하는 신호.
  • 고속 데이터 전송: SPI는 매우 높은 속도로 데이터를 전송할 수 있어, SD 카드디스플레이 같은 고속 장치에 많이 사용됩니다.
  • 풀-듀플렉스 통신: 마스터와 슬레이브가 동시에 데이터를 주고받을 수 있습니다.

SPI 사용 예:

  • SD 카드 제어: SD 카드와 같은 저장 장치에 데이터를 쓰거나 읽을 때 SPI를 사용합니다.
  • 디스플레이 제어: SPI는 빠른 속도로 데이터를 전송할 수 있어, TFT LCD나 OLED 같은 디스플레이 제어에 많이 사용됩니다.

SPI 코드 예시 (C++):

#include <iostream>
#include <fcntl.h>      // open()
#include <unistd.h>     // read(), write()
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>

using namespace std;

int main() {
    const char *device = "/dev/spidev0.0";  // SPI 버스와 장치 설정
    int file;
    
    if ((file = open(device, O_RDWR)) < 0) {
        cout << "Failed to open the SPI bus.\n";
        return 1;
    }
    
    // SPI 통신 설정
    uint8_t mode = 0;
    uint32_t speed = 500000;  // 500kHz
    if (ioctl(file, SPI_IOC_WR_MODE, &mode) < 0 || ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
        cout << "Failed to set SPI mode or speed.\n";
        return 1;
    }

    // 송신할 데이터
    uint8_t send_data[3] = {0x01, 0x02, 0x03};
    uint8_t receive_data[3] = {0};

    // SPI 통신
    struct spi_ioc_transfer transfer = {};
    transfer.tx_buf = (unsigned long)send_data;
    transfer.rx_buf = (unsigned long)receive_data;
    transfer.len = sizeof(send_data);
    transfer.speed_hz = speed;
    transfer.bits_per_word = 8;

    if (ioctl(file, SPI_IOC_MESSAGE(1), &transfer) < 0) {
        cout << "Failed to send SPI message.\n";
        return 1;
    }

    // 수신 데이터 출력
    cout << "Received data: ";
    for (int i = 0; i < 3; i++) {
        cout << (int)receive_data[i] << " ";
    }
    cout << endl;

    close(file);
    return 0;
}

 

GPIO, I2C, SPI 프로토콜 비교

 

특징 GPIO I2C SPI
사용 핀 1개 핀(입출력) 2개 핀(SCL, SDA) 4개 핀(MOSI, MISO, SCK, SS)
데이터 전송 단순 신호 (HIGH/LOW) 양방향 데이터 전송 (반이중) 양방향 데이터 전송 (풀 듀플렉스)
속도 매우 느림 중간 매우 빠름
다중 장치 연결 가능 마스터-슬레이브, 다중 슬레이브 마스터-슬레이브, 다중 슬레이브 가능

마무리

이 세 가지 통신 프로토콜은 임베디드 시스템에서 자주 사용되는 기술로, GPIO는 간단한 제어, I2C는 여러 장치와의 통신, SPI는 고속 데이터 전송에 각각 적합합니다. 각 프로토콜의 특성을 이해하고, 상황에 맞는 프로토콜을 선택하여 하드웨어를 제어하는 것이 개발자의 역할입니다.

댓글