iFLine-코딩으로 뽀샤버리기

6강. DC 모터 제어하기

Posted by INDIFROG on 2020-06-28
Words 1.3k and Reading Time 7 Minutes
Viewed Times

6강. iFLine에 모터 PIN 연결하기

iFLine

Motor Driver      GPIO

  IN1             IO32
  EN1             IO33
  IN2             IO25
  IN3             IO26
  EN2             IO27
  IN4             IO4

이렇게 소켓들을 연결합니다.

모터 드라이버를 제어하기 위한 하드웨어 부분 코딩 준비가 이제 완료되었습니다.

다음편은 IFZero에서 PWM 제어를 위한 프로그램 설명을 하도록 하겠습니다.

모터 드라이버 ( L293DD IC )

iFLine에서 사용 중인 모터 드라이버는 L293DD 라는 IC입니다.
비교적 오래된 IC 이면서도 그래서 그런지 비싼 IC 중에 하나입니다.

아래 그림에서 왼쪽은 SMD타입의 IC로 보드의 공간을 최소화하기 위해 ESP32기반 IFZero 보드의 바로 아래쪽 메인 보드에 내장되도록 설계하였습니다..

iFLine_motor_ic

모터 드라이버 L293DD IC - 내부 블럭도

L293DD IC내부의 블럭도는 다음과 같습니다.

iFLine_motor_ic3

이 장치는 표준 DTL 또는 TTL 로직 레벨에 의해 유도성 부하 (예 : 릴레이 솔레노이드, DC 및 스테핑 모터) 및 스위칭 전원 트랜시버를 구동하도록 설계된 모놀리식 통합 고전압, 고전류 4 채널 드라이버입니다라고 오픈된 데이터시트에 잘 정리가 되어 있습니다.

  • 채널1, 채널2는 왼쪽과 오른쪽 바퀴의 모터를 구동(전진/후진)하기 위해 할당됩니다.
  • 각 채널엔 Enable 입력 핀이 존재합니다.
  • 이 Enable 핀들은 모터에 연결되는 모터 구동 전원(VS)이 OUT1,2 / 3,4를 통해 흘러가는 것을 ON/OFF하는 역할을 합니다.
  • 또한 OUT1,2는 DC 모터의 빨간선/검정선 에 연결되어 있는데 IN1/IN2가 H/L이면 전진, L/H이면 후진 이렇게 동작하도록 되어 있습니다.

이 때, ENABLE 핀들을 빠르게 ON/OFF 스위칭하게 되면 펄스와 같은 형태로 전원 즉, 전압이 모터에 인가됩니다.

모터 드라이버 L293DD IC - PWM 제어

이와 같이 일반적으로 모터가 소비하는 전력 효율을 좋게 하기 위해 펄스 폭을 제어하는 PWM(펄스폭변조) 방식으로 모터를 구동합니다.
L293DD IC는 최대 5 kHz의 주파수내에서 PWM 방식으로 ENABLE 핀을 제어하하도록 되어 있습니다.

PWM 주파수

여기서 5 KHz의 주파수란 용어가 중요한 포인트입니다.
갑자기 주파수가 왜 나오죠? 모터를 제어하는데 말이죠. 추후 언급이 되겠지만 모터를 제어하기 위해서는 먼저 PWM 제어 방법을 알아야 합니다. 일종의 Pulse의 진폭으로 High 구간에만 전원을 공급해서 모터를 돌린다는 얘기죠. 이러한 펄스의 최대 주파수를 의미합니다.

그렇다면 더 빠르게 하면 안 될까요? 머… 안되는게 있나요… 스펙이 그렇다는 것이죠. 가능은 합니다.
그러나 속도 제어가 의도하는 대로 즉, 코딩하는 대로 동작하지 않을 뿐이죠.
그리고 IC 사양의 주파수 이상으로 제어를 하다 보면 이상 현상이 생깁니다. 원하지 않는 소음이 크게 발생합니다.

이렇게 PWM으로 모터를 구동하게 되면 전력을 보다 효율적으로 사용할 수 있으며 이를 통해 모터의 회전 속도를 조절할 수 있다는 점입니다.

모터 드라이버는

  • 칩내부의 디지털 로직을 제어하기 위해선 별도의 안정적인 입력 전압과
  • 모터를 구동하기 위한 구동전압

    이 필요합니다.

iFLine_motor_ic4

로직 전압

