산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

개요

이 튜토리얼에서는 I2C 통신 프로토콜의 작동 원리를 배우고, 이 프로토콜을 사용하는 센서와 아두이노 보드로 실전 예제를 만들어 보겠습니다.

I2C 통신 버스는 마스터와 여러 슬레이브 장치 간 통신이 필요한 전자 설계에 쉽게 구현할 수 있어 많은 전자 장치에서 널리 사용됩니다. 7비트 주소 지정 시 최대 128개(실제 112개), 10비트 주소 지정 시 최대 1024개(실제 1008개) 장치까지 단 두 개의 선만으로 통신할 수 있다는 점이 큰 장점입니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

I2C 작동 원리

단 두 개의 선으로 어떻게 수많은 장치와 통신할 수 있을까요? 각 장치마다 미리 설정된 고유 ID(장치 주소)가 있어 마스터가 통신할 장치를 선택할 수 있기 때문입니다.

두 선은 SCL(Serial Clock)과 SDA(Serial Data)라고 부릅니다. SCL은 마스터 장치가 생성하는 클럭 신호로 I2C 버스 상의 장치 간 데이터 전송을 동기화합니다. SDA는 실제 데이터를 전달하는 선입니다.

두 선은 "오픈 드레인(open-drain)" 방식이므로 풀업 저항을 연결하여 선이 높은 상태를 유지하도록 해야 합니다. 장치들이 액티브 로우(active low) 방식으로 동작하기 때문입니다. 일반적으로 2K(고속 약 400kbps) ~ 10K(저속 약 100kbps) 저항을 사용합니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

I2C 프로토콜

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

데이터 신호는 8비트 시퀀스로 전송됩니다. 특수 시작 조건(Start Condition) 이후 첫 8비트 시퀀스는 데이터가 전송될 슬레이브의 주소를 나타냅니다. 각 8비트 시퀀스 뒤에는 Acknowledge 비트가 옵니다. 첫 번째 Acknowledge 비트 뒤에는 보통 슬레이브 장치의 내부 레지스터 주소를 위한 또 다른 주소 지정 시퀀스가 옵니다. 주소 지정 시퀀스 이후 데이터 시퀀스가 데이터가 완전히 전송될 때까지 계속되고, 특수 정지 조건(Stop Condition)으로 끝납니다.

이 이벤트들을 더 자세히 살펴보겠습니다. 시작 조건은 클럭 라인이 여전히 높은 상태에서 데이터 라인이 낮아질 때 발생합니다. 이후 클럭이 시작되고 각 클럭 펄스마다 데이터 비트가 전송됩니다.

장치 주소 지정 시퀀스는 최상위 비트(MSB)부터 시작하여 최하위 비트(LSB)로 끝나며, 실제로는 7비트로 구성됩니다. 8번째 비트는 마스터가 슬레이브에 쓸지(로우) 읽을지(하이)를 나타내는 R/W 비트로 사용됩니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

다음 ACK/NACK 비트는 슬레이브 장치가 이전 비트 시퀀스를 성공적으로 수신했는지 나타냅니다. 이때 마스터는 SDA 라인의 제어권을 슬레이브에게 넘기며, 슬레이브가 성공적으로 수신했다면 SDA 라인을 낮게 당겨 Acknowledge 조건을 만듭니다. 슬레이브가 SDA 라인을 내리지 않으면 Not Acknowledge 조건으로, 이전 시퀀스를 성공적으로 받지 못했음을 의미합니다(슬레이브가 바쁘거나, 데이터를 이해 못하거나, 더 이상 데이터를 받을 수 없는 등 여러 원인). 이때 마스터가 어떻게 진행할지 결정합니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

다음은 내부 레지스터 주소 지정입니다. 내부 레지스터는 슬레이브 메모리 내 다양한 정보나 데이터를 담고 있는 위치입니다. 예를 들어 ADXL345 가속도계는 고유 장치 주소와 X, Y, Z축용 추가 내부 레지스터 주소를 가집니다. X축 데이터를 읽으려면 먼저 장치 주소를 보내고, 그 다음 X축용 특정 내부 레지스터 주소를 보냅니다. 이 주소들은 센서 데이터시트에서 확인할 수 있습니다.

주소 지정 후, R/W 비트에서 선택된 모드에 따라 마스터 또는 슬레이브로부터 데이터 전송 시퀀스가 시작됩니다. 데이터 전송이 완료되면 SDA 라인이 SCL 라인이 높은 상태에서 낮음에서 높음으로 전환되는 정지 조건으로 전송이 종료됩니다.

예제

예제로 5개 센서가 있는 GY-80 브레이크아웃 보드와 3개 센서가 있는 GY-521 브레이크아웃 보드를 사용합니다. I2C 버스로 단 두 선만으로 8개 센서에서 데이터를 읽을 수 있습니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

필요한 부품

보드 연결 방법은 다음과 같습니다. 아두이노의 SCL 핀을 두 브레이크아웃 보드의 SCL 핀에, SDA 핀도 동일하게 연결하고, GND와 5V 핀으로 보드에 전원을 공급합니다. 브레이크아웃 보드에 이미 풀업 저항이 있으므로 별도 풀업 저항은 사용하지 않습니다.

