본문 바로가기

교육 자료/Raduino

3. I2C를 이용한 관성센서 제어

I2C를 이용한 관성센서 제어 


I2C 통신의 이해


I2C(Inter-Integrated Circuit)는 마이크로컨트롤러와 저속의 주변 장치 사이에 디지털 정보를 간편하게 전송할 수 있는 방법을 제공하기 위해 필립스에서 개발한 직렬 통신 프로토콜 중 하나이다. I2C 통신을 위해서는 SCL(serial clock)SDA(serial data)의 두 개 연결만이 필요하기 때문에 TWI(Two Wire Interface)라고도 불린다. 이러한 전송방식은 직렬방식의 메모리(serial EEPROM), LCD장치, 오디오 코덱, 온도 센서, 기압 센서, 자이로센서, 나침반 센서 등과 같은 외부 디바이스 연결용으로 다양하게 사용되고 있다.

 

SCL(Serial Data) : 양방향 직렬 데이터 라인

SDA(Serial Clock) : 양방향 데이터와 동기화 된 클럭 라인

 

각 장치의 SDASCL 핀은 모두 open-drain(또는 open-collector)방식으로 구현되어 있으며, SCL은 마스터와 슬레이브를 동기화하는 역할, SDA는 데이터를 전송하기 위한 신호이다. SDASCL의 조합으로 여러 개의 디바이스를 제어할 수 있다


< I2C 통신 >


I2C 통신은 마스터-슬레이브(master-slave) 구조이다. 연결의 한쪽에는 마스터(master) 장치가 존재하여 다른 슬레이브(slave) 장치들과의 통신을 책임진다. 일반적으로 아두이노가 마스터의 역할을 수행하며, 아두이노와 연결된 하나 이상의 장치들이 슬레이브 역할을 한다. 두 개의 슬레이브 장치들이 두 개의 동일한 데이터 선을 공유하므로 슬레이브 장치를 구별하기 위해 I2C에서는 슬레이브의 주소를 사용한다. 슬레이브 장치들은 7비트의 고유 주소에 의해 식별되므로 최대 128(2)개의 슬레이브 장치가 연결될 수 있다.


아두이노 개발환경에서는 I2C 통신을 지원을 위한 전용 라이브러리인 Wire 라이브러리가 포함돼 있으며, Wire 라이브러리 내의 함수에도 동일하게 적용된다. 우리가 사용하는 아두이노 우노에서는 SCL에 아날로그 5(A5), SDA에 아날로그 4(A4)을 사용하도록 지정하고 있어 이들 핀을 통해서만 I2C 통신이 가능하다. 안정적인 I2C통신을 위해서는 SDASCL 연결선에 풀업 저항을 사용해야 하며, 아두이노와 주변 기기를 연결하는 경우에도 풀업 저항을 사용하는 것이 좋다.

 

I2C의 장점은 신호선 2개만 연결하면 사용할 수 있으며, 여러 개의 장치를 연결하는 경우에도 필요한 신호선의 수가 증가하지 않는다. SPI의 경우 통신에 필요한 핀이 많아지고 추가로 장치를 더할 때마다 라인도 하나씩 추가되는데 반해 I2C는 두 개의 신호선 만으로 모든 주변 장치들이 공용으로 사용하기 때문에 연결된 주변 장치의 개수가 늘어도 필요한 신호선의 개수는 늘어나지 않는다는 장점이 있다


하지만 I2C는 다른 프로토콜에 비해 속도가 느리고, 반이중(half-duplex) 방식으로 송신과 수신이 동시에 불가능하기 때문에 양방향 통신이 필요한 경우 전송 속도가 더 느려진다는 단점이 있다. 그래서 주로 마이크로컨트롤러에서는 많은 데이터 전송을 요구하지 않는 센서 연결을 위해 I2C 통신을 사용한다.







9축 모션센서 | MPU-9250 



I2C통신을 이용하여 MPU-9250를 제어 하는 방법을 알아보자. 먼저 핀맵 1-2와 같이 아두이노와 MPU-9250를 연결한다.


MPU-9250

VCC

GND

SCL/SCLK

SDA/SDI

아두이노 우노

3.3V

GND

A5

A4

라두이노

SCL

SDA

I2C 통신을 위한 MPU-9250의 연결 PIN MAP 






I2C를 이용한 9축 모듈(GY9250)-아두이노 연결 회로도 


I2C를 이용한 9축 모듈(GY9250)-아두이노 연결회로도는 다음과 같다.




라두이노는 아두이노 우노 기반으로 회로도와 프로그램이 설계되었으며,

라두이노를 이용해서 동일한 예제를 구현할 수 있도록 라두이노용 회로도와 프로그램을 추가한다.

라두이노용 회로도와 프로그램은 다음과 같으며 본 예제는 동일한 프로그램을 수정 없이 사용한다.



I2C를 이용한 MPU-9250과 라두이노용 회로도








I2C를 이용한 통신 스케치코드 




코드설명


SPI 통신과는 통신 방법만 I2C로 바뀌었을 뿐 동일한 결과를 갖는 프로그램이다. 따라서, I2C 통신 방법을 구현한 MPU9250_readByteWire()함수에 대한 설명을 위주로 하겠다.

 

