[아두이노] [강좌] 30. I2C 통신 (2) - Wire 함수 알아보기
지난 시간에 I2C 통신과 통신 방식에 대해 알아 봤었.. 지만 그래서 정확히 뭘 어떻게 해야하는지는 하나도 안 알려주고 끝났었다. 이번 강좌에서 알아보자.
(타이틀 용 이미지임)
아두이노에서는 I2C 통신을 쉽게 사용할 수 있도록 하기 위해 “Wire”라는 객체를 제공하고 있으며, I2C 통신을 위한 핀으로 SDA, SCL 핀을 하나씩 제공한다. 메가(Mega 2560/ADK)의 경우에는 20번 핀이 SDA, 21번 핀이 SCL 핀으로, 핀 옆에 SDA, SCL이라고 친절하게 표시되어 있다.
우노(Uno)의 경우에는 SDA, SCL 핀이 따로 표시되어 있지는 않기 때문에 핀 순서의 조금 유의해야 하네. 우노는 ‘A4’번 핀이 SDA, ‘A5’번 핀이 SCL 핀이다. 듀에(Due)의 경우에는 20, 21번 핀을 SDA, SCL 핀으로 사용하며, 왼쪽 제일 위에 위치하는 핀 두 개를 SDA1, SCL1 핀으로 추가 제공하고 있다. 듀에는 I2C 통신 포트가 2개라는 말. 좋구먼.
다른 보드의 핀에 대한 내용은 아두이노 공식 홈페이지를 확인하자.
>> 홈페이지 I2C 관련 바로 가기 : http://arduino.cc/en/Reference/Wire
“Wire” 객체는 “Wire.h” 파일에 선언되어 있으므로 “#include” 구문을 이용해 추가해줘야 한다. Wire 객체는 전역으로 이미 선언된 객체이므로 따로 선언해 줄 필요는 없다.
#include <Wire.h> |
그럼 Wire 객체의 함수에 대해 알아보자.
Wire.begin()
Wire.begin(address)
I2C 통신을 초기화하고, 활성화하는 함수. 슬레이브 모드일 경우 자신의 주소 값을 지정한다. 지정하지 않을 경우 자동으로 마스터 모드로 설정된다. 통신을 시작하기 전 한 번만 호출하면 된다.
매개 변수
address : 슬레이브 모드의 주소 값
|
Wire.beginTransmission(address)
마스터에서 전송을 시작하기 위해 슬레이브의 주소 값을 지정한다. 슬레이브 주소 값이 데이터 버퍼에 저장되어 전송될 준비를 한다. 실제로 Wire.beginTransmission() 함수가 호출된 시점에 전송되지는 않고, 데이터 버퍼에 데이터를 저장한 후 Wire.endTransmission() 함수가 호출될 때 한꺼번에 전송된다.
매개 변수
address : 데이터를 전송할 슬레이브의 주소 값. 주소 값은 7비트 데이터이므로, 127을 넘을 수 없다.
|
Wire.write(value)
Wire.write(String)
Wire.write(dataArray, length)
마스터 모드에서는 Wire.beginTransmission() 함수가 호출된 후 데이터 버퍼에 실제로 전송될 데이터를 저장하는 함수이다. Wire.write() 함수로 버퍼에 저장된 데이터는 Wire.endTransmission() 함수가 호출될 때 한꺼번에 전송된다.
슬레이브 모드에서는 마스터로부터 데이터 요청이 있을 때 요청에 대한 데이터를 전송하기 위해 사용된다. 이 때는 마스터에서 생성되는 클럭 신호에 맞춰 자동으로 전송된다.
매개 변수
value : 전송할 데이터. 기본으로 byte 형 데이터를 전송하며, int, long 타입의 값이 들어갈 수도 있으나 실제로 전송될 때 byte 형으로 변환되므로 데이터가 손실될 수 있다.
String : String 데이터를 전송한다. String의 길이만큼 버퍼에 저장된다.
dataArray : byte 형의 배열. length 값으로 지정한 길이만큼 버퍼에 저장된다.
length : dataArray 배열의 길이 또는 전송할 바이트 수를 지정한다. |
byte Wire.endTransmission()
byte Wire.endTransmission(stop)
데이터 버퍼에 저장된 데이터를 전송한다. 마스터 모드에서만 사용한다. Wire.endTransmission() 함수를 호출했을 때 시작 신호부터 슬레이브 주소, 데이터가 전송되며, 정지 신호의 생성 여부를 지정할 수 있다 . 전송이 완료된 후 전송 성공 여부를 byte 형의 값으로 반환한다.
매개 변수
stop : 정지 신호를 생성할지 말지를 Boolean 값으로 지정한다. 지정하지 않을 경우 자동으로 TRUE가 설정되어 정지 신호를 생성하며, FALSE로 설정했을 경우 정지 신호를 생성하지 않고 연속하여 데이터를 전송할 수 있다.
반환 값
byte : 전송 상태를 나타내는 지정된 값을 반환한다. 지정된 값은 다음과 같다.
- 0 : 전송 성공 - 1 : 데이터 버퍼의 길이가 허용 범위를 초과하였을 경우 - 2 : 주소 전송 단계에서 NACK 응답이 발생한 경우, 즉 해당 주소 값을 가진 슬레이브 기기가 없는 경우 - 3 : 데이터 전송 단계에서 NACK 응답이 발생한 경우. 데이터 전송이 실패한 경우. - 4 : 그 외의 오류가 발생한 경우
|
NACK 신호는 데이터에 대한 응답이 HIGH 값일 경우를 말하는데, ACK와 NACK의 차이점은 이전 강좌를 참고하도록 하자.
byte Wire.requestFrom(address, quantity)
byte Wire.requestFrom(address, quantity, stop)
마스터 모드에서 슬레이브로 데이터를 요청한다. 요청할 슬레이브 기기의 주소 값을 지정하고, 수신할 데이터의 길이(바이트 개수)를 설정한다. boolean 형의 stop 값으로 데이터 요청 후 정지 신호를 생성할지 말지를 지정할 수 있다. 요청 후 수신된 데이터의 길이(바이트 수)가 반환된다.
매개 변수
address : 데이터를 요청할 슬레이브 기기의 주소 값
quantity : 요청할 데이터의 바이트 수
stop : 정지 신호 생성 여부를 설정. 지정하지 않을 경우 자동으로 TRUE가 설정되며, FALSE로 설정할 경우 정지 신호를 생성하지 않는다.
반환 값
byte : 요청 후 수신된 데이터의 바이트 수 |
Wire.endTransmission() 함수에서도 그렇고, 정지 신호를 생성할지 말지를 지정할 수 있는데, 이것은 하나의 I2C 라인에 둘 이상의 마스터가 존재하는 경우를 위해 사용된다. (I2C 통신에는 하나 이상의 마스터가 존재할 수 있다. 잘 사용되지는 않는다.) 데이터 전송 라인이 하나밖에 없기 때문에(SDA) 한 번에 하나의 마스터만 데이터를 전송할 수 있는데, 데이터 전송 후 다른 마스터가 데이터를 전송하는 것을 방지하기 위해 정지 신호를 만들지 않고 바로 다른 데이터를 전송할 수 있도록 하기 위해서이다. 잘 사용되지는 않지만 알아두도록 하자.
Wire.available()
수신된 데이터의 바이트 수를 반환한다. Serial.available() 함수와 기능이 같다. 마스터 모드에서는 Wire.requestFrom() 함수 이후에 사용될 수 있으며, 슬레이브 모드일 경우 뒤에 설명할 Wire.onReceive() 함수에 지정한 수신 이벤트 처리 함수 내에서 사용될 수 있다.
매개 변수
없음
반환 값
int : 새로 수신된 데이터의 바이트 수 |
Wire.read()
수신된 데이터를 읽어온다. Serial.read() 함수와 기능이 같다. Wire.available() 함수로 수신 여부가 확인된 후 사용한다.
매개 변수 없음 반환 값 byte : 새로 수신된 데이터 값 |
Wire.onReceive(handler)
슬레이브 모드에서 사용되며, 마스터로부터 데이터가 수신되었을 때 호출될 이벤트 처리 함수를 등록한다. 이 때 등록되는 이벤트 처리 함수는 반드시 정의되어 있는 함수여야 하며, 이벤트 처리 함수에는 다른 이벤트 처리 함수들과 달리 int 형의 매개 변수가 사용되어야 한다. 매개 변수로 수신된 데이터의 길이가 전달된다.
매개 변수
handler : 마스터로부터 데이터가 수신되었을 때 호출될, int 형의 매개 변수를 가지는 이벤트 처리 함수
|
헷갈릴 수 있으니 다음 예제를 참고하자. 슬레이브 모드에서 Wire.onReceive() 함수로 이벤트 처리 함수를 등록하는 예제이다.
WireReceiveForSlave.ino |
#include <Wire.h> byte address = 100; byte recvDataBuf[128]; void setup() { Wire.begin(address); Wire.onReceive(receiveHandler); } void loop() { } void receiveHandler(int nByteNum) { for(int i=0; i<nByteNum; i++) { recvDataBuf[i] = Wire.read(); } } |
Wire.onRequest(handler)
역시 슬레이브 모드에서 사용되며, 마스터로부터 데이터를 요청 받았을 때 호출될 이벤트 처리 함수를 등록한다. 이벤트 처리 함수의 매개 변수는 없다. 당연히 정의되어 있는 함수여야 한다.
매개 변수
handler : 마스터로부터 데이터 요청이 왔을 때 호출될 이벤트 처리 함수 |
다음 예제를 참고하자.
WireRequestForSlave.ino |
#include <Wire.h> byte address = 100; void setup() { Wire.begin(address); Wire.onRequest(requestHandler); } void loop() { } void requestHandler() { byte data = 10; Wire.write(data); } |
슬레이브 모드에서 사용하는 함수들은 잘 사용되지는 않는 것 같다. 아두이노가 마스터로 동작하는 경우가 대부분이니까. 혹시 아두이노와 아두이노가 I2C 통신으로 연결되는 경우에는 슬레이브로 동작할 수도 있겠네.
함수들에 대해 살펴보면서 느꼈을지 모르겠지만, 아두이노에서 실제로 I2C 통신을 사용하기 위해 이전 강좌에서 설명했던 통신 방식이라던지, 신호라던지 하는 것들에 대해 전혀! 알 필요가 없다. 함수를 호출하는 순서와 각 모드에서 사용되는 함수에 대한 이해만 있으면 될 뿐.
쉽다.
다음 강좌에서는 I2C 통신을 사용하는 온도 센서에 대한 실습으로 I2C 통신을 사용하는 법에 대해 완벽히 알아보도록 하자. 그럼 이만, 안녕!