아두이노 보드들 중에서 가장 작은 크기를 가지는 보드로 arduino pro mini 가 있다. 동작 전압에 따라 두 가지 모델이 분리되어 있다. (5V 동작 모델/ 3.3V동작 모델)

[그림 1] 아두이노 프로 미니

스펙은 다음과 같으며 단순 스펙 상으로는 아두이노 우노보다 핀수가 조금 더 많다.

  • 메인 프로세서 : ATmega328 (초기 버전에서는 ATmega168)

  • 디지털핀 16개 (6개의 PWM포함)

  • 아날로그핀 8개

  • 크기 : 1.3" x 0.7" (약 33 mm x 18 mm)

  • 클럭 : 16MHz (5V 모델) / 8MHz (3.3V 모델)

  • 가격 : US $9,95 (정품)

  • 전원 : 3.35 -12 V (3.3V model) or 5 - 12 V (5V model)

전원을 인가할 때 한 가지 주의할 점은 정전압 (즉, 정류된 정확한 3.3V/5V 전압) 은 VCC핀에 연결해야 한다는 것이다. 정류되지 않은 전압(즉 정확히 3.3V/5V 가 아니라면)은 RAW핀에 연결해야 하는데 내장된 레귤레이터가 필요한 전압으로 바꿔준다. 그리고 VCC 와 RAW 핀 두 개에 동시에 전원을 인가하면 안된다. 보드가 상할 수도 있다고 한다.

 핀아웃 다이어그램은 다음 그림과 같다.

          [그림 2] 아두이노 프로미니의 핀 기능 다이어그램

 아두이노 우노와의 차이점은 핀 배치가 틀리므로 표준 쉴드(shield)를 사용할 수 없다는 것과 크기를 줄이기 위해서 usb 인터페이스가 생략되어 있다는 점이다. 따라서 프로그램을 다운로드 하려면 별도의 UART to USB 변환기를 사용해야 한다. (아래 그림 참조)

[그림 3] usb-to-serial 장치로 프로그래밍을 하는 모습

따라서 이 제품은 다량의 완성품을 만들어야 하는 경우에 하나의 변환기로 여러 개의 보드를 프로그램할 수 있으므로 단가를 낮출 수 있어 유리하다. 쇼핑몰 sparkfun.com 에서 정품이 개당 9.95$ 이고 ebay.com 같은 곳에서는 3.00$ 짜리 중국산 저가 복제품도 쉽게 찾을 수 있다.(그 가격에 팔면서 이윤이 남는다는 것이 정말 신기하다.) 또한 크기가 작으므로 소형 제품을 설계할 때에도 유리하게 작용한다. 그리고 3.3V 모델이 별도로 마련되어 있어서 라즈베리 파이와의 인터페이스도 용이할 것 같다.

 이것과 유사한 소형 보드로는 아두이노 나노(nano)와 아두이노 미니(mini) 가 있는데 모두 Atmega328 기반의 제품들이다. 나노는 usb시리얼 변환 칩과 usb단자가 있다는 점이 프로미니와 다르다. 미니와 프로미니는 핀배열이 조금 다르고 입력 전압을 인가하는 방식이 약간 틀린 것 같다. 즉 다음과 같은 모델들이 ATmega328 기반의 유사한 스펙이라고 보면 될 것 같다.

  • 아두이노 우노 (uno)

  • 아두이노 나노 (nano)

  • 아두이노 미니 (mini)

  • 아두이노 프로미니 (pro mini)


