极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1694|回复: 0

机翻Easydriver让步进器运行的示例代码和项目(2)

[复制链接]
发表于 2019-5-5 21:36:00 | 显示全部楼层 |阅读模式
本帖最后由 方恨少 于 2019-5-5 21:39 编辑

例1.8:使用操纵杆对两个电机进行精细速度控制
这个例子是由RA Stephenson写的,他恳请我们使用它。他在两个模拟输入上有一个双轴操纵杆,两个EasyDrivers控制着两个步进电机。对模拟输入进行采样,并使用非阻塞记录时间方法来确定何时应发生步进脉冲。他的代码得到了很好的评论,并表明可以在没有复杂库的情况下制作有用的现实生活项目,而只需要仔细跟踪时间。如果您有任何疑问,请与他联系,他的电子邮件地址是theropod@yahoo.com

/*

Written by R. A. Stephenson for prototype Pan/Tilt Alt/Az small telescope or
binocular set pointer application.

Significant advice, and editorial guidance, supplied by Brian Schmalz
(designer of the Easy Driver® bipolar stepper motor driver board).

Test equipment:
Arduino® Nano, 2 - Easy Driver® bipolar stepper motor driver boards,
analog 2 axis joystick and salvage bipolar stepper motors,
which are both 1.8 degree per full step, being driven at 1/8 microstepping (Easy Driver default),
which equates to 200 full steps multiplied by 8 = 1600 pulses for one complete revolution of the motor
left right gearing provided by windshield wiper worm and wheel gears (ratio TBD)
up and down gearing provided by paper shredder square cut reduction set (ratio TBD)
project origin; July, 2018, this edit, Version 5.1, began Jan. 2019

last modified: Jan. 23, 2019


*/
//define Arduino pin assignments
//
#define step_pinx 2 // Arduino Digital output pin #2 is step signal pin for X axis L/R
#define dir_pinx 3 // Arduino Digital output pin #3 is direction control pin for X axis L/R
#define x_pin A0 // Arduino analog input from joystick for X axis L/R
#define y_pin A1 // Arduino analog input from joystick for Y axis U/D
#define step_piny 5 // Arduino Digital output pin #5 is step speed select signal pin for Y axis U/D
#define dir_piny 4 // Arduino Digital output pin #4 is direction control pin for Y axis U/D
//
//declare global variable
//
int xaxisState = LOW; //state of x axis stepper pulse set to ON
int yaxisState = LOW; //state of y axis stepper pulse set to ON
//
int XStepDelay;
int YStepDelay;
//
//
const long xhigh = 180; //interval to pulse X axis at slowest speed, change to fit application
const long xlow = 1; //interval to pulse X axis at highest speed, change to fit application
//
const long yhigh = 180; //interval to pulse Y axis at slowest speed, change to fit application
const long ylow = 2; //interval to pulse Y axis at highest speed, change to fit application
//
//
unsigned long previousMillisX = 0; //
unsigned long previousMillisY = 0; //
//
//
//
void setup() {
  //
  delay(1000); //1 second delay to allow easy drivers to power up
  //
  pinMode(dir_pinx, OUTPUT);
  pinMode(step_pinx, OUTPUT);
  pinMode(dir_piny, OUTPUT);
  pinMode(step_piny, OUTPUT);
  digitalWrite(step_pinx, xaxisState) ; //used in state machine switching for the stepper logic final drive (on or off setting)
  digitalWrite(step_piny, yaxisState) ; //used in state machine switching for the stepper logic final drive (on or off setting)
  //
}
//
void loop() {
  //
  unsigned long currentMillisX = millis(); //
  unsigned long currentMillisY = millis(); //
  //
  //
  int xValue = analogRead(A0); // x axis variable for joystick inputs controlling r/l PIN DEPENDENT!
  int yValue = analogRead(A1); // y axis variable for joystick inputs controlling u/d PIN DEPENDENT!
  //
  //
  if (xValue > 520) {
    //if joystick moved RIGHT out of "null zone" to limit jitter
    digitalWrite(dir_pinx, HIGH); // motor direction signal sent to Easy Driver X axis DIR pin (ON)
    XStepDelay = map(xValue, 520, 1023, xhigh, xlow);
  }
  else if (xValue < 480) {
    //if joystick moved LEFT out of null zone to limit jitter
    digitalWrite(dir_pinx, LOW); // motor direction signal sent to Easy Driver X axis DIR pin (OFF)
    XStepDelay = map(xValue, 0, 480, xlow, xhigh);
  }
  else if (yValue > 520) {
    // if joystick moved DOWN out of null zone to limit jitter
    digitalWrite(dir_piny, HIGH); //motor direction signal to Y axis Easy Driver DIR pin (ON)
    YStepDelay = map(yValue, 530, 1023, yhigh, ylow); //
  }
  else if (yValue < 480) {
    //if joystick moved UP out of null zone to limit jitter
    digitalWrite(dir_piny, LOW); //motor direction signal to Y axis Easy Driver DIR pin (OFF)
    YStepDelay = map(yValue, 0, 480, ylow, yhigh); //
  } else {
    digitalWrite(dir_piny, LOW); //motor direction signal to Y axis Easy Driver DIR pin (OFF)
    digitalWrite(dir_pinx, LOW); // motor direction signal sent to Easy Driver X axis DIR pin (OFF)
    // Setting LOW saves a bit of power
    //could include signal to set Easy Driver(s) in "sleep" mode for geared drivetrains
  }
  //_____________
  // If X axis (L/R) (Azimuth) motor needs to take a step, give it a step for the correct length of time,
  //when that time has expired end the pulse signal
  if ((xValue >= 520 || xValue <= 480) && ((unsigned long)(currentMillisX - previousMillisX) >= XStepDelay)) {
    //if the joystick X axis values goes outside the null zone calculate
    //how long it's been against those joystick values and control step pulse length
    xaxisState = !xaxisState; // logic switch between states of step pulse pin on/off
    digitalWrite (step_pinx, xaxisState); //control signal for step pin
    //if the motor pulse is off, turn it on, and if the pulse is on, turn it off
    previousMillisX = currentMillisX; //save the last x channel/axis pulse time on or off
  }
  else if (xValue <= 520 || xValue >= 480){ //if the joystick input isn't out of the null zone
    digitalWrite(step_pinx, LOW); //turn off the step pulse
  }
  //________________
  // If Y axis (U/D) (Altitude) motor needs to take a step, give it a step over the correct length of time,
  //after that time has expired end the pulse signal
  if ((yValue >= 520 || yValue <= 480) && ((unsigned long)(currentMillisY - previousMillisY) >= YStepDelay)){
    //if the joystick Y axis values goes outside the null zone calculate
    //how long it's been against those joystick values and control step pulse length
    yaxisState = !yaxisState; // logic switch between states of step pulse pin on/off
    digitalWrite (step_piny, yaxisState); //control signal for step pin
    //if the motor pulse is off, turn it on, and if the pulse is on, turn it off
    previousMillisY = currentMillisY; //save the last y channel/axis pulse time
  }
  else if (yValue <= 520 || yValue >= 480){ //if the joystick input isn't out of the null zone
    digitalWrite(step_piny, LOW); //turn off the step pulse
    //
    //restart loop
  }
}