IFZero의 기본 로직 전압은 3.3V입니다. 아래 표에서 처럼 L293DD 데이터 시트에는 최소전압이 4.5V로 나와 있는데 3.3V에서도 잘 동작합니다.
즉, Vss 전압에 3.3V를 제공해 주고 GPIO로 ENABLE1, ENABLE2, IN1, IN2, IN3, IN4를 제어하면 됩니다.

구동 전압

디지털 로직과는 별도로 모터를 구동하기 위한 공급 전원은 Vs에 연결을 해 주어야 합니다.
간혹 초보 개발자들은 Vs를 LDO의 출력 전압을 사용해야 되지 않냐고 의심과 질문을 합니다.
그래야 안정적으로 동작하지 않냐고. 가능은 합니다만, LDO가 제공하는 제한된 전압레벨 내에서 동작을 하긴합니다.

예를들어, 모터가 6V에서 동작하고, 배터리 전압이 6V, 3.3V에서 동작하는 MCU를 사용한다고 가정하면, 
MCU에 안정적인 전압을 공급하기 위해 LDO를 사용하여 6V를 3.3V로 낮추게 됩니다. 
결국, 3.3V밖에 되지 않는 전압으로 모터를 구동 시키고 전류도 LDO가 제공하는 
제한된 전압, 전류만 사용하게 된다면 토크가 많이 부족하게 될 겁니다.

일반적으로, DC 모터를 Power Supply(전원공급장치)를 사용하여 직접 전원을 인가해서 구동을 해 보면 
즉, 전압을 직접 걸어 주면 모터가 회전하게 되고 이에 따라 전류가 소모량을 확인할수 있습니다. 
여기에 모터 부하를 걸어주면( 즉, 손으로 회전축을 안돌아가게 잡아주면 ) 전류 소모량이 순간 크게 증가하게 됩니다. 
즉, Power Supply가 공급 가능한 한계까지 전류를 끌어다 사용하게 되겠죠.

따라서 모터를 구동하기 위해서는 디지털 로직 전압보다 훨씬 크고 전류 공급이 원할한 전원을 사용해야 합니다.
중요한 포인트입니다. 모터를 회전시키는 가장 손 쉬운 방법은 배터리를 모터에 직접 연결해 주는 것입니다.
그러나 이것은 한방향으로만 회전하게 하는 것이죠.

양방향으로 제어하기 위해서는 위에 언급한 별도의 모터 드라이버 칩을 사용하거나 브리지 회로를 직접 구현해야 합니다.

여기서 한가지 중요한 사실은 IFLine은 리튬이온 배터리 3.7V, 700mA를 사용하고 있습니다.
흠.. 그렇다면 모터는 3.7V를 사용할까요? 아닙니다. 모터의 구동 성능을 위해 3.7V를 대략 5V로 Step Up,
즉, 승압을 한 전원을 사용합니다. 모터의 회전 속도와 토크를 최대한 끌어내기 위해서 전압을 올린 것이죠.

자. 이제 IFLine에서 모터를 구동할 기본적인 지식은 어느 정도 설명이 된 것 같습니다. ​

코딩을 위해 이제 전선을 연결해 보도록 하죠.  iFLine 메인 보드는 버전에 따라 약간씩 차이가 있습니다. 
iFLine v1.1과 그 이후 버전의 가장 큰 차이점은 모터 드라이버의 연결 소켓에 표시된 포트 이름입니다. 
IN1, ... IN4 이 이름들이 역순으로 배치가 되어 있습니다.

iFLine_motor_05

i모터 드라이버 L293DD IC - PWM 제어 소스코드

여기서 중요한 점은 GPIO4를 왜 연결하는가라는 점인데…
바로 iFZero 보드의 GPIO27번 핀 옆에 GPIO14이 있는데도 말이죠…
그 이유는 부팅시에 GPIO14, 15번은 HIGH상태가 됩니다. 만약 여기에 모터 구동 핀들이 연결되어 있다면 의도치 않게 모터가 오동작할 소지가 있습니다. 따라서 이 핀들은 피하는 것이 좋습니다.

PWM 신호 생성하기

ESP32에는 16개의 독립적인 LED PWM 채널이 있고 각각 다른 속성의 PWM 신호를
만들어 낼 수 있습니다. IFLine에서는 이 LED PWM 신호발생 기능으로 모터를 제어할 계획입니다.

  • PWM 채널 선택. 0~15
  • PWM 주파수 : 앞서 L293DD의 주파수 특성이 최대 5000 Hz 정도 였음.
  • PWM duty cycle resolution : 1비트에서 16비트까지
