본문 바로가기

개발자/Arduino

아두이노에서 멀티태스킹 구현하기 2 - Multi-tasking the arduino : Blink without delay

반응형

 

 

 

다음 연재글 참고

 

전체 포스팅한 자료는 아래와 같다.

아두이노에서 멀티태스킹 구현하기 1 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 2 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 3 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 3.5 - Multi-tasking the arduino 라이브러리 링크 페이지

아두이노에서 멀티태스킹 구현하기 4 - Multi-tasking the arduino

 

 

 

이전에 하던 작업을 깨끗하게 마무리 해보자. 일부 서보모터 제어코드에 동일한 원칙을 적용하고 몇 가지 추가적인 작업을 수행해 보자. 아래 그림과 같이 브레드 보드에 두 개의 서보모터를 연결하고, 가지고 있다면 세 번째 LED도 연결하자.

 

아래 설명의 코드와 이미지 출처는 https://learn.adafruit.com/multi-tasking-the-arduino-part-1/ 임을 밝혀둔다. 

 

 

 

아래 코드는 표준 서보모터를 제어하는 코드다. 여기서는 두려움에 떠는 delay() 함수를 호출한다는 것에 주의하자. 깨끗한 상태 머신을 만들기 위해 필요한 부분을 가져갈 것이다. 

 

 

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
// Sweep
// by BARRAGAN <http://barraganstudio.com> 
// This example code is in the public domain.
 
 
#include <Servo.h> 
 
Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 
 
int pos = 0;    // variable to store the servo position 
 
void setup() 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
 
 
void loop() 
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
cs

 

 

아래의 Sweeper 클래스는 스윕 액션을 캡슐화하지만 타이밍에 millis( ) 함수를 사용한다. 이것은 Flasher 클래스가 LED에 대해 수행하는 것과 매우 비슷하다. 또한 서보를 특정 핀과 연결하기 위해 Attach( ) 및 Detach( ) 함수를 추가해야 한다.

 

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
class Sweeper
{
  Servo servo;              // the servo
  int pos;              // current servo position 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
 
public
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)  // time to update
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 180|| (pos <= 0)) // end of sweep
      {
        // reverse direction
        increment = -increment;
      }
    }
  }
};
cs

 

얼마나 작업을 해야 할까 ?

 

아래의 코드를 보면 

이제 우리는 필요한 만큼의 Flashers와 Sweepers를 인스턴스화 할 수 있다. Flashers 각 인스턴스에는 2 줄의 코드가 필요하다.

 

- 인스턴스를 선언하는 한 줄 : Flasher led1(11, 123, 400);

- 루프에서 업데이트를 호출하는 한 줄 : led1.Update();

 

스위퍼의 각 인스턴스에는 다음과 같이 3 줄의 코드 만 있으면 된다.

 

- 인스턴스를 선언하는 하나 : Sweeper sweeper1(15);

- 하나를 설정에서 핀에 연결 : sweeper1.Attach(9);

- 그리고 루프에서 업데이트 할 하나의 호출 : sweeper1.Update();

 

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <Servo.h> 
 
class Flasher
{
    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time
 
    // These maintain the current state
    int ledState;                     // ledState used to set the LED
    unsigned long previousMillis;      // will store last time LED was updated
 
  // Constructor - creates a Flasher 
  // and initializes the member variables and state
  public:
  Flasher(int pin, long on, long off)
  {
    ledPin = pin;
    pinMode(ledPin, OUTPUT);     
      
    OnTime = on;
    OffTime = off;
    
    ledState = LOW; 
    previousMillis = 0;
  }
 
  void Update()
  {
    // check to see if it's time to change the state of the LED
    unsigned long currentMillis = millis();
     
    if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
    {
        ledState = LOW;  // Turn it off
      previousMillis = currentMillis;  // Remember the time
      digitalWrite(ledPin, ledState);  // Update the actual LED
    }
    else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
    {
      ledState = HIGH;  // turn it on
      previousMillis = currentMillis;   // Remember the time
      digitalWrite(ledPin, ledState);      // Update the actual LED
    }
  }
};
 
class Sweeper
{
  Servo servo;              // the servo
  int pos;              // current servo position 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
 
public
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)  // time to update
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 180|| (pos <= 0)) // end of sweep
      {
        // reverse direction
        increment = -increment;
      }
    }
  }
};
 
 
Flasher led1(11123400);
Flasher led2(12350350);
Flasher led3(13200222);
 