示例2:来回移动
如果我们采用示例1,并简单地稍微改变草图,我们可以向前或向后移动一定数量的步骤。像这样:
int Distance = 0;  // Record the number of steps we've taken

void setup() {               
  pinMode(8, OUTPUT);     
  pinMode(9, OUTPUT);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
}

void loop() {
  digitalWrite(9, HIGH);
  delayMicroseconds(100);         
  digitalWrite(9, LOW);
  delayMicroseconds(100);
  Distance = Distance + 1;   // record this step
  
  // Check to see if we are at the end of our move
  if (Distance == 3600)
  {
    // We are! Reverse direction (invert DIR signal)
    if (digitalRead(8) == LOW)
    {
      digitalWrite(8, HIGH);
    }
    else
    {
      digitalWrite(8, LOW);
    }
    // Reset our distance back to zero since we're
    // starting a new move
    Distance = 0;
    // Now pause for half a second
    delay(500);
  }
}

现在使用这个草图,我们在一个方向上移动3600步,暂停一点,然后向另一个方向移动3600步。我相信你现在可以弄清楚如何制作许多不同长度的动作。并且您可以更改每个移动以不同速度发生的步骤之间的延迟。

请注意,就像示例1一样,从代码的角度来看,每个“步”都是电机全步的1/8,因为我们假设在1/8微步模式下使用EasyDriver。如果您使用Big Easy Driver,它的默认值为1/16微步,因此请相应调整您对电机运动的预期。

