다음 사진에서 보면 좌측의 모터부가 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 살레시오

댓글을 달아 주세요

 DC모터는 영구자석의 자기장과 그 자기장 속에 놓여 있는 도체에 흐르는 전류에 의해 발생한 전자력 간의 반발력으로 회전하는 구동기이며 DC전원을 사용한다. 기동 토크가 크며 인가되는 전압에 대해 회전 특성이 선형적으로 비례하며 가격이 저렴한 장점이 있는 반면에 구조상 브러시(brush)와 정류자(commutator)에 의한 기계적 점점이 있다는 단점이 있다. 이 접점으로 인해 회전 시 전기적인 불꽃이나 잡음이 발생하며 이는 uC로 제어할 때는 고려하여 제거하여야 한다. DC모터는 인가 전압의 극성을 바꾸어 인가하면 방향을 바꾸어 회전한다.


[그림 1] 다양한 DC모터들


 만약 아두이노의 전원이 PC의 USB에서만 공급된다면 DC모터를 직접 구동하는 것은 무리가 있다. DC모터는 보통 큰 전류가 필요하기 때문에 최대 공급 전류가 최대 500mA 정도인(노트북인 경우 공급 전류량은 더 작아진다.) USB로 직접 구동할 수는 없다. 그러나 구동 전류가 작은 초소형 모터의 경우 USB전원으로도 충분해 제어할 수 있으며 이 경우 높은 회전 속도나 큰 토크(회전력)을 기대할 수는 없다. 중형 DC모터를 아두이노로 제어하려면 전용 모터 제어 쉴드를 사용하는 것이 유리하다.


소형 DC모터의 제어

 실험을 위해서 [그림 2]와 같은 3V로 구동되는 소형 DC모터를 선택하였으며 드라이버 IC로는 소형 DC모터의 구동에 많이 쓰이는 BA6208을 사용하였다. 소전류로도 구동이 가능한 초소형 모터이므로 USB전원만으로도 제어하는데 충분하다.

[그림 2] 실험에 사용된 초소형 DC모터

BA6208은 소형 DC모터 구동 IC이다. SIP패키지의 BA6208 외형은 [그림 3]과 같다. 그림에서 보면 홈이 파인 쪽이 1번 핀이다. 논리부와 출력부로 구성되는데 논리부는 모터의 회전 방향을 제어하며, 출력부는 논리 제어에 따라 100mA까지 출력 전류를 공급할 수 있다. 공급 전압의 최대 값은 18V이다


[그림 3] BA6208의 외형과 핀 기능


다음 표는 BA6208의 논리 값에 따른 동작을 정리한 것이다.

[표 1] BA6208의 입력 신호에 따른 모터 동작 표


아두이노 우노의 5번과 6번 핀을 BA6208로 연결하여 모터를 제어하도록 하겠다. 결선도는 다음 그림과 같다. VCC는 아두이노 우노의 3.3V핀과 연결한다.

[그림 4] 아두이노와 BA6208 그리고 모터의 결선도

 한 가지 유의할 것은 DC모터가 회전하면서 발생하는 전기적인 잡음 때문에 전원이 불안정해질 가능성이 있으며 이것은 전체 시스템이 불안정하게 만드는 요인이 되기도 한다. 이를 억제하기 위해서 BA6208단의 VCC와 GND 사이에 커패시터(10uF)을 연결하여 전원을 안정시키는 것이 좋으나 아두이노 우노 보드를 사용하는 경우 전원을 안정화 시키는 회로가 포함되어 있으므로 별도로 커패시터를 달아 줄 필요는 없다.

 다음 예는 1초 간격으로 모터의 회전 속도를 바꾸는 예제이다.


#define A_IN 5
#define B_IN 6
void setup() {
   pinMode(A_IN, OUTPUT);
   pinMode(B_IN, OUTPUT);
}
void loop() {
   digitalWrite(A_IN, HIGH); // 정회전
   digitalWrite(B_IN, LOW);
   delay(1000);
   digitalWrite(A_IN, LOW); // 역회전
   digitalWrite(B_IN, HIGH);
   delay(1000);
}


PWM을 이용한 속도제어

 이제 analogWrite() 함수를 이용하여 속도를 제어해보도록 하겠다. 다음 예제는 시리얼 통신으로 PC에서 -255~255 사이의 정수를 입력하면 그 값을 analogWrite()함수로 내보내어 모터의 속도를 조절하는 예제이다.


#define A_IN 5
#define B_IN 6
void setup() {
   Serial.begin(9600);
   Serial.setTimeout(100);
}
void loop() {
   if (Serial.available()) { // 만약 시리얼 버퍼에 데이터가 있다면
   // 정수로 해석하여 읽어들인다.
       short motorSpeed = Serial.parseInt();
       if (motorSpeed >= 0){
           analogWrite(A_IN, motorSpeed);
           analogWrite(B_IN, 0);
       } else {
           analogWrite(A_IN, 0);
           analogWrite(B_IN, -motorSpeed);
       }
   }
}


이전에 언급한 바와 같이 아두이노의 PWM 주파수는 490Hz, 980Hz인데 소형 모터를 제어하기에는 무리가 없다.



Posted by 살레시오

댓글을 달아 주세요

  1. ElectricShock 2018.11.23 22:58 신고  댓글주소  수정/삭제  댓글쓰기

    BA6208 이 18V까지 허용된다는 말은 Vcc에 18V까지 인가해도 된다는 말씀이죠 ?