[Arduino] 한번에 2개이상의 스테핑 모터를 제어하기
이전 글에서 'Stepper.h'는 '.step()'이 동작하는 동안 다른 동작을 할 수 없다는 것을 이야기하였습니다.
(참고 : [Arduino] 'Stepper.h'는 두개의 모터를 제어할 수 없다.)
그래서 'Stepper.h'를 약간 개조하여 한번에 여러 개의 동작(스탭모터라던가 led같은 것들)을 할 수 있도록 수정하겠습니다.
* Stepper library for Wiring/Arduino - Version 0.4 기준 입니다. *
* 2023-06-06 기준 최신버전은 5버전입니다 ->
Stepper Async 5
*
참고 : [Arduino] Stepper Async 5 - 비동기 스탭 모터 라이브러리
1. 기존 라이브러리 분석하기
저번 글에서도 잠깐 이야기했습니다만 기존 라이브러리의 문제는 'while()'문이 끝나야 '.step()'가 끝난다는 것입니다.
그러니 기존 '.step()'에서 'while()'문 안의 내용을 따로 메소드로 빼서 호출해야 합니다.
2. 개조 하기
이제 기존에 있는 라이브러리의 코드를 그대로 복사하여 이름을 'StepperMulti'로 바꿔 준다음 내용을 수정합니다.
2-1. 'StepperMulti.h' 수정하기
(1) '.step()'의 역할이 좀 달라졌으므로 'setStep()'로 이름을 바꾸고 'void moveStep();'를 추가합니다.
(2) '.step()'의 안에 지역변수가 있는데 이것을 꺼내서 클래스 지역변수로 만듭니다.
/*
StepperMulti.h
( http://blog.danggun.net/2092 )
이 라이브러리는 아두이노의 'Stepper.h'를 개조한 라이브러리입니다.
(Stepper.h : http://arduino.cc/en/reference/stepper )
모든 내용은 'Stepper.h'와 거의 동일합니다.
(0.4버전 기준)
*/
// ensure this library description is only included once
#ifndef StepperMulti_h
#define StepperMulti_h
// library interface description
class StepperMulti
{
public:
// constructors:
StepperMulti(int number_of_steps, int motor_pin_1, int motor_pin_2);
StepperMulti(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4);
// speed setter method:
void setSpeed(long whatSpeed);
// mover method:
void setStep(int number_of_steps);
//'loop()'안에서 이 메소드를 호출해야 한다.
void moveStep();
private:
void stepMotor(int this_step);
int direction; // Direction of rotation
int speed; // Speed in RPMs
unsigned long step_delay; // delay between steps, in ms, based on speed
int number_of_steps; // total number of steps this motor can take
int pin_count; // whether you're driving the motor with 2 or 4 pins
int step_number; // which step the motor is on
// motor pin numbers:
int motor_pin_1;
int motor_pin_2;
int motor_pin_3;
int motor_pin_4;
long last_step_time; // time stamp in ms of when the last step was taken
//스텝 저장
int steps_left;
};
#endif
2-2. 'StepperMulti.cpp' 수정 하기
'StepperMulti.h'에서 수정한 'setStep()'과 'void moveStep();'을 만들어야 합니다.
(1) 기존에 있던 'step()'의 이름을 'setStep()'로 바꿉니다.
(2) 'void moveStep()'을 만듭니다.
(3) 'setStep()'의 내용 중 'while()'문의 내용을 'void moveStep()'으로 옮깁니다.
(4) 'while()'를 if로 바꿉니다.
'while'의 역할을 'loop'가 대신 해주기 때문입니다.
github - StepperMulti.h
#include "Arduino.h"
#include "StepperMulti.h"
/*
* two-wire constructor.
* Sets which wires should control the motor.
*/
StepperMulti::StepperMulti(int number_of_steps, int motor_pin_1, int motor_pin_2)
{
this->steps_left = 0; //스탭초기화
this->step_number = 0; // which step the motor is on
this->speed = 0; // the motor speed, in revolutions per minute
this->direction = 0; // motor direction
this->last_step_time = 0; // time stamp in ms of the last step taken
this->number_of_steps = number_of_steps; // total number of steps for this motor
// Arduino pins for the motor control connection:
this->motor_pin_1 = motor_pin_1;
this->motor_pin_2 = motor_pin_2;
// setup the pins on the microcontroller:
pinMode(this->motor_pin_1, OUTPUT);
pinMode(this->motor_pin_2, OUTPUT);
// When there are only 2 pins, set the other two to 0:
this->motor_pin_3 = 0;
this->motor_pin_4 = 0;
// pin_count is used by the stepMotor() method:
this->pin_count = 2;
}
/*
* constructor for four-pin version
* Sets which wires should control the motor.
*/
StepperMulti::StepperMulti(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4)
{
this->steps_left = 0; //스탭초기화
this->step_number = 0; // which step the motor is on
this->speed = 0; // the motor speed, in revolutions per minute
this->direction = 0; // motor direction
this->last_step_time = 0; // time stamp in ms of the last step taken
this->number_of_steps = number_of_steps; // total number of steps for this motor
// Arduino pins for the motor control connection:
this->motor_pin_1 = motor_pin_1;
this->motor_pin_2 = motor_pin_2;
this->motor_pin_3 = motor_pin_3;
this->motor_pin_4 = motor_pin_4;
// setup the pins on the microcontroller:
pinMode(this->motor_pin_1, OUTPUT);
pinMode(this->motor_pin_2, OUTPUT);
pinMode(this->motor_pin_3, OUTPUT);
pinMode(this->motor_pin_4, OUTPUT);
// pin_count is used by the stepMotor() method:
this->pin_count = 4;
}
/*
Sets the speed in revs per minute
*/
void StepperMulti::setSpeed(long whatSpeed)
{
this->step_delay = 60L * 1000L / this->number_of_steps / whatSpeed;
}
/*
Moves the motor steps_to_move steps. If the number is negative,
the motor moves in the reverse direction.
*/
void StepperMulti::setStep(int steps_to_move)
{
this->steps_left = abs(steps_to_move); // how many steps to take
// determine direction based on whether steps_to_mode is + or -:
if (steps_to_move > 0) {this->direction = 1;}
if (steps_to_move < 0) {this->direction = 0;}
//스탭을 진행 한다.
moveStep();
}
void StepperMulti::moveStep()
{
//남은 스텝이 있는지?
if(this->steps_left > 0)
{
// move only if the appropriate delay has passed:
if (millis() - this->last_step_time >= this->step_delay)
{
// get the timeStamp of when you stepped:
this->last_step_time = millis();
// increment or decrement the step number,
// depending on direction:
if (this->direction == 1)
{
this->step_number++;
if (this->step_number == this->number_of_steps)
{
this->step_number = 0;
}
}
else
{
if (this->step_number == 0)
{
this->step_number = this->number_of_steps;
}
this->step_number--;
}
// decrement the steps left:
this->steps_left--;
// step the motor to step number 0, 1, 2, or 3:
stepMotor(this->step_number % 4);
}
}
}
/*
* Moves the motor forward or backwards.
*/
void StepperMulti::stepMotor(int thisStep)
{
if (this->pin_count == 2) {
switch (thisStep) {
case 0: /* 01 */
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
break;
case 1: /* 11 */
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, HIGH);
break;
case 2: /* 10 */
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
break;
case 3: /* 00 */
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, LOW);
break;
}
}
if (this->pin_count == 4) {
switch (thisStep) {
case 0: // 1010
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
digitalWrite(motor_pin_3, HIGH);
digitalWrite(motor_pin_4, LOW);
break;
case 1: // 0110
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
digitalWrite(motor_pin_3, HIGH);
digitalWrite(motor_pin_4, LOW);
break;
case 2: //0101
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
digitalWrite(motor_pin_3, LOW);
digitalWrite(motor_pin_4, HIGH);
break;
case 3: //1001
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
digitalWrite(motor_pin_3, LOW);
digitalWrite(motor_pin_4, HIGH);
break;
}
}
}
3. 테스트 하기
설마 여기까지 왔는데 라이브러리에 추가하는 것을 모르시는 건 아니겠죠?
모르시면 이전 글을 참고하시면 됩니다.
(참고 : [Arduino] 라이브러리 작성하기)
테스트용 프로그램을 만듭니다.
여기서 중요한 것은 'loop()'안에서 '.moveStep()'를 호출해야 합니다.
그 외에는 기존 'Stepper.h'와 비슷합니다.
github - StepperMulti.cpp
#include <StepperMulti.h>
StepperMulti stepper(200, 8, 9, 10, 11);
StepperMulti stepper2(200, 4, 5, 6, 7);
void setup()
{
Serial.begin(9600);
stepper.setSpeed(30);
stepper2.setSpeed(60);
}
void loop()
{
if (Serial.available())
{
Serial.read();
stepper.setStep(200);
stepper2.setStep(200);
}
stepper.moveStep();
stepper2.moveStep();
}
이제 시리얼 모니터를 열고 데이터를 날려 봅시다.
잘 보시면 각각 모터가 속도가 다른걸 알 수 있습니다.
기존의 'Stepper.h'를 이용했다면 모터 한 개가 동작을 끝내야 다음 모터가 동작할 수 있죠.
하지만 우리가 개조한 'StepperMulti.h'는 그렇지 않습니다.
이전 글에서처럼 led를 연결해도 마치 안켜진 것 처럼 켜졌다 꺼집니다.
마무리
완성된 샘플 : Github dang-gun - Arduino_StepperMulti/StepperMulti/
이렇게 만든 라이브러리를 다운 받으세요 ㅎㅎㅎ
아두이노 폴더 밑에 'libraries'에 압축 푸시거나 '라이브러리 추가(Add Library...)'로 추가하시면 됩니다.
(참고 : [Arduino] 라이브러리 작성하기 )
여전히 왜 이런 자료를 못 찾은 것인지는 의문이지만 원하는 데로 작동하니 기분은 좋네요.
저번에도 말했다싶이 이렇게 수정한 코드는 'loop()'의 딜레이에 의해 잘못된 동작을 할 여지가 있습니다.
그건 각자 알아서 처리하셔야 합니다 ㅎㅎㅎ