示例3:使用预先构建的库 - AccelStepper
上述示例无法做到的一件事是处理来自相同Arduino或chipKIT的多个步进器。而且,加速和减速也是困难的。其他人遇到了这个问题,所以现在我们可以下载并安装到Arduino IDE或MPIDE中的库来解决这些问题。

从此页面下载AccelStepper库的zip文件。解压缩下载的文件,并将AccelStepper放入Arduino安装目录中的libraries文件夹中。请注意,对于MPIDE(chipKIT)用户,您需要将AccelStepper文件夹复制到顶层的libraries文件夹以及\ hardware \ pic32 \ libraries中,以便AVR和PIC32端都可以使用它。

使用示例1中的相同硬件,重新启动IDE,然后输入以下草图:
#include <AccelStepper.h>

// Define a stepper and the pins it will use
AccelStepper stepper(AccelStepper:RIVER, 9, 8);

int pos = 3600;

void setup()
{  
  stepper.setMaxSpeed(3000);
  stepper.setAcceleration(1000);
}

void loop()
{
  if (stepper.distanceToGo() == 0)
  {
    delay(500);
    pos = -pos;
    stepper.moveTo(pos);\
  }
  stepper.run();
}


此代码与示例2基本相同,但通过AccelStepper库使用加速/减速,并运行两倍的步骤。(感谢Duffy先生指出这个重要事实!)它运行两倍步骤的原因是因为我们做“pos = -pos”来保持简短。这意味着它将从0到3600,然后从3600到-3600(这是7200步)。

示例4:运行多个步进电机
AccelStepper库的一大优点是,您可以根据需要运行尽可能多的步进电机,同时只需制作更多的AccelStepper对象。现在,如果你试图以太快的速度运行它们,步骤将不会顺利,所以你必须小心不要过多地加载Arduino。chipKIT没有这个问题,因为它比Arduino快得多。

在此图中,我们现在有两个Easy Drivers和两个步进电机。我们只需要Arduino的另外两个引脚来添加第二个电机。
Example4_bb.png
此示例的代码如下所示:
#include <AccelStepper.h>

// Define two steppers and the pins they will use
AccelStepper stepper1(AccelStepper:RIVER, 9, 8);
AccelStepper stepper2(AccelStepper:RIVER, 7, 6);

int pos1 = 3600;
int pos2 = 5678;

void setup()
{  
  stepper1.setMaxSpeed(3000);
  stepper1.setAcceleration(1000);
  stepper2.setMaxSpeed(2000);
  stepper2.setAcceleration(800);
}

void loop()
{
  if (stepper1.distanceToGo() == 0)
  {
     pos1 = -pos1;
    stepper1.moveTo(pos1);
  }
  if (stepper2.distanceToGo() == 0)
  {
    pos2 = -pos2;
    stepper2.moveTo(pos2);
  }
  stepper1.run();
  stepper2.run();
}
如果运行此代码,您可能会发现加速和减速并不像单个电机那样平稳(在Arduino上 - 再次,chipKIT上不会出现此问题) - 这是因为我们的两个最大速度(3000和1000)处理器处理它们的能力相当高。一种解决方案是降低最高速度,然后从1/8微步进切换到1/4,半步或全步模式。如果操作正确,您将看到相同的轴旋转速度,但CPU负载较少(因为您不会产生每秒的步数。)

您可以看到,对于此示例,我只是复制并粘贴了示例3中的代码,并创建了两个位置和两个步进器。这个示例代码非常简单,并不是很有用,但您可以从AccelStepper库中学习现有示例,并阅读不同函数的帮助页面,并获得有关使用步进控件还能做些什么的好主意。
例5:改变电动机速度
有时您需要实时控制步进电机的速度。比如说,你正在为你的望远镜做一个装载。你得到一个非常漂亮的齿轮式步进电机,你可以得到一个16x微步进的Big Easy Driver,然后将它们连接起来。但是你如何控制步进器的速度以使其与天空中恒星的速度相匹配?

