코코아팹은 누구나 창의적 아이디어를 현실로 만들어 낼 수 있도록
만들고, 공유하고, 배울 수 있는 터전이
되고자 합니다.
아이디와 비밀번호를 잊으셨나요?아이디 / 비밀번호 찾기
코코아팹 회원이 아니신가요? 회원가입
2015-03-30 17:02:44
이번 컨텐츠에서는 두 개의 오렌지 보드로 UART, I2C, SPI 통신 방법을 이용하여 한쪽에서 알파벳을 순서대로 보내면 다른쪽에서 그 알파벳을 받아서 시리얼 모니터에 출력해 보도록 하겠습니다.
NO | 부품명 | 수량 | 상세설명 |
1 | 오렌지 보드 | 2 | |
2 | 10kΩ저항 | 2 | I2C 통신에서만 사용됩니다. |
3 | 점퍼케이블 | 8 | 2개(UART) ~ 8개(I2C) |
부품명 | 오렌지 보드 | 10kΩ저항 | 점퍼케이블 |
파트 |
UART는 병렬 및 직렬 방식으로 데이터를 전송하는 컴퓨터 하드웨어의 일종입니다. 마이크로컨트롤러에서 사용되는 대표적인 시리얼 통신 방법 중 하나로, 많은 장비들이 지원하고 연결이 간단하여 많이 사용되고 있습니다.
* UART 시리얼 통신은 1:1통신이라서 따로 마스터-슬레이브를 따로 정하지 않습니다. 이번 컨텐츠에서는 한쪽은 송신부 다른 한쪽은 수신부로 두고 사용하겠습니다.
* 밑의 연결방법에서도 수신부에서는 TX만, 송신부에서는 RX만 연결합니다.(TX-RX 교차 연결)
우선 UART 통신을 이용하여 2개의 오렌지보드 간의 데이터를 주고 받는 방법을 알아보겠습니다.
char message = 'A';
void setup(){
Serial.begin(9600);
Serial.println("UART");
}
void loop(){
Serial.println(message);
message++;
if(message > 'z'){
message = 'A';
}
delay(1000);
}
* 수신부에 업로드시 꼭 RX(디지털 0번)핀에 연결되있는 케이블을 빼시고 업로드 해야 합니다. RX핀이 연결되어 있는 상태에서 컴퓨터에서 아두이노로 업로드를 할 경우 핀의 중복사용으로 인하여 업로드 오류가 발생합니다.
void setup(){
Serial.begin(9600);
}
void loop(){
while(Serial.available()){
char data = Serial.read();
Serial.print(data);
}
}
송신부에서 A부터 z 까지의 값을 하나씩 UART 통신을 이용하여 수신부로 보내고, 수신부에서는 송신부에서 날라오는 문자를 하나씩 받아서 시리얼 모니터에 출력하는 코드입니다. (밑에 다른 방법으로 진행할 경우도 기본 동작은 같습니다.)
// 수신부
Serial.println(message); // 데이터 전송
// 송신부
while(Serial.available()){ // 시리얼 통신으로 온 데이터가 있을 경우
char data = Serial.read(); // 데이터 1byte를 읽습니다.
Serial.print(data); // 읽은 데이터 출력
}
UART 통신으로 데이터를 전송할 경우는 시리얼 모니터에서 입출력 받을 때와 마찬가지로 Serial.println(), Serial.read()을 이용하여 전송하시면 됩니다.
(아두이노 스토리 - 클라이버 님의 아두이노-아두이노 SPI 연결을 참고 하였습니다.)
* SPI 통신은 1:N연결을 지원하고 Master-Slave 모드로 통신합니다.
* SPI 통신을 위해서는 MISO와 MOSI, SCK, SS를 사용합니다.(4-wire) SPI 통신은 SS를 이용하기 때문에 같은 주소 충돌이 발생하지 않지만, 연결하는 장치수가 늘어날 수록 연결선의 수가 늘어납니다.
* 아두이노에서 SPI통신을 위한 라이브러리를 제공하므로, 주변 장치와 SPI통신이 쉽게 가능합니다. 하지만 아두이노 SPI 라이브러리는 마스터 모드만 지원하므로 슬레이브를 설정하실 경우는 직접 레지스터를 통해 제어하셔야 합니다.
SPI 통신을 이용하여 2개의 오렌지보드 간의 데이터를 주고 받는 방법을 알아보겠습니다.
#include <SPI.h>
void setup (void)
{
SPI.begin (); // SPI 통신 초기화
digitalWrite(SS, HIGH); // 슬레이브가 선택되지 않은 상태로 유지
// 안정적인 전송을 위해 분주비를 높여 전송 속도를 낮춤
SPI.setClockDivider(SPI_CLOCK_DIV16);
Serial.begin(9600);
}
void loop (void)
{
if(Serial.available()){
char data = Serial.read(); // 데이터 입력 확인
if(data == 'K'){
digitalWrite(SS, LOW); // 슬레이브를 선택한다.
// 1바이트 데이터 수신을 위해 의미 없는 1바이트 데이터를 전송한다.
char received = SPI.transfer(0);
digitalWrite(SS, HIGH); // 슬레이브 선택을 해제한다.
Serial.println(received);
}
}
}
// 출처 : 아두이노 스토리 - 클라이버님 SPI강좌
#include <SPI.h>
byte count;
void setup (void)
{
// SPI 통신을 위한 핀들의 입출력 설정
pinMode(MISO, OUTPUT);
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
pinMode(SS, INPUT);
// 마스터의 전송 속도에 맞추어 통신 속도를 설정한다.
SPI.setClockDivider(SPI_CLOCK_DIV16);
// SPI 통신을 위한 레지스터를 설정
SPCR |= _BV(SPE); // SPI 활성화
SPCR &= ~_BV(MSTR); // Slave 모드 선택
SPCR |= _BV(SPIE); // 인터럽트 허용
count = '0'; // 카운터 초기화
}
// SPI 통신으로 데이터가 수신될 때 발생하는 인터럽트 처리 루틴
ISR (SPI_STC_vect)
{
SPDR = count; // 카운터 값을 ASCII 값으로 전달
}
void loop (void)
{
count = (count + 1) ; // 카운터 값 증가
delay(1000);
}
// 출처 : 아두이노 스토리 - 클라이버님 SPI강좌
마스터 시리얼 모니터에서 K(대문자) 를 입력하시면 슬레이브에게 데이터를 요청하여 알파벳 A부터 하나씩 받아옵니다. (현재 해당하는 알파벳이 전송됩니다. ex : A를 받은후 3초뒤 다시 K입력 -> D 수신)
SPI 통신에서 마스터가 슬레이브로부터 데이터를 수신하기 위해서는 슬레이브로 데이터를 송신하여야 합니다. 1바이트 데이터를 얻기위해서는 1바이트의 데이터를 보내야만 합니다.
위 코드에서 슬레이브는 알파벳 A부터 1초마다 하나씩 증가하는 카운터입니다. 마스터에 'K'가 입력이 되면 마스터는 슬레이브에게 현재 카운트 값을 요청하고 슬레이브에서는 현재 카운트를 마스터로 보냅니다.
I2C는 필립스에서 개발한 직렬 컴퓨터 버스이며, 마더보드, 임베디드 시스템, 휴대전화 등에 저속의 주변기기를 연결하기 위해 사용됩니다.
* I2C 통신은 1:N연결을 지원하고 Master-Slave 모드로 통신합니다.
* I2C 통신은 SPI와 다르게 슬레이브 선택을 위해 소프트웨어적인 주소를 사용하므로 연결하는 장치가 늘어나도 사용하는 핀이 증가하지 않습니다.
* I2C 통신은 데이터 송수신을 위한 SDA와 동기화 클록을 위한 SCL 핀 2개만 사용합니다.(2-Wire)
* UART나 SPI와 비교할 때 속도의 한계가 있으므로 사용에는 제한됩니다. 그러나 아두이노에서는 SPI와 달리 Wire라이브러리에서 슬레이브 모드를 지원하고, UART와 달리 1:N통신이 지원되므로 비교적 간단하게 다양한 기능 구현이 가능합니다.
우선 I2C 통신을 이용하여 2개의 오렌지보드 간의 데이터를 주고 받는 방법을 알아보겠습니다.
#include <Wire.h>
#define SLAVE 4 // 슬레이브 주소
void setup() {
Wire.begin(); // Wire 라이브러리 초기화
Serial.begin(9600); // 직렬 통신 초기화
Serial.println("I2C");
}
void loop() {
i2c_communication(); // 슬레이브로 데이터 요구 및 수신 데이터 처리
delay(1000);
}
void i2c_communication() {
Wire.requestFrom(SLAVE, 1); // 1 바이트 크기의 데이터 요청
char c = Wire.read(); // 수신 데이터 읽기
Serial.println(c); // 수신 데이터 출력
}
// 출처 : 아두이노 스토리 - 클라이버님의 I2C 강좌
#include <Wire.h>
#define SLAVE 4
byte count = 'A'; // 카운터
void setup() {
// Wire 라이브러리 초기화
// 슬레이브로 참여하기 위해서는 주소를 지정해야 한다.
Wire.begin(SLAVE);
// 마스터의 데이터 전송 요구가 있을 때 처리할 함수 등록
Wire.onRequest(sendToMaster);
}
void loop () {
}
void sendToMaster() {
Wire.write(++count); // 카운터 값을 증가시키고 마스터로 전송
if(count >= 'z'){
count = 'A';
}
}
// 출처 : 아두이노 스토리 - 클라이버님의 I2C 강좌
마스터가 슬레이브에게 요청을 할 경우 슬레이브에서 1byte의 알파벳 'A'부터 1증가시킨 후 마스터로 전송합니다.
// Wire 라이브러리 초기화
// 슬레이브로 참여하기 위해서는 주소를 지정해야 한다.
Wire.begin(SLAVE);
// 마스터의 데이터 전송 요구가 있을 때 처리할 함수 등록
Wire.onRequest(sendToMaster);
슬레이브는 자신의 주소를 설정하고 마스터의 요청을 기다립니다. 마스터의 요청이 들어왔을 경우 sendToMaster 함수를 실행 합니다.
Wire.begin(); // Wire 라이브러리 초기화
마스터는 자신의 주소를 따로 설정할 필요 없이 Wire 라이브러리만 초기화 시켜주면 됩니다.
void loop() {
i2c_communication(); // 슬레이브로 데이터 요구 및 수신 데이터 처리
delay(1000);
}
void i2c_communication() {
Wire.requestFrom(SLAVE, 1); // 1 바이트 크기의 데이터 요청
char c = Wire.read(); // 수신 데이터 읽기
Serial.println(c); // 수신 데이터 출력
}
1초마다 지정된 슬레이브(여기서는 4번 슬레이브입니다.)에 데이터를 요청합니다. requestFrom() 함수를 통해 4번 슬레이브에게 1바이트의 데이터를 요청하고, Wire.read()를 통해 전송된 1바이트의 데이터를 c에 저장합니다.
이렇게 받은 c(전송받은 데이터)를 시리얼 모니터에 출력합니다.
kocoafabeditor
항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!