또 다른 소형 보드로 아두이노 마이크로(micro)라는 제품도 있는데 이것은 레오나르도를 소형화 시켜놓은 것이라고 보면 된다. ( 개인적인 경험으로 레오나르도 보드를 가지고 개발하는 것은 별로 권하고 싶지 않은데, 프로그램을 다운로드 하고 리셋이 걸릴 때마나 가상 시리얼 포트를 매번 잡아주는 것이 영 번거로웠기 때문이다. 우노 계열이 개발하기에 제일 편하다.)

 이것을 프로그램하려면 아두이노IDE에서 [도구]>[보드]>[Arduino Pro or Pro Mini] 메뉴를 선택한다. (아래 그림 참조) 그러면 [도구] 메뉴에 [프로세서] 라는 새로운 메뉴가 생성된다. 여기서 프로세서(328/168)와 전압(5V/3.3V)을 선택하면 된다. 최근의 모델은 모두 ATmega328을 사용한다.

  

[그림 4] 아두이노 프로미니 선택

올바르게 설정한 이후에는 일반적인 아두이노를 프로그램하는 방식과 동일하게 다룰 수 있다.


Posted by 살레시오
,

 아두이노의 인터럽트 기능을 이용하면 추가적인 하드웨어 없이 로터리 엔코더 (rotaty encoder)의 출력을 간단하게 읽어들일 수 있다. 여기에서는 아두이노 Due와 오토닉스(autonics)사의 1800펄스/1회전 분해능의 고정밀 로터리 엔코더를 이용해서 실험을 진행해 보겠다.


 기본적인 아이디어는 다음과 같다.


[그림 1] 오토닉스사의 로커리 엔코더 (E40H8-1800-6-L-5 )


엔코더에서는 세 가지 신호가 나오는데 각각 A상, B상, 그리고 Z상이라고 한다. A상과 B상은 축이 회전하면 위 그림과 같은 펄스를 발생하는데 자세히 보면 위상이 90도 만큼 엇갈려 있다. 일단 회전각은 A상을 기준으로 rising edge 혹은 falling edge 에서 인터럽트를 발생하여 내부 변수를 하나씩 증가하는 식으로 저장하면 된다. 회전 방향은 B상을 참조하면 되는데 예를 들어 시계방향으로 회전시에는 A상의 falling edge에서 B상이 1이고 반대 방향에서는 0이다. 이것으로 회전방향을 판단하면 된다. Z상의 펄스는 1회전에 한 번 발생하므로 이것으로 영점을 잡으면 된다. 즉 Z상의 펄스가 발생하면 카운트를 0으로 리셋시킨다.


 이제 아두이노Due 쪽의 프로그램을 살펴보면 Due는 다른 아두이노와 달리 고성능 보드 답게 모든 핀에 인터럽트를 설정할 수 있다. (다른 아두이노는 보통 2개 많아야 6개이다.) 필자는


  • 26번핀: A상

  • 28번핀: B상

  • 30번핀: Z상

이렇게 연결하였다. 한 가지 주의할 것은 autonics사이 이 엔코더는 datasheet에서 5V로 동작한다고 쓰여져 있는데 그렇다고 5V전원을 연결하면 신호선에 과전압이 걸려서 Due보드에 무리를 줄 수 있다.(Due보드는 동작 전압이 3.3V임) 다행히 3.3V를 연결해도 잘 동작하는 것 같았다. 테스트 프로그램은 다음과 같다.


int pA = 26; // phase-A of rotary encoder
int pB = 28; // phase-B of rotary encoder
int pZ = 30; // phase-B of rotary encoder

volatile signed long cnt = 0;
volatile signed char dir = 1;

void setup() {
   Serial.begin(115200);
   attachInterrupt(pA, encoderCount, FALLING);
   pinMode(pB, INPUT);
   attachInterrupt(pZ, encoderReset, FALLING);
}

void loop() {
   Serial.print(micros());  Serial.print(',');
   Serial.println(cnt);
}

void encoderCount() { // A상 신호의 폴링 에지에서 호출됨
   //B상의 값에 따라 방향을 결정한다.
   dir = (digitalRead(pB)==HIGH)? 1:-1;
   cnt += dir;
}
void encoderReset() { // Z상 신호의 폴링 에지에서 호출됨
   cnt = 0;
}