一种简单的方法是使用电位计,产生可以控制的模拟电压输出。草图代码可以使用analogRead()命令读取此模拟值。然后我们可以使用该值来改变我们移动电机的速度。

这是这个概念的一个简单例子。我们在单步进电机电路中增加了一个电位器和三个按钮。按下开关S1将使电动机向一个方向转动,S3将使其向另一个方向转动,S2将使电动机停止。然后,您可以使用电位计在踩踏时调整电机的准确速度。您可能希望在应用程序的代码中调整MAX_SPEED和MIN_SPEED值 - 根据您的特定设置,您可能希望底池的速度范围与我们在此处编写的速度范围不同。

为了展示这些电路的外观,我们还提供了一个带有chipKIT Uno32板的图表版本。(当然,任何Arduino或chipKIT板都适用于这些示例中的任何一个.Uno和Uno32很容易绘制。)
ExamplePotMotion_bb.png ExamplePotMotionUno32_bb.png


此示例的代码如下所示:
// Example5 code for Brian Schmalz's Easy Driver Example page
// http://www.schmalzhaus.com/EasyDriver/EasyDriverExamples.html

#include <AccelStepper.h>

// Define the stepper and the pins it will use
AccelStepper stepper1(AccelStepper:RIVER, 9, 8);

// Define our three input button pins
#define  LEFT_PIN  4
#define  STOP_PIN  3
#define  RIGHT_PIN 2

// Define our analog pot input pin
#define  SPEED_PIN 0

// Define our maximum and minimum speed in steps per second (scale pot to these)
#define  MAX_SPEED 500
#define  MIN_SPEED 0.1

void setup() {
  // The only AccelStepper value we have to set here is the max speeed, which is higher than we'll ever go
  stepper1.setMaxSpeed(10000.0);
  
  // Set up the three button inputs, with pullups
  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
}

void loop() {
  static float current_speed = 0.0;         // Holds current motor speed in steps/second
  static int analog_read_counter = 1000;    // Counts down to 0 to fire analog read
  static char sign = 0;                     // Holds -1, 1 or 0 to turn the motor on/off and control direction
  static int analog_value = 0;              // Holds raw analog value.
  
  // If a switch is pushed down (low), set the sign value appropriately
  if (digitalRead(LEFT_PIN) == 0) {
    sign = 1;
  }
  else if (digitalRead(RIGHT_PIN) == 0) {   
    sign = -1;
  }
  else if (digitalRead(STOP_PIN) == 0) {
    sign = 0;
  }

  // We only want to read the pot every so often (because it takes a long time we don't
  // want to do it every time through the main loop).  
  if (analog_read_counter > 0) {
    analog_read_counter--;
  }
  else {
    analog_read_counter = 3000;
    // Now read the pot (from 0 to 1023)
    analog_value = analogRead(SPEED_PIN);
    // Give the stepper a chance to step if it needs to
    stepper1.runSpeed();
    //  And scale the pot's value from min to max speeds
    current_speed = sign * (((analog_value/1023.0) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED);
    // Update the stepper to run at this new speed
    stepper1.setSpeed(current_speed);
  }

  // This will run the stepper at a constant speed
  stepper1.runSpeed();
}
关于这个示例代码的一些解释:因为读取模拟值需要(相对)一段时间,并且在此期间我们无法更新步进电机的位置(只发生在runSpeed()调用中)我们只抓取通过主循环每3000次获得一个新的模拟值。我们通过使用一个名为analog_read_counter的计数器来做到这一点,并且每次通过循环递减它直到它变为零。然后我们用3000重新加载它,并执行模拟转换。

我们还在模拟转换和将结果缩放到MAX_SPEED和MIN_SPEED所需的数学之间插入了一个runSpeed()调用。这是因为数学也需要(相对)长时间,因此我们希望步进器有机会在这些时间密集型操作之间步进(如果需要)。

您可以调整MIN_SPEED和MAX_SPEED的值,以便根据需要设置速度范围。请注意,analogRead()调用只能返回1024个可能的值,因此电机只能承受很多离散速度。

对于这个例子(因为我们希望它只是一个固定的速度),我们没有使用正常的AccelStepper run()调用,而是使用runSpeed()调用。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊 ( 浙ICP备09023225号 )

GMT+8, 2020-1-25 02:32 , Processed in 0.049017 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表