MPU9250_readByteWire 함수는 I2C 통신 방법을 이용하여 아두이노가 MPU9250의 레지스터 정보를 1-Byte 단위로 읽어온다. 프로그램은 이 함수를 사용하여 MPU-9250의 연결여부를 확인한 후 3-축 가속도를 읽어 와서 시리얼 통신을 이용해 PC 화면에 결과 값을 출력하는 프로그램이다.

 

먼저, 아래의 도식화된 그림에서 보여지는 것처럼 MPU9250의 원하는 레지스터 정보를 가져오기 위해 아두이노에서 MPU-9250에 해당되는 주소(0x68 : AD0 = 0 일 때, 0x69 : AD0 = 1일 때)를 송신하고 읽고자 하는 레지스터 주소 (0x75: WHO_AM_I_MPU9250)를 보낸다. 다시 MPU-9250의 주소를 송신 한 후 MPU-9250에서 해당 레지스터 주소에 해당 되는 값을 수신 받는다. 다음과 같은 코드의 결과 값은 0x71 이다


byte c = MPU9250_readByteWire(MPU9250_ADDRESS, WHO_AM_I_MPU9250);


DA : I2C 장치의 주소

RA : MPU-9250의 내부 레지스터 주소

DATA : 송신 또는 수신 데이터


다음 코드는 마찬가지로 loop() 함수에서 MPU9250_readByteWire()함수를 이용하여 MPU-9250으로부터 X,Y,Z 축의 가속도 정보를 읽어 온다.

 

rawData[0] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_XOUT_H );

rawData[1] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_XOUT_L );

rawData[2] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_YOUT_H );

rawData[3] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_YOUT_L );

rawData[4] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_ZOUT_H );

rawData[5] = MPU9250_readByteWire(MPU9250_ADDRESS, ACCEL_ZOUT_L );

 

그 후 측정된 가속도 정보를 mG로 변환하여 PC화면에 출력돼는 과정과 결과는  SPI 통신 예제 와 동일하다.






함수설명 


Wire.begin(address)

매개변수

address : 슬레이브 모드의 주소 값

설명

I2C통신을 초기화, 활성화 시키는 함수 / 통신 시작 전 한번 호출

마스터 모드로 사용할 경우 address를 지정하지 않고 슬레이브 모드일 경우 자신의 주소 값을 지정


Wire.beginTransmission(address)

매개변수

 

address : 7비트 데이터의 슬레이브 주소 값, 127을 넘을 수 없다

 

설명

마스터에서 address에 지정된 주소의 슬레이브로, 송신이 시작하는 것을 알리는 함수. 이때 실제로 데이터가 전송되지 않고 Wire.write()함수를 사용하여 전송할 바이트를 대기열에 저장 후 Wire.endTransmission()함수를 호출하여 전송


Wire.endTransmission(stop)

매개변수

 

stop : boolean값으로 지정 하며 지정하지 않을 경우 true로 설정되어 전송 후 I2C버스 해제

false로 지정할 경우 전송 후 재시작 메시지 송신

반환 값

 

byte : 지정된 값을 반환함으로써 전송상태를 나타냄

- 0 : 성공

- 1 : 데이터의 길이가 허용범위 초과

- 2 : 주소 전송 시 NACK 수신

- 3 : 데이터 전송 시 NACK 수신

- 4 : 기타 오류 발생

NACK(Not ACK) : 슬레이브가 더 이상의 데이터를 받을 수 없는 상태

데이터 전송 중단 (STOP상태)

 

설명

Wire.beginTransmission()함수에서 지정된 주소의 슬레이브에 송신이 끝났음을 알리는 함수

Wire.write()에 의해 대기열에 저장된 바이트를 전송


Wire.requestFrom(address, quantity, stop)

매개 변수

address : 데이터를 요청할 슬레이브의 7비트 주소

quantity : 수신할 데이터의 바이트 수

stop : boolean값으로 지정하며, 지정하지 않을 경우 true로 설정되어

전송 후 I2C버스 해제

false로 지정할 경우 전송 후 재시작 메시지 송신

반환값

byte : 슬레이브에서 수신 된 데이터의 바이트 수

설명

마스터가 슬레이브에게 데이터를 요청하는 함수

Wire.available()함수와 Wire.read()함수를 이용해서 데이터를 검색가능


Wire.available()

반환값

int : 수신된 데이터의 바이트 수

설명

Wire.requestFrom()함수 요청된 후 또는 Wire.onReceive (handler)함수 핸들러 내부에서 사용

Wire.read()함수로 검색할 수 있는 데이터의 바이트 수를 반환


Wire.read()

반환값

byte : 수신된 데이터 값

설명

마스터에서 슬레이브, 또는 슬레이브에서 마스터로 전송된 데이터를 읽는 함수

Wire.available()함수로 데이터 수신여부 확인 후 사용


Wire.write()

매개변수

 

value : single byte 데이터

string : string 데이터

data, length : data에는 데이터 배열, lengh에는 전송할 바이트 수

설명

마스터로부터의 요구에 대한 슬레이브의 응답데이터 기록 또는 마스터에서 슬레이브로 전송을 위한 데이터를 대기

Wire.beginTransmission(address)함수와 Wire.endTransmission(stop)함수 사이에 호출