1
2
3
4
5
6
7
8

int MC_LEFT_CH = 0; // PWM channel (0~15)
int MC_RIGHT_CH = 15; // PWM channel (0~15)
int FREQUENCY = 5000; // PWM 주파수

int _nPWMResBit = 10; // PWM 해상도 비트

ledcSetup(MC_LEFT_CH, FREQUENCY, _nPWMResBit);
  • 선택한 채널을 어떤 GPIO 핀으로 보낼 것인지 지정합니다.
    위 IFLine의 소켓 번호 EN1는 IO33에 연결하여 PWM신호를 발생시킵나다. 아래 코드처럼 오른쪽 바퀴에 할당된 L293DD의 EN1 핀을 PWM 신호로 제어할 수 있습니다.
1
2
3
4
5
6
7
8
#define EN1   33  // GPIO번호
#define EN2 27 // GPIO번호

pinMode(EN1, OUTPUT); // 먼저, pin Mode를 OUTPUT으로 설정합니다.
pinMode(EN2, OUTPUT);

ledcAttachPin( EN1, MC_RIGHT_CH);
ledcAttachPin( EN2, MC_LEFT_CH);
  • 마지막으로 ledcWrite() 함수를 사용해 duty 구간을 최대 2의 nPWMResBit 자승값 범위 내에서 조절할 수 있고 duty가 High인 레벨에서만 모터가 동작하도록 해 줍니다. 즉, 모터의 회전 속도를 가변하기 위해 duty 값을 변화시켜줌으로써 PWM으로 제어를 할 수 있게 하는 것이죠. 3.에서 설정한 nPWMResBit이 10이라면 최대 duty 값은 1023이 되겠죠.
1
2
3
4

ledcWrite(MC_LEFT_CH, 1020);
// MC_LEFT_CH : PWM channel,
// 1020 : Duty cycle

이제까지 설정한 대로만 하면 모터가 원하는 대로 회전을 할까요?

머… 그렇게 쉽게 코딩이 성공하면 재미가 없죠.

이전 장에서 설명드린 모터 드라이버 IC L293DD 블럭도를 다시 떠올려 보세요.
여기에 IN1, IN2가 있을 겁니다.

이 신호들을 High, Low 값으로 서로 각각 반대로 바꾸어 동시에 입력을 인가해 주어야만 모터가 정방향, 역방향으로 회전이 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
//  RIGHT
#define IN1 (32)
#define EN1 (33)
#define IN2 (25)

pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);

int direct = FORWARD;

digitalWrite( IN1, direct? HIGH : LOW );
digitalWrite( IN2, direct? LOW : HIGH );

전체 코드는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <Arduino.h>

/////////////////////////////////////////////////////////////////
// for DC Motor
#define IFLINE_HW_VERSION (1)

#define MC_LEFT_CH (0)
#define MC_RIGHT_CH (15)
#define FREQUENCY (5000)

// RIGHT
#define IN1 (32)
#define EN1 (33)
#define IN2 (25)

// LEFT
#define IN3 (26)
#define EN2 (27)
#define IN4 (4)

#define FORWARD 1
#define BACKWARD 0

const int _nPWMResBit = 10;

void pwmWrite(int ch, int value, int direct)
{

#if IFLINE_HW_VERSION < 2

if( ch == MC_RIGHT_CH ){
digitalWrite( IN1, direct? HIGH : LOW);
digitalWrite( IN2, direct? LOW : HIGH );
}
else if( ch == MC_LEFT_CH ){
digitalWrite( IN3, direct? LOW : HIGH );
digitalWrite( IN4, direct? HIGH : LOW );
}

#else



#endif

ledcWrite(ch, value);

}

void setup()
{
Serial.begin(115200);

////////////////////////////////////////////////////////////////
// RIGHT
pinMode(EN1, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);

ledcSetup(MC_RIGHT_CH, FREQUENCY, _nPWMResBit);
ledcAttachPin(EN1, MC_RIGHT_CH);

// LEFT
pinMode(EN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);

ledcSetup(MC_LEFT_CH, FREQUENCY, _nPWMResBit);
ledcAttachPin(EN2, MC_LEFT_CH);

pwmWrite( MC_LEFT_CH, 1000, FORWARD );
pwmWrite( MC_RIGHT_CH, 1000, FORWARD );

}

void loop() {

delay( 1000 );

}

참고로 위 코드는 동작 순간 iFLine이 미친척하고 앞쪽으로 달려갑니다.

정신이 번쩍 드실 겁니다.