아두이노 I2C 통신 마스터하기: 실전 튜토리얼

이 칩/센서들과 통신하려면 고유 주소를 알아야 합니다. 센서 데이터시트에서 확인할 수 있습니다. GY-80 보드의 4개 주소: 3축 가속도계 0x53, 3축 자이로 0x69, 3축 자력계 0x1E, 기압계+온도계 0x77. GY-521 보드는 0x68 주소 하나만 가집니다. 아두이노 I2C 스캐너 스케치(아두이노 공식 웹사이트에서 다운로드 가능)를 업로드하여 실행하면 연결된 장치들의 주소를 확인할 수 있습니다.


센서 부품 번호I2C 주소
3축 가속도계 Analog Devices ADXL3450x53
3축 자이로 ST Microelectronics L3G4200D0x69
3축 자력계 Honeywell MC5883L0x1E
기압계 + 온도계 Bosch BMP0850x77

장치 주소를 찾은 후, 데이터를 읽기 위한 내부 레지스터 주소도 찾아야 합니다. 예를 들어 GY-80의 3축 가속도계에서 X축 데이터를 읽으려면 X축 데이터가 저장된 내부 레지스터 주소를 찾아야 합니다. 데이터시트에서 X축 데이터는 두 레지스터에 저장됨을 알 수 있습니다: DATAX0 (0x32), DATAX1 (0x33).

아두이노 I2C 코드

이제 X축 데이터를 가져오는 코드를 작성해 보겠습니다. 아두이노 Wire 라이브러리를 포함하고, 앞서 찾은 센서 주소와 두 내부 레지스터 주소를 정의합니다. Wire.begin()으로 Wire 라이브러리를 초기화하고, 시리얼 모니터에 데이터를 표시하기 위해 시리얼 통신도 시작합니다.

loop()에서 Wire.beginTransmission()으로 특정 센서(3축 가속도계)로 전송을 시작합니다. Wire.write()로 X축의 두 레지스터 데이터를 요청합니다. Wire.endTransmission()으로 전송을 끝내고 레지스터에서 데이터를 전송합니다. Wire.requestFrom()으로 전송된 데이터(두 레지스터의 2바이트)를 요청합니다.

Wire.available()으로 검색 가능한 바이트 수를 확인하고, 요청한 바이트 수(2바이트)와 일치하면 Wire.read()로 X축 두 레지스터에서 바이트를 읽습니다. 마지막으로 시리얼 모니터에 데이터를 출력합니다. 이 데이터는 원시 데이터이므로 올바른 X축 값을 얻으려면 추가 연산이 필요합니다. 자세한 내용은 가속도계 튜토리얼을 참조하세요.

/* 
 * I2C 통신 프로토콜 작동 원리 - 아두이노 I2C 튜토리얼
 * by Dejan, www.HowToMechatronics.com
 */
#include <Wire.h>

int ADXLAddress = 0x53;  // 장치 주소 (8비트는 읽기 모드용)
#define X_Axis_Register_DATAX0 0x32
#define X_Axis_Register_DATAX1 0x33
#define Power_Register 0x2D  // 전원 제어 레지스터

int X0, X1, X_out;

void setup() {
  Wire.begin();  // Wire 라이브러리 초기화
  Serial.begin(9600);
  delay(100);
  
  // 측정 활성화
  Wire.beginTransmission(ADXLAddress);
  Wire.write(Power_Register);
  Wire.write(8);  // 비트 D3 높게 설정 (0000 1000)
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(ADXLAddress);
  // 특정 레지스터에 데이터 요청
  Wire.write(X_Axis_Register_DATAX0);
  Wire.write(X_Axis_Register_DATAX1);
  Wire.endTransmission();  // 전송 종료 및 레지스터에서 데이터 전송
  
  Wire.requestFrom(ADXLAddress, 2);  // 두 레지스터에서 2바이트 요청
  
  if (Wire.available() <= 2) {
    X0 = Wire.read();  // 레지스터에서 데이터 읽기
    X1 = Wire.read();
  }
  
  Serial.print("X0= "); Serial.print(X0);
  Serial.print(" X1= "); Serial.println(X1);
}

제조공정

  1. 아두이노-라즈베리파이 2 I2C 통합으로 IoT 센서 프로젝트 구현하기
  2. 아두이노 에너지 모니터 & 데이터 로거 만들기 – Dr. Wattson 보드 활용
  3. 아두이노 DHT11 이더넷 실드 온습도 데이터 로거
  4. 파이썬 3로 아두이노 우노 제어하기 – 시리얼 통신 LED 제어 기초
  5. 아두이노를 NMEA-0183에 연결하기: 단계별 가이드
  6. 아두이노 나노로 JARVIS v1 홈 오토메이션 구축하기 - 단계별 튜토리얼
  7. 아두이노 프로토스레딩으로 멀티태스킹 마스터하기: 단계별 튜토리얼
  8. nRF24L01 마스터하기: 무선 통신, 아두이노 연동, 회로 및 코드 가이드
  9. MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼
  10. ADXL345 가속도계로 아두이노 방향 추적하기: 단계별 가이드