이렇게 구현하면 매우 간단하게 로터리 엔코더 신호를 읽어들일 수 있다.



Posted by 살레시오
,

 아두이노 제로(zero) 라는 보드가 출시 예정이다.

[그림 1] 아두이노 제로의 외형

아두이노와 Atmel사가 공동 개발한 것 같고 (뒷면에 그렇게 표기되어 있다.) 프로세서는 AVR 이 아니라 ATSAMD21 이라는 ARM 계열의 32비트 프로세서를 채용하였다. 48MHz 의 클럭 주파수로 동작하는데 (이것만 놓고 단순 비교하기는 무리지만) 성능이 아두이노 우노의 3배이다.

 클럭 속도 외에 우노와 비교하여 특이한 점은 0번과 1번을 제외한 모든 디지털 핀에서 PWM 기능을 사용할 수 있다는 점과 플래시메모리가 256KB 로서 우노의 8배 정도로 늘었다는 것이다. 개발 환경에서도 별도의 usb 통신 단자를 통해서 atmel 의 Embedded Debugger (EDBG) 기능을 사용할 수 있어서 디버깅에 별도의 장치가 필요없다는 점도 눈에 띈다.

 우노와의 차이점을 정리하면 다음과 같다.

  • 32비트 프로세서 채용

  • 3.3V로 동작

  • 48MHz의 클럭 주파수 (우노대비 4배)

  • 12개의 PWM핀 (우노의 PWM핀은 6개)

  • 256KB의 플래시메모리 (우노대비 8배)

  • 디버깅(EDBG)을 위한 별도의 usb 단자 내장

  • 12비트 ADC (우노는 10비트 ADC임)

문제는 가격인데 개인적인 짐작으로는 아두이노 우노에 비해서 많이 비쌀 것 같지는 않다.



Posted by 살레시오
,

 센서(sensor)란 자연계의 물리량을 그에 비려/반비례하는  전기적인 신호(전압, 저항값 등)로 변환시켜주는 소자이다. 아두이노를 이용한 실험에서 자주 사용되는 센서를 나열해 보면 다음과 같다.


초음파 센서

 초음파를 발생하여 반사되어 오는 시간을 측정하여 장애물과의 거리를 재는 센서이다. 이동 로봇의 거리 측정이나 차량의 후방 장애물 감지에 널리 사용되고 있다.

[그림 1] 초음파 센서 모듈


적외선 센서

 적외선(infrared rays)은 전자기파의 일종으로 가시광선보다는 파장이 길고 전자레인지에 사용되는 마이크로파보다는 파장이 짧다. 적외선을 발광하는 IRED (infrared ray emitting diode)는 일반적인 LED와 외형과 구동 회로면에서 유사하며 정방향 전압을 걸면 적외선이 나오는 전기소자이다. 흔히 사용되는 리모콘의 머리 부분에 달려있다.

[그림 2] 적외선 송수신 센서

광량 센서

 CdS 광전도 셀(CdS photoconductive cell) 또는 CdS셀은 황화카드뮴을 주성분으로 하는 광전도 소자로, 빛의 양에 따라 저항값이 변하는 일종의 가변 저항으로 생각할 수 있다. 빛의 양에 따라 내부 저항값이 변하는 특성이 있으므로 광 가변 저항기라고도 불리는데, 조사되는 빛의 양이 클수록 저항값이 낮아지지만 입사광이 거의 없으면 거의 절연체에 가까워질 정도록 저항값이 커지게 된다. 따라서 광량에 의해서 개폐되는 전기적인 스위치로도 생각할 수 있다.


[그림 3] CdS 광전도 센서

온도 센서

 온도에 비례하는 전압 발생하는 소자이다.

[그림 4] 온도 센서


기울기 센서

  기울기 센서는 기울기에 반응하는 센서이다. 일정 각도 이상 기울어지면 내부의 접점이 on되는 디지털식 센서와 각도에 비례하는 전기적인 신호를 발생하는 아날로그식 센서로 구분할 수 있다.

  