Sweeper sweeper1(15);
Sweeper sweeper2(25);
 
void setup() 
  Serial.begin(9600);
  sweeper1.Attach(9);
  sweeper2.Attach(10);
 
 
void loop() 
  sweeper1.Update();
  sweeper2.Update();
  
  led1.Update();
  led2.Update();
  led3.Update();
}
cs

 

 

이제 간섭이 없이 논스톱으로 실행되는 5 개의 독립적인 작업을 가지게 되었다. loop ()는 단지 5 줄의 코드다. 다음으로 이러한 작업 중 일부와 상호 작용할 수 있도록 버튼을 추가 할 것이다.

 

아래 그림을 구현하는 일이다.

 

 

아래 코드는 루프의 각 단계에서 단추 상태를 확인한다. 버튼을 누르면 Led1 및 sweeper2가 업데이트 되지 않는다.

 

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <Servo.h> 
 
 
class Flasher
{
    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time
 
    // These maintain the current state
    int ledState;                     // ledState used to set the LED
    unsigned long previousMillis;      // will store last time LED was updated
 
  // Constructor - creates a Flasher 
  // and initializes the member variables and state
  public:
  Flasher(int pin, long on, long off)
  {
    ledPin = pin;
    pinMode(ledPin, OUTPUT);     
      
    OnTime = on;
    OffTime = off;
    
    ledState = LOW; 
    previousMillis = 0;
  }
 
  void Update()
  {
    // check to see if it's time to change the state of the LED
    unsigned long currentMillis = millis();
     
    if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
    {
        ledState = LOW;  // Turn it off
      previousMillis = currentMillis;  // Remember the time
      digitalWrite(ledPin, ledState);  // Update the actual LED
    }
    else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
    {
      ledState = HIGH;  // turn it on
      previousMillis = currentMillis;   // Remember the time
      digitalWrite(ledPin, ledState);      // Update the actual LED
    }
  }
};
 
class Sweeper
{
  Servo servo;              // the servo
  int pos;              // current servo position 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
  unsigned long lastUpdate; // last update of position
 
public
  Sweeper(int interval)
  {
    updateInterval = interval;
    increment = 1;
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)  // time to update
    {
      lastUpdate = millis();
      pos += increment;
      servo.write(pos);
      Serial.println(pos);
      if ((pos >= 180|| (pos <= 0)) // end of sweep
      {
        // reverse direction
        increment = -increment;
      }
    }
  }
};
 
 
Flasher led1(11123400);
Flasher led2(12350350);
Flasher led3(13200222);
 
Sweeper sweeper1(15);
Sweeper sweeper2(25);
 
void setup() 
  Serial.begin(9600);
  sweeper1.Attach(9);
  sweeper2.Attach(10);
 
 
void loop() 
  sweeper1.Update();
  
  if(digitalRead(2== HIGH)
  {
     sweeper2.Update();
     led1.Update();
  }
  
  led2.Update();
  led3.Update();
cs

 

 

위 코드는 3 개의 LED가 자체 속도로 깜박인다. 2 명의 Sweeper 는 자신의 비율로 청소한다. 그러나 버튼을 누르면 sweeper2와 led1은 버튼을 놓을 때까지 멈추게 된다. 루프에 지연이 없으므로 버튼 입력에 거의 즉각적으로 반응하여 실행된다.

 

결 론 

이 가이드에서는 Arduino가 사용자 입력과 같은 외부 이벤트에 응답하면서 여러 독립적인 작업을 수행하는 것이 실제로 가능하다는 것을 보여주었다. 우리는 delay ( ) 대신 millis ( )를 사용하여 시간을 정하는 방법을 배웠고 다른 작업들을 하기 위해 프로세서를 놓아주는 방법을 알 수 있었다. 

 

우리는 태스크를 다른 상태 머신과 독립적으로 동시에 실행할 수있는 상태 머신으로 정의하는 방법을 배웠다. 그리고이 상태 머신을 C ++ 클래스로 캡슐화하여 코드를 간단하고 간결하게 유지하는 방법을 배웠다.

이 기술은 Arduino를 슈퍼 컴퓨터로 만들지 않지만, 작지만 놀랍도록 강력한 작은 프로세서를 최대한 활용할 수 있도록 도와준다.

 

이 연재의 2 부에서는 이러한 기술을 토대로 Arduino가 외부 이벤트에 반응하고 여러 작업을 관리하는 다른 방법을 알아보자.

 

 

 

 

 

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