[그림 5] 디지털/아날로그 기울기 센서

 

터치 센서

 도전체나 피부의 접촉에 반응하는 센서이다.

[그림 6] 터치 센서 모듈


인체 감지 센서

인체감지센서 모듈은 적외선을 감지하는 센서를 장착하고 있으며 약 6미터 이내의 인간이나 동물의 움직임을 감지할 수 있다. 인체나 동물의 몸과 같이 열을 발산하는 곳에서 발생하는 적외선을 감지하는 원리이다.

[그림 7] 인체 감지 센서


이 밖에도 습도 센서, 소리에 반응하는 음향 센서, 각종 가스 센서, 등 많은 것들이 있다.





Posted by 살레시오
,

 오랜만에 아두이노 due 로 프로그래밍할 일이 있어서 새로 나왔다는 arduino ide 1.6.4 버전을 설치하고 프로그래밍을 시작하려는데 보드 메뉴에 새로운 보드들이 몇몇 추가된 것은 보이는데 정작 기존에 있던 arduino due 가 없어서 살짝 당황했다.


 조금 헤매다가 도구>보드>boards manger (맨 위에 있음) 옵션으로 타고 들어가니 다음과 같은 메뉴창이 뜬다.



위 그림처럼 Arduino SAM Boards 항목의 우하단에 설치 버튼이 있는데 그걸 눌러서 설치하면 보드 메뉴에 추가된다. (위 그림은 이미 인스톨한 후 캡쳐해서 설치 버튼이 안 보이는데 처음에는 install 버튼이 있다.



처음에 다운로드 받은 설치 프로그램 이전 버전보다 용량이 많이 줄어서 의아했었는데 이런 식으로 필요한 컴포넌트를 다운로드 받아서 설치하도록 구조가 바뀌어서 그런가 보다하는 짐작이 든다.




Posted by 살레시오
,

 초음파 센서로 다음과 같이 작동하는 아두이노 프로그램을 작성해 보자.

    ❶ 장애물이 30cm 밖에 있다면 부저는 울리지 않는다.

    ❷ 30cm 이내 10cm 바깥에 있다면 0,5초 주기로 삑삑거린다.

    ❸ 10cm보다 안쪽에 있다면 연속적인 삐~ 신호를 울린다.

마치 자동차의 후방 경고음과 같이 동작하게끔 하는 것이다.


 실험에서는 초음파 센서는 7번 핀에, 부저는 11번 핀에 달려있다고 가정한다. 다음 예제를 잘 분석해 보고 각자 응용해 보자.


#define ULTRA_SONIC_SENSOR  7
#define BUZ 11
void setup() {
 Serial.begin(9600);
 pinMode(BUZ, OUTPUT);
}
void loop() {
 float fDist = measureDist();
 int iTmBuzOn = 0;
 int iTmBuzOff = 0;
        
 if (fDist > 30) {
   iTmBuzOn = -1;
 } else if (fDist > 10) {
   iTmBuzOn = 50;
   iTmBuzOff = 450;
 }  else {
   iTmBuzOn = 0;
 }
 
 if (iTmBuzOn == 0) {
   digitalWrite(BUZ, HIGH);
 } else if (iTmBuzOn > 0) {
   digitalWrite(BUZ, HIGH);
   delay(iTmBuzOn);
   digitalWrite(BUZ, LOW);
   delay(iTmBuzOff);
 } else {
   digitalWrite(BUZ, LOW);
 }
   Serial.print(fDist);
   Serial.println(" cm");
}
#define MS_PER_CM           58.31f
float measureDist() {
pinMode(ULTRA_SONIC_SENSOR, OUTPUT); //(1)단계
digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(2)단계
delayMicroseconds(2);
digitalWrite(ULTRA_SONIC_SENSOR, HIGH); //(3)단계
delayMicroseconds(5);
digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(4)단계
pinMode(ULTRA_SONIC_SENSOR, INPUT);
// (5) 단계
unsigned long ulPulseTime = pulseIn(ULTRA_SONIC_SENSOR, HIGH);
if (ulPulseTime == 0 )
  return -1.0f;
else
  return ulPulseTime / MS_PER_CM;
}


여기서 measureDist()함수는 이전 포스트에서 작성한 함수를 그대로 사용하였다.


C++ 언어 전체 강좌 목록 >>>

Posted by 살레시오
,

 아두이노의 TWI 통신 (I2C라고도 한다)은 두 가닥의 선(SDA, SCL)만으로 여러 개의 디바이스와 통신을 할 수 있다는 장점을 가진다. 이것의 통신 속도는 마스터 기기에서 발생시키는 클럭(SCL)신호를 기준으로 정해지며 보통 많이 사용되는 표준 주파수는 100kHz 와 400kHz 이다. 단순하게 이론적으로 계산하면 100kHz 의 주파수라면 초당 100k 비트(바이트 아님)를 전송할 수 있으며 초당 약 12.5k 바이트를 전송할 수 있다.


 아두이노의 I2C 통신에 사용되는 클럭 주파수는 100kHz로 맞추어져 있다. 아두이노에서 쓰이는 AVR은 400kHz 의 주파수도 지원을 하며 대부분의 I2C 통신 기기들이 이 주파수를 지원한다. 그런데 아두이노 API에서는 이 클럭 주파수를 조절하는 함수나 메쏘드가 없다. 이것을 400kHz로 상향시키기 위해서는 다음과 같이 약간 번거로운 과정을 거쳐야 한다.


 아두이노 IDE 1.5.5 와 윈도즈를 기준으로 설명하도록 하겠다. 먼저 다음 파일을 연다

C:\Program Files \ Arduino \ hardware \ arduino \ avr \ libraries \ Wire \ utility \ twi.h

이 코드의 윗 부분에 보면 다음과 같은 상수가 있다.


#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif


이름에서 알 수 있듯이 TWI_FREQ 상수가 I2C 통신의 클럭 주파수를 정의한 상수이다. 이 상수를 400000L 로 바꾸면 된다.


#ifndef TWI_FREQ
#define TWI_FREQ 400000L
#endif

이렇게 변경하고 저장한 후 한 가지 과정을 더 거쳐야 한다. 현재 프로젝트의 (과거에 100kHz 상수 값으로 생성되었던)오브젝트 파일들인 wire.cpp.o , twi.c.o 파일들을 제거해야 하는데 이것을 제거하지 않으면 과거에 컴파일된 오브젝트파일을 가지고 링크를 하기 때문에 변경 사항이 적용되지 않는다.

다음의 폴더를 열어보자. (윈도7의 경우임)

C:\ Users \ [user id] \ AppData \ Local \ Temp

이 폴더 하위에 많은 build***********.tmp 폴더는 아두이노 프로젝트가 컴파일되면서 생성되는 임시파일들을 저장하는 폴더이다. 이것들을 모두 삭제한 다음 다시 컴파일하면 변경된 속도가 적용된다. 만약  위와 같이 오브젝트 (임시)파일을 삭제하는 절차가 번거로우면 아예 프로젝트를 새로 생성해서 코드를 붙여넣은 후 컴파일하면 된다.



Posted by 살레시오
,

 아두이노의 TWI로 한 바이트나 문자열을 주고 받는 것은 Wire.read(), Wire.write() 함수를 사용하면 쉽게 수행할 수 있으므로 전혀 문제가 없다. 문제는 멀티 바이트로 구성된 short, long, float 등의 데이터를 주고 받는 것이다. 예를 들어서 signed short형 (아두이노에서는 2 bytes임) 데이터를 전송하려고 하면 바이트로 쪼개서 보낸 다음, 받는 곳에서 다시 이를 조합해서 원래의 데이터로 복구시켜야 하는데 무척 번거롭다.


 이런 경우에 union 이라는 자료형을 사용하면 문제를 쉽게 해결할 수 있다. 예를 들어서 다음과 같이 (signed) short 형 데이터를 저장하기 위한 union을 정의한다. volatile 은 인터럽트 루틴 안에서 사용되기 때문에 붙인 것이다.


union UShort{
   volatile short sVal;
   volatile byte byArr[2];
};


union자료형은 포함된 변수의 메모리를 공유하기 때문에 여기서 보면 byArr[0]은 short형 sVal 변수의 하위바이트를, byArr[1]은 상위 바이트를 가진다.


이제 이것을 이용해서 변수를 생성한다. volatile 을 붙인 이유는 이전과 같다.


volatile UShort uMtr; // pwm value to motor
volatile UShort usv; // encoder value


그러면 usv.sVal 변수를 보통의 short형 변수처럼 사용하다가 TWI로 전송할 때는 usv.byArr[0]과 usv.byArr[1] 두 바이트를 차례로 보내주면 된다. 예를 들어서 슬레이브의 TWI 전송함수는 다음과 같다.


void requestEvent()
{
   Wire.write( (const byte *)usv.byArr, 2);
   usv.sVal = 0;
}


주의할 것은 usv.byArr 을 반드시 (const byte *) 형으로 캐스트해줘야 한다는 점이다. 그렇지 않으면 컴파일 시에 에러가 발생한다.


 수신단에서도 동일한 union을 정의해 놓고, 받은 데이터를 바이트 배열 byArr 에 차례로 넣어주면 곧바로 sVal 변수를 통해서 short형 변수 값을 쓸 수 있다.


void receiveEvent(int howMany)
{
   uMtr.byArr[0] = Wire.read(); // 하위바이트 받음
   uMtr.byArr[1] = Wire.read(); // 상위 바이트 받음
   MotorInput( uMtr.sVal ); // 받은 short형 데이터를 바로 사용한다.
}


이 함수에서 보면 uMtr.byArr[0] 과 uMtr.byArr[1] 을 차례로 전송받은 후 바로 uMtr.sVal 변수값을 사용하였다. 이렇게 별도로 원래 데이터를 쪼개서 보내고, 받은 후에 (비트 연산자 같은 것을 사용해서)복구하는 과정이 전혀 필요가 없는 것이다.



Posted by 살레시오
,

 다음 사진에서 보면 좌측의 모터부가 DC모터-모토쉴드R3-아두이노우노 이렇게 연결되어 있고, 우측의 아두이노프로미니와 TWI로 연결되어 있다. 프로미니에서 모터의 PWM값을 전송하면 그것을 우노가 받아서 모터를 회전시키고 발생하는 엔코더 신호를 우노에서 프로미니로 전송하는데 이 값들이 모두 signed short 형이다.

[그림 1] TWI 통신 실험 세팅


 다음은 아두이노 우노의 전체 소스코드이다. _DEBUG 상수값을 정의시키면 디버그를 위한 시리얼 통신부가 컴파일 되고, 디비깅이 끝났다면 이것을 comment-out 시켜 시리얼 통신부를 컴파일에서 제외시키면 된다.


//#define _DEBUG
#include <PWM.h>
#include <Wire.h>
union UShort{
   volatile short sVal;
   volatile byte byArr[2]; //uint8_t
};
#define ID_TWI 2
// motor shield constants
#define PWM_A 3
#define DIR_A 12
#define BRAKE_A 9
#define SNS_A A0 // current sening pin
// encoder constants
#define phaseA 0 // phase-A of rotary encoder to *INT0*
#define phaseB 7 // phase-B of rotary encoder to D7
volatile UShort usv; // encoder value
volatile UShort uMtr; // pwm value to motor
int32_t frequency = 120000; // PWN frequency in [Hz], maximum: 2,000,000
void setup() {
//(pwm.h)initialize all timers except for 0, to save time keeping functions
InitTimersSafe();
//(pwm.h)sets the frequency for the specified pin
bool success = SetPinFrequencySafe(PWM_A, frequency);
// Configure the motorshield A output
pinMode(BRAKE_A, OUTPUT); // Brake pin on channel A
pinMode(DIR_A, OUTPUT); // Direction pin on channel A
// set ISR for encoder
attachInterrupt(phaseA, EncoderCount, FALLING);
pinMode(phaseB, INPUT);
usv.sVal = 0;
uMtr.sVal = 0;
// TWI setup
Wire.begin(ID_TWI); // join i2c bus as SLAVE with address #4
Wire.onRequest(requestEvent); // register event to send sensor value
Wire.onReceive(receiveEvent); // register event to receive motor pwm value
MotorForcedStop();
#ifdef _DEBUG //////////////////////////////////////////////////////
Serial.begin(115200); // working!!! others: 115200, 250000, 460800
#endif //////////////////////////////////////////////////////////////
}
#ifdef _DEBUG
void ReadEncoder()
{
   Serial.print("en:");
   Serial.println(usv.sVal);
   usv.sVal = 0;
}
#endif
void loop() {
#ifdef _DEBUG /////////////////////////////////////////////////
short sTmp = 0;
if (Serial.available())
{
   sTmp = Serial.parseInt();
   while(Serial.available()) Serial.read(); // empty buffer
   Serial.print("Received: ");
   Serial.println(sTmp);
   MotorInput(sTmp);
}
delay(50);
ReadEncoder();
#endif /////////////////////////////////////////////////////////
}
void EncoderCount() {
   usv.sVal += (1 - digitalRead(phaseB)*2); // LOW: +1, HIGH: -1
}
void MotorInput(short sIn)
{
   if (digitalRead(BRAKE_A) == HIGH)
       digitalWrite(BRAKE_A, LOW); // setting brake LOW disable motor brake
   if (sIn >= 0) {
       digitalWrite(DIR_A, HIGH);
       pwmWrite(PWM_A, sIn); // pwm.h
   } else if (sIn < 0) {
       digitalWrite(DIR_A, LOW);
       pwmWrite(PWM_A, -sIn); // pwm.h
   }
}

void MotorForcedStop()
{
digitalWrite(BRAKE_A, HIGH); // setting brake LOW disable motor brake
// pwmWrite(PWM_A, 0); // pwm.h <- this HINDERS motor from forcing stop
}
void requestEvent()
{
Wire.write((const byte *)usv.byArr, 2);
#ifdef _DEBUG ////////////////////////////////////////////////////
Serial.print("enc : ");
Serial.println(usv.sVal);
#endif ///////////////////////////////////////////////////////////
usv.sVal = 0;
}
void receiveEvent(int howMany)
{
uMtr.byArr[0] = Wire.read();
uMtr.byArr[1] = Wire.read();
#ifdef _DEBUG ///////////////////////////////////////////////////
Serial.print("TWI reads: ");
Serial.println(uMtr.sVal);
#endif //////////////////////////////////////////////////////////
MotorInput(uMtr.sVal);
}



Posted by 살레시오
,

 아두이노로 TWI (또는 I2C) 통신을 하기 위해서는 Wire 라는 라이브러리를 이용해야 한다. 시리얼 통신이 1:1 통신 규약인 반면, TWI는 1:n 통신 규약으로서 하나의 마스터(master) 기기가 여러 개의 슬레이브(slave) 디바이스들과 통신을 할 수 있다는 장점이 있다. Wire라이브러리는 아두이노의 표준 라이브러리이므로 아두이노 IDE에 기본으로 내장되어 있다.


#include <Wire.h>


그리고 통신을 하는 기기들 간에 SDA, SCL, 그리고 GND 끼리 연결하고 전압 레벨이 맞는지 반드시 확인해야 한다. 즉, 5V 기기는 5V 끼리, 3.3V기기는 3,3V 기기 상호간에 연결해야 하면 3.3V와 5V를 연결하려면 레벨 컨버터를 통해야 한다.


 연결된 기기는 하나의 마스터와 다수의 슬레이브로 구분하고 슬레이브들은 7bit 혹은 8bit 아이디(숫자)로 서로를 구분한다. TWI는 모든 통신 주도권을 master가 가진다. 슬레이브에서 데이터를 읽어올 때도 마스터에서 요구하고 슬레이브로 데이터를 보낼 때도 마찬가지로 마스터에서 통신을 요구한 후 보낸다. 마스터에서는 다음과 같이 초기화 함수를 호출한다.


Wire.begin(); // 마스터의 TWI 초기화


슬레이브에서는 다음과 같이 초기화 함수를 아이디를 가지고 호출한다.


Wire.begin(n); //슬레이브의 TWI 초기화


예를 들어서 2번 슬레이브는 Wire.begin(2); 라고 하면 된다. 이 아이디를 가지고 마스터에서 기기간 구별을 하게 된다.

마스터에서 슬레이브로 데이터 보내기

마스터에서 슬레이브로 데이터를 보낼 때는 다음과 같은 순서를 따른다.


//❶ 아이디가 id인 슬레이브와 TWI 통신을 시작한다.
Wire.beginTransmission(id);
Wire.write(by); //❷ byte형 데이터를 보낸다.
Wire.endTransmission(); //❸ 통신을 끝낸다.


Wire.write()함수는 입력 변수의 자료형에 따라 다음과 같이 세 가지가 오버로드 되어 있다.


Wire.write(byte by); // byte형 데이터를 하나를 전송
Wire.write(string str); // 문자열 str을 전송
Wire.write(const byte *ptrByte, int n); // byte형 포인터에 저장된 byte 배열에서 n개의 데이터를 전송


슬레이브 쪽에서는 마스터에서 데이터를 보냈을 때 호출되는 이벤트 함수를 등록해 두면 데이터가 보내졌을 때 이 함수가 호출되어 알맞게 처리를 해주면 된다.


// 데이터가 보내졌을 때 호출될 receiveEvent() 라는 함수를 등록한다.
void setup() {
   Wire.onReceive(receiveEvent);
}
....
....

// 이 이벤트 핸들러는 넘겨받은 바이트 수를 입력으로 건네준다.
void receiveEvent(int iNum)
{
   byte by[iNum];
   for(int n=0; n<iNum; n++)
       by[n] = Wire.read();
}


마스터에서 슬레이브로부터 데이터 읽기

  마스터가 슬레이브에서 데이터를 받을 때도 마스터에서 먼저 요구하며 그 요구에 슬레이브가 반응하도록 되어 있다. 마스터에서 슬레이브에 데이터를 요구하고 읽어오는 과정은 다음과 같다.


byte by[n], m=0;
Wire.requestFrom(id, n); // id를 가지는 슬레이브에 n 바이트를 보내기를 요구한다.
while( Wire.available() ) // 읽어올 데이터가 버퍼에 남아있다면
{
   by[m++]=Wire.read(); // 데이터를 읽는다.
}


슬레이브 측에서는 마스터에서 요구가 올 때 처리할 이벤터 핸들러를 등록해 두면 된다.


// 마스터의 전송 요구시 호출된 requestEvent()라는 함수를 등록
Wire.onRequest( requestEvent );
...
void requestEvent()
{
   Wire.write(100); // (예를 들어) 100이라는 숫자를 전송한다.
}


이와 같이 마스터와 슬레이브 사이에 통신을 할 경우 마스터에서 슬레이브 쪽으로 통신을 하겠다는 요구를 먼저 하게 되어 있다.



Posted by 